Restore: follow blobs that another server's cleanup repacked during the restore
This commit is contained in:
@@ -771,16 +771,48 @@ final class Repository {
|
||||
$ranges[] = [$curPath, $curOffset, $curLength];
|
||||
$parts[] = $curParts;
|
||||
}
|
||||
foreach ($this->backend->getRanges($ranges) as $i => $data) {
|
||||
foreach ($parts[$i] as [$id, $at, $length]) {
|
||||
yield $this->decodeBlob($id, substr($data, $at, $length));
|
||||
$done = 0;
|
||||
try {
|
||||
foreach ($this->backend->getRanges($ranges) as $i => $data) {
|
||||
foreach ($parts[$i] as [$id, $at, $length]) {
|
||||
yield $this->decodeBlob($id, substr($data, $at, $length));
|
||||
$done++;
|
||||
}
|
||||
}
|
||||
} catch (BackendException $e) {
|
||||
// A pack moved (see loadBlob()): the rest one blob at a time, from the fresh index.
|
||||
$this->refreshIndex($e);
|
||||
foreach (array_slice($ids, $done) as $id) {
|
||||
yield $this->loadBlob($id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function loadBlob(string $id): string {
|
||||
[$pack, $offset, $length] = $this->index->get($id);
|
||||
return $this->decodeBlob($id, $this->packReader->read($pack, $offset, $length));
|
||||
try {
|
||||
$encrypted = $this->packReader->read($pack, $offset, $length);
|
||||
} catch (BackendException $e) {
|
||||
// Another server's cleanup may have repacked this blob into a new pack since the index
|
||||
// was loaded (a disaster recovery test while the original server still runs). Reload
|
||||
// the index from the location; read again only if the blob has moved.
|
||||
$this->refreshIndex($e);
|
||||
[$moved, $offset, $length] = $this->index->get($id);
|
||||
if ($moved === $pack) {
|
||||
throw $e;
|
||||
}
|
||||
$encrypted = $this->packReader->read($moved, $offset, $length);
|
||||
}
|
||||
return $this->decodeBlob($id, $encrypted);
|
||||
}
|
||||
|
||||
/** Reload the index from the location after a failed pack read; rethrows that failure if it cannot. */
|
||||
private function refreshIndex(BackendException $cause): void {
|
||||
try {
|
||||
$this->index->load();
|
||||
} catch (\Throwable) {
|
||||
throw $cause;
|
||||
}
|
||||
}
|
||||
|
||||
/** Decrypts and checks a blob read from its pack; throws when it is corrupted or tampered with. */
|
||||
|
||||
Reference in New Issue
Block a user