Skip to content

Commit 6b3c703

Browse files
authored
Merge pull request #29735 from nextcloud/background-scan-one-by-one
find users for background scan one by one
2 parents 7acb438 + e95745c commit 6b3c703

12 files changed

Lines changed: 64 additions & 64 deletions

File tree

apps/files/lib/BackgroundJob/ScanFiles.php

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
* along with this program. If not, see <http://www.gnu.org/licenses/>
2222
*
2323
*/
24+
2425
namespace OCA\Files\BackgroundJob;
2526

2627
use OC\Files\Utils\Scanner;
@@ -29,7 +30,6 @@
2930
use OCP\IConfig;
3031
use OCP\IDBConnection;
3132
use OCP\ILogger;
32-
use OCP\IUserManager;
3333

3434
/**
3535
* Class ScanFiles is a background job used to run the file scanner over the user
@@ -40,8 +40,6 @@
4040
class ScanFiles extends \OC\BackgroundJob\TimedJob {
4141
/** @var IConfig */
4242
private $config;
43-
/** @var IUserManager */
44-
private $userManager;
4543
/** @var IEventDispatcher */
4644
private $dispatcher;
4745
/** @var ILogger */
@@ -53,14 +51,12 @@ class ScanFiles extends \OC\BackgroundJob\TimedJob {
5351

5452
/**
5553
* @param IConfig $config
56-
* @param IUserManager $userManager
5754
* @param IEventDispatcher $dispatcher
5855
* @param ILogger $logger
5956
* @param IDBConnection $connection
6057
*/
6158
public function __construct(
6259
IConfig $config,
63-
IUserManager $userManager,
6460
IEventDispatcher $dispatcher,
6561
ILogger $logger,
6662
IDBConnection $connection
@@ -69,7 +65,6 @@ public function __construct(
6965
$this->setInterval(60 * 10);
7066

7167
$this->config = $config;
72-
$this->userManager = $userManager;
7368
$this->dispatcher = $dispatcher;
7469
$this->logger = $logger;
7570
$this->connection = $connection;
@@ -81,10 +76,10 @@ public function __construct(
8176
protected function runScanner(string $user) {
8277
try {
8378
$scanner = new Scanner(
84-
$user,
85-
null,
86-
$this->dispatcher,
87-
$this->logger
79+
$user,
80+
null,
81+
$this->dispatcher,
82+
$this->logger
8883
);
8984
$scanner->backgroundScan('');
9085
} catch (\Exception $e) {
@@ -94,20 +89,20 @@ protected function runScanner(string $user) {
9489
}
9590

9691
/**
97-
* Find all storages which have unindexed files and return a user for each
92+
* Find a storage which have unindexed files and return a user with access to the storage
9893
*
99-
* @return string[]
94+
* @return string|false
10095
*/
101-
private function getUsersToScan(): array {
96+
private function getUserToScan() {
10297
$query = $this->connection->getQueryBuilder();
103-
$query->select($query->func()->max('user_id'))
98+
$query->select('user_id')
10499
->from('filecache', 'f')
105100
->innerJoin('f', 'mounts', 'm', $query->expr()->eq('storage_id', 'storage'))
106101
->where($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
107-
->groupBy('storage_id')
108-
->setMaxResults(self::USERS_PER_SESSION);
102+
->andWhere($query->expr()->gt('parent', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
103+
->setMaxResults(1);
109104

110-
return $query->execute()->fetchAll(\PDO::FETCH_COLUMN);
105+
return $query->execute()->fetchOne();
111106
}
112107

113108
/**
@@ -119,10 +114,18 @@ protected function run($argument) {
119114
return;
120115
}
121116

122-
$users = $this->getUsersToScan();
123-
124-
foreach ($users as $user) {
117+
$usersScanned = 0;
118+
$lastUser = '';
119+
$user = $this->getUserToScan();
120+
while ($user && $usersScanned < self::USERS_PER_SESSION && $lastUser !== $user) {
125121
$this->runScanner($user);
122+
$lastUser = $user;
123+
$user = $this->getUserToScan();
124+
$usersScanned += 1;
125+
}
126+
127+
if ($lastUser === $user) {
128+
$this->logger->warning("User $user still has unscanned files after running background scan, background scan might be stopped prematurely");
126129
}
127130
}
128131
}

apps/files/tests/BackgroundJob/ScanFilesTest.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
use OCP\IConfig;
3131
use OCP\ILogger;
3232
use OCP\IUser;
33-
use OCP\IUserManager;
3433
use Test\TestCase;
3534
use Test\Traits\MountProviderTrait;
3635
use Test\Traits\UserTrait;
@@ -54,7 +53,6 @@ protected function setUp(): void {
5453
parent::setUp();
5554

5655
$config = $this->createMock(IConfig::class);
57-
$userManager = $this->createMock(IUserManager::class);
5856
$dispatcher = $this->createMock(IEventDispatcher::class);
5957
$logger = $this->createMock(ILogger::class);
6058
$connection = \OC::$server->getDatabaseConnection();
@@ -63,7 +61,6 @@ protected function setUp(): void {
6361
$this->scanFiles = $this->getMockBuilder('\OCA\Files\BackgroundJob\ScanFiles')
6462
->setConstructorArgs([
6563
$config,
66-
$userManager,
6764
$dispatcher,
6865
$logger,
6966
$connection,

apps/files_sharing/lib/ISharedStorage.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,7 @@
2222
*/
2323
namespace OCA\Files_Sharing;
2424

25-
interface ISharedStorage {
25+
use OCP\Files\Storage\IStorage;
26+
27+
interface ISharedStorage extends IStorage {
2628
}

apps/files_sharing/lib/Scanner.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
* along with this program. If not, see <http://www.gnu.org/licenses/>
2323
*
2424
*/
25+
2526
namespace OCA\Files_Sharing;
2627

2728
use OC\Files\ObjectStore\NoopScanner;

apps/workflowengine/lib/Check/FileSystemTags.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,8 @@ protected function getFileIds(ICache $cache, $path, $isExternalStorage) {
135135
// TODO: Fix caching inside group folders
136136
// Do not cache file ids inside group folders because multiple file ids might be mapped to
137137
// the same combination of cache id + path.
138-
$shouldCacheFileIds = !$this->storage
139-
->instanceOfStorage(\OCA\GroupFolders\Mount\GroupFolderStorage::class);
138+
/** @psalm-suppress InvalidArgument */
139+
$shouldCacheFileIds = !$this->storage->instanceOfStorage(\OCA\GroupFolders\Mount\GroupFolderStorage::class);
140140
$cacheId = $cache->getNumericStorageId();
141141
if ($shouldCacheFileIds && isset($this->fileIds[$cacheId][$path])) {
142142
return $this->fileIds[$cacheId][$path];

lib/private/Files/Cache/Scanner.php

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
use Doctrine\DBAL\Exception;
3939
use OC\Files\Filesystem;
40+
use OC\Files\Storage\Wrapper\Jail;
4041
use OC\Files\Storage\Wrapper\Encoding;
4142
use OC\Hooks\BasicEmitter;
4243
use OCP\Files\Cache\IScanner;
@@ -509,19 +510,31 @@ public static function isPartialFile($file) {
509510
* walk over any folders that are not fully scanned yet and scan them
510511
*/
511512
public function backgroundScan() {
512-
if (!$this->cache->inCache('')) {
513-
$this->runBackgroundScanJob(function () {
514-
$this->scan('', self::SCAN_RECURSIVE, self::REUSE_ETAG);
515-
}, '');
513+
if ($this->storage->instanceOfStorage(Jail::class)) {
514+
// for jail storage wrappers (shares, groupfolders) we run the background scan on the source storage
515+
// this is mainly done because the jail wrapper doesn't implement `getIncomplete` (because it would be inefficient).
516+
//
517+
// Running the scan on the source storage might scan more than "needed", but the unscanned files outside the jail will
518+
// have to be scanned at some point anyway.
519+
$unJailedScanner = $this->storage->getUnjailedStorage()->getScanner();
520+
$unJailedScanner->backgroundScan();
516521
} else {
517-
$lastPath = null;
518-
while (($path = $this->cache->getIncomplete()) !== false && $path !== $lastPath) {
519-
$this->runBackgroundScanJob(function () use ($path) {
520-
$this->scan($path, self::SCAN_RECURSIVE_INCOMPLETE, self::REUSE_ETAG | self::REUSE_SIZE);
521-
}, $path);
522-
// FIXME: this won't proceed with the next item, needs revamping of getIncomplete()
523-
// to make this possible
524-
$lastPath = $path;
522+
if (!$this->cache->inCache('')) {
523+
// if the storage isn't in the cache yet, just scan the root completely
524+
$this->runBackgroundScanJob(function () {
525+
$this->scan('', self::SCAN_RECURSIVE, self::REUSE_ETAG);
526+
}, '');
527+
} else {
528+
$lastPath = null;
529+
// find any path marked as unscanned and run the scanner until no more paths are unscanned (or we get stuck)
530+
while (($path = $this->cache->getIncomplete()) !== false && $path !== $lastPath) {
531+
$this->runBackgroundScanJob(function () use ($path) {
532+
$this->scan($path, self::SCAN_RECURSIVE_INCOMPLETE, self::REUSE_ETAG | self::REUSE_SIZE);
533+
}, $path);
534+
// FIXME: this won't proceed with the next item, needs revamping of getIncomplete()
535+
// to make this possible
536+
$lastPath = $path;
537+
}
525538
}
526539
}
527540
}

lib/private/Files/Utils/Scanner.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,6 @@ public function backgroundScan($dir) {
166166
continue;
167167
}
168168

169-
// don't scan received local shares, these can be scanned when scanning the owner's storage
170-
if ($storage->instanceOfStorage(SharedStorage::class)) {
171-
continue;
172-
}
173169
$scanner = $storage->getScanner();
174170
$this->attachListener($mount);
175171

lib/public/Files/IHomeStorage.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626

2727
namespace OCP\Files;
2828

29+
use OCP\Files\Storage\IStorage;
30+
2931
/**
3032
* Interface IHomeStorage
3133
*
3234
* @since 7.0.0
3335
*/
34-
interface IHomeStorage {
36+
interface IHomeStorage extends IStorage {
3537
}

lib/public/Files/Storage.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,12 @@ public function isLocal();
368368
/**
369369
* Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
370370
*
371+
* @template T of IStorage
371372
* @param string $class
373+
* @psalm-param class-string<T> $class
372374
* @return bool
373375
* @since 7.0.0
376+
* @psalm-assert-if-true T $this
374377
*/
375378
public function instanceOfStorage($class);
376379

lib/public/Files/Storage/IDisableEncryptionStorage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@
2828
*
2929
* @since 16.0.0
3030
*/
31-
interface IDisableEncryptionStorage {
31+
interface IDisableEncryptionStorage extends IStorage {
3232
}

0 commit comments

Comments
 (0)