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.
Comment le stockage persistant est activé
Section intitulée « Comment le stockage persistant est activé »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.
Opt-in côté plugin (storage_mode)
Section intitulée « Opt-in côté plugin (storage_mode) »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 PersistentStoreModefrom 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.AUTOModes de stockage persistant
Section intitulée « Modes de stockage persistant »| Mode | Comportement | Quand l’utiliser |
|---|---|---|
MEMORY | Mémoire seule, pas d’usage du disque | Environnements éphémères ou restreints ; c’est le défaut pour tous les plugins sauf surcharge via la variable de classe storage_mode |
AUTO | Disque 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.
Accéder au stockage persistant dans un plugin
Section intitulée « Accéder au stockage persistant dans un plugin »Chaque plugin reçoit un store prêt à l’emploi via self.store.
# À l'intérieur d'un plugin NotifyBasestore = self.storeLe store se comporte comme un dictionnaire, mais avec des notions supplémentaires d’expiration, de persistance et de sécurité.
Identité du cache et url_identifier
Section intitulée « Identité du cache et url_identifier »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.
Schémas d’usage clé / valeur
Section intitulée « Schémas d’usage clé / valeur »Écrire et lire des valeurs
Section intitulée « Écrire et lire des valeurs »# Stocker une valeurself.store['server_version'] = '1.8.2'
# Récupérer une valeurversion = self.store.get('server_version')if version: ...- les valeurs peuvent être de n’importe quel type sérialisable en JSON ;
- la lecture renvoie
Nonesi la valeur est absente ou expirée ; - la persistance disque est automatique lorsqu’elle est activée.
Faire expirer des valeurs mises en cache
Section intitulée « Faire expirer des valeurs mises en cache »# Mettre en cache pour 1 heureself.store.set( 'capabilities', {'markdown': True, 'html': False}, expires=3600,)L’expiration peut être exprimée comme :
- un nombre de secondes à partir de maintenant (
intoufloat) ; - un
datetime; True(expiration immédiate) ;NoneouFalse(n’expire jamais).
Les entrées expirées deviennent automatiquement invalides.
Entrées mémoire seule
Section intitulée « Entrées mémoire seule »# Mettre en cache uniquement pour la durée du processusself.store.set( 'session_token', token, persistent=False,)- stockées uniquement en mémoire ;
- jamais écrites sur disque ;
- utiles pour des valeurs sensibles ou limitées à une requête.
Comprendre la sémantique d’expiration
Section intitulée « Comprendre la sémantique d’expiration »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 à
Falselorsqu’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.
Stockage basé sur des fichiers (avancé)
Section intitulée « Stockage basé sur des fichiers (avancé) »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 brutesself.store.write(b'my binary content')
# Les relirecontent = 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.
# Écrire du contenu non compresséself.store.write( 'plain text', key='custom-key', compress=False,)
content = self.store.read('custom-key', compress=False)Chaque clé correspond à son propre fichier persistant.
with self.store.open('key', 'wb') as fp: fp.write(b'data')
with self.store.open('key', 'rb') as fp: content = fp.read()Cela reflète la sémantique standard des fichiers Python tout en restant sûr au niveau du namespace.
Maintenance et nettoyage du cache
Section intitulée « Maintenance et nettoyage du cache »Effacer des entrées
Section intitulée « Effacer des entrées »# Supprimer des clés spécifiquesself.store.clear('key1', 'key2')
# Tout supprimerself.store.clear()Supprimer des fichiers persistants
Section intitulée « Supprimer des fichiers persistants »self.store.delete('key')self.store.delete('key1', 'key2')Purger les entrées expirées
Section intitulée « Purger les entrées expirées »self.store.prune()Les entrées expirées sont retirées de la mémoire et marquées pour nettoyage disque.
Opérations disque (avancé)
Section intitulée « Opérations disque (avancé) »Ces opérations sont facultatives et surtout utiles pour les services longue durée.
# Supprimer les fichiers persistants expirésPersistentStore.disk_prune(path)
# Scanner les namespacesPersistentStore.disk_scan(path)Ces fonctions opèrent en dehors d’une instance de plugin et doivent être utilisées avec prudence.
Garanties de sécurité automatiques
Section intitulée « Garanties de sécurité automatiques »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.
Bonnes pratiques pour les auteurs de plugins
Section intitulée « Bonnes pratiques pour les auteurs de plugins »- 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.
Plugins qui utilisent le stockage persistant
Section intitulée « Plugins qui utilisent le stockage persistant »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_serveretuser_idobtenus lors de la connexion ou de l’inscription. Il stocke aussi untransaction_idpour é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
fromet 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.ascetpgp-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 :