Skip to content

Commit 4c18fc1

Browse files
committed
feat: use time-based cutoff for share updating instead of count
Signed-off-by: Robin Appelman <robin@icewind.nl>
1 parent ab477c7 commit 4c18fc1

6 files changed

Lines changed: 78 additions & 32 deletions

File tree

apps/files_sharing/lib/Config/ConfigLexicon.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ class ConfigLexicon implements ILexicon {
2424
public const SHOW_FEDERATED_AS_INTERNAL = 'show_federated_shares_as_internal';
2525
public const SHOW_FEDERATED_TO_TRUSTED_AS_INTERNAL = 'show_federated_shares_to_trusted_servers_as_internal';
2626
public const EXCLUDE_RESHARE_FROM_EDIT = 'shareapi_exclude_reshare_from_edit';
27-
public const UPDATE_SINGLE_CUTOFF = 'update_single_cutoff';
28-
public const UPDATE_ALL_CUTOFF = 'update_all_cutoff';
27+
public const UPDATE_CUTOFF_TIME = 'update_cutoff_time';
2928
public const USER_NEEDS_SHARE_REFRESH = 'user_needs_share_refresh';
3029

3130
public function getStrictness(): Strictness {
@@ -38,8 +37,7 @@ public function getAppConfigs(): array {
3837
new Entry(self::SHOW_FEDERATED_TO_TRUSTED_AS_INTERNAL, ValueType::BOOL, false, 'shows federated shares to trusted servers as internal shares', true),
3938
new Entry(self::EXCLUDE_RESHARE_FROM_EDIT, ValueType::BOOL, false, 'Exclude reshare permission from "Allow editing" bundled permissions'),
4039

41-
new Entry(self::UPDATE_SINGLE_CUTOFF, ValueType::INT, 10, 'For how many users do we update the share data immediately for single-share updates'),
42-
new Entry(self::UPDATE_ALL_CUTOFF, ValueType::INT, 3, 'For how many users do we update the share data immediately for all-share updates'),
40+
new Entry(self::UPDATE_CUTOFF_TIME, ValueType::FLOAT, 3.0, 'For how how long do we update the share data immediately before switching to only marking the user'),
4341
];
4442
}
4543

apps/files_sharing/lib/Listener/SharesUpdatedListener.php

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use OCP\Share\Events\ShareCreatedEvent;
2424
use OCP\Share\Events\ShareTransferredEvent;
2525
use OCP\Share\IManager;
26+
use Psr\Clock\ClockInterface;
2627

2728
/**
2829
* Listen to various events that can change what shares a user has access to
@@ -31,26 +32,24 @@
3132
*/
3233
class SharesUpdatedListener implements IEventListener {
3334
/**
34-
* for how many users do we update the share date immediately,
35-
* before just marking the other users when we know the relevant share
35+
* for how long do we update the share date immediately,
36+
* before just marking the other users
3637
*/
37-
private int $cutOffMarkAllSingleShare;
38+
private float $cutOffMarkTime;
39+
3840
/**
39-
* for how many users do we update the share date immediately,
40-
* before just marking the other users when the relevant shares are unknown
41+
* The total amount of time we've spent so far processing updates
4142
*/
42-
private int $cutOffMarkAllShares ;
43-
44-
private int $updatedCount = 0;
43+
private float $updatedTime = 0.0;
4544

4645
public function __construct(
4746
private readonly IManager $shareManager,
4847
private readonly ShareRecipientUpdater $shareUpdater,
4948
private readonly IUserConfig $userConfig,
49+
private readonly ClockInterface $clock,
5050
IAppConfig $appConfig,
5151
) {
52-
$this->cutOffMarkAllSingleShare = $appConfig->getValueInt(Application::APP_ID, ConfigLexicon::UPDATE_SINGLE_CUTOFF, 10);
53-
$this->cutOffMarkAllShares = $appConfig->getValueInt(Application::APP_ID, ConfigLexicon::UPDATE_ALL_CUTOFF, 3);
52+
$this->cutOffMarkTime = $appConfig->getValueFloat(Application::APP_ID, ConfigLexicon::UPDATE_CUTOFF_TIME, 3.0);
5453
}
5554

5655
public function handle(Event $event): void {
@@ -67,14 +66,16 @@ public function handle(Event $event): void {
6766
$shareTarget = $share->getTarget();
6867
foreach ($this->shareManager->getUsersForShare($share) as $user) {
6968
if ($share->getSharedBy() !== $user->getUID()) {
70-
$this->updatedCount++;
71-
if ($this->cutOffMarkAllSingleShare === -1 || $this->updatedCount <= $this->cutOffMarkAllSingleShare) {
69+
$start = floatval($this->clock->now()->format('U.u'));
70+
if ($this->cutOffMarkTime === -1.0 || $this->updatedTime < $this->cutOffMarkTime) {
7271
$this->shareUpdater->updateForShare($user, $share);
7372
// Share target validation might have changed the target, restore it for the next user
7473
$share->setTarget($shareTarget);
7574
} else {
7675
$this->markUserForRefresh($user);
7776
}
77+
$end = floatval($this->clock->now()->format('U.u'));
78+
$this->updatedTime += $end - $start;
7879
}
7980
}
8081
}
@@ -86,23 +87,21 @@ public function handle(Event $event): void {
8687
}
8788

8889
private function updateOrMarkUser(IUser $user, bool $verifyMountPoints, array $ignoreShares = []): void {
89-
$this->updatedCount++;
90-
if ($this->cutOffMarkAllShares === -1 || $this->updatedCount <= $this->cutOffMarkAllShares) {
90+
$start = $this->clock->now();
91+
if ($this->cutOffMarkTime === -1.0 || $this->updatedTime < $this->cutOffMarkTime) {
9192
$this->shareUpdater->updateForUser($user, $verifyMountPoints, $ignoreShares);
9293
} else {
9394
$this->markUserForRefresh($user);
9495
}
96+
$end = $this->clock->now();
97+
$this->updatedTime += $start->diff($end)->f;
9598
}
9699

97100
private function markUserForRefresh(IUser $user): void {
98101
$this->userConfig->setValueBool($user->getUID(), Application::APP_ID, ConfigLexicon::USER_NEEDS_SHARE_REFRESH, true);
99102
}
100103

101-
public function setCutOffMarkAllSingleShare(int $cutOffMarkAllSingleShare): void {
102-
$this->cutOffMarkAllSingleShare = $cutOffMarkAllSingleShare;
103-
}
104-
105-
public function setCutOffMarkAllShares(int $cutOffMarkAllShares): void {
106-
$this->cutOffMarkAllShares = $cutOffMarkAllShares;
104+
public function setCutOffMarkTime(float|int $cutOffMarkTime): void {
105+
$this->cutOffMarkTime = (float)$cutOffMarkTime;
107106
}
108107
}

apps/files_sharing/tests/SharesUpdatedListenerTest.php

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
use OCP\Share\Events\ShareCreatedEvent;
1919
use OCP\Share\IManager;
2020
use OCP\Share\IShare;
21+
use PHPUnit\Framework\Attributes\DataProvider;
2122
use PHPUnit\Framework\MockObject\MockObject;
23+
use Psr\Clock\ClockInterface;
2224
use Test\Mock\Config\MockAppConfig;
2325
use Test\Mock\Config\MockUserConfig;
2426
use Test\Traits\UserTrait;
@@ -31,21 +33,32 @@ class SharesUpdatedListenerTest extends \Test\TestCase {
3133
private IManager&MockObject $manager;
3234
private IUserConfig $userConfig;
3335
private IAppConfig $appConfig;
36+
private ClockInterface&MockObject $clock;
37+
private $clockFn;
3438

3539
protected function setUp(): void {
3640
parent::setUp();
3741

3842
$this->shareRecipientUpdater = $this->createMock(ShareRecipientUpdater::class);
3943
$this->manager = $this->createMock(IManager::class);
4044
$this->appConfig = new MockAppConfig([
41-
ConfigLexicon::UPDATE_ALL_CUTOFF => -1,
42-
ConfigLexicon::UPDATE_SINGLE_CUTOFF => -1,
45+
ConfigLexicon::UPDATE_CUTOFF_TIME => -1,
4346
]);
4447
$this->userConfig = new MockUserConfig();
48+
$this->clock = $this->createMock(ClockInterface::class);
49+
$this->clockFn = function () {
50+
return new \DateTimeImmutable('@0');
51+
};
52+
$this->clock->method('now')
53+
->willReturnCallback(function () {
54+
// extra wrapper so we can modify clockFn
55+
return ($this->clockFn)();
56+
});
4557
$this->sharesUpdatedListener = new SharesUpdatedListener(
4658
$this->manager,
4759
$this->shareRecipientUpdater,
4860
$this->userConfig,
61+
$this->clock,
4962
$this->appConfig,
5063
);
5164
}
@@ -133,4 +146,42 @@ public function testShareDeleted() {
133146

134147
$this->sharesUpdatedListener->handle($event);
135148
}
149+
150+
public static function shareMarkAfterTimeProvider(): array {
151+
// note that each user will take exactly 1s in this test
152+
return [
153+
[0, 0],
154+
[0.9, 1],
155+
[1.1, 2],
156+
[-1, 2],
157+
];
158+
}
159+
160+
#[DataProvider('shareMarkAfterTimeProvider')]
161+
public function testShareMarkAfterTime(float $cutOff, int $expectedCount) {
162+
$share = $this->createMock(IShare::class);
163+
$user1 = $this->createUser('user1', '');
164+
$user2 = $this->createUser('user2', '');
165+
166+
$this->manager->method('getUsersForShare')
167+
->willReturn([$user1, $user2]);
168+
169+
$event = new ShareCreatedEvent($share);
170+
171+
$this->sharesUpdatedListener->setCutOffMarkTime($cutOff);
172+
$time = 0;
173+
$this->clockFn = function () use (&$time) {
174+
$time++;
175+
return new \DateTimeImmutable('@' . $time);
176+
};
177+
178+
$this->shareRecipientUpdater
179+
->expects($this->exactly($expectedCount))
180+
->method('updateForShare');
181+
182+
$this->sharesUpdatedListener->handle($event);
183+
184+
$this->assertEquals($expectedCount < 1, $this->userConfig->getValueBool($user1->getUID(), 'files_sharing', ConfigLexicon::USER_NEEDS_SHARE_REFRESH));
185+
$this->assertEquals($expectedCount < 2, $this->userConfig->getValueBool($user2->getUID(), 'files_sharing', ConfigLexicon::USER_NEEDS_SHARE_REFRESH));
186+
}
136187
}

apps/files_sharing/tests/TestCase.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,7 @@ public static function setUpBeforeClass(): void {
101101
$groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_GROUP1);
102102
Server::get(IGroupManager::class)->addBackend($groupBackend);
103103

104-
Server::get(SharesUpdatedListener::class)->setCutOffMarkAllShares(-1);
105-
Server::get(SharesUpdatedListener::class)->setCutOffMarkAllSingleShare(-1);
104+
Server::get(SharesUpdatedListener::class)->setCutOffMarkTime(-1);
106105
}
107106

108107
protected function setUp(): void {

build/integration/features/bootstrap/SharingContext.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
use Behat\Behat\Context\Context;
88
use Behat\Behat\Context\SnippetAcceptingContext;
9+
use OCA\Files_Sharing\Config\ConfigLexicon;
910

1011
require __DIR__ . '/autoload.php';
1112

@@ -32,8 +33,7 @@ protected function resetAppConfigs() {
3233
$this->deleteServerConfig('core', 'shareapi_allow_federation_on_public_shares');
3334
$this->deleteServerConfig('files_sharing', 'outgoing_server2server_share_enabled');
3435
$this->deleteServerConfig('core', 'shareapi_allow_view_without_download');
35-
$this->deleteServerConfig('files_sharing', 'update_single_cutoff');
36-
$this->deleteServerConfig('files_sharing', 'update_all_cutoff');
36+
$this->deleteServerConfig('files_sharing', ConfigLexicon::UPDATE_CUTOFF_TIME);
3737

3838
$this->runOcc(['config:system:delete', 'share_folder']);
3939
}

build/integration/sharing_features/sharing-v1-part2.feature

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ Feature: sharing
4949

5050
Scenario: getting all shares of a file with a received share after revoking the resharing rights with delayed share check
5151
Given user "user0" exists
52-
And parameter "update_single_cutoff" of app "files_sharing" is set to "0"
53-
And parameter "update_all_cutoff" of app "files_sharing" is set to "0"
52+
And parameter "update_cutoff_time" of app "files_sharing" is set to "0"
5453
And user "user1" exists
5554
And user "user2" exists
5655
And file "textfile0.txt" of user "user1" is shared with user "user0"

0 commit comments

Comments
 (0)