Batched SFTP reads: no more batches on a connection whose server did not confirm a close
This commit is contained in:
@@ -55,6 +55,13 @@ final class SftpReadStream {
|
||||
*/
|
||||
private static ?\WeakMap $chunks = null;
|
||||
private static int $requestId = 0x4e470000;
|
||||
/**
|
||||
* Connections whose server did not confirm closing a batch's files: readMany() is not used on
|
||||
* them again, so handles cannot pile up.
|
||||
*
|
||||
* @var \WeakMap<object, true>|null
|
||||
*/
|
||||
private static ?\WeakMap $noBatches = null;
|
||||
/** Streams in a row that failed; at MAX_FAILURES only the storage's own stream is used. */
|
||||
private static int $failures = 0;
|
||||
public const MAX_FAILURES = 3;
|
||||
@@ -182,7 +189,7 @@ final class SftpReadStream {
|
||||
self::$failures++;
|
||||
return null;
|
||||
}
|
||||
if ($sftp === null) {
|
||||
if ($sftp === null || self::$noBatches?->offsetExists($sftp) === true) {
|
||||
return null;
|
||||
}
|
||||
$result = array_fill_keys($paths, null);
|
||||
@@ -200,8 +207,9 @@ final class SftpReadStream {
|
||||
$result[$path] = $contents;
|
||||
}
|
||||
if (!$closed) {
|
||||
// The server did not confirm closing these files: no more batches, so handles
|
||||
// cannot pile up. The rest is read the usual way, one at a time.
|
||||
// The server did not confirm closing these files: no more batches on this
|
||||
// connection, so handles cannot pile up. The rest is read the usual way.
|
||||
self::noMoreBatches($sftp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -209,6 +217,11 @@ final class SftpReadStream {
|
||||
return $result;
|
||||
}
|
||||
|
||||
private static function noMoreBatches(object $sftp): void {
|
||||
self::$noBatches ??= new \WeakMap();
|
||||
self::$noBatches[$sftp] = true;
|
||||
}
|
||||
|
||||
private static function disconnect(object $sftp): void {
|
||||
try {
|
||||
if (is_callable([$sftp, 'disconnect'])) {
|
||||
@@ -306,6 +319,7 @@ final class SftpReadStream {
|
||||
/** Test hook: forget the learned READ sizes and earlier failures. */
|
||||
public static function reset(): void {
|
||||
self::$chunks = null;
|
||||
self::$noBatches = null;
|
||||
self::$failures = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -286,6 +286,8 @@ class SftpReadStreamTest extends TestCase {
|
||||
$this->assertSame('file 31', $got['31']);
|
||||
$this->assertNull($got['32'], 'no second batch after unconfirmed closes');
|
||||
$this->assertCount(SftpReadStream::BATCH, array_filter($got, fn ($v) => $v !== null));
|
||||
// Nor on a later call over the same connection.
|
||||
$this->assertNull(SftpReadStream::readMany(new FakeSftpStorage($sftp), ['0']));
|
||||
}
|
||||
|
||||
public function testReadManyLeavesAMalformedAnswerToTheCaller(): void {
|
||||
|
||||
Reference in New Issue
Block a user