Skip to content

Commit 6e4c5d5

Browse files
artongebackportbot[bot]
authored andcommitted
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> [skip ci]
1 parent 1fe7f80 commit 6e4c5d5

File tree

2 files changed

+44
-44
lines changed

2 files changed

+44
-44
lines changed

apps/files_trashbin/lib/BackgroundJob/ExpireTrash.php

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,31 @@
66
*/
77
namespace OCA\Files_Trashbin\BackgroundJob;
88

9+
use OC\Files\SetupManager;
910
use OC\Files\View;
1011
use OCA\Files_Trashbin\Expiration;
1112
use OCA\Files_Trashbin\Helper;
1213
use OCA\Files_Trashbin\Trashbin;
1314
use OCP\AppFramework\Utility\ITimeFactory;
1415
use OCP\BackgroundJob\TimedJob;
1516
use OCP\IAppConfig;
17+
use OCP\IUser;
1618
use OCP\IUserManager;
1719
use Psr\Log\LoggerInterface;
1820

1921
class ExpireTrash extends TimedJob {
22+
private const THIRTY_MINUTES = 30 * 60;
23+
2024
public function __construct(
2125
private IAppConfig $appConfig,
2226
private IUserManager $userManager,
2327
private Expiration $expiration,
2428
private LoggerInterface $logger,
29+
private SetupManager $setupManager,
2530
ITimeFactory $time,
2631
) {
2732
parent::__construct($time);
28-
// Run once per 30 minutes
29-
$this->setInterval(60 * 30);
33+
$this->setInterval(self::THIRTY_MINUTES);
3034
}
3135

3236
protected function run($argument) {
@@ -40,44 +44,47 @@ protected function run($argument) {
4044
return;
4145
}
4246

43-
$stopTime = time() + 60 * 30; // Stops after 30 minutes.
44-
$offset = $this->appConfig->getValueInt('files_trashbin', 'background_job_expire_trash_offset', 0);
45-
$users = $this->userManager->getSeenUsers($offset);
47+
$stopTime = time() + self::THIRTY_MINUTES;
48+
49+
do {
50+
$this->appConfig->clearCache();
51+
$offset = $this->appConfig->getValueInt('files_trashbin', 'background_job_expire_trash_offset', 0);
52+
$this->appConfig->setValueInt('files_trashbin', 'background_job_expire_trash_offset', $offset + 10);
53+
54+
$users = $this->userManager->getSeenUsers($offset, 10);
55+
$count = 0;
4656

47-
foreach ($users as $user) {
48-
try {
57+
foreach ($users as $user) {
4958
$uid = $user->getUID();
50-
if (!$this->setupFS($uid)) {
51-
continue;
59+
$count++;
60+
61+
try {
62+
if ($this->setupFS($user)) {
63+
$dirContent = Helper::getTrashFiles('/', $uid, 'mtime');
64+
Trashbin::deleteExpiredFiles($dirContent, $uid);
65+
}
66+
} catch (\Throwable $e) {
67+
$this->logger->error('Error while expiring trashbin for user ' . $uid, ['exception' => $e]);
68+
} finally {
69+
$this->setupManager->tearDown();
5270
}
53-
$dirContent = Helper::getTrashFiles('/', $uid, 'mtime');
54-
Trashbin::deleteExpiredFiles($dirContent, $uid);
55-
} catch (\Throwable $e) {
56-
$this->logger->error('Error while expiring trashbin for user ' . $user->getUID(), ['exception' => $e]);
5771
}
5872

59-
$offset++;
73+
} while (time() < $stopTime && $count === 10);
6074

61-
if ($stopTime < time()) {
62-
$this->appConfig->setValueInt('files_trashbin', 'background_job_expire_trash_offset', $offset);
63-
\OC_Util::tearDownFS();
64-
return;
65-
}
75+
if ($count < 10) {
76+
$this->appConfig->setValueInt('files_trashbin', 'background_job_expire_trash_offset', 0);
6677
}
67-
68-
$this->appConfig->setValueInt('files_trashbin', 'background_job_expire_trash_offset', 0);
69-
\OC_Util::tearDownFS();
7078
}
7179

7280
/**
7381
* Act on behalf on trash item owner
7482
*/
75-
protected function setupFS(string $user): bool {
76-
\OC_Util::tearDownFS();
77-
\OC_Util::setupFS($user);
83+
protected function setupFS(IUser $user): bool {
84+
$this->setupManager->setupForUser($user);
7885

7986
// Check if this user has a trashbin directory
80-
$view = new View('/' . $user);
87+
$view = new View('/' . $user->getUID());
8188
if (!$view->is_dir('/files_trashbin/files')) {
8289
return false;
8390
}

apps/files_trashbin/tests/BackgroundJob/ExpireTrashTest.php

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

88
namespace OCA\Files_Trashbin\Tests\BackgroundJob;
99

10+
use OC\Files\SetupManager;
1011
use OCA\Files_Trashbin\BackgroundJob\ExpireTrash;
1112
use OCA\Files_Trashbin\Expiration;
1213
use OCP\AppFramework\Utility\ITimeFactory;
@@ -18,23 +19,14 @@
1819
use Test\TestCase;
1920

2021
class ExpireTrashTest extends TestCase {
21-
/** @var IAppConfig&MockObject */
22-
private $appConfig;
2322

24-
/** @var IUserManager&MockObject */
25-
private $userManager;
26-
27-
/** @var Expiration&MockObject */
28-
private $expiration;
29-
30-
/** @var IJobList&MockObject */
31-
private $jobList;
32-
33-
/** @var LoggerInterface&MockObject */
34-
private $logger;
35-
36-
/** @var ITimeFactory&MockObject */
37-
private $time;
23+
private IAppConfig&MockObject $appConfig;
24+
private IUserManager&MockObject $userManager;
25+
private Expiration&MockObject $expiration;
26+
private IJobList&MockObject $jobList;
27+
private LoggerInterface&MockObject $logger;
28+
private ITimeFactory&MockObject $time;
29+
private SetupManager&MockObject $setupManager;
3830

3931
protected function setUp(): void {
4032
parent::setUp();
@@ -44,6 +36,7 @@ protected function setUp(): void {
4436
$this->expiration = $this->createMock(Expiration::class);
4537
$this->jobList = $this->createMock(IJobList::class);
4638
$this->logger = $this->createMock(LoggerInterface::class);
39+
$this->setupManager = $this->createMock(SetupManager::class);
4740

4841
$this->time = $this->createMock(ITimeFactory::class);
4942
$this->time->method('getTime')
@@ -63,7 +56,7 @@ public function testConstructAndRun(): void {
6356
->with('files_trashbin', 'background_job_expire_trash_offset', 0)
6457
->willReturn(0);
6558

66-
$job = new ExpireTrash($this->appConfig, $this->userManager, $this->expiration, $this->logger, $this->time);
59+
$job = new ExpireTrash($this->appConfig, $this->userManager, $this->expiration, $this->logger, $this->setupManager, $this->time);
6760
$job->start($this->jobList);
6861
}
6962

@@ -74,7 +67,7 @@ public function testBackgroundJobDeactivated(): void {
7467
$this->expiration->expects($this->never())
7568
->method('getMaxAgeAsTimestamp');
7669

77-
$job = new ExpireTrash($this->appConfig, $this->userManager, $this->expiration, $this->logger, $this->time);
70+
$job = new ExpireTrash($this->appConfig, $this->userManager, $this->expiration, $this->logger, $this->setupManager, $this->time);
7871
$job->start($this->jobList);
7972
}
8073
}

0 commit comments

Comments
 (0)