1919use OCP \BackgroundJob \IJobList ;
2020use OCP \BackgroundJob \TimedJob ;
2121use OCP \DB \Exception ;
22+ use OCP \DB \QueryBuilder \IQueryBuilder ;
2223use OCP \Files \Config \ICachedMountInfo ;
2324use OCP \Files \Config \IUserMountCache ;
2425use OCP \Files \File ;
2526use OCP \Files \InvalidPathException ;
2627use OCP \Files \IRootFolder ;
2728use OCP \Files \NotFoundException ;
2829use OCP \Files \NotPermittedException ;
30+ use OCP \IDBConnection ;
2931use OCP \Lock \LockedException ;
3032use Psr \Log \LoggerInterface ;
3133
34+ /**
35+ * Indexer Job
36+ * Makes use of the following app config settings:
37+ *
38+ * auto_indexing: bool = true The job only runs if this is true
39+ * indexing_batch_size: int = 100 The number of files to index per run
40+ * indexing_max_time: int = 30*60 The number of seconds to index files for per run, regardless of batch size
41+ * indexing_max_jobs_count: int = 3 The maximum number of Indexer jobs allowed to run at the same time
42+ */
3243class IndexerJob extends TimedJob {
3344
3445 public const DEFAULT_MAX_INDEXING_TIME = 30 * 60 ;
46+ public const DEFAULT_MAX_JOBS_COUNT = 3 ;
3547
3648 public function __construct (
3749 ITimeFactory $ time ,
@@ -44,6 +56,8 @@ public function __construct(
4456 private IRootFolder $ rootFolder ,
4557 private IAppConfig $ appConfig ,
4658 private DiagnosticService $ diagnosticService ,
59+ private IDBConnection $ db ,
60+ private ITimeFactory $ timeFactory ,
4761 ) {
4862 parent ::__construct ($ time );
4963 $ this ->setInterval ($ this ->getMaxIndexingTime ());
@@ -66,6 +80,9 @@ public function run($argument): void {
6680 if ($ this ->appConfig ->getAppValue ('auto_indexing ' , 'true ' ) === 'false ' ) {
6781 return ;
6882 }
83+ if ($ this ->hasEnoughRunningJobs ()) {
84+ return ;
85+ }
6986 $ this ->logger ->debug ('Index files of storage ' . $ storageId );
7087 try {
7188 $ this ->logger ->debug ('fetching ' . $ this ->getBatchSize () . ' files from queue ' );
@@ -122,6 +139,37 @@ protected function getMaxIndexingTime(): int {
122139 return $ this ->appConfig ->getAppValueInt ('indexing_max_time ' , self ::DEFAULT_MAX_INDEXING_TIME );
123140 }
124141
142+ protected function hasEnoughRunningJobs (): bool {
143+ // Sleep a bit randomly to avoid a scenario where all jobs are started at the same time and kill themselves directly
144+ sleep (rand (1 , 3 * 60 ));
145+ if (!$ this ->jobList ->hasReservedJob (static ::class)) {
146+ // short circuit to false if no jobs are running, yet
147+ return false ;
148+ }
149+ $ count = 0 ;
150+ foreach ($ this ->jobList ->getJobsIterator (static ::class, null , 0 ) as $ job ) {
151+ // Check if job is running
152+ $ query = $ this ->db ->getQueryBuilder ();
153+ $ query ->select ('* ' )
154+ ->from ('jobs ' )
155+ ->where ($ query ->expr ()->gt ('reserved_at ' , $ query ->createNamedParameter ($ this ->timeFactory ->getTime () - 6 * 3600 , IQueryBuilder::PARAM_INT )))
156+ ->andWhere ($ query ->expr ()->eq ('id ' , $ query ->createNamedParameter ($ job ->getId (), IQueryBuilder::PARAM_INT )))
157+ ->setMaxResults (1 );
158+
159+ try {
160+ $ result = $ query ->executeQuery ();
161+ if ($ result ->fetch () !== false ) {
162+ // count if it's running
163+ $ count ++;
164+ }
165+ $ result ->closeCursor ();
166+ } catch (Exception $ e ) {
167+ $ this ->logger ->warning ('Querying reserved jobs failed ' , ['exception ' => $ e ]);
168+ }
169+ }
170+ return $ count >= $ this ->appConfig ->getAppValueInt ('indexing_max_jobs_count ' , self ::DEFAULT_MAX_JOBS_COUNT );
171+ }
172+
125173 /**
126174 * @param QueueFile[] $files
127175 * @return void
0 commit comments