Identité

Cycle de vie d'IndexedDB : versions, quota et nettoyage

Ouvrez et versionnez IndexedDB sans risque, mettez à niveau sans bloquer les autres onglets, anticipez quota et éviction, et supprimez les données à la déconnexion.

Documentation

Vous voulez la documentation structurée pour Identité ?

Cet article fait partie de la bibliothèque éditoriale. Pour les étapes de configuration, la référence et les mises à jour continues, passez directement à la section docs.

Ouvrez une base de données avec une version explicite

Une base de données IndexedDB suit un cycle de vie en quatre étapes que le code de l'application doit gérer volontairement : l'ouverture avec une version, la mise à niveau du schéma, la vie dans le quota géré par le navigateur, et la suppression lorsque les données ne doivent plus exister. Chaque étape dispose d'un événement ou d'une API qui signale ce qui s'est passé, et chacune a une issue d'échec visible par l'utilisateur. Une application fiable ouvre la base avec une version explicite, ne modifie le schéma que dans l'étape de mise à niveau, ferme sa connexion lorsqu'un autre onglet demande une version plus récente, consigne ce qui se passe quand l'espace manque, et supprime les bases du compte précédent à la déconnexion avec un résultat borné lorsque la suppression est bloquée.

Tout ce qui suit concerne les données que stocke votre propre application. Lire ou inspecter les données stockées par un autre site est une tâche différente, et les noms de bases et de magasins des exemples sont inventés, pas tirés d'un site réel. Pour situer IndexedDB à côté des cookies et de Web Storage, consultez Cookies, localStorage et IndexedDB : où ranger l’état.

Cycle de vie d'une base IndexedDB : ouvrir avec une version explicite, mettre à niveau uniquement dans upgradeneeded, anticiper quota et éviction, supprimer à la déconnexion et vérifier dans les outils de développement

Le point d'entrée est indexedDB.open(name, version). Il renvoie une requête d'ouverture et non une base de données. La connexion arrive dans l'événement success de la requête, et toute création ou modification du schéma a lieu dans l'événement upgradeneeded, qui se déclenche en premier lorsque la version demandée est supérieure à la version stockée. Si la base n'existe pas encore, elle est créée à la version demandée et upgradeneeded s'exécute une fois pour construire les magasins d'objets initiaux. L'API Indexed Database du W3C définit cette séquence, et le guide MDN d'utilisation d'IndexedDB la parcourt avec des exemples.

Passez la version de façon explicite et conservez-la comme constante nommée dans le code qui possède le schéma. Lorsque l'argument de version est omis, le navigateur ouvre la base existante à sa version actuelle, ou en crée une nouvelle à la première version, si bien que le code ne peut pas demander une mise à niveau volontairement. Un entier que l'équipe augmente à chaque changement de schéma rend l'historique vérifiable : chaque numéro correspond à un ensemble connu de magasins d'objets et d'index, et une demande de fusion qui modifie le schéma modifie aussi la constante.

Une version inférieure à celle qui est stockée est également une issue définie. La requête d'ouverture échoue avec une VersionError. Cela arrive lorsqu'un utilisateur garde un ancien onglet ouvert alors qu'un onglet plus récent a déjà mis à niveau la base, ou lorsqu'une ancienne copie en cache du code de l'application s'exécute après une version plus récente. Traitez l'erreur comme un état de l'application avec un message et une action de rechargement, et ne réessayez pas en boucle avec un numéro de version deviné.

Gérez les issues de la requête à un seul endroit. L'événement success fournit la connexion, error signale un échec tel qu'une VersionError ou un problème de stockage dont le navigateur ne peut pas se remettre, et blocked signale que d'autres connexions gardent la base ouverte pendant qu'une mise à niveau attend. Enveloppez l'appel d'ouverture dans une petite fonction qui renvoie une promesse, attachez le gestionnaire versionchange à la connexion avant de la renvoyer, et faites demander la base à cette fonction par le reste de l'application au lieu d'ouvrir la sienne. Un seul responsable de l'ouverture et de la fermeture garde le cycle de vie de la connexion facile à raisonner.

Nommez les bases et les magasins d'objets d'après ce qu'ils contiennent. Utilisez un nom de base stable choisi par l'application et, lorsqu'une même origine sert plusieurs comptes, placez dans le nom une clé locale opaque du compte plutôt qu'une adresse e-mail ou d'autres données personnelles. Le nom est visible dans les outils de développement et apparaît souvent dans les rapports d'assistance, et c'est aussi l'identifiant dont un parcours de déconnexion a besoin plus tard pour retrouver et supprimer les données d'un compte.

Mettez à niveau le schéma sans bloquer les autres onglets

Les changements de schéma ne sont autorisés que dans le gestionnaire upgradeneeded. Pendant cet événement, la connexion détient une transaction spéciale de changement de version, et c'est seulement à l'intérieur que le code peut appeler createObjectStore, deleteObjectStore, createIndex ou deleteIndex. En dehors, ces appels lèvent une erreur. La spécification du W3C donne à l'événement les valeurs oldVersion et newVersion, qui permettent à un seul gestionnaire de faire avancer une base depuis n'importe quelle version antérieure.

Écrivez le gestionnaire comme une suite d'étapes indexées sur l'ancienne version : si l'ancienne version est inférieure à la première cible, créez les magasins initiaux ; si elle est inférieure à la deuxième, ajoutez le nouvel index ; et ainsi de suite. Un utilisateur peut arriver depuis n'importe quelle version antérieure, donc chaque étape doit s'exécuter correctement dans l'ordre et de façon autonome. Limitez le gestionnaire au travail sur IndexedDB. La transaction de mise à niveau se termine lorsqu'elle n'a plus de requêtes en attente, de sorte qu'attendre un appel réseau ou une promesse étrangère dans le gestionnaire peut mettre fin à la transaction avant l'étape suivante. Récupérez les données distantes une fois la requête d'ouverture réussie, pas pendant la mise à niveau.

Convertir des enregistrements existants vers une nouvelle forme est la partie risquée. Exécutez la conversion dans la même transaction de mise à niveau en parcourant l'ancien magasin avec un curseur, afin que l'étape soit validée en entier ou pas du tout. Si le gestionnaire lève une exception, ou appelle abort() sur la transaction, la version reste inchangée et la requête d'ouverture échoue avec une AbortError, ce qui laisse intact le schéma précédent. Gardez chaque migration petite et testez-la sur une base créée à chaque ancienne version que l'application prend encore en charge, pas seulement la plus récente.

Le deuxième onglet est l'endroit où les mises à niveau se passent le plus souvent mal. IndexedDB ne permet à une base de changer de version que lorsqu'aucune connexion plus ancienne n'est ouverte. Lorsqu'un onglet demande une version supérieure, le navigateur déclenche versionchange sur les connexions que d'autres onglets détiennent encore. Le guide MDN d'utilisation d'IndexedDB indique que le gestionnaire doit fermer la connexion afin que l'autre page puisse effectuer la mise à niveau. S'il ne le fait pas, la nouvelle requête d'ouverture déclenche blocked et reste en attente, la mise à niveau ne s'exécute pas, et l'utilisateur voit le nouvel onglet attendre dans un état de chargement.

Un bon gestionnaire fait deux choses. Il appelle db.close() immédiatement, puis informe l'utilisateur de ce qui s'est passé, par exemple en indiquant que l'application a été mise à jour dans un autre onglet et en proposant de recharger. La fermeture libère la base pour l'onglet qui effectue la mise à niveau, et le message explique pourquoi cet onglet a cessé de fonctionner. Ne continuez pas à utiliser une connexion après l'avoir fermée, car les nouvelles transactions sur celle-ci lèvent une erreur. Le même événement se déclenche lorsqu'un onglet appelle deleteDatabase, donc un seul gestionnaire couvre à la fois les mises à niveau et les suppressions.

L'onglet qui a demandé la mise à niveau doit aussi gérer blocked. Affichez un court avis indiquant qu'une autre fenêtre reste ouverte sur une version plus ancienne, laissez la requête d'ouverture en attente et laissez la mise à niveau se terminer dès que l'autre connexion se ferme. Si l'application exécute aussi un service worker ou un worker partagé qui ouvre la base, incluez-le dans la revue. Un worker est une autre connexion qui peut maintenir une mise à niveau en attente, et il a besoin du même traitement de versionchange.

Anticipez le quota et l'éviction

Les navigateurs se partagent entre les origines une quantité limitée d'espace disque, et les données IndexedDB vivent dans ce budget. Le Storage Standard décrit le stockage d'une origine comme étant au mieux par défaut : le navigateur peut le supprimer sous pression de stockage sans demander. La page MDN sur les quotas et critères d'éviction explique que les limites et l'ordre d'éviction varient selon le navigateur. Les chiffres de capacité diffèrent selon les navigateurs, les appareils et les versions, donc l'application ne doit pas dépendre d'un nombre qu'elle ne peut pas vérifier.

Deux issues demandent une réponse définie de l'application. La première est une écriture qui ne tient pas. Lorsqu'une transaction ne peut pas être validée parce que l'espace est épuisé, l'échec est signalé par une QuotaExceededError et la transaction est annulée. Écoutez abort et error sur la transaction, pas seulement sur chaque requête, et décidez à l'avance ce que voit l'utilisateur. Parmi les réponses raisonnables figurent l'abandon des enregistrements en cache les moins utiles, la mise en pause de la synchronisation, ou la demande à l'utilisateur de libérer de l'espace, selon le type de données.

La seconde issue est celle de données tout simplement disparues. Une origine au mieux peut être évincée, et les navigateurs qui évincent suppriment généralement les données d'une origine d'un seul bloc plutôt que quelques enregistrements à la fois. La visite suivante trouve une base vide, et l'application la rouvre à la première version. Concevez pour ce cas. Conservez un enregistrement témoin ou un magasin de métadonnées pour que le code de démarrage distingue une première installation d'une éviction, et reconstruisez depuis le serveur lorsque les données y ont une copie.

L'appel navigator.storage.estimate() renvoie des valeurs approximatives d'usage et de quota qui aident à décider quand réduire un cache, et navigator.storage.persist() demande au navigateur de traiter les données de l'origine comme persistantes. Ce sont des estimations et des demandes, telles que les décrit le Storage Standard. Le navigateur peut refuser la persistance, peut interroger l'utilisateur, ou peut décider sans invite. Consignez dans la revue le résultat de la demande plutôt que de supposer qu'elle a été accordée, et gardez le même chemin de reprise pour le cas où elle ne l'a pas été.

Consignez les décisions de chaque magasin d'objets dans un petit tableau placé à côté du code du schéma. Pour chaque magasin, notez son responsable, sa règle de rétention et ce que fait l'application lorsque le quota est dépassé ou que les données ont été évincées. Un magasin de brouillons que l'utilisateur n'a pas synchronisés demande un avertissement et un chemin d'export. Un magasin de réponses de serveur en cache demande seulement une reconstruction. Un magasin de réglages peut demander une valeur par défaut. Les règles de rétention appartiennent au même tableau : un cache d'éléments récents peut être élagué au démarrage en parcourant avec un curseur un index sur un champ d'horodatage et en supprimant les enregistrements plus anciens que l'âge indiqué, par petits lots pour que chaque transaction reste courte.

Supprimez les données de l'application à la déconnexion et au changement de compte

La déconnexion est une décision sur les données, pas seulement sur la session. Lorsqu'une personne se déconnecte d'un ordinateur partagé, ou qu'un compte en remplace un autre dans le même profil de navigateur, les données IndexedDB du compte précédent restent sur le disque sous la même origine jusqu'à ce que l'application les supprime. Un parcours de nettoyage a besoin d'une liste claire de ce qu'il faut supprimer, d'un moyen de le retrouver et d'une issue définie lorsque la suppression ne peut pas aboutir.

Tenez un registre des noms de bases que crée l'application, par exemple une courte liste dans le code ou un enregistrement de métadonnées, plutôt que de compter sur la découverte. Lorsqu'elle est disponible, indexedDB.databases() peut lister les bases de l'origine et sert bien à une étape de vérification, mais la prise en charge par les navigateurs a varié avec le temps, donc consultez les notes de compatibilité avant d'en faire la seule source. Avec un registre, la déconnexion devient une boucle : fermez les connexions de cet onglet, puis appelez indexedDB.deleteDatabase(name) pour chaque nom qui appartient au compte qui s'en va.

La méthode renvoie une requête, comme open. Sa page de référence indique que la suppression déclenche versionchange sur les connexions ouvertes et, si certaines restent ouvertes, déclenche blocked sur la requête, et que la suppression attend leur fermeture. C'est pourquoi le gestionnaire versionchange de la section sur la mise à niveau compte aussi ici, et pourquoi un parcours de déconnexion qui ne ferme pas d'abord sa propre connexion se bloquera lui-même. Donnez au parcours une issue bornée : attendez success et, si blocked arrive sans se résoudre dans une courte attente choisie par l'application, consignez que la purge est en attente, informez l'utilisateur et réessayez au prochain démarrage avant de lire la moindre donnée du compte précédent. Évitez un indicateur d'attente sans fin sur l'écran de déconnexion.

Un serveur peut aussi demander au navigateur d'effacer des données avec l'en-tête de réponse Clear-Site-Data. La directive "storage" couvre IndexedDB ainsi que d'autres stockages de l'origine comme localStorage et les enregistrements de service workers, ce qui signifie qu'elle efface plus qu'IndexedDB et convient mieux à une déconnexion complète qu'à la suppression d'un compte parmi plusieurs. La référence de l'en-tête note qu'il n'est pris en compte que sur des réponses sécurisées et que la prise en charge diffère selon les navigateurs. Des connexions ouvertes peuvent retarder ou limiter ce qui est effacé, donc gardez la suppression côté application décrite plus haut comme chemin fiable et traitez l'en-tête comme une étape supplémentaire.

Aucun de ces outils ne prouve qu'il ne subsiste aucune copie des données de l'utilisateur. Les enregistrements du serveur, les sauvegardes, les autres appareils et tout ce que le navigateur conserve hors du stockage de l'origine sont des sujets distincts, avec leurs propres règles de rétention. Ce que l'application peut montrer est plus étroit : les bases du compte précédent n'apparaissent plus dans la vue de stockage du navigateur, et le compte suivant démarre d'un état vide. Dites-le clairement dans les procédures internes et dans tout texte de confidentialité montré aux utilisateurs.

Le changement de compte ajoute une règle : terminez la suppression, ou cloisonnez les données, avant que le compte suivant ne lise quoi que ce soit. Ouvrir une base nommée d'après le nouveau compte est sûr par construction, alors que réutiliser une base partagée pour plusieurs comptes impose qu'une purge par clé de compte se termine d'abord. Préférez des bases séparées par compte lorsque les comptes sont distincts dans l'esprit de l'utilisateur, car supprimer une base est plus simple à vérifier que filtrer des enregistrements par clé. Pour le volet service worker du même nettoyage, consultez Cycle de vie et confidentialité du cache des service workers.

Passez en revue le cycle de vie entre contextes de navigateur

Un cycle de vie inspire davantage confiance lorsque vous pouvez partir d'un état vide connu et observer chaque étape. Deux contextes de navigateur qui ne partagent pas le stockage vous l'offrent : l'un joue le compte connecté, l'autre joue l'utilisateur suivant ou un second compte, et aucun ne voit l'IndexedDB de l'autre. Le guide sur l'isolation de navigateur pour plusieurs comptes couvre le volet de séparation des comptes de cette configuration.

BotBrowser documente que chaque BrowserContext créé avec browser.newContext() possède son propre stockage, ses cookies et son état de session, de sorte qu'un relecteur peut démarrer chaque parcours de compte depuis un état IndexedDB distinct et vérifier le comportement de nettoyage par contexte. BotBrowser ne gère, ne migre ni ne purge le schéma ou les enregistrements IndexedDB d'une application web, et il ne peut pas rendre correcte la logique de mise à niveau, de quota ou de déconnexion d'une application ; cela reste du code d'application. La documentation sur l'isolation multi-comptes décrit la frontière de contexte sur laquelle repose cette revue.

Utilisez les outils de développement du navigateur comme instrument commun. Dans les navigateurs basés sur Chromium, le panneau Application liste les bases IndexedDB, leurs magasins d'objets et leurs enregistrements, et les autres navigateurs proposent une vue de stockage similaire. Actualiser cette vue après chaque étape transforme une affirmation comme « la déconnexion a supprimé les données » en quelque chose qu'une deuxième personne peut observer.

Gardez la revue centrée sur votre propre application. Utilisez des comptes de test et des enregistrements synthétiques que vous avez créés, et n'appliquez pas ces étapes à des données stockées par un autre site.

Exécutez les vérifications du cycle de vie

Exécutez ces vérifications sur une version de l'application et notez réussite ou échec pour chacune.

  1. Ouverture et mise à niveau : la base s'ouvre avec une version explicite, et les appels à createObjectStore et createIndex n'apparaissent que dans upgradeneeded. Augmentez la version et rechargez. Réussite si le nouveau magasin apparaît sous Application > IndexedDB et que les enregistrements existants restent lisibles. Échec si un appel de schéma existe en dehors du gestionnaire de mise à niveau, ou si le nouveau magasin est absent ou que les enregistrements existants sont illisibles après le rechargement.
  2. Deuxième onglet : ouvrez l'application dans deux onglets, puis augmentez la version dans le second. Réussite si le premier onglet ferme sa connexion sur versionchange, affiche un message de rechargement, et que le second onglet termine la mise à niveau. Échec si le second onglet reste en chargement ou signale blocked sans aucun avis.
  3. Revue des magasins : pour chaque magasin d'objets, la revue consigne un responsable, une règle de rétention et la réponse à un quota dépassé et à une éviction. Effacez les données de l'origine dans les outils de développement et rouvrez l'application. Réussite si chaque magasin a les trois entrées et que l'application détecte l'état vide et se rétablit ou affiche son message documenté. Échec si une entrée est vide ou si l'application continue sur une base vide sans s'en apercevoir.
  4. Nettoyage à la déconnexion : déconnectez-vous, puis actualisez Application > IndexedDB. Réussite si aucune base du compte précédent ne subsiste et que le compte suivant démarre vide. Échec si une base du compte précédent figure encore dans la liste.
  5. Suppression bloquée : gardez un second onglet ouvert sur l'ancien compte et déconnectez-vous. Réussite si le parcours se termine dans un état défini, comme un avis de purge en attente et une nouvelle tentative au prochain démarrage, dans l'attente choisie par l'application. Échec si l'écran de déconnexion attend sans fin ou si les anciennes données sont lisibles au prochain démarrage.
  6. Contextes séparés : démarrez deux contextes de navigateur. Vérifiez que Application > IndexedDB est vide dans les deux contextes. Écrivez un enregistrement témoin dans le premier, puis ouvrez la même adresse dans le second. Réussite si le témoin est absent de l'IndexedDB du second contexte. Échec s'il apparaît.

Répétez les vérifications après un changement de schéma, une version qui touche au code de stockage ou une mise à jour majeure du navigateur, et conservez le dernier enregistrement réussi jusqu'à ce que la nouvelle exécution réussisse.

Sources

#IndexedDB#Stockage Du Navigateur#Données De Site#Contextes De Navigateur

Faites passer BotBrowser de la recherche à la production

Utilisez ces guides pour comprendre le modèle, puis passez à la validation multi-plateforme, aux contextes isolés et au déploiement navigateur prêt pour l'échelle.