Skip to content

Commit 03c7c6d

Browse files
committed
feat(files_trashbin): Refactor expire background job to support parallel run
- Follow-up of #51600 The original PR introduced the possibility to continue an `ExpireTrash` job by saving the offset. This was to prevent having to start over the whole user list when the job crashed or was killed. But on big instances, one process is not enough to go through all the users in a timely manner. Supporting parallel run allows covering more ground faster. This PR introduced this possibility. We are now storing the offset right away to allow another parallel job to pick up the task at that point. We are arbitrarily cutting the user list in chunk of 10 to not drastically overflow the 30 minutes time limit. Signed-off-by: Louis Chemineau <louis@chmn.me>
1 parent cffc134 commit 03c7c6d

2 files changed

Lines changed: 45 additions & 45 deletions

File tree

apps/files_trashbin/lib/BackgroundJob/ExpireTrash.php

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,31 @@
2626
*/
2727
namespace OCA\Files_Trashbin\BackgroundJob;
2828

29+
use OC\Files\SetupManager;
30+
use OC\Files\View;
2931
use OCA\Files_Trashbin\Expiration;
3032
use OCA\Files_Trashbin\Helper;
3133
use OCA\Files_Trashbin\Trashbin;
3234
use OCP\AppFramework\Utility\ITimeFactory;
3335
use OCP\BackgroundJob\TimedJob;
3436
use OCP\IAppConfig;
37+
use OCP\IUser;
3538
use OCP\IUserManager;
3639
use Psr\Log\LoggerInterface;
3740

3841
class ExpireTrash extends TimedJob {
42+
private const THIRTY_MINUTES = 30 * 60;
3943

4044
public function __construct(
4145
private IAppConfig $appConfig,
4246
private IUserManager $userManager,
4347
private Expiration $expiration,
4448
private LoggerInterface $logger,
45-
ITimeFactory $time
49+
private SetupManager $setupManager,
50+
ITimeFactory $time,
4651
) {
4752
parent::__construct($time);
48-
// Run once per 30 minutes
49-
$this->setInterval(60 * 30);
53+
$this->setInterval(self::THIRTY_MINUTES);
5054
}
5155

5256
protected function run($argument) {
@@ -60,44 +64,47 @@ protected function run($argument) {
6064
return;
6165
}
6266

63-
$stopTime = time() + 60 * 30; // Stops after 30 minutes.
64-
$offset = $this->appConfig->getValueInt('files_trashbin', 'background_job_expire_trash_offset', 0);
65-
$users = $this->userManager->getSeenUsers($offset);
67+
$stopTime = time() + self::THIRTY_MINUTES;
6668

67-
foreach ($users as $user) {
68-
try {
69+
do {
70+
$this->appConfig->clearCache();
71+
$offset = $this->appConfig->getValueInt('files_trashbin', 'background_job_expire_trash_offset', 0);
72+
$this->appConfig->setValueInt('files_trashbin', 'background_job_expire_trash_offset', $offset + 10);
73+
74+
$users = $this->userManager->getSeenUsers($offset, 10);
75+
$count = 0;
76+
77+
foreach ($users as $user) {
6978
$uid = $user->getUID();
70-
if (!$this->setupFS($uid)) {
71-
continue;
79+
$count++;
80+
81+
try {
82+
if ($this->setupFS($user)) {
83+
$dirContent = Helper::getTrashFiles('/', $uid, 'mtime');
84+
Trashbin::deleteExpiredFiles($dirContent, $uid);
85+
}
86+
} catch (\Throwable $e) {
87+
$this->logger->error('Error while expiring trashbin for user ' . $uid, ['exception' => $e]);
88+
} finally {
89+
$this->setupManager->tearDown();
7290
}
73-
$dirContent = Helper::getTrashFiles('/', $uid, 'mtime');
74-
Trashbin::deleteExpiredFiles($dirContent, $uid);
75-
} catch (\Throwable $e) {
76-
$this->logger->error('Error while expiring trashbin for user ' . $user->getUID(), ['exception' => $e]);
7791
}
7892

79-
$offset++;
93+
} while (time() < $stopTime && $count === 10);
8094

81-
if ($stopTime < time()) {
82-
$this->appConfig->setValueInt('files_trashbin', 'background_job_expire_trash_offset', $offset);
83-
\OC_Util::tearDownFS();
84-
return;
85-
}
95+
if ($count < 10) {
96+
$this->appConfig->setValueInt('files_trashbin', 'background_job_expire_trash_offset', 0);
8697
}
87-
88-
$this->appConfig->setValueInt('files_trashbin', 'background_job_expire_trash_offset', 0);
89-
\OC_Util::tearDownFS();
9098
}
9199

92100
/**
93101
* Act on behalf on trash item owner
94102
*/
95-
protected function setupFS(string $user): bool {
96-
\OC_Util::tearDownFS();
97-
\OC_Util::setupFS($user);
103+
protected function setupFS(IUser $user): bool {
104+
$this->setupManager->setupForUser($user);
98105

99106
// Check if this user has a trashbin directory
100-
$view = new \OC\Files\View('/' . $user);
107+
$view = new View('/' . $user->getUID());
101108
if (!$view->is_dir('/files_trashbin/files')) {
102109
return false;
103110
}

apps/files_trashbin/tests/BackgroundJob/ExpireTrashTest.php

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
namespace OCA\Files_Trashbin\Tests\BackgroundJob;
2727

28+
use OC\Files\SetupManager;
2829
use OCA\Files_Trashbin\BackgroundJob\ExpireTrash;
2930
use OCA\Files_Trashbin\Expiration;
3031
use OCP\AppFramework\Utility\ITimeFactory;
@@ -36,23 +37,14 @@
3637
use Test\TestCase;
3738

3839
class ExpireTrashTest extends TestCase {
39-
/** @var IAppConfig&MockObject */
40-
private $appConfig;
4140

42-
/** @var IUserManager&MockObject */
43-
private $userManager;
44-
45-
/** @var Expiration&MockObject */
46-
private $expiration;
47-
48-
/** @var IJobList&MockObject */
49-
private $jobList;
50-
51-
/** @var LoggerInterface&MockObject */
52-
private $logger;
53-
54-
/** @var ITimeFactory&MockObject */
55-
private $time;
41+
private IAppConfig&MockObject $appConfig;
42+
private IUserManager&MockObject $userManager;
43+
private Expiration&MockObject $expiration;
44+
private IJobList&MockObject $jobList;
45+
private LoggerInterface&MockObject $logger;
46+
private ITimeFactory&MockObject $time;
47+
private SetupManager&MockObject $setupManager;
5648

5749
protected function setUp(): void {
5850
parent::setUp();
@@ -62,6 +54,7 @@ protected function setUp(): void {
6254
$this->expiration = $this->createMock(Expiration::class);
6355
$this->jobList = $this->createMock(IJobList::class);
6456
$this->logger = $this->createMock(LoggerInterface::class);
57+
$this->setupManager = $this->createMock(SetupManager::class);
6558

6659
$this->time = $this->createMock(ITimeFactory::class);
6760
$this->time->method('getTime')
@@ -81,7 +74,7 @@ public function testConstructAndRun(): void {
8174
->with('files_trashbin', 'background_job_expire_trash_offset', 0)
8275
->willReturn(0);
8376

84-
$job = new ExpireTrash($this->appConfig, $this->userManager, $this->expiration, $this->logger, $this->time);
77+
$job = new ExpireTrash($this->appConfig, $this->userManager, $this->expiration, $this->logger, $this->setupManager, $this->time);
8578
$job->start($this->jobList);
8679
}
8780

@@ -92,7 +85,7 @@ public function testBackgroundJobDeactivated(): void {
9285
$this->expiration->expects($this->never())
9386
->method('getMaxAgeAsTimestamp');
9487

95-
$job = new ExpireTrash($this->appConfig, $this->userManager, $this->expiration, $this->logger, $this->time);
88+
$job = new ExpireTrash($this->appConfig, $this->userManager, $this->expiration, $this->logger, $this->setupManager, $this->time);
9689
$job->start($this->jobList);
9790
}
9891
}

0 commit comments

Comments
 (0)