Full restore: an empty listing never empties the index; read a failed pack once more; recognise and replace SFTP connections a Storage Box dropped quietly; version 0.9.11
This commit is contained in:
@@ -184,6 +184,38 @@ class SftpRangeFetcherTest extends TestCase {
|
||||
$this->assertSame(array_fill(0, 10, null), $result);
|
||||
}
|
||||
|
||||
public function testABrokenConnectionIsReplacedAndTheRestIsReadAhead(): void {
|
||||
$connections = $this->connections(1, [['loseAt' => 1048576]]);
|
||||
$made = [];
|
||||
$connect = function () use (&$made): object {
|
||||
$made[] = $c = new FakeSftpConnection($this->files);
|
||||
return SftpReadStream::connection(new FakeSftpStorage($c));
|
||||
};
|
||||
$ranges = $this->ranges();
|
||||
|
||||
$result = self::collect(SftpRangeFetcher::fetch(self::opened($connections), '/home/', $ranges, $this->tempDir(), $connect));
|
||||
|
||||
$expected = $this->expected($ranges);
|
||||
$expected[0] = null; // the range it was reading when it broke: read the usual way
|
||||
$this->assertSame($expected, $result);
|
||||
$this->assertCount(1, $made);
|
||||
$this->assertTrue($made[0]->disconnected, 'the new one ends with the fetcher too');
|
||||
}
|
||||
|
||||
public function testConnectionsThatKeepBreakingAreReplacedOnlySoOften(): void {
|
||||
$made = 0;
|
||||
$connect = function () use (&$made): object {
|
||||
$made++;
|
||||
return SftpReadStream::connection(new FakeSftpStorage(new FakeSftpConnection($this->files, loseAt: 0)));
|
||||
};
|
||||
$ranges = array_merge($this->ranges(), $this->ranges(), $this->ranges());
|
||||
|
||||
$result = self::collect(SftpRangeFetcher::fetch(self::opened($this->connections(1, [['loseAt' => 0]])), '/home/', $ranges, $this->tempDir(), $connect));
|
||||
|
||||
$this->assertSame(array_fill(0, count($ranges), null), $result);
|
||||
$this->assertSame(SftpRangeFetcher::MAX_RECONNECTS, $made);
|
||||
}
|
||||
|
||||
public function testStoppingHalfwayEndsTheConnectionsAndLeavesNothingBehind(): void {
|
||||
$connections = $this->connections(2);
|
||||
$spool = $this->tempDir();
|
||||
|
||||
@@ -77,6 +77,24 @@ class StorageBackendReconnectTest extends TestCase {
|
||||
$this->assertSame(1, $backend->reconnects);
|
||||
}
|
||||
|
||||
public function testASilentlyDroppedConnectionIsReplacedAfterAPause(): void {
|
||||
$backend = $this->backend();
|
||||
$this->connections[0]->dropped = true;
|
||||
$this->connections[0]->silent = true; // phpseclib still says connected
|
||||
self::idle($backend);
|
||||
$this->assertTrue($backend->exists('config'));
|
||||
$this->assertSame(1, $backend->reconnects, 'the check failed, so the connection was replaced');
|
||||
}
|
||||
|
||||
public function testAFailureOnASilentlyDroppedConnectionIsRepeated(): void {
|
||||
$backend = $this->backend();
|
||||
$this->connections[0]->dropped = true;
|
||||
$this->connections[0]->silent = true;
|
||||
$fh = $backend->get('config');
|
||||
$this->assertSame('config', stream_get_contents($fh));
|
||||
$this->assertSame(1, $backend->reconnects);
|
||||
}
|
||||
|
||||
public function testAMissingFileOnALiveConnectionIsJustMissing(): void {
|
||||
$backend = $this->backend();
|
||||
$this->assertFalse($backend->exists('nothing'));
|
||||
@@ -121,6 +139,8 @@ interface SftpLikeStorage extends IStorage {
|
||||
/** The public phpseclib calls the backend uses to tell a dead connection. */
|
||||
final class ReconnectFakeSftp {
|
||||
public bool $dropped = false;
|
||||
/** Dropped without phpseclib noticing (as a Hetzner Storage Box does): it still says connected. */
|
||||
public bool $silent = false;
|
||||
private bool $connected = true;
|
||||
public int $requests = 0;
|
||||
|
||||
@@ -139,6 +159,9 @@ final class ReconnectFakeSftp {
|
||||
public function request(): bool {
|
||||
$this->requests++;
|
||||
if ($this->dropped) {
|
||||
if ($this->silent) {
|
||||
return false;
|
||||
}
|
||||
$this->connected = false;
|
||||
}
|
||||
return $this->connected;
|
||||
|
||||
Reference in New Issue
Block a user