Backups to SMB no longer crawl; add occ backup:cancel
Each 30-second step opened the location again and read its list of index files. Over SMB that cost a request per file, so on a large repository opening took longer than the step: each step stored one 4 MiB pack and the list grew further. A first backup of nc01 to a Storage Box over SMB managed 12 GiB in 35 hours. occ backup:run and system cron now keep the repository open for the whole run (reopened when another process took a step or the location was edited), opening no longer counts towards the step's time, and SMB folders are listed in one request. occ backup:cancel stops a running backup: no new step starts and the step in progress finishes first. A run of an earlier version, which does not hold back its next step, is marked cancelled after two minutes unless it is recording its snapshot.
This commit is contained in:
@@ -10,6 +10,8 @@ use OCA\NgBackup\Tests\Unit\TempDirTrait;
|
||||
use OCP\Files\Storage\IStorage;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
require_once __DIR__ . '/../../stubs/SmbStorage.php';
|
||||
|
||||
/** On a remote storage every is_dir() is a round trip: listing index/ must not cost one per pack. */
|
||||
class StorageBackendListTest extends TestCase {
|
||||
use TempDirTrait;
|
||||
@@ -54,4 +56,58 @@ class StorageBackendListTest extends TestCase {
|
||||
$this->expectException(BackendException::class);
|
||||
(new StorageBackend($storage, 'repo'))->listFiles('catalog');
|
||||
}
|
||||
public function testSmbFoldersAreListedInOneRequest(): void {
|
||||
$calls = new \ArrayObject();
|
||||
$share = new class($calls) {
|
||||
public function __construct(private \ArrayObject $calls) {
|
||||
}
|
||||
|
||||
public function dir(string $path): array {
|
||||
$this->calls[] = "dir $path";
|
||||
return array_map(fn (string $n) => new class($n, $this->calls) {
|
||||
public function __construct(private string $n, private \ArrayObject $calls) {
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->n;
|
||||
}
|
||||
|
||||
public function isHidden(): bool {
|
||||
$this->calls[] = "attributes {$this->n}";
|
||||
return $this->n === 'desktop.ini';
|
||||
}
|
||||
}, ['b1', 'a2', '.hidden', 'desktop.ini', 'c3.part-0123abcd']);
|
||||
}
|
||||
};
|
||||
$storage = $this->getMockBuilder(\OCA\Files_External\Lib\Storage\SMB::class)->disableOriginalConstructor()->getMock();
|
||||
(fn () => $this->share = $share)->call($storage);
|
||||
$storage->method('instanceOfStorage')->willReturn(false);
|
||||
$storage->method('is_dir')->willReturn(true);
|
||||
$storage->expects($this->never())->method('opendir');
|
||||
|
||||
$this->assertSame(['index/a2', 'index/b1'], (new StorageBackend($storage, 'repo'))->listFiles('index'));
|
||||
$this->assertSame(['dir repo/index', 'attributes desktop.ini', 'attributes c3.part-0123abcd'], $calls->getArrayCopy(),
|
||||
'only names a repository does not use cost a request');
|
||||
}
|
||||
|
||||
public function testSmbWithAccessControlListsIsListedTheUsualWay(): void {
|
||||
$dir = $this->tempDir();
|
||||
touch("$dir/a1");
|
||||
$storage = $this->getMockBuilder(\OCA\Files_External\Lib\Storage\SMB::class)->disableOriginalConstructor()->getMock();
|
||||
(fn () => $this->checkAcl = true)->call($storage);
|
||||
$storage->method('instanceOfStorage')->willReturn(false);
|
||||
$storage->method('is_dir')->willReturn(true);
|
||||
$storage->expects($this->once())->method('opendir')->willReturnCallback(fn () => opendir($dir));
|
||||
$this->assertSame(['index/a1'], (new StorageBackend($storage, 'repo'))->listFiles('index'));
|
||||
}
|
||||
|
||||
public function testSmbFallsBackToTheUsualListing(): void {
|
||||
$dir = $this->tempDir();
|
||||
touch("$dir/a1");
|
||||
$storage = $this->getMockBuilder(\OCA\Files_External\Lib\Storage\SMB::class)->disableOriginalConstructor()->getMock(); // no share
|
||||
$storage->method('instanceOfStorage')->willReturn(false);
|
||||
$storage->method('is_dir')->willReturn(true);
|
||||
$storage->method('opendir')->willReturnCallback(fn () => opendir($dir));
|
||||
$this->assertSame(['index/a1'], (new StorageBackend($storage, 'repo'))->listFiles('index'));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user