Aller au contenu

Stockage Persistant

Le stockage persistant permet aux plugins Apprise de conserver en toute sécurité de petites quantités d’état entre plusieurs exécutions.

Cette fonctionnalité est destinée aux auteurs de plugins et existe pour éviter des requêtes inutiles ou répétées vers des services amont lorsque l’information peut être réutilisée localement.

Le stockage persistant est opportuniste et best-effort. Si le stockage sur disque est indisponible ou jugé non sûr, Apprise repasse automatiquement en mode mémoire seule sans faire échouer l’envoi de notifications.

Le stockage persistant est :

  • un stockage clé/valeur léger ;
  • compartimenté par plugin ;
  • conscient de l’expiration ;
  • résilient aux crashs ;
  • sûr aussi bien dans des processus courts que longs.

Le stockage persistant n’est pas :

  • une base de données générale ;
  • un cache orienté utilisateur ;
  • un remplacement pour l’état applicatif ;
  • une couche de persistance long terme garantie.

Il est volontairement limité à des métadonnées petites et fréquemment réutilisées, comme :

  • les résultats de découverte de capacités ;
  • les endpoints API négociés ;
  • les identifiants distants ;
  • des jetons d’authentification ou indicateurs de fonctionnalités ;
  • des métadonnées de service mises en cache.

Le stockage persistant est contrôlé par la configuration d’asset Apprise, pas directement par le plugin.

L’objet AppriseAsset définit :

  • où les données persistantes peuvent être écrites ;
  • si la persistance sur disque est autorisée ;
  • comment Apprise se comporte si le stockage est indisponible.

Si aucun chemin de stockage n’est disponible, Apprise bascule de façon transparente vers un stockage mémoire seule.

Les plugins n’ont pas besoin de gérer eux-mêmes ce repli.

Le stockage persistant est toujours disponible via self.store, mais la persistance disque nécessite un opt-in par plugin.

Par défaut, les plugins considèrent le store comme mémoire seule. Pour permettre à Apprise de réutiliser l’état mis en cache entre plusieurs exécutions (lorsque l’AppriseAsset actif l’autorise), définissez un storage_mode au niveau de la classe :

from apprise.common import PersistentStoreMode
from apprise.plugins import NotifyBase
class MyPlugin(NotifyBase):
# Autoriser le stockage persistant à utiliser le disque quand il est disponible.
# Sans cela, le store se comporte en mémoire seule pour ce plugin.
storage_mode = PersistentStoreMode.AUTO
ModeComportementQuand l’utiliser
MEMORYMémoire seule, pas d’usage du disqueEnvironnements éphémères ou restreints ; c’est le défaut pour tous les plugins sauf surcharge via la variable de classe storage_mode
AUTODisque si disponible, sinon mémoireÉcrit sur disque uniquement quand le plugin est détruit (en fin de vie) ; choix recommandé par défaut
FLUSHÉcrit agressivement sur disqueÉchange davantage d’I/O contre plus de durabilité. Adapté aux démons longue durée qui conservent des métadonnées critiques

AUTO est le choix le plus sûr et le plus portable à travers les conteneurs, la CI et les services système.

Chaque plugin reçoit un store prêt à l’emploi via self.store.

# À l'intérieur d'un plugin NotifyBase
store = self.store

Le store se comporte comme un dictionnaire, mais avec des notions supplémentaires d’expiration, de persistance et de sécurité.

Le stockage persistant est délimité à l’aide de l’identité d’URL du plugin. Cette identité est dérivée de ce que votre plugin renvoie dans url_identifier.

Lorsque vous pensez à la syntaxe URL universelle, vous ne construisez un url_identifier qu’avec service:// et les credentials, pour obtenir les meilleurs résultats, par exemple :

service://credentials/direction/?parameter=value
| |
| |
| |
| @property |
| url_identifier() |

Il ne doit pas inclure :

  • le routage destinataire ou cible (tout ce qui appartient au chemin d’URL pour la livraison) ;
  • les composants de type direction servant seulement à choisir une destination ;
  • les options de chaîne de requête qui ne changent pas l’identité amont.

Cela vous permet d’exécuter de nombreuses notifications vers différents destinataires tout en partageant le même état mis en cache pour le même serveur amont, comme des jetons OAuth, des résultats de découverte ou des identifiants résolus.

# Stocker une valeur
self.store['server_version'] = '1.8.2'
# Récupérer une valeur
version = self.store.get('server_version')
if version:
...
  • les valeurs peuvent être de n’importe quel type sérialisable en JSON ;
  • la lecture renvoie None si la valeur est absente ou expirée ;
  • la persistance disque est automatique lorsqu’elle est activée.

Une valeur stockée :

  • existe en mémoire jusqu’à expiration ou suppression ;
  • peut rester sur disque après expiration ;
  • est traitée comme invalide une fois expirée ;
  • s’évalue à False lorsqu’elle a expiré.
if 'key' in self.store:
# Vrai uniquement si la clé existe ET n'a pas expiré

L’expiration est toujours appliquée, même si le fichier existe encore sur disque.

Le stockage persistant prend aussi en charge l’accès direct à des fichiers pour des données spécifiques au plugin.

# Écrire des données brutes
self.store.write(b'my binary content')
# Les relire
content = self.store.read()
  • le contenu est renvoyé sous forme de bytes ;
  • les fichiers peuvent être compressés par défaut ;
  • utile pour des certificats, des jetons ou des blobs.
# Supprimer des clés spécifiques
self.store.clear('key1', 'key2')
# Tout supprimer
self.store.clear()
self.store.delete('key')
self.store.delete('key1', 'key2')
self.store.prune()

Les entrées expirées sont retirées de la mémoire et marquées pour nettoyage disque.

Ces opérations sont facultatives et surtout utiles pour les services longue durée.

# Supprimer les fichiers persistants expirés
PersistentStore.disk_prune(path)
# Scanner les namespaces
PersistentStore.disk_scan(path)

Ces fonctions opèrent en dehors d’une instance de plugin et doivent être utilisées avec prudence.

Le stockage persistant est conçu pour ne jamais bloquer les notifications.

Si l’accès au disque échoue à cause de :

  • permissions ;
  • systèmes de fichiers en lecture seule ;
  • corruption ;
  • restrictions de conteneur ;

Apprise repasse automatiquement en mode MEMORY et continue de fonctionner.

Aucune gestion d’erreur côté plugin n’est requise.

  • traitez le stockage persistant comme une optimisation, pas comme une dépendance ;
  • ne stockez que de petites valeurs ;
  • gérez toujours proprement les données absentes ou expirées ;
  • préférez l’expiration à l’invalidation manuelle ;
  • utilisez des clés claires et spécifiques au plugin ;
  • partez du principe que le stockage peut être indisponible à l’exécution.

Les plugins principaux suivants l’utilisent déjà :

  • Matrix : met en cache les métadonnées de session et de découverte Matrix, y compris access_token, home_server et user_id obtenus lors de la connexion ou de l’inscription. Il stocke aussi un transaction_id pour éviter le traitement en double des messages, met en cache les résultats de résolution d’alias de salon et persiste les résultats de découverte .well-known. Cela évite des réauthentifications, découvertes et résolutions répétées d’une exécution à l’autre.

  • Nextcloud : met en cache les résultats de découverte de destinataires. La résolution des noms de groupe et des recherches « tous les utilisateurs » est mise en cache pour éviter des appels API répétés lorsque les mêmes groupes sont fréquemment référencés. La durée de vie du cache est contrôlée par la configuration du plugin.

  • Office365 : met en cache les métadonnées d’identité d’expéditeur résolues. Lorsque l’expéditeur configuré n’est pas une adresse email pleinement qualifiée, le plugin résout la bonne adresse from et le nom d’affichage via Microsoft Graph puis conserve le résultat pour les envois suivants.

  • Opsgenie : met en cache les identifiants de requête d’alerte retournés lors de la création d’alertes. Ces identifiants sont indexés sous une clé stable dérivée et conservés longtemps, permettant à des actions de suivi comme acknowledge, close, note ou delete d’agir sur des alertes déjà créées sans demander à l’utilisateur de fournir à nouveau l’ID.

  • SendPulse : met en cache des jetons d’accès OAuth. Les jetons sont stockés avec une expiration dérivée de la valeur amont expires_in (avec une marge de sécurité), permettant aux notifications suivantes de les réutiliser et d’éviter des réauthentifications inutiles.

  • Telegram : met en cache l’identifiant utilisateur du propriétaire du bot lorsqu’une détection automatique est activée. Une fois découvert, cet identifiant est réutilisé pour les notifications futures afin de ne pas répéter cette détection aux exécutions suivantes.

  • Email (PGP) : utilise le stockage persistant pour conserver les paires de clés PGP générées automatiquement. Lorsque le chiffrement PGP est activé sans fichiers de clés explicites, le plugin génère une paire de clés publique/privée à la première utilisation et écrit les deux dans le chemin de stockage persistant (pgp-public.asc et pgp-private.asc). Les exécutions suivantes chargent les mêmes clés depuis le disque afin que chaque message chiffré soit signé et chiffré avec une identité stable et cohérente.

N’hésitez pas à vous inspirer de ces exemples pour concevoir votre propre stratégie ou améliorer un plugin existant qui pourrait vraiment bénéficier du stockage persistant.

Questions ou commentaires ?

Documentation

Vous avez repéré une faute de frappe ou une erreur ? Signalez-la ou proposez une correction .

Problèmes Techniques

Vous rencontrez un problème avec le code ? Ouvrez un ticket sur GitHub :

Conçu avec amour depuis le Canada