Verify: retry storage exceptions only, let programming errors surface

Catch \Exception (storage adapters throw their own exception types) instead
of \Throwable, so an \Error such as a TypeError is not retried and reported
as an unreadable blob.
This commit is contained in:
2026-10-08 01:21:15 +02:00
parent f415bab519
commit 0b50bb6b81
2 changed files with 12 additions and 2 deletions
+1 -1
View File
@@ -840,7 +840,7 @@ final class Repository {
for ($attempt = 1; $attempt <= self::VERIFY_READ_ATTEMPTS && $encrypted === null; $attempt++) {
try {
$encrypted = $this->backend->getRange('packs/' . substr($pack, 0, 2) . '/' . $pack, $offset, $length);
} catch (\Throwable $e) {
} catch (\Exception $e) { // storage adapters throw their own exceptions; \Error (a bug) is not retried
$stats['readError'] = $e->getMessage();
}
}
+11 -1
View File
@@ -118,6 +118,15 @@ class VerifyTest extends TestCase {
$this->assertNotSame([], $result['unreadable']);
$this->assertSame('connection reset', $result['readError']);
}
public function testDeepVerifyDoesNotHideAProgrammingError(): void {
$repo = Repository::initWithKey($this->backend, $this->keys, $this->keys->wrap('pw'));
$s = $repo->backupDirectory($this->src);
$buggy = new FlakyRangeBackend($this->backend, failures: PHP_INT_MAX, error: new \TypeError('bug'));
$this->expectException(\TypeError::class);
Repository::openWithKey($buggy, $this->keys, null, null)->verify($s['snapshot'], deep: true);
}
}
/** Delegates to a real backend, but getRange() throws for the first $failures calls. */
@@ -125,6 +134,7 @@ final class FlakyRangeBackend implements IBackend {
public function __construct(
private IBackend $inner,
private int $failures,
private ?\Throwable $error = null,
) {
}
@@ -139,7 +149,7 @@ final class FlakyRangeBackend implements IBackend {
public function getRange(string $path, int $offset, int $length): string {
if ($this->failures > 0) {
$this->failures--;
throw new BackendException('connection reset');
throw $this->error ?? new BackendException('connection reset');
}
return $this->inner->getRange($path, $offset, $length);
}