-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4192e4b
commit 362055d
Showing
1 changed file
with
80 additions
and
0 deletions.
There are no files selected for viewing
80 changes: 80 additions & 0 deletions
80
docs/adr/0058-modification-du-cache-du-contenu-pedagogique.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# 57. Modification du fonctionnement du cache de contenu pédagogique | ||
|
||
Date : 2024-12-16 | ||
|
||
## État | ||
|
||
Validée | ||
|
||
## Historique | ||
|
||
Pour ceux qui veulent toute l'histoire, voici le lien vers les précédentes ADR expliquant le fonctionnement du cache de contenu pédagogique aujourd'hui : | ||
|
||
- [ADR de la mise en place du cache](0005-ajout-d-un-cache-memoire-distribute-pour-le-contenu-pedagogique.md) | ||
- [ADR traitant du stockage en clé unique dans Redis et en mémoire vive](0016-stockage-du-referentiel-en-cache.md) | ||
|
||
## Contexte | ||
|
||
Le fonctionnement aujourd'hui consiste à stocker l'entièreté du contenu pédagogique en deux endroits : | ||
|
||
- en mémoire vive | ||
- sur Redis, de sorte que une machine qui vient de lancer un serveur récupère rapidement la donnée | ||
|
||
### Limitations et problèmes | ||
|
||
#### Crash mémoire | ||
|
||
Le contenu pédagogique est un ensemble dont la taille grossit, doucement mais sûrement. Cela fait plusieurs semaines qu'on constate un dépassement mémoire sur les containers | ||
lorsqu'ils traitent des opérations liées au cache de contenu pédagogique. Ces dépassements sont liés aux moments de lecture et d'écriture, depuis et vers le cache Redis. | ||
Voici un extrait du fichier `RedisCache.js` : | ||
```js | ||
class RedisCache extends Cache { | ||
// Lecture depuis le cache Redis | ||
async get(key, generator) { | ||
const value = await this._client.get(key); | ||
|
||
if (value) { | ||
const parsed = JSON.parse(value); // Dépassement mémoire ici | ||
const patches = await this._client.lrange(`${key}:${PATCHES_KEY}`, 0, -1); | ||
patches.map((patchJSON) => JSON.parse(patchJSON)).forEach((patch) => applyPatch(parsed, patch)); | ||
return parsed; | ||
} | ||
|
||
return this._manageValueNotFoundInCache(key, generator); | ||
} | ||
// Ecriture vers le cache Redis | ||
async set(key, object) { | ||
const objectAsString = JSON.stringify(object); // Dépassement mémoire ici | ||
|
||
logger.info({ key, length: objectAsString.length }, 'Setting Redis key'); | ||
|
||
await this._client.set(key, objectAsString); | ||
await this._client.del(`${key}:${PATCHES_KEY}`); | ||
|
||
return object; | ||
} | ||
/* ... */ | ||
} | ||
``` | ||
Redis stocke des chaînes de caractères, tandis qu'en mémoire vive nous conservons le contenu pédagogique en **_POJO_** (**_plain old javascript object_**). Cette conversion dans | ||
les deux sens est donc faite dans le code (via `JSON.stringify` et `JSON.parse`), ce qui signifie qu'à un moment donné, dans la pile mémoire de traitement de la fonction, | ||
on a simultanément le contenu pédagogique en **_POJO_** et en chaînes de caractères. Sachant que, ce jour, le contenu pédagogique fait environ 40 Mo, on peut estimer à, au | ||
minimum, 80 Mo de données, sans parler des allocations diverses et variées nécessaires à l'exécution du code. | ||
|
||
#### Peu optimisé | ||
Le contenu pédagogique est stocké dans une seule clé. Cela pose deux défauts majeurs. | ||
|
||
D'une part, nous n'avons pas le choix de ce qui mérite d'être promu dans le cache mémoire ou pas. Tout y est. | ||
Pourtant, on sait qu'un certain nombre d'entités sont très peu ou pas consultés dans le fonctionnement des applications Pix, voici une liste non exhaustive : | ||
- Les données sur des entités peu affichées (`frameworks` ou `thematics`) | ||
- Les épreuves non jouables (car périmées ou en atelier) | ||
- Les épreuves dans une langue encore peu jouée sur Pix | ||
|
||
|
||
D'autre part, et on pense en particulier aux acquis et aux épreuves, il est fréquent de vouloir récupérer toujours le même sous-ensemble. | ||
Par exemple, tous les utilisateurs qui se positionnent sur la même compétence, dans le code pour récupérer la prochaine épreuve, | ||
on récupère les mêmes acquis et les mêmes épreuves juste avant de dérouler l'algorithme du choix d'épreuve. Aujourd'hui, on effectue donc les mêmes boucles et les mêmes filtres. | ||
|
||
|
||
## Solution | ||
|