You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/apis/subsystems/muc/index.md
+17-20Lines changed: 17 additions & 20 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -270,38 +270,35 @@ So if you want very high performance caching then you need to write you code so
270
270
Not all caches are good candidates to localize and some can have a detrimental effect if localized for the wrong reasons.
271
271

272
272
273
-
### Revision numbers as key suffix
273
+
### Versioned caches
274
274
275
-
A simple method is storing a version number somewhere and appending that to your key. This is the most common method used in Moodle, simply store a number somewhere which is globally shared such as in a custom database field, or using `set_config` / `get_config`.
275
+
When data changes, you can often identify a 'version' by either a numeric time value, or an incremented version number, in the database.
276
276
277
-
One small edge case with this approach is you now need to make sure that the incrementing code is atomic, which means you should use a DB transaction, or gain a lock, before bumping the version so you don't get two conflicting changes ending up on the same version with a race condition. However if you are anticipating high turnover rate of the cache you probably have a deeper issue, see 'Fast churning keys' below.
277
+
When you have a version number, the best way to ensure that cached data is current (the cache does not return data from old versions) is to use the `set_versioned` and `get_versioned` functions within the cache. These must be used as a pair - you cannot set data with `set_versioned` and retrieve it with `get`, or vice versa.
278
278
279
-
One potential benefit of a simple version number strategy if your cache misses are very expensive, is that you can check for the presence of a version, and if it doesn't exist it is easy to simply retrieve the previous version and use it in the mean time, while you could generate the new version asynchronously. This is an advanced concept and depends on the use case and has the obvious disadvantage of showing stale data.
279
+
The `get_versioned` function takes a required version integer, and will only return cached data that is equal to, or newer than, the required version. Optionally, you can also have it provide the actual version that was returned, in case it is newer than requested.
280
280
281
-
:::info
282
-
283
-
An example of this strategy in Moodle is the theme versions cache: https://github.com/moodle/moodle/blob/main/lib/outputlib.php#L88-L100
281
+
These functions work efficiently with multi-layer caches:
284
282
285
-
Note this is not actually in MUC but the caching concepts are the same.
283
+
- If the correct version is available in local cache, it will be returned directly without accessing the shared cache; otherwise it will be requested from shared cache and stored in the local cache.
284
+
- When a new value is set, it will be set in both local cache and shared cache.
285
+
- These properties mean that if a new cache version needs to be computed, it will usually only be computed on a single server (except for race conditions, which you can avoid using locking if required) and that reading the cached data is efficient.
286
286
287
-
:::
287
+
At each point, the cache stores only a single version, which is important for large cached data such as course modinfo because the older approaches (below) can waste cache memory by storing older unnecessary versions.
288
288
289
-
It works best with a cache store that supports Least Recently Used garbage collection.
289
+
### Version numbers as key suffix
290
290
291
-
### Revision numbers as value suffix instead of key suffix
291
+
An earlier approach is using the same type of version number and appending that to your key. This works, but uses more storage in the cache, especially if data items are large or change frequently.
292
292
293
-
This is conceptually the same as above but has two important differences. Firstly because the key itself is always the same then only a single version of some value will be stored on disk or in memory for any given cache store which makes it much more efficient in terms of storage. But secondly this comes with a higher coding complexity cost because it will no longer be guaranteed to be correct because a local cache could return a hit with a stale version. So if you need it to be correct you will need to parse out the revision from the value and then confirm the revision is correct before using the value. If it is invalid then you need to treat it as a miss and handle that. One way is to rebuild and set it, but this loses the advantage of primary and final caches (see below). A better way is to delete the local cache but not the final cache by passing a second param of false to delete, and then getting the value again which will repopulate the local cache from the shared cache:
293
+
### Version numbers as value suffix instead of key suffix
294
294
295
-
```php
296
-
$cache->delete('foo', false); // Delete the primary / local stale version
297
-
$cache->get('foo'); // Get the final / shared version (which may also be stale!)
298
-
```
295
+
Another earlier approach was to store revision numbers inside the cache value. This avoids storing duplicate copies of the same cache entry but is not recommended because it is difficult to ensure correct data on multi-level caches. Instead, the versioned cache functions should now be used.
299
296
300
-
But this also is imperfect if there are 3 layers of caching, see [MDL-72837](https://moodle.atlassian.net/browse/MDL-72837) for a full discussion and a possible new api to handle this.
297
+
### Incrementing version numbers
301
298
302
-
This method is how the course modinfo cache is localized.
299
+
If you increment a number for each version, you need to use database transactions or locks to ensure that the same number is not used twice due to race conditions.
303
300
304
-
### Using time as a key suffix
301
+
### Timestamps as version numbers
305
302
306
303
Another common approach is to use a timestamp, this is how some of the core cache numbers work, see `increment_revision_number()` for some examples. This has the benefit of not needing any transaction or locking, but you do run the risk of two processes clashing if they happen to run in the same second.
307
304
@@ -311,7 +308,7 @@ It may look like Moodle theme-related caching uses this strategy, but actually i
0 commit comments