Skip to content

Commit f41c1ff

Browse files
authored
Merge pull request #30056 from nextcloud/backport/29735/stable21
[stable21] find users for background scan one by one
2 parents 26089a7 + d89ce5a commit f41c1ff

File tree

11 files changed

+62
-45
lines changed

11 files changed

+62
-45
lines changed

apps/files/lib/BackgroundJob/ScanFiles.php

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
use OCP\IConfig;
3131
use OCP\IDBConnection;
3232
use OCP\ILogger;
33-
use OCP\IUserManager;
3433

3534
/**
3635
* Class ScanFiles is a background job used to run the file scanner over the user
@@ -41,8 +40,6 @@
4140
class ScanFiles extends \OC\BackgroundJob\TimedJob {
4241
/** @var IConfig */
4342
private $config;
44-
/** @var IUserManager */
45-
private $userManager;
4643
/** @var IEventDispatcher */
4744
private $dispatcher;
4845
/** @var ILogger */
@@ -54,14 +51,12 @@ class ScanFiles extends \OC\BackgroundJob\TimedJob {
5451

5552
/**
5653
* @param IConfig $config
57-
* @param IUserManager $userManager
5854
* @param IEventDispatcher $dispatcher
5955
* @param ILogger $logger
6056
* @param IDBConnection $connection
6157
*/
6258
public function __construct(
6359
IConfig $config,
64-
IUserManager $userManager,
6560
IEventDispatcher $dispatcher,
6661
ILogger $logger,
6762
IDBConnection $connection
@@ -70,7 +65,6 @@ public function __construct(
7065
$this->setInterval(60 * 10);
7166

7267
$this->config = $config;
73-
$this->userManager = $userManager;
7468
$this->dispatcher = $dispatcher;
7569
$this->logger = $logger;
7670
$this->connection = $connection;
@@ -82,10 +76,10 @@ public function __construct(
8276
protected function runScanner(string $user) {
8377
try {
8478
$scanner = new Scanner(
85-
$user,
86-
null,
87-
$this->dispatcher,
88-
$this->logger
79+
$user,
80+
null,
81+
$this->dispatcher,
82+
$this->logger
8983
);
9084
$scanner->backgroundScan('');
9185
} catch (\Exception $e) {
@@ -95,20 +89,20 @@ protected function runScanner(string $user) {
9589
}
9690

9791
/**
98-
* 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
9993
*
100-
* @return string[]
94+
* @return string|false
10195
*/
102-
private function getUsersToScan(): array {
96+
private function getUserToScan() {
10397
$query = $this->connection->getQueryBuilder();
104-
$query->select($query->func()->max('user_id'))
98+
$query->select('user_id')
10599
->from('filecache', 'f')
106100
->innerJoin('f', 'mounts', 'm', $query->expr()->eq('storage_id', 'storage'))
107101
->where($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
108-
->groupBy('storage_id')
109-
->setMaxResults(self::USERS_PER_SESSION);
102+
->andWhere($query->expr()->gt('parent', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)))
103+
->setMaxResults(1);
110104

111-
return $query->execute()->fetchAll(\PDO::FETCH_COLUMN);
105+
return $query->execute()->fetchOne();
112106
}
113107

114108
/**
@@ -120,10 +114,18 @@ protected function run($argument) {
120114
return;
121115
}
122116

123-
$users = $this->getUsersToScan();
124-
125-
foreach ($users as $user) {
117+
$usersScanned = 0;
118+
$lastUser = '';
119+
$user = $this->getUserToScan();
120+
while ($user && $usersScanned < self::USERS_PER_SESSION && $lastUser !== $user) {
126121
$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");
127129
}
128130
}
129131
}

apps/files/tests/BackgroundJob/ScanFilesTest.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
use OCP\IConfig;
3232
use OCP\ILogger;
3333
use OCP\IUser;
34-
use OCP\IUserManager;
3534
use Test\TestCase;
3635
use Test\Traits\MountProviderTrait;
3736
use Test\Traits\UserTrait;
@@ -55,7 +54,6 @@ protected function setUp(): void {
5554
parent::setUp();
5655

5756
$config = $this->createMock(IConfig::class);
58-
$userManager = $this->createMock(IUserManager::class);
5957
$dispatcher = $this->createMock(IEventDispatcher::class);
6058
$logger = $this->createMock(ILogger::class);
6159
$connection = \OC::$server->getDatabaseConnection();
@@ -64,7 +62,6 @@ protected function setUp(): void {
6462
$this->scanFiles = $this->getMockBuilder('\OCA\Files\BackgroundJob\ScanFiles')
6563
->setConstructorArgs([
6664
$config,
67-
$userManager,
6865
$dispatcher,
6966
$logger,
7067
$connection,

apps/files_sharing/lib/ISharedStorage.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,7 @@
2424

2525
namespace OCA\Files_Sharing;
2626

27-
interface ISharedStorage {
27+
use OCP\Files\Storage\IStorage;
28+
29+
interface ISharedStorage extends IStorage {
2830
}

apps/workflowengine/lib/Check/FileSystemTags.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ protected function getFileIds(ICache $cache, $path, $isExternalStorage) {
130130
// TODO: Fix caching inside group folders
131131
// Do not cache file ids inside group folders because multiple file ids might be mapped to
132132
// the same combination of cache id + path.
133-
$shouldCacheFileIds = !$this->storage
134-
->instanceOfStorage(\OCA\GroupFolders\Mount\GroupFolderStorage::class);
133+
/** @psalm-suppress InvalidArgument */
134+
$shouldCacheFileIds = !$this->storage->instanceOfStorage(\OCA\GroupFolders\Mount\GroupFolderStorage::class);
135135
$cacheId = $cache->getNumericStorageId();
136136
if ($shouldCacheFileIds && isset($this->fileIds[$cacheId][$path])) {
137137
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
@@ -38,6 +38,7 @@
3838

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

lib/private/Files/Utils/Scanner.php

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

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

lib/public/Files/IHomeStorage.php

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

3333
namespace OCP\Files;
3434

35+
use OCP\Files\Storage\IStorage;
36+
3537
/**
3638
* Interface IHomeStorage
3739
*
3840
* @since 7.0.0
3941
*/
40-
interface IHomeStorage {
42+
interface IHomeStorage extends IStorage {
4143
}

lib/public/Files/Storage.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,9 +374,12 @@ public function isLocal();
374374
/**
375375
* Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
376376
*
377+
* @template T of IStorage
377378
* @param string $class
379+
* @psalm-param class-string<T> $class
378380
* @return bool
379381
* @since 7.0.0
382+
* @psalm-assert-if-true T $this
380383
*/
381384
public function instanceOfStorage($class);
382385

lib/public/Files/Storage/IDisableEncryptionStorage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@
2929
*
3030
* @since 16.0.0
3131
*/
32-
interface IDisableEncryptionStorage {
32+
interface IDisableEncryptionStorage extends IStorage {
3333
}

lib/public/Files/Storage/IStorage.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,9 +362,12 @@ public function isLocal();
362362
/**
363363
* Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
364364
*
365+
* @template T of IStorage
365366
* @param string $class
367+
* @psalm-param class-string<T> $class
366368
* @return bool
367369
* @since 9.0.0
370+
* @psalm-assert-if-true T $this
368371
*/
369372
public function instanceOfStorage($class);
370373

0 commit comments

Comments
 (0)