1. Fichiers sur le disque1.1. Le fichier principal de la base de données1.2. Le fichier Write-Ahead-Log ou "-wal" 1.3. Le fichier Wal-Index ou "-shm" 1.4. Cycle de vie des fichiers1.5. Variations2. Le format du fichier WAL-Index2.1. L'en-tête du fichier WAL-Index2.1.1. Le champ mxFrame2.1.2. Le champ nBackfill2.1.3. Verrous WAL2.2. Tables de hachage de l'index WAL2.3. Matrice de verrouillage2.3.1. Comment les différents verrous sont utilisés2.3.2. Opérations qui nécessitent des verrous et quels verrous ces opérations utilisent3. Récupération

Ce document décrit les détails de bas niveau sur la façon dont le mode WAL est mis en œuvre sur unix et des fenêtres.

La description du format de fichier séparé fournit des détails sur la structure d'un fichier de base de données et du fichier journal write-head utilisé en mode WAL. Mais les détails du protocole de verrouillage et du format de l'index WAL sont délibérément omis puisque ces détails sont laissés à la discrétion des implémentations VFS individuelles. Ce document remplit ces détails manquants pour les VFSes unix et windows.

Pour être complet, certaines des informations de formatage de niveau supérieur contenues dans le document sur le format de fichier et ailleurs sont reproduites ici, lorsqu'elles concernent le traitement en mode WAL.

Lorsqu'il est utilisé activement, l'état d'une base de données en mode WAL est décrit par trois fichiers distincts :

  1. Le fichier principal de la base de données avec un nom arbitraire "X".
  2. Le fichier journal en tête d'écriture, généralement nommé "X-wal".
  3. Le fichier wal-index, généralement nommé "X-shm".

1.1. Le fichier de base de données principal

Le format du fichier de la base de données principale est tel que décrit dans le document sur le format de fichier. Les numéros de version du format de fichier aux décalages 18 et 19 dans la base de données principale doivent tous deux être 2 pour indiquer que la base de données est en mode WAL. La base de données principale peut porter un nom arbitraire autorisé par le système de fichiers sous-jacent. Aucun suffixe de fichier spécial n'est requis, bien que ".db", ".sqlite", et ".sqlite3" semblent être des choix populaires.

1.2. Le fichier Write-Ahead-Log ou "-wal".

Le journal write-ahead ou fichier "wal" est un journal de roll-forward qui enregistre les transactions qui ont été validées mais pas encore appliquées à la base de données principale. Les détails sur le format du fichier wal sont décrits dans la sous-section format WAL du document sur le format du fichier principal. Le fichier wal est nommé en ajoutant les quatre caractères "-wal" à la fin du nom du fichier de la base de données principale. Sauf sur les systèmes de fichiers 8+3, de tels noms ne sont pas autorisés, et dans ce cas le suffixe du fichier est changé en ".WAL". Mais comme les systèmes de fichiers 8+3 sont de plus en plus rares, ce cas exceptionnel peut généralement être ignoré.

1.3. Le fichier Wal-Index ou "-shm".

Le fichier wal-index ou "shm" n'est pas réellement utilisé comme un fichier. Plutôt, les clients individuels de la base de données mmap le fichier shm et l'utilisent comme mémoire partagée pour coordonner l'accès à la base de données et comme cache pour localiser rapidement le cadre dans le fichier wal. Le nom du fichier shm est le nom du fichier principal de la base de données avec les quatre caractères "-shm" ajoutés. Ou, pour les systèmes de fichiers 8+3, le fichier shm est le fichier de base de données principal avec le suffixe changé en ".SHM".

Le shm ne contient aucun contenu de base de données et n'est pas nécessaire pour récupérer la base de données après un crash. Pour cette raison, le premier client à se connecter à une base de données quiescente va normalement tronquer le fichier shm s'il existe. Puisque le contenu du fichier shm n'a pas besoin d'être préservé lors d'un crash, le fichier shm n'est jamais fsync()-ed sur le disque. En fait, s'il y avait un mécanisme par lequel SQLite pourrait dire au système d'exploitation de ne jamais persister le fichier shm sur le disque mais de toujours le garder en mémoire cache, SQLite utiliserait ce mécanisme pour éviter toute E/S disque inutile associée au fichier shm. Cependant, un tel mécanisme existe dans posix standard.

Parce que le shm est seulement utilisé pour coordonner l'accès entre les clients concurrents, le fichier shm est omis si le mode de verrouillage exclusif est défini, comme une optimisation. Lorsque le mode de verrouillage exclusif est défini, SQLite utilise la mémoire du tas à la place du fichier shm mappé en mémoire.

1.4. Cycle de vie des fichiers

Lorsqu'une base de données en mode WAL est en utilisation active, les trois fichiers ci-dessus existent généralement. Sauf que, le fichier Wal-Index est omis si le mode de verrouillage exclusif est défini.

Si le dernier client utilisant la base de données s'arrête proprement en appelant sqlite3_close(), alors un point de contrôle est exécuté automatiquement afin de transférer toutes les informations du fichier wal vers la base de données principale, et le fichier shm et le fichier wal sont déliés. Ainsi, lorsque la base de données n'est utilisée par aucun client, il est généralement le cas que seul le fichier de base de données principal existe sur le disque. Cependant, si le dernier client n'a pas appelé sqlite3_close() avant de se fermer, ou si le dernier client à se déconnecter était un client en lecture seule, alors l'opération de nettoyage final n'a pas lieu et les fichiers shm et wal peuvent encore exister sur le disque même lorsque la base de données n'est pas utilisée.

1.5. Variations

Lorsque PRAGMA locking_mode=EXCLUSIVE (mode de verrouillage exclusif) est défini, la base de données ne peut être ouverte que par un seul client à la fois. Comme un seul client peut utiliser la base de données, le fichier shm est omis. Le client unique utilise un tampon dans la mémoire du tas comme substitut du fichier shm mappé en mémoire.

Si un client en lecture/écriture invoque sqlite3_file_control(SQLITE_FCNTL_PERSIST_WAL) avant l'arrêt, alors à l'arrêt un point de contrôle est toujours exécuté, mais le fichier shm et le fichier wal ne sont pas supprimés. Cela permet aux clients ultérieurs en lecture seule de se connecter à la base de données et de la lire.

Le fichier WAL-index ou "shm" est utilisé pour coordonner l'accès à la base de données par plusieurs clients, et comme cache pour aider les clients à localiser rapidement les cadres dans le fichier wal.

Comme le fichier shm n'est pas impliqué dans la récupération, il n'a pas besoin d'être indépendant de l'ordre des octets de la machine. Par conséquent, les valeurs numériques dans le fichier shm sont écrites dans l'ordre d'octet natif de l'ordinateur hôte, plutôt que d'être converties dans un ordre d'octet spécifique multi-plateforme comme cela est fait avec le fichier de base de données principal et le fichier wal.

Le fichier shm est constitué d'une ou plusieurs tables de hachage, où chaque table de hachage a une taille de 32768 octets. Sauf qu'un en-tête de 136 octets est découpé à l'avant de la toute première table de hachage, de sorte que la première table de hachage ne fait que 32632 octets. La taille totale du fichier shm est toujours un multiple de 32768. Dans la plupart des cas, la taille totale du fichier shm est exactement de 32768 octets. Le fichier shm ne doit croître au-delà d'une seule table de hachage que si le fichier wal devient très grand (plus de 4079 images). Comme le seuil de checkpoint automatique par défaut est de 1000, les fichiers WAL atteignent rarement le seuil de 4079 nécessaire pour faire croître le fichier shm.

2.1. L'en-tête WAL-Index

Les 136 premiers octets du fichier shm sont un en-tête. L'en-tête shm comporte trois divisions principales comme suit :

Divisions de l'en-tête WAL-Index

Octets Description
0..47 Première copie de l'information sur l'index WAL
48..95 Deuxième copie de l'information sur l'index WAL
96..135 Informations sur le point de contrôle et verrous

Les champs individuels de l'en-tête shm, à l'exception des valeurs de sel copiées de l'en-tête WAL, sont des entiers non signés dans l'ordre natif des octets de la machine hôte. Les valeurs de sel sont des copies exactes de l'en-tête WAL et sont dans l'ordre des octets utilisé par le fichier WAL. La taille des entiers peut être de 8, 16, 32 ou 64 bits. Voici une répartition détaillée des différents champs de l'en-tête shm :

Détails de l'en-tête WAL-Index

Octets Nom Signification
0..3 iVersion Le numéro de version du format WAL-index. Toujours 3007000.
4..7 Espace de remplissage inutilisé. Doit être égal à zéro.
8..11 iChange Compteur entier non signé, incrémenté à chaque transaction.
12 isInit L'indicateur "isInit". 1 lorsque le fichier shm a été initialisé.
13 bigEndCksum Vrai si le fichier WAL utilise des sommes de contrôle de type big-endian. 0 si le WAL utilise des sommes de contrôle little-endian.
14..15 szPage La taille de la page de la base de données en octets, ou 1 si la taille de la page est de 65536.
16..19 mxFrame Nombre de trames valides et engagées dans le fichier WAL.
20..23 nPage Taille du fichier de base de données en pages.
24..31 aFrameCksum Somme de contrôle de la dernière trame dans le fichier WAL.
32..39 aSalt Les deux valeurs de sel copiées à partir de l'en-tête du fichier WAL. Ces valeurs sont dans l'ordre des octets du fichier WAL, qui peut être différent de l'ordre natif des octets de la machine.
40..47 aCksum Une somme de contrôle sur les octets 0 à 39 de cet en-tête.
48..95 Une copie des octets 0 à 47 de cet en-tête.
96..99 nBackfill Nombre de trames WAL qui ont déjà été remblayées dans la base de données par des points de contrôle antérieurs.
100..119 marque de lecture[0..4] Cinq "marques de lecture". Chaque marque de lecture est un nombre entier non signé de 32 bits (4 octets).
120..127 Espace inutilisé mis de côté pour 8 verrous de fichiers.
128..132 nBackfillAttempted Nombre de trames WAL qui ont tenté d'être remblayées mais qui pourraient ne pas avoir été remblayées avec succès.
132..136 Espace inutilisé réservé pour une expansion ultérieure.

2.1.1. Le champ mxFrame

L'entier non signé de 32 bits au décalage 16 (et répété au décalage 64) est le nombre de trames valides dans le WAL. Comme les trames du WAL sont numérotées en commençant par 1, mxFrame est également l'index de la dernière trame commit valide dans le WAL. Une trame commit est une trame qui a une valeur "size of database" non nulle dans les octets 4 à 7 de l'en-tête de trame, et qui indique la fin d'une transaction.

Lorsque le champ mxFrame est égal à zéro, cela indique que le WAL est vide et que tout le contenu doit être obtenu directement à partir du fichier de base de données.

Lorsque mxFrame est égal à nBackfill, cela indique que tout le contenu du WAL a été réécrit dans la base de données. Dans ce cas, tout le contenu peut être lu directement depuis la base de données. De plus, le prochain écrivain est libre de réinitialiser le WAL si aucune autre connexion ne détient de verrou sur WAL_READ_LOCK(N) pour N>0.

La valeur mxFrame est toujours supérieure ou égale à la fois à nBackfill et nBackfillAttempted.

2.1.2. Le champ nBackfill

L'entier non signé de 32 bits à l'offset 128 dans l'en-tête WAL-index est appelé "nBackfill". Ce champ contient le nombre de trames du fichier WAL qui ont été recopiées dans la base de données principale.

Le nombre nBackfill n'est jamais supérieur à mxFrame. Lorsque nBackfill est égal à mxFrame, cela signifie que le contenu du WAL a été complètement réécrit dans la base de données et qu'il est correct de réinitialiser le WAL s'il n'y a pas de verrous détenus sur l'un des WAL_READ_LOCK(N) pour N>0.

Le nBackfill ne peut être augmenté que si l'on détient le WAL_CKPT_LOCK. Cependant, nBackfill est changé à zéro pendant une réinitialisation de WAL, et cela se produit en tenant le WAL_WRITE_LOCK.

2.1.3. Verrous WAL

Huit octets d'espace sont mis de côté dans l'en-tête pour supporter le verrouillage de fichier en utilisant la méthode xShmLock() de l'objet sqlite3_io_methods. Ces huit octets ne sont jamais lus ni écrits par SQLite puisque certains VFS (ex : Windows) pourraient implémenter des verrouillages utilisant des verrouillages de fichiers obligatoires.

Ce sont les huit verrous supportés :

Verrous WAL-Index contrôlés par xShmLock()

Nom Offset
xShmLock Fichier
WAL_WRITE_LOCK 0 120
WAL_CKPT_LOCK 1 121
WAL_RECOVER_LOCK 2 122
WAL_READ_LOCK(0) 3 123
WAL_READ_LOCK(1) 4 124
WAL_READ_LOCK(2) 5 125
WAL_READ_LOCK(3) 6 126
WAL_READ_LOCK(4) 7 127

TBD : Plus d'informations sur l'en-tête

2.2. Tables de hachage WAL-Index

Les tables de hachage du fichier shm sont conçues pour répondre rapidement à la question suivante :

FindFrame(P,M) : Étant donné un numéro de page P et un indice de trame WAL maximum M, retourner le plus grand indice de trame WAL pour la page P qui ne dépasse pas M, ou retourner NULL s'il n'y a pas de trames pour la page P qui ne dépasse pas M.

Soit les types de données "u8", "u16", et "u32" signifient des entiers non signés de longueur 8, 16, et 32 bits, respectivement. Ensuite, la première unité de 32768 octets du fichier shm est organisée comme suit :

u8 aWalIndexHeader[136];
u32 aPgno[4062];
u16 aHash[8192];

La deuxième unité de 32768 octets et toutes les unités suivantes du fichier shm sont organisées comme suit :

u32 aPgno[4096];
u16 aHash[8192];

Collectivement, les entrées aPgno enregistrent le numéro de page de la base de données stocké dans tous les cadres du fichier WAL. Les entrées aPgno[0] de la première table de hachage enregistre le numéro de page de la base de données stocké dans la toute première trame du fichier WAL. L'entrée aPgno[i] de la première table de hachage enregistre le numéro de page de la base de données pour la i-ème trame du fichier WAL. L'entrée aPgno[k] de la deuxième table de hachage est le numéro de page de la base de données pour la (k+4062)-ième trame du fichier WAL. L'entrée aPgno[k] pour la n-ième table de hachage de 32768 octets dans le fichier shm (pour n>1) contient le numéro de page de base de données stocké dans la (k+4062+4096*(n-2))-ième trame du fichier WAL.

Voici une façon légèrement différente de décrire les valeurs aPgno : Si vous considérez toutes les valeurs aPgno comme un tableau contigu, alors le numéro de page de la base de données stocké dans la i-ème trame du fichier WAL est stocké dans aPgno.[i]. Bien sûr, aPgno n'est pas un tableau contigu. Les 4062 premières entrées sont sur la première unité de 32768 octets du fichier shm et les valeurs suivantes sont dans des chunks de 4096 entrées dans les unités ultérieures du fichier shm.

Une façon de calculer FindFrame(P,M) serait de balayer le tableau aPgno en commençant par la M-ième entrée et en remontant vers le début et de retourner J où aPgno.[J]==P. Un tel algorithme fonctionnerait, et il serait plus rapide que de rechercher dans tout le fichier WAL la dernière trame portant le numéro de page P. Mais la recherche peut être rendue beaucoup plus rapide encore en utilisant la structure aHash.

Un numéro de page de base de données P est mappé en une valeur de hachage en utilisant la fonction de hachage suivante :

h = (P * 383)%8192

Cette fonction mappe chaque numéro de page en un nombre entier compris entre 0 et 8191 inclus. Le champ aHash de chaque unité de fichier shm de 32768 octets mappe les valeurs P dans les index du champ aPgno de la même unité comme suit :

  1. Calcul de la valeur de hachage : h = P * 383
  2. Soit X le plus grand ensemble d'entiers consécutifs {h, h+1, h+2, ..., h+N} tel que, pour chaque j dans X, aPgno[j%8192]!=0. L'ensemble X sera vide si aPgno[h%8192]==0. L'ensemble X est facilement calculé en commençant par la valeur h%8192, et en ajoutant h%8192 à X et en incrémentant h jusqu'à rencontrer le premier aPgno[h%8192] qui est zéro.
  3. L'ensemble X contient l'index dans aPgno de chaque entrée dans l'unité courante de 32768 octets du fichier shm qui pourrait éventuellement être une solution à la fonction FindFrame(P,M). Chacune de ces entrées doit être vérifiée séparément pour s'assurer que la valeur de aPgno est P et que le numéro de trame ne dépasse pas M. Le plus grand numéro de trame qui passe ces deux tests est la réponse.

Chaque entrée dans le tableau aPgno a une seule entrée correspondante dans le tableau aHash. Il y a plus d'emplacements disponibles dans aHash qu'il n'y en a dans aPgno. Les emplacements non utilisés dans aHash sont remplis de zéro. Et comme il est garanti qu'il y aura des emplacements inutilisés dans aHash, cela signifie que la boucle qui calcule X est garantie de se terminer. La taille attendue de X est inférieure à 2. Dans le pire des cas, X sera égal au nombre d'entrées dans aPgno, auquel cas l'algorithme fonctionne à peu près à la même vitesse qu'un balayage linéaire de aPgno. Mais cette performance est extrêmement rare. Habituellement, la taille de X sera petite et l'utilisation du tableau aHash permet de calculer FindFrame(P,M) beaucoup plus rapidement.

Voici une autre façon de décrire l'algorithme de consultation de hachage : Commencez par h = (P * 383)%8192 et regardez aHash.[h] et les entrées suivantes, en passant à zéro lorsque h atteint 8192, jusqu'à trouver une entrée avec aHash[h]==0. Toutes les entrées aPgno ayant un numéro de page de P auront un index qui est l'un des aHash[h] ainsi calculées. Mais toutes les valeurs aHash[h] calculées ne répondront pas aux critères de correspondance, vous devez donc les vérifier indépendamment. L'avantage en termes de vitesse vient du fait que normalement cet ensemble de valeurs h est très petit.

Notez que chaque unité de 32768 octets du fichier shm possède ses propres tableaux aHash et aPgno. Le tableau aHash pour une seule unité n'est utile que pour trouver les entrées aPgno dans cette même unité. La fonction FindFrame(P,M) globale doit effectuer des recherches de hachage en commençant par la dernière unité et en remontant jusqu'à l'unité la plus ancienne jusqu'à ce qu'elle trouve une réponse.

2.3. Matrice de verrouillage

L'accès est coordonné en mode WAL en utilisant à la fois les verrous hérités du mode DELETE contrôlés par les méthodes xLock et xUnlock de l'objet sqlite3_io_methods et les verrous WAL contrôlés par la méthode xShmLock de l'objet sqlite3_io_methods.

Conceptuellement, il n'y a qu'un seul verrou en mode DELETE. Le verrou en mode DELETE pour une seule connexion de base de données peut être dans exactement un des états suivants :

  1. SQLITE_LOCK_NONE (déverrouillé)
  2. SQLITE_LOCK_SHARED (lecture)
  3. SQLITE_LOCK_RESERVED (lecture, attente d'écriture)
  4. SQLITE_LOCK_PENDING (nouveaux lecteurs bloqués, en attente d'écriture).
  5. SQLITE_LOCK_EXCLUSIVE (écriture)

Les verrous en mode DELETE sont stockés sur la page lock-byte du fichier principal de la base de données. Seuls SQLITE_LOCK_SHARED et SQLITE_LOCK_EXCLUSIVE sont des facteurs pour les bases de données en mode WAL. Les autres états de verrouillage sont utilisés en mode rollback, mais pas en mode WAL.

Les verrouillages en mode WAL sont décrits ci-dessus.

2.3.1. Comment les différents verrous sont utilisés.

Les règles suivantes montrent comment chacun des verrous est utilisé.

  • SQLITE_LOCK_SHARED

    Toutes les connexions détiennent SQLITE_LOCK_SHARED en permanence lorsqu'elles sont attachées à une base de données en mode WAL. Ceci est vrai pour les connexions en lecture/écriture et les connexions en lecture seule. Le verrou SQLITE_LOCK_SHARED est maintenu même par les connexions qui ne sont pas en transaction. Ceci est différent du mode rollback, où le verrou SQLITE_LOCK_SHARED est libéré à la fin de chaque transaction.

  • SQLITE_LOCK_EXCLUSIVE

    Les connexions détiennent un verrou exclusif lors du changement entre le mode WAL et l'un des différents modes rollback. Les connexions pourraient également tenter d'obtenir un verrou EXCLUSIF lorsqu'elles se déconnectent du mode WAL. Si une connexion est capable d'obtenir un verrou EXCLUSIF, cela signifie qu'elle est la seule connexion à la base de données et qu'elle peut donc tenter de faire un checkpoint puis de supprimer l'index WAL et les fichiers WAL.

    Lorsqu'une connexion détient un verrou SHARED sur la base de données principale, cela empêchera toute autre connexion d'acquérir le verrou EXCLUSIVE, qui à son tour empêche l'index WAL et les fichiers WAL d'être supprimés sous d'autres utilisateurs, et empêche une transition hors du mode WAL alors que d'autres utilisateurs accèdent à la base de données en mode WAL.

  • WAL_WRITE_LOCK

    Le WAL_WRITE_LOCK est verrouillé de manière exclusive. Il n'y a jamais de verrou partagé pris sur WAL_WRITE_LOCK.

    Un WAL_WRITE_LOCK EXCLUSIF est détenu par toute connexion qui ajoute du contenu à la fin du WAL. Par conséquent, seul un seul processus à la fois peut ajouter du contenu au WAL. Si une réinitialisation du WAL se produit à la suite d'une écriture, alors le champ nBackfill de l'en-tête WAL-index est remis à zéro pendant la détention de ce verrou.

    Un EXCLUSIF est également détenu WAL_WRITE_LOCK, et sur plusieurs autres octets de verrouillage, lorsqu'une connexion exécute une récupération sur le WAL-index partagé.

  • WAL_CKPT_LOCK

    Le WAL_CKPT_LOCK est verrouillé de manière exclusive. Il n'y a jamais de verrou partagé pris sur WAL_CKPT_LOCK.

    Un WAL_CKPT_LOCK EXCLUSIF est détenu par toute connexion qui exécute un point de contrôle. Le champ nBackfill de l'en-tête WAL-index peut être augmenté pendant la détention de ce verrou exclusif, mais il ne peut pas être diminué.

    Un EXCLUSIF est également détenu WAL_CKPT_LOCK, et sur plusieurs autres octets de verrouillage, lorsqu'une connexion exécute une récupération sur le WAL-index partagé.

  • WAL_RECOVER_LOCK

    Le WAL_RECOVER_LOCK est verrouillé de manière exclusive. Il n'y a jamais de verrou partagé pris sur WAL_RECOVER_LOCK.

    Un WAL_RECOVER_LOCK EXCLUSIF est détenu par toute connexion qui exécute une récupération pour reconstruire l'index WAL partagé.

    Une connexion en lecture seule qui reconstruit son WAL-index privé en mémoire de tas ne détient pas ce verrou. (Elle ne le peut pas, puisque les connexions en lecture seule ne sont pas autorisées à détenir de verrou exclusif). Ce verrou est uniquement détenu lors de la reconstruction de l'index WAL global partagé contenu dans le fichier SHM mappé en mémoire.

    En plus de verrouiller cet octet, une connexion exécutant la récupération obtient également un verrou exclusif sur tous les autres verrous WAL, à l'exception de WAL_READ_LOCK(0).

  • WAL_READ_LOCK(N)

    Il existe cinq verrous de lecture distincts, les numéros 0 à 4. Les verrous de lecture peuvent être soit PARTAGÉS, soit EXCLUSIFS. Les connexions obtiennent un verrou partagé sur l'un des octets de verrous de lecture tant qu'elles sont dans une transaction. Les connexions obtiennent également un verrou exclusif sur les verrous de lecture, un par un, pendant le bref moment où elles mettent à jour les valeurs des marques de lecture correspondantes. Les verrous de lecture 1 à 4 sont détenus exclusivement lors de l'exécution de la récupération.

    Chaque octet de verrou de lecture correspond à l'un des cinq entiers de marque de lecture de 32 bits situés dans les octets 100 à 119 de l'en-tête WAL-index, comme suit :

    Nom du verrou Décalage du verrou Nom de la marque de lecture Décalage de la marque de lecture
    WAL_READ_LOCK(0) 123 Marque de lecture[0] 100..103
    WAL_READ_LOCK(1) 124 lecture de marque[1] 104..107
    WAL_READ_LOCK(2) 125 lecture de marque[2] 108..111
    WAL_READ_LOCK(3) 126 lecture de marque[3] 112..115
    WAL_READ_LOCK(4) 127 lecture de marque[4] 116..119

    Lorsqu'une connexion détient un verrou partagé sur WAL_READ_LOCK(N), c'est une promesse de la connexion qu'elle utilisera le WAL et non le fichier de base de données pour toute page de base de données modifiée par la première marque de lecture[N] dans le WAL. La marque de lecture[0] est toujours égale à zéro. Si une connexion détient un verrou partagé sur WAL_READ_LOCK(0), cela signifie que la connexion s'attend à pouvoir ignorer le WAL et lire tout le contenu qu'elle veut depuis la base de données principale. Si N>0 alors la connexion est libre d'utiliser plus du fichier WAL au-delà de la marque de lecture.[N] si elle le souhaite, jusqu'aux premières trames mxFrame. Mais lorsqu'une connexion détient un verrou partagé sur WAL_READ_LOCK(0), c'est une promesse qu'elle ne lira jamais de contenu depuis le WAL et qu'elle acquerra tout le contenu directement depuis la base de données principale.

    Quand un checkpoint s'exécute, s'il voit un verrou sur WAL_READ_LOCK(N), alors il ne doit pas déplacer le contenu du WAL dans la base de données principale pour plus que la première marque de lecture....[N] trames. S'il le faisait, il écraserait le contenu que le processus détenant le verrou s'attendait à pouvoir lire dans le fichier de la base de données principale. Une conséquence de cela est que si le fichier WAL contient plus que la marque de lecture[N] cadres (si mxFrame>read-mark[N] pour toute marque de lecture pour laquelle WAL_READ_LOCK(N) est détenu par un autre processus), alors le point de contrôle ne peut pas s'exécuter jusqu'à la fin.

    Quand un écrivain veut réinitialiser le WAL, il doit s'assurer qu'il n'y a pas de verrous sur WAL_READ_LOCK(N) pour N>0 parce que de tels verrous indiquent qu'une autre connexion utilise toujours le fichier WAL actuel et qu'une réinitialisation du WAL supprimerait le contenu de ces autres connexions. Il est correct qu'une réinitialisation de la WAL se produise si d'autres connexions détiennent WAL_READ_LOCK(0) parce qu'en détenant WAL_READ_LOCK(0), ces autres connexions promettent de ne pas utiliser le contenu de la WAL.

2.3.2. Opérations qui nécessitent des verrous et quels verrous ces opérations utilisent.

  • Transition vers et hors du mode WAL

    Le verrou SQLITE_LOCK_EXCLUSIVE doit être détenu par une connexion qui veut passer en mode WAL ou en sortir. La transition vers le mode WAL se fait donc comme toute autre transaction d'écriture, puisque toute transaction d'écriture en mode rollback nécessite le verrou SQLITE_LOCK_EXCLUSIVE. Si le fichier de la base de données est déjà en mode WAL (donc si l'on souhaite le faire repasser en mode rollback) et s'il y a deux ou plusieurs connexions à la base de données, alors chacune de ces connexions détiendra un verrou SQLITE_LOCK_SHARED. Cela signifie que le SQLITE_LOCK_EXCLUSIVE ne pourra pas être obtenu, et que la transition hors du mode WAL ne sera pas autorisée. Cela empêche une connexion de supprimer le mode WAL sous une autre. Cela signifie également que la seule façon de faire passer une base de données du mode WAL au mode rollback est de fermer toutes les connexions à la base de données sauf une.

  • Fermer une connexion à une base de données en mode WAL

    Lorsqu'une connexion à une base de données se ferme (via sqlite3_close() ou sqlite3_close_v2()), une tentative est faite pour acquérir SQLITE_LOCK_EXCLUSIVE. Si cette tentative réussit, cela signifie que la connexion qui se ferme est la dernière connexion à la base de données. Dans ce cas, il est souhaitable de nettoyer les fichiers WAL et WAL-index, donc la connexion qui se ferme exécute un point de contrôle (tout en conservant SQLITE_LOCK_EXCLUSIVE) et supprime les fichiers WAL et WAL-index. Le SQLITE_LOCK_EXCLUSIVE n'est pas libéré avant que les fichiers WAL et WAL-index aient été supprimés.

    Si l'application invoque sqlite3_file_control(SQLITE_FCNTL_PERSIST_WAL) sur la connexion à la base de données avant la fermeture, alors le point de contrôle final est toujours exécuté mais les fichiers WAL et WAL-index ne sont pas supprimés comme ils le seraient normalement. Cela laisse la base de données dans un état qui permet à d'autres processus sans permission d'écriture sur la base de données, WAL ou les fichiers WAL-index d'ouvrir la base de données en lecture seule. Si les fichiers WAL et WAL-index sont manquants, alors un processus qui n'a pas la permission de créer et d'initialiser ces fichiers ne pourra pas ouvrir la base de données, sauf si la base de données est désignée comme immuable à l'aide du paramètre de requête immuable.

  • Reconstruire l'index WAL partagé global pendant la récupération.

    Tous les verrous de l'index WAL, à l'exception de WAL_READ_LOCK(0), sont détenus exclusivement pendant la reconstruction de l'index WAL global partagé pendant la récupération.

  • Ajouter une nouvelle transaction à la fin du WAL.

    Un verrou exclusif est maintenu sur WAL_WRITE_LOCK pendant l'ajout d'une nouvelle trame à la fin d'un fichier WAL.

  • Lire le contenu de la base de données et du WAL dans le cadre d'une transaction.

  • Exécutez un point de contrôle

  • Réinitialisation du fichier WAL

    Une réinitialisation du WAL signifie rembobiner le WAL et commencer à ajouter de nouvelles trames au début. Cela se produit lors de l'ajout de nouvelles trames à un WAL qui a mxFrame égal à nBackfill et qui n'a aucun verrou sur WAL_READ_LOCK(1) à WAL_READ_LOCK(4). Le verrou WAL_WRITE_LOCK est maintenu.

La récupération est le processus de reconstruction de l'index WAL afin qu'il soit synchronisé avec le WAL.

La récupération est exécutée par le premier thread à se connecter à une base de données en mode WAL. La récupération restaure le WAL-index de sorte qu'il décrive précisément le fichier WAL. S'il n'y a pas de fichier WAL présent lorsque le premier thread se connecte à la base de données, il n'y a rien à récupérer, mais le processus de récupération s'exécute quand même pour initialiser le WAL-index.

Si le WAL-index est implémenté comme un fichier mappé en mémoire et que ce fichier est en lecture seule pour le premier thread à se connecter, alors ce thread crée un WAL-index privé en mémoire de tas ersazt et exécute la routine de récupération pour remplir ce WAL-index privé. Les mêmes données résultent, mais elles sont conservées de manière privée plutôt que d'être écrites dans la zone de mémoire partagée publique.

La récupération fonctionne en faisant un seul passage sur le WAL, du début à la fin. Les sommes de contrôle sont vérifiées sur chaque trame du WAL au fur et à mesure de sa lecture. L'analyse s'arrête à la fin du fichier ou à la première somme de contrôle invalide. Le champ mxFrame est défini sur l'index de la dernière trame de commit valide dans WAL. Comme les numéros de trame du WAL sont indexés en commençant par 1, mxFrame est également le nombre de trames valides dans le WAL. Une "frame commit" est une frame qui a une valeur non nulle dans les octets 4 à 7 de l'en-tête de frame. Comme la procédure de récupération n'a aucun moyen de savoir combien de trames du WAL ont pu être précédemment recopiées dans la base de données, elle initialise la valeur nBackfill à zéro.

Pendant la récupération de l'index WAL global en mémoire partagée, des verrous exclusifs sont maintenus sur WAL_WRITE_LOCK, WAL_CKPT_LOCK, WAL_RECOVER_LOCK, et WAL_READ_LOCK(1) à WAL_READ_LOCK(4). En d'autres termes, tous les verrous associés à l'index WAL, à l'exception de WAL_READ_LOCK(0), sont détenus de manière exclusive. Cela empêche tout autre thread d'écrire dans la base de données et de lire les transactions contenues dans le WAL, jusqu'à ce que la récupération soit terminée.