1515use OC \Config \ConfigManager ;
1616use OC \Config \PresetManager ;
1717use OCP \Config \Lexicon \Entry ;
18- use OCP \Config \Lexicon \ILexicon ;
1918use OCP \Config \Lexicon \Strictness ;
2019use OCP \Config \ValueType ;
2120use OCP \DB \Exception as DBException ;
2423use OCP \Exceptions \AppConfigTypeConflictException ;
2524use OCP \Exceptions \AppConfigUnknownKeyException ;
2625use OCP \IAppConfig ;
26+ use OCP \ICache ;
27+ use OCP \ICacheFactory ;
2728use OCP \IConfig ;
2829use OCP \IDBConnection ;
2930use OCP \Security \ICrypto ;
@@ -53,10 +54,12 @@ class AppConfig implements IAppConfig {
5354 private const KEY_MAX_LENGTH = 64 ;
5455 private const ENCRYPTION_PREFIX = '$AppConfigEncryption$ ' ;
5556 private const ENCRYPTION_PREFIX_LENGTH = 21 ; // strlen(self::ENCRYPTION_PREFIX)
57+ private const LOCAL_CACHE_KEY = 'OC \\AppConfig ' ;
58+ private const LOCAL_CACHE_TTL = 180 ;
5659
57- /** @var array<string, array<string, mixed >> ['app_id' => ['config_key' => 'config_value']] */
60+ /** @var array<string, array<string, string >> ['app_id' => ['config_key' => 'config_value']] */
5861 private array $ fastCache = []; // cache for normal config keys
59- /** @var array<string, array<string, mixed >> ['app_id' => ['config_key' => 'config_value']] */
62+ /** @var array<string, array<string, string >> ['app_id' => ['config_key' => 'config_value']] */
6063 private array $ lazyCache = []; // cache for lazy config keys
6164 /** @var array<string, array<string, int>> ['app_id' => ['config_key' => bitflag]] */
6265 private array $ valueTypes = []; // type for all config values
@@ -68,14 +71,23 @@ class AppConfig implements IAppConfig {
6871 /** @var ?array<string, string> */
6972 private ?array $ appVersionsCache = null ;
7073
74+ /**
75+ * @var null|ICache<array{'fastCache': array<string, array<string, string>>, 'lazyCache': array<string, array<string, string>>|null, 'valueTypes': array<string, array<string, int>>}
76+ */
77+ private ?ICache $ localCache = null ;
78+
7179 public function __construct (
7280 protected IDBConnection $ connection ,
7381 protected IConfig $ config ,
7482 private readonly ConfigManager $ configManager ,
7583 private readonly PresetManager $ presetManager ,
7684 protected LoggerInterface $ logger ,
7785 protected ICrypto $ crypto ,
86+ readonly ICacheFactory $ cacheFactory ,
7887 ) {
88+ if ($ config ->getSystemValueBool ('cache__app_config ' ) && $ cacheFactory ->isLocalCacheAvailable ()) {
89+ $ this ->localCache = $ cacheFactory ->createLocal ();
90+ }
7991 }
8092
8193 /**
@@ -85,7 +97,7 @@ public function __construct(
8597 * @since 7.0.0
8698 */
8799 public function getApps (): array {
88- $ this ->loadConfigAll ( );
100+ $ this ->loadConfig (lazy: true );
89101 $ apps = array_merge (array_keys ($ this ->fastCache ), array_keys ($ this ->lazyCache ));
90102 sort ($ apps );
91103
@@ -103,7 +115,7 @@ public function getApps(): array {
103115 */
104116 public function getKeys (string $ app ): array {
105117 $ this ->assertParams ($ app );
106- $ this ->loadConfigAll ($ app );
118+ $ this ->loadConfig ($ app, true );
107119 $ keys = array_merge (array_keys ($ this ->fastCache [$ app ] ?? []), array_keys ($ this ->lazyCache [$ app ] ?? []));
108120 sort ($ keys );
109121
@@ -149,7 +161,7 @@ public function searchKeys(string $app, string $prefix = '', bool $lazy = false)
149161 */
150162 public function hasKey (string $ app , string $ key , ?bool $ lazy = false ): bool {
151163 $ this ->assertParams ($ app , $ key );
152- $ this ->loadConfig ($ app , $ lazy );
164+ $ this ->loadConfig ($ app , $ lazy ?? true );
153165 $ this ->matchAndApplyLexiconDefinition ($ app , $ key );
154166
155167 if ($ lazy === null ) {
@@ -175,7 +187,7 @@ public function hasKey(string $app, string $key, ?bool $lazy = false): bool {
175187 */
176188 public function isSensitive (string $ app , string $ key , ?bool $ lazy = false ): bool {
177189 $ this ->assertParams ($ app , $ key );
178- $ this ->loadConfig (null , $ lazy );
190+ $ this ->loadConfig (null , $ lazy ?? true );
179191 $ this ->matchAndApplyLexiconDefinition ($ app , $ key );
180192
181193 if (!isset ($ this ->valueTypes [$ app ][$ key ])) {
@@ -227,7 +239,7 @@ public function isLazy(string $app, string $key): bool {
227239 public function getAllValues (string $ app , string $ prefix = '' , bool $ filtered = false ): array {
228240 $ this ->assertParams ($ app , $ prefix );
229241 // if we want to filter values, we need to get sensitivity
230- $ this ->loadConfigAll ($ app );
242+ $ this ->loadConfig ($ app, true );
231243 // array_merge() will remove numeric keys (here config keys), so addition arrays instead
232244 $ values = $ this ->formatAppValues ($ app , ($ this ->fastCache [$ app ] ?? []) + ($ this ->lazyCache [$ app ] ?? []));
233245 $ values = array_filter (
@@ -479,7 +491,7 @@ private function getTypedValue(
479491 return $ default ;
480492 }
481493
482- $ this ->loadConfig ($ app , $ lazy );
494+ $ this ->loadConfig ($ app , $ lazy ?? true );
483495
484496 /**
485497 * We ignore check if mixed type is requested.
@@ -551,7 +563,7 @@ public function getValueType(string $app, string $key, ?bool $lazy = null): int
551563 }
552564
553565 $ this ->assertParams ($ app , $ key );
554- $ this ->loadConfig ($ app , $ lazy );
566+ $ this ->loadConfig ($ app , $ lazy ?? true );
555567
556568 if (!isset ($ this ->valueTypes [$ app ][$ key ])) {
557569 throw new AppConfigUnknownKeyException ('unknown config key ' );
@@ -788,7 +800,7 @@ private function setTypedValue(
788800 if (!$ this ->matchAndApplyLexiconDefinition ($ app , $ key , $ lazy , $ type )) {
789801 return false ; // returns false as database is not updated
790802 }
791- $ this ->loadConfig (null , $ lazy );
803+ $ this ->loadConfig (null , $ lazy ?? true );
792804
793805 $ sensitive = $ this ->isTyped (self ::VALUE_SENSITIVE , $ type );
794806 $ inserted = $ refreshCache = false ;
@@ -803,7 +815,7 @@ private function setTypedValue(
803815 * no update if key is already known with set lazy status and value is
804816 * not different, unless sensitivity is switched from false to true.
805817 */
806- if ($ origValue === $ this ->getTypedValue ($ app , $ key , $ value , $ lazy , $ type )
818+ if ($ origValue === $ this ->getTypedValue ($ app , $ key , $ value , $ lazy ?? true , $ type )
807819 && (!$ sensitive || $ this ->isSensitive ($ app , $ key , $ lazy ))) {
808820 return false ;
809821 }
@@ -835,7 +847,7 @@ private function setTypedValue(
835847 if (!$ inserted ) {
836848 $ currType = $ this ->valueTypes [$ app ][$ key ] ?? 0 ;
837849 if ($ currType === 0 ) { // this might happen when switching lazy loading status
838- $ this ->loadConfigAll ( );
850+ $ this ->loadConfig (lazy: true );
839851 $ currType = $ this ->valueTypes [$ app ][$ key ] ?? 0 ;
840852 }
841853
@@ -856,7 +868,7 @@ private function setTypedValue(
856868 && ($ type | self ::VALUE_SENSITIVE ) !== ($ currType | self ::VALUE_SENSITIVE )) {
857869 try {
858870 $ currType = $ this ->convertTypeToString ($ currType );
859- $ type = $ this ->convertTypeToString ($ type );
871+ $ this ->convertTypeToString ($ type );
860872 } catch (AppConfigIncorrectTypeException ) {
861873 // can be ignored, this was just needed for a better exception message.
862874 }
@@ -895,6 +907,7 @@ private function setTypedValue(
895907 $ this ->fastCache [$ app ][$ key ] = $ value ;
896908 }
897909 $ this ->valueTypes [$ app ][$ key ] = $ type ;
910+ $ this ->updateCache ();
898911
899912 return true ;
900913 }
@@ -916,7 +929,7 @@ private function setTypedValue(
916929 */
917930 public function updateType (string $ app , string $ key , int $ type = self ::VALUE_MIXED ): bool {
918931 $ this ->assertParams ($ app , $ key );
919- $ this ->loadConfigAll ( );
932+ $ this ->loadConfig (lazy: true );
920933 $ this ->matchAndApplyLexiconDefinition ($ app , $ key );
921934 $ this ->isLazy ($ app , $ key ); // confirm key exists
922935
@@ -959,7 +972,7 @@ public function updateType(string $app, string $key, int $type = self::VALUE_MIX
959972 */
960973 public function updateSensitive (string $ app , string $ key , bool $ sensitive ): bool {
961974 $ this ->assertParams ($ app , $ key );
962- $ this ->loadConfigAll ( );
975+ $ this ->loadConfig (lazy: true );
963976 $ this ->matchAndApplyLexiconDefinition ($ app , $ key );
964977
965978 try {
@@ -1019,7 +1032,7 @@ public function updateSensitive(string $app, string $key, bool $sensitive): bool
10191032 */
10201033 public function updateLazy (string $ app , string $ key , bool $ lazy ): bool {
10211034 $ this ->assertParams ($ app , $ key );
1022- $ this ->loadConfigAll ( );
1035+ $ this ->loadConfig (lazy: true );
10231036 $ this ->matchAndApplyLexiconDefinition ($ app , $ key );
10241037
10251038 try {
@@ -1055,7 +1068,7 @@ public function updateLazy(string $app, string $key, bool $lazy): bool {
10551068 */
10561069 public function getDetails (string $ app , string $ key ): array {
10571070 $ this ->assertParams ($ app , $ key );
1058- $ this ->loadConfigAll ( );
1071+ $ this ->loadConfig (lazy: true );
10591072 $ this ->matchAndApplyLexiconDefinition ($ app , $ key );
10601073 $ lazy = $ this ->isLazy ($ app , $ key );
10611074
@@ -1227,12 +1240,13 @@ public function deleteApp(string $app): void {
12271240 public function clearCache (bool $ reload = false ): void {
12281241 $ this ->lazyLoaded = $ this ->fastLoaded = false ;
12291242 $ this ->lazyCache = $ this ->fastCache = $ this ->valueTypes = $ this ->configLexiconDetails = [];
1243+ $ this ->localCache ?->remove(self ::LOCAL_CACHE_KEY );
12301244
12311245 if (!$ reload ) {
12321246 return ;
12331247 }
12341248
1235- $ this ->loadConfigAll ( );
1249+ $ this ->loadConfig (lazy: true );
12361250 }
12371251
12381252
@@ -1293,51 +1307,68 @@ private function assertParams(string $app = '', string $configKey = '', bool $al
12931307 }
12941308 }
12951309
1296- private function loadConfigAll (?string $ app = null ): void {
1297- $ this ->loadConfig ($ app , null );
1298- }
1299-
13001310 /**
13011311 * Load normal config or config set as lazy loaded
13021312 *
1303- * @param bool|null $lazy set to TRUE to load config set as lazy loaded, set to NULL to load all config
1313+ * @param bool $lazy set to TRUE to also load config values set as lazy loaded
13041314 */
1305- private function loadConfig (?string $ app = null , ? bool $ lazy = false ): void {
1315+ private function loadConfig (?string $ app = null , bool $ lazy = false ): void {
13061316 if ($ this ->isLoaded ($ lazy )) {
13071317 return ;
13081318 }
13091319
13101320 // if lazy is null or true, we debug log
1311- if (( $ lazy ?? true ) !== false && $ app !== null ) {
1321+ if ($ lazy === true && $ app !== null ) {
13121322 $ exception = new \RuntimeException ('The loading of lazy AppConfig values have been triggered by app " ' . $ app . '" ' );
13131323 $ this ->logger ->debug ($ exception ->getMessage (), ['exception ' => $ exception , 'app ' => $ app ]);
13141324 }
13151325
1316- $ qb = $ this ->connection ->getQueryBuilder ();
1317- $ qb ->from ('appconfig ' );
1326+ $ loadLazyOnly = $ lazy && $ this ->isLoaded (false );
1327+
1328+ /** @var array<mixed> */
1329+ $ cacheContent = $ this ->localCache ?->get(self ::LOCAL_CACHE_KEY ) ?? [];
1330+ $ includesLazyValues = !empty ($ cacheContent ) && !empty ($ cacheContent ['lazyCache ' ]);
1331+ if (!empty ($ cacheContent ) && (!$ lazy || $ includesLazyValues )) {
1332+ $ this ->valueTypes = $ cacheContent ['valueTypes ' ];
1333+ $ this ->fastCache = $ cacheContent ['fastCache ' ];
1334+ $ this ->fastLoaded = !empty ($ this ->fastCache );
1335+ if ($ includesLazyValues ) {
1336+ $ this ->lazyCache = $ cacheContent ['lazyCache ' ];
1337+ $ this ->lazyLoaded = !empty ($ this ->lazyCache );
1338+ }
1339+ return ;
1340+ }
13181341
1319- // we only need value from lazy when loadConfig does not specify it
1320- $ qb ->select ('appid ' , 'configkey ' , 'configvalue ' , 'type ' );
1342+ // Otherwise no cache available and we need to fetch from database
1343+ $ qb = $ this ->connection ->getQueryBuilder ();
1344+ $ qb ->from ('appconfig ' )
1345+ ->select ('appid ' , 'configkey ' , 'configvalue ' , 'type ' );
13211346
1322- if ($ lazy !== null ) {
1323- $ qb ->where ($ qb ->expr ()->eq ('lazy ' , $ qb ->createNamedParameter ($ lazy ? 1 : 0 , IQueryBuilder::PARAM_INT )));
1347+ if ($ lazy === false ) {
1348+ $ qb ->where ($ qb ->expr ()->eq ('lazy ' , $ qb ->createNamedParameter (0 , IQueryBuilder::PARAM_INT )));
13241349 } else {
1350+ if ($ loadLazyOnly ) {
1351+ $ qb ->where ($ qb ->expr ()->eq ('lazy ' , $ qb ->createNamedParameter (1 , IQueryBuilder::PARAM_INT )));
1352+ }
13251353 $ qb ->addSelect ('lazy ' );
13261354 }
13271355
13281356 $ result = $ qb ->executeQuery ();
13291357 $ rows = $ result ->fetchAll ();
13301358 foreach ($ rows as $ row ) {
13311359 // most of the time, 'lazy' is not in the select because its value is already known
1332- if (( $ row ['lazy ' ] ?? ( $ lazy ?? 0 ) ? 1 : 0 ) === 1 ) {
1360+ if ($ lazy && $ row ['lazy ' ] === 1 ) {
13331361 $ this ->lazyCache [$ row ['appid ' ]][$ row ['configkey ' ]] = $ row ['configvalue ' ] ?? '' ;
13341362 } else {
13351363 $ this ->fastCache [$ row ['appid ' ]][$ row ['configkey ' ]] = $ row ['configvalue ' ] ?? '' ;
13361364 }
13371365 $ this ->valueTypes [$ row ['appid ' ]][$ row ['configkey ' ]] = (int )($ row ['type ' ] ?? 0 );
13381366 }
1367+
13391368 $ result ->closeCursor ();
1340- $ this ->setAsLoaded ($ lazy );
1369+ $ this ->updateCache ();
1370+ $ this ->fastLoaded = true ;
1371+ $ this ->lazyLoaded = $ lazy ;
13411372 }
13421373
13431374 /**
@@ -1358,37 +1389,14 @@ private function isLoaded(?bool $lazy): bool {
13581389 return $ lazy ? $ this ->lazyLoaded : $ this ->fastLoaded ;
13591390 }
13601391
1361- /**
1362- * if $lazy is:
1363- * - false: set fast config as loaded
1364- * - true : set lazy config as loaded
1365- * - null : set both config as loaded
1366- *
1367- * @param bool $lazy
1368- */
1369- private function setAsLoaded (?bool $ lazy ): void {
1370- if ($ lazy === null ) {
1371- $ this ->fastLoaded = true ;
1372- $ this ->lazyLoaded = true ;
1373-
1374- return ;
1375- }
1376-
1377- if ($ lazy ) {
1378- $ this ->lazyLoaded = true ;
1379- } else {
1380- $ this ->fastLoaded = true ;
1381- }
1382- }
1383-
13841392 /**
13851393 * Gets the config value
13861394 *
13871395 * @param string $app app
13881396 * @param string $key key
13891397 * @param string $default = null, default value if the key does not exist
13901398 *
1391- * @return string the value or $default
1399+ * @return ? string the value or $default
13921400 * @deprecated 29.0.0 use getValue*()
13931401 *
13941402 * This function gets a value from the appconfig table. If the key does
@@ -1421,7 +1429,7 @@ public function setValue($app, $key, $value) {
14211429 * or enabled (lazy=lazy-2)
14221430 *
14231431 * this solution would remove the loading of config values from disabled app
1424- * unless calling the method {@see loadConfigAll()}
1432+ * unless calling the method.
14251433 */
14261434 return $ this ->setTypedValue ($ app , $ key , (string )$ value , false , self ::VALUE_MIXED );
14271435 }
@@ -1733,7 +1741,7 @@ private function matchAndApplyLexiconDefinition(
17331741 *
17341742 * @return bool TRUE if conflict can be fully ignored, FALSE if action should be not performed
17351743 * @throws AppConfigUnknownKeyException if strictness implies exception
1736- * @see ILexicon::getStrictness()
1744+ * @see \OCP\Config\Lexicon\ ILexicon::getStrictness()
17371745 */
17381746 private function applyLexiconStrictness (
17391747 ?Strictness $ strictness ,
@@ -1772,8 +1780,9 @@ public function getConfigDetailsFromLexicon(string $appId): array {
17721780 $ configLexicon = $ bootstrapCoordinator ->getRegistrationContext ()?->getConfigLexicon($ appId );
17731781 foreach ($ configLexicon ?->getAppConfigs() ?? [] as $ configEntry ) {
17741782 $ entries [$ configEntry ->getKey ()] = $ configEntry ;
1775- if ($ configEntry ->getRename () !== null ) {
1776- $ aliases [$ configEntry ->getRename ()] = $ configEntry ->getKey ();
1783+ $ newName = $ configEntry ->getRename ();
1784+ if ($ newName !== null ) {
1785+ $ aliases [$ newName ] = $ configEntry ->getKey ();
17771786 }
17781787 }
17791788
@@ -1819,4 +1828,16 @@ public function getAppInstalledVersions(bool $onlyEnabled = false): array {
18191828 }
18201829 return $ this ->appVersionsCache ;
18211830 }
1831+
1832+ private function updateCache (): void {
1833+ $ this ->localCache ?->set(
1834+ self ::LOCAL_CACHE_KEY ,
1835+ [
1836+ 'fastCache ' => $ this ->fastCache ,
1837+ 'lazyCache ' => $ this ->lazyCache ,
1838+ 'valueTypes ' => $ this ->valueTypes ,
1839+ ],
1840+ self ::LOCAL_CACHE_TTL ,
1841+ );
1842+ }
18221843}
0 commit comments