Skip to content

Commit 62a82ae

Browse files
committed
feat(appconfig+userconfig): lexicon update lazy status
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
1 parent 0dc9711 commit 62a82ae

4 files changed

Lines changed: 136 additions & 1 deletion

File tree

lib/private/AppConfig.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -969,7 +969,7 @@ public function updateLazy(string $app, string $key, bool $lazy): bool {
969969
if ($lazy === $this->isLazy($app, $key)) {
970970
return false;
971971
}
972-
} catch (AppConfigUnknownKeyException $e) {
972+
} catch (AppConfigUnknownKeyException) {
973973
return false;
974974
}
975975

@@ -1605,6 +1605,12 @@ private function matchAndApplyLexiconDefinition(
16051605
$this->logger->notice('App config key ' . $app . '/' . $key . ' is set as deprecated.');
16061606
}
16071607

1608+
if ($lazy && isset($this->fastCache[$app][$key])) {
1609+
// while the Lexicon indicate that the config value is expected Lazy, we could
1610+
// have a previous entry still in fast cache. Updating Laziness.
1611+
$this->updateLazy($app, $key, true);
1612+
}
1613+
16081614
return true;
16091615
}
16101616

lib/private/Config/UserConfig.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,6 +1865,19 @@ private function matchAndApplyLexiconDefinition(
18651865
$this->logger->notice('User config key ' . $app . '/' . $key . ' is set as deprecated.');
18661866
}
18671867

1868+
// There should be no downside to load all config values if search for
1869+
// a lazy config value while fast value are still not loaded.
1870+
if ($lazy && !($this->fastLoaded[$userId] ?? false)) {
1871+
$this->loadConfigAll($userId);
1872+
}
1873+
1874+
// while the Lexicon indicate that the config value is expected Lazy, we could
1875+
// have a previous entry still in fast cache. Updating Laziness for all users.
1876+
if ($lazy && isset($this->fastCache[$userId][$app][$key])) {
1877+
$this->updateGlobalLazy($app, $key, true);
1878+
}
1879+
1880+
// TODO: remove this feature before 32 if https://github.com/nextcloud/server/issues/51804 is implemented
18681881
$enforcedValue = $this->config->getSystemValue('lexicon.default.userconfig.enforced', [])[$app][$key] ?? false;
18691882
if (!$enforcedValue && $this->hasKey($userId, $app, $key, $lazy)) {
18701883
// if key exists there should be no need to extract default

tests/lib/Config/LexiconTest.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@
1010
use NCU\Config\Exceptions\TypeConflictException;
1111
use NCU\Config\Exceptions\UnknownKeyException;
1212
use NCU\Config\IUserConfig;
13+
use OC\AppConfig;
1314
use OC\AppFramework\Bootstrap\Coordinator;
15+
use OC\Config\UserConfig;
1416
use OCP\Exceptions\AppConfigTypeConflictException;
1517
use OCP\Exceptions\AppConfigUnknownKeyException;
1618
use OCP\IAppConfig;
19+
use OCP\IConfig;
20+
use OCP\IDBConnection;
21+
use OCP\Security\ICrypto;
1722
use OCP\Server;
23+
use Psr\Log\LoggerInterface;
1824
use Test\TestCase;
1925

2026
/**
@@ -62,6 +68,80 @@ public function testAppLexiconSetCorrect() {
6268
$this->appConfig->deleteKey(TestConfigLexicon_E::APPID, 'key1');
6369
}
6470

71+
public function testAppConfigMigrationToLazy() {
72+
$app = TestConfigLexicon_Migration::APPID . '_app';
73+
// to avoid filling cache with an empty Lexicon, we use a new IAppConfig
74+
$appConfig = new AppConfig(
75+
Server::get(IDBConnection::class),
76+
Server::get(LoggerInterface::class),
77+
Server::get(ICrypto::class),
78+
);
79+
$this->assertSame(true, $appConfig->setValueString($app, 'key1', 'value1'));
80+
81+
// confirm that key1 is not lazy, even after a refresh of the cache
82+
$appConfig->clearCache();
83+
$this->assertSame('value1', $appConfig->getValueString($app, 'key1'));
84+
$this->assertSame(true, array_key_exists('key1', $appConfig->statusCache()['fastCache'][$app]));
85+
86+
// loading new Lexicon that set key1 as lazy
87+
$bootstrapCoordinator = \OCP\Server::get(Coordinator::class);
88+
$bootstrapCoordinator->getRegistrationContext()?->registerConfigLexicon($app, TestConfigLexicon_Migration::class);
89+
90+
// caching
91+
$this->assertSame('default0', $this->appConfig->getValueString($app, 'key0'));
92+
// still not lazy
93+
$this->assertSame(true, array_key_exists('key1', $this->appConfig->statusCache()['fastCache'][$app]));
94+
// trigger update of status
95+
$this->assertSame('value1', $this->appConfig->getValueString($app, 'key1'));
96+
// should be lazy now
97+
$this->assertSame(true, array_key_exists('key1', $this->appConfig->statusCache()['lazyCache'][$app]));
98+
99+
$this->appConfig->clearCache();
100+
$this->assertSame('default0', $this->appConfig->getValueString($app, 'key0'));
101+
// definitively lazy
102+
$this->assertSame(false, array_key_exists($app, $this->appConfig->statusCache()['fastCache']));
103+
104+
$this->appConfig->deleteKey($app, 'key1');
105+
}
106+
107+
public function testUserConfigMigrationToLazy() {
108+
$app = TestConfigLexicon_Migration::APPID . '_user';
109+
// to avoid filling cache with an empty Lexicon, we use a new IAppConfig
110+
$userConfig = new UserConfig(
111+
Server::get(IDBConnection::class),
112+
Server::get(IConfig::class),
113+
Server::get(LoggerInterface::class),
114+
Server::get(ICrypto::class),
115+
);
116+
$this->assertSame(true, $userConfig->setValueString('user1', $app, 'key1', 'value1'));
117+
118+
// confirm that key1 is not lazy, even after a refresh of Lexicon
119+
$userConfig->clearCache('user1');
120+
$this->assertSame('value1', $userConfig->getValueString('user1', $app, 'key1'));
121+
$this->assertSame(true, array_key_exists('key1', $userConfig->statusCache()['fastCache']['user1'][$app]));
122+
123+
// loading new Lexicon that set key1 as lazy
124+
$bootstrapCoordinator = \OCP\Server::get(Coordinator::class);
125+
$bootstrapCoordinator->getRegistrationContext()?->registerConfigLexicon($app, TestConfigLexicon_Migration::class);
126+
127+
// caching
128+
$this->assertSame('default0', $this->userConfig->getValueString('user1', $app, 'key0'));
129+
// still not lazy
130+
$this->assertSame(true, array_key_exists('key1', $this->userConfig->statusCache()['fastCache']['user1'][$app]));
131+
// trigger update of status
132+
$this->assertSame('value1', $this->userConfig->getValueString('user1', $app, 'key1'));
133+
// should be lazy now
134+
$this->assertSame(true, array_key_exists('key1', $this->userConfig->statusCache()['lazyCache']['user1'][$app]));
135+
136+
$this->userConfig->clearCache('user1');
137+
$this->assertSame('default0', $this->userConfig->getValueString('user1', $app, 'key0'));
138+
// definitively lazy
139+
$this->assertSame(false, array_key_exists($app, $this->userConfig->statusCache()['fastCache']['user1']));
140+
141+
$this->userConfig->deleteKey($app, 'key1');
142+
}
143+
144+
65145
public function testAppLexiconGetCorrect() {
66146
$this->assertSame('abcde', $this->appConfig->getValueString(TestConfigLexicon_E::APPID, 'key1', 'default'));
67147
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace Tests\lib\Config;
10+
11+
use NCU\Config\Lexicon\ConfigLexiconEntry;
12+
use NCU\Config\Lexicon\ConfigLexiconStrictness;
13+
use NCU\Config\Lexicon\IConfigLexicon;
14+
use NCU\Config\ValueType;
15+
16+
class TestConfigLexicon_Migration implements IConfigLexicon {
17+
public const APPID = 'lexicon_test_migration';
18+
19+
public function getStrictness(): ConfigLexiconStrictness {
20+
return ConfigLexiconStrictness::EXCEPTION;
21+
}
22+
23+
public function getAppConfigs(): array {
24+
return [
25+
new ConfigLexiconEntry('key0', ValueType::STRING, 'default0'),
26+
new ConfigLexiconEntry('key1', ValueType::STRING, lazy: true)
27+
];
28+
}
29+
30+
public function getUserConfigs(): array {
31+
return [
32+
new ConfigLexiconEntry('key0', ValueType::STRING, 'default0'),
33+
new ConfigLexiconEntry('key1', ValueType::STRING, lazy: true)
34+
];
35+
}
36+
}

0 commit comments

Comments
 (0)