docs(cache): add comprehensive cache configuration and permission handling guides
- Introduce `cache-configuration.md` for detailed instructions on cache setup, permission troubleshooting, and best practices. - Add `cache-permissions-quick-fix.md` for concise resolutions to common permission errors. - Include a detailed `FILECACHE_PERMISSION_FIX_PLAN.md` outlining solutions for permission-related issues. - Enhance `docker-entrypoint.sh` with permission fixes for multi-user caches. - Update `Makefile` with cache clear commands for local and staging environments. - Improve `FileCache` for graceful degradation on permission errors, ensuring reliability under multi-user scenarios.
This commit is contained in:
@@ -190,8 +190,16 @@ final readonly class FileCache implements CacheDriver, Scannable
|
||||
// File expired - try to delete it
|
||||
try {
|
||||
$this->fileSystem->delete($fullPath);
|
||||
} catch (\App\Framework\Filesystem\Exceptions\FilePermissionException $e) {
|
||||
// Permission denied - continue (graceful degradation)
|
||||
// Datei wird beim n?chsten Cleanup oder manuell gel?scht
|
||||
continue;
|
||||
} catch (\App\Framework\Filesystem\Exceptions\FileNotFoundException $e) {
|
||||
// File already deleted - continue
|
||||
continue;
|
||||
} catch (\Throwable $e) {
|
||||
// Continue even if deletion fails
|
||||
// Any other error - continue (graceful degradation)
|
||||
continue;
|
||||
}
|
||||
|
||||
continue;
|
||||
@@ -221,10 +229,15 @@ final readonly class FileCache implements CacheDriver, Scannable
|
||||
}
|
||||
|
||||
if ($content === null || $content === '') {
|
||||
// Empty file - try to delete it
|
||||
try {
|
||||
$this->fileSystem->delete($bestFile);
|
||||
} catch (\App\Framework\Filesystem\Exceptions\FilePermissionException $e) {
|
||||
// Permission denied - treat as miss (graceful degradation)
|
||||
} catch (\App\Framework\Filesystem\Exceptions\FileNotFoundException $e) {
|
||||
// File already deleted - treat as miss
|
||||
} catch (\Throwable $e) {
|
||||
// Continue even if deletion fails
|
||||
// Any other error - treat as miss (graceful degradation)
|
||||
}
|
||||
|
||||
return CacheItem::miss($key);
|
||||
@@ -259,8 +272,21 @@ final readonly class FileCache implements CacheDriver, Scannable
|
||||
$ttlSeconds = $item->ttl !== null ? $item->ttl->toCacheSeconds() : null;
|
||||
$expiresAt = $ttlSeconds ? (time() + $ttlSeconds) : null;
|
||||
|
||||
// Delete old cache files for this key (graceful degradation)
|
||||
foreach ($this->getFilesForKey($item->key) as $file) {
|
||||
$this->fileSystem->delete($file);
|
||||
try {
|
||||
$this->fileSystem->delete($file);
|
||||
} catch (\App\Framework\Filesystem\Exceptions\FilePermissionException $e) {
|
||||
// Permission denied - continue (graceful degradation)
|
||||
// Alte Datei bleibt, wird beim n?chsten Cleanup gel?scht
|
||||
continue;
|
||||
} catch (\App\Framework\Filesystem\Exceptions\FileNotFoundException $e) {
|
||||
// File already deleted - continue
|
||||
continue;
|
||||
} catch (\Throwable $e) {
|
||||
// Any other error - continue (graceful degradation)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->getFileName($item->key, $expiresAt);
|
||||
@@ -302,10 +328,25 @@ final readonly class FileCache implements CacheDriver, Scannable
|
||||
$success = true;
|
||||
foreach ($keys as $key) {
|
||||
$result = $this->withKeyLock($key, function () use ($key) {
|
||||
$deletedAny = false;
|
||||
foreach ($this->getFilesForKey($key) as $file) {
|
||||
$this->fileSystem->delete($file);
|
||||
try {
|
||||
$this->fileSystem->delete($file);
|
||||
$deletedAny = true;
|
||||
} catch (\App\Framework\Filesystem\Exceptions\FilePermissionException $e) {
|
||||
// Permission denied - continue (graceful degradation)
|
||||
// Datei bleibt, wird beim n?chsten Cleanup gel?scht
|
||||
continue;
|
||||
} catch (\App\Framework\Filesystem\Exceptions\FileNotFoundException $e) {
|
||||
// File already deleted - continue
|
||||
continue;
|
||||
} catch (\Throwable $e) {
|
||||
// Any other error - continue (graceful degradation)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if at least one file was deleted or no files existed
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -329,14 +370,21 @@ final readonly class FileCache implements CacheDriver, Scannable
|
||||
return str_ends_with($file, '.cache.php');
|
||||
});
|
||||
|
||||
// Delete all cache files
|
||||
// Delete all cache files (graceful degradation)
|
||||
foreach ($cacheFiles as $file) {
|
||||
try {
|
||||
// Convert to absolute path if needed
|
||||
$fullPath = str_starts_with($file, '/') ? $file : self::CACHE_PATH . DIRECTORY_SEPARATOR . $file;
|
||||
$this->fileSystem->delete($fullPath);
|
||||
} catch (\App\Framework\Filesystem\Exceptions\FilePermissionException $e) {
|
||||
// Permission denied - continue with other files (graceful degradation)
|
||||
// Diese Dateien bleiben, werden beim n?chsten Cleanup gel?scht
|
||||
continue;
|
||||
} catch (\App\Framework\Filesystem\Exceptions\FileNotFoundException $e) {
|
||||
// File already deleted - continue
|
||||
continue;
|
||||
} catch (\Throwable $e) {
|
||||
// Continue with other files even if one fails
|
||||
// Any other error - continue with other files (graceful degradation)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,15 +213,18 @@ final readonly class FileStorage implements Storage, AtomicStorage, AppendableSt
|
||||
}
|
||||
|
||||
// Prüfe Directory-Permissions
|
||||
// WICHTIG: Für unlink() braucht man nur Schreibrechte im Verzeichnis, nicht auf der Datei selbst!
|
||||
$dir = dirname($resolvedPath);
|
||||
if (! is_writable($dir)) {
|
||||
throw FilePermissionException::delete($path, 'Directory is not writable: ' . $dir);
|
||||
}
|
||||
|
||||
// Prüfe File-Permissions
|
||||
if (! is_writable($resolvedPath)) {
|
||||
throw FilePermissionException::delete($path, 'File is not writable');
|
||||
}
|
||||
// ENTFERNT: Prüfe File-Permissions
|
||||
// Für unlink() benötigt man nur Schreibrechte im Parent-Verzeichnis, nicht auf der Datei selbst.
|
||||
// Dies ist ein Unix-Standard-Verhalten: unlink() entfernt einen Directory-Eintrag, nicht die Datei selbst.
|
||||
// if (! is_writable($resolvedPath)) {
|
||||
// throw FilePermissionException::delete($path, 'File is not writable');
|
||||
// }
|
||||
|
||||
if (! @unlink($resolvedPath)) {
|
||||
$error = error_get_last();
|
||||
|
||||
Reference in New Issue
Block a user