setInterval(self::THIRTY_MINUTES); } protected function run($argument) { $backgroundJob = $this->appConfig->getValueBool(Application::APP_ID, self::TOGGLE_CONFIG_KEY_NAME, true); if (!$backgroundJob) { return; } $maxAge = $this->expiration->getMaxAgeAsTimestamp(); if (!$maxAge) { return; } $startTime = time(); // Process users in batches of 10, but don't run for more than 30 minutes while (time() < $startTime + self::THIRTY_MINUTES) { $offset = $this->getNextOffset(); $users = $this->userManager->getSeenUsers($offset, self::USER_BATCH_SIZE); $count = 0; foreach ($users as $user) { $uid = $user->getUID(); $count++; try { if ($this->setupFS($user)) { Trashbin::expire($uid); } } catch (\Throwable $e) { $this->logger->error('Error while expiring trashbin for user ' . $uid, ['exception' => $e]); } finally { $this->setupManager->tearDown(); } } // If the last batch was not full it means that we reached the end of the user list. if ($count < self::USER_BATCH_SIZE) { $this->resetOffset(); break; } } } /** * Act on behalf on trash item owner */ protected function setupFS(IUser $user): bool { $this->setupManager->setupForUser($user); // Check if this user has a trashbin directory $view = new View('/' . $user->getUID()); if (!$view->is_dir('/files_trashbin/files')) { return false; } return true; } private function getNextOffset(): int { return $this->runMutexOperation(function () { $this->appConfig->clearCache(); $offset = $this->appConfig->getValueInt(Application::APP_ID, self::OFFSET_CONFIG_KEY_NAME, 0); $this->appConfig->setValueInt(Application::APP_ID, self::OFFSET_CONFIG_KEY_NAME, $offset + self::USER_BATCH_SIZE); return $offset; }); } private function resetOffset() { $this->runMutexOperation(function (): void { $this->appConfig->setValueInt(Application::APP_ID, self::OFFSET_CONFIG_KEY_NAME, 0); }); } private function runMutexOperation($operation): mixed { $acquired = false; while ($acquired === false) { try { $this->lockingProvider->acquireLock(self::OFFSET_CONFIG_KEY_NAME, ILockingProvider::LOCK_EXCLUSIVE, 'Expire trashbin background job offset'); $acquired = true; } catch (LockedException $e) { // wait a bit and try again usleep(100000); } } try { $result = $operation(); } finally { $this->lockingProvider->releaseLock(self::OFFSET_CONFIG_KEY_NAME, ILockingProvider::LOCK_EXCLUSIVE); } return $result; } }