Journal
Sections
Réglages de lecture
Voix naturelle enregistrée pour ce cours, la même dans tous les navigateurs.
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
Code et termes techniques en anglais
Désactivé : la voix de la page lit tout, sans pause entre deux voix.
Aucune voix pour cette langue dans ce navigateur.
Plus de voix
D’autres voix : Microsoft Edge propose des voix naturelles en ligne, et Windows (Paramètres › Heure et langue › Voix), macOS et Android en installent d’autres.
Progression
Section intitulée « Progression »- Données : les pages, les liens et l’historique Git de ce dépôt au commit
cbcbb42, et les exécutions de CI du cours DuckDB - CI : le Cypher de chaque leçon comparé à sa sortie attendue sur trois OS, et une vérification croisée en SQL avec DuckDB
- Leçon 1 : un premier graphe
- Leçon 2 : charger des fichiers
- Leçon 3 : chemins
- Leçon 4 : l’historique Git et les exécutions de CI en graphe
- Données : les projets .NET de GuitarAlchemist/ga au commit
a26a7893 - Leçon 5 : LadybugDB depuis C#
- Leçon 6 : LadybugDB depuis Java
- Leçon 7 : algorithmes de graphe et recherche plein texte
- Leçon 8 : persistance, transactions et concurrence
- Leçon 9 : un laboratoire de graphe — traductions, cycles de prérequis, atteignabilité bornée
LadybugDB 0.20.4 est la base de quelqu’un d’autre, et écrire huit leçons dessus a fait remonter neuf constats. Six sont des bugs silencieux — pas d’erreur, un résultat faux — chacun avec une reproduction qui tourne dans une base en mémoire vide ; aucun n’avait de ticket amont quand le tracker a été fouillé le 14 septembre 2026, et aucun n’a été signalé. Les trois derniers viennent des leçons elles-mêmes, et l’un d’eux était déjà ouvert en amont. Le laboratoire de la leçon 9 en a ajouté un dixième, l’ordre inversé d’un témoin fermé ; il n’a pas été cherché en amont.
| Attendu | Ce qui se passe | Où | Mesure | État |
|---|---|---|---|---|
Un COPY dans une table de relations depuis une sous-requête relie les nœuds trouvés par le MATCH |
Il relie les mauvais nœuds : les clés sont appariées par position plutôt que par le MATCH |
COPY … FROM (LOAD FROM … MATCH …) |
Reproduction 1 2026-09-14 | Reproduit, aucun ticket amont, non signalé |
count(DISTINCT …) à côté de sum(…) laisse la somme tranquille |
La sum revient à NULL dès qu’un count(DISTINCT …) la précède |
Agrégation | Reproduction 2 2026-09-14 | Reproduit, aucun ticket amont, non signalé |
ACYCLIC écarte tout chemin qui répète un nœud |
Il en garde certains | Motifs de chemins récursifs | Reproduction 3 2026-09-14 | Reproduit, aucun ticket amont, non signalé |
Un COPY qui échoue laisse la table de nœuds dans son état |
Il la corrompt | COPY dans une table de nœuds |
Reproduction 4 2026-09-14 | Reproduit, aucun ticket amont, non signalé |
Grouper par une sous-requête COUNT conserve le groupe dont le compte vaut 0 |
Ce groupe disparaît | GROUP BY sur une sous-requête |
Reproduction 5 2026-09-14 | Reproduit, aucun ticket amont, non signalé |
timestamp() et CAST(… AS TIMESTAMP) respectent le décalage écrit dans le littéral |
Les deux l’ignorent | Conversion temporelle | Reproduction 6 2026-09-14 | Reproduit, aucun ticket amont, non signalé |
INSTALL algo récupère la build correspondant à la CLI |
La CLI 0.20.4 télécharge la build 0.20.0, et LOAD algo échoue ensuite sur les trois exécuteurs, avec dans le message des chemins de la machine de build de LadybugDB |
extension algo, CLI 0.20.4 |
libnetworkit.so: cannot open shared object file sous Linux, Library not loaded: @rpath/libnetworkit.dylib sous macOS, The specified module could not be found. sous Windows |
Ouvert en amont sous le ticket #857, pas ouvert par ce cours 2026-09-14 |
| Louvain rend les mêmes communautés pour le même graphe et le même nombre de fils | Les tailles changent d’un processus à l’autre, CALL threads = 1 posé |
extension algo, Louvain |
[27,20,17,14,14] dans un processus, [25,20,18,18,11] dans les deux suivants ; trois appels dans un même processus concordent. Les nœuds sans relation reçoivent louvain_id -1 |
Reproduit, non signalé 2026-09-14 |
| Un processus en lecture seule et un écrivain sur le même fichier se comportent comme la documentation le dit | La page sur la concurrence dit que la combinaison n’est pas permise, mais c’est bien ce qui se passe : un processus en lecture seule ouvre le fichier d’un écrivain sous Linux et macOS et voit ses changements validés via le .wal ; un écrivain ouvre et checkpointe un fichier tenu par un lecteur, sur les trois, et le lecteur continue de répondre depuis l’ancien état |
storage_manager.cpp 87-91 |
Sous Windows, le LockFileEx de l’écrivain fait plutôt échouer la lecture avec Error 33 |
Reproduit ; la documentation et le comportement se contredisent 2026-09-14 |
nodes(p) sur un motif fermé (a)-[…]->(a) liste les pages dans l’ordre des flèches, comme sur un motif ouvert |
Elle les liste à contresens des flèches ; les pages et la longueur sont justes | nodes() sur un motif fermé de longueur variable ; source non localisée |
Flèches /lab/cycles/ → /lab/paths/ → /lab/intro/ → retour : [/lab/cycles/,/lab/intro/,/lab/paths/,/lab/cycles/]. Un chemin ouvert depuis la même page est dans l’ordre. Reproduit sur les cycles de 3 et de 6 pages 2026-09-27 |
Reproduit sous Windows ; suivi amont non consulté, non signalé |
Expériences
Section intitulée « Expériences »Une ligne par expérience mesurée, avec l’hypothèse telle qu’écrite avant l’exécution dans data/lab/preregistration.md.
| Question | Hypothèse (avant l’exécution) | Résultat | Verdict | Entrée et code |
|---|---|---|---|---|
Après un COPY échoué dans une table de relations, que reste-t-il ? |
La table inchangée, et l’index de clé des nœuds intact | 2 liens, comme avant ; found_by_key 1 |
Confirmé, 0.20.4 sous Windows ; observé, pas une garantie | 2026-09-27, 09-graph-lab.cypher |
| Traductions manquantes par identité de contenu ou par titre | Identité : 1 page sœur manquante ; titre : 4 lignes, dont 3 fausses | 1 (/lab/paths/, es) ; 4 lignes, dont 3 fausses |
Confirmé | 2026-09-27 |
| Une borne de piste fermée est-elle complète dès qu’elle atteint le nombre de pages ? | DAG : 0 à la borne 3 ; cycle de 6 : 0 à 5, 6 pistes de longueur 6 à 6 | 0 ; 0 ; 6 de longueur 6 | Confirmé | 2026-09-27 |
| Navigation et prérequis demandent-ils des tables de relations séparées ? | Navigation : 2 pistes fermées de longueur 2 ; prérequis : 0 | 2 de longueur 2 ; 0 | Confirmé | 2026-09-27 |
| Un témoin fermé liste-t-il ses pages dans l’ordre des flèches ? | Oui, comme un chemin ouvert | Non : inversé (ligne QA ci-dessus) | Réfuté | 2026-09-27 |
2026-09-14 — Les données
Section intitulée « 2026-09-14 — Les données »extract.py, lancé avec le commitcbcbb42en argument, lit les fichiers Markdown et l’historique Git à ce commit, avecgit ls-tree,git showetgit log, et non la copie de travail : les fichiers CSV ne changent pas quand le site change.- 319 pages, 712 liens internes, 3 712 liens externes, 74 commits, 73 liens de parenté et 1 019 fichiers modifiés.
- Les exécutions sont le
runs.jsondu cours DuckDB, 125 exécutions exportées vers 14:04 UTC, réutilisées telles quelles. - Les dates des commits gardent leur décalage
-04:00danscommits.csv; LadybugDB les stocke en UTC.
2026-09-14 — Installer LadybugDB
Section intitulée « 2026-09-14 — Installer LadybugDB »- Windows :
lbug_cli-windows-x86_64.zipde la release v0.20.4 ne contient quelbug.exe, 14 772 736 octets.lbug --versionafficheLbug 0.20.4. - CI : le workflow télécharge l’asset de release de chaque OS (
linux-x86_64,windows-x86_64,osx-arm64) et ajoute le dossier àGITHUB_PATH. Il installe DuckDB 1.5.5 de la même façon, pour la vérification croisée en SQL de la leçon 3. INSTALL jsona mis l’extension dans~/.lbdb/extension/0.20.0/win_amd64/json/: le dossier porte le nom de 0.20.0, pas de 0.20.4. L’historique du shell se trouve dans~/.lbdb/history.txt.- Dans une fenêtre Git Bash,
lbug.exene trouve pas les chemins/c/Users/…, et les barres obliques inverses dans une chaîne Cypher sont des caractères d’échappement : les scripts utilisent des chemins relatifs àcode/ladybugdb, etC:/…fonctionne quand il faut un chemin complet.
2026-09-14 — La CI
Section intitulée « 2026-09-14 — La CI »- La CLI se termine avec 0 même quand une requête échoue, et affiche les erreurs sur la sortie standard (
embedded_shell.cpp, lignes 608-611) :check.shcompare toute la sortie, erreurs comprises, donc une erreur inattendue fait quand même échouer le job. - La barre de progression écrit des codes d’échappement même quand la sortie va vers un pipe :
check.shpasse--no_progress_baret--no_stats. - Premier push : échec sous Linux, succès sous Windows et macOS. Supprimer une leçon qui avait encore des relations donnait un message nommant
HAS_LESSONsous Windows et macOS, etLINKS_TOsous Linux : l’exécuteur nomme la première table de relations qu’il vérifie (delete_executor.cpp, lignes 33-42), et leur ordre diffère. La leçon 1 supprime désormais une leçon dont toutes les relations sont dans une seule table. - Même push :
ACYCLICa donné 14 chemins sous Windows et 29 sous Linux et macOS (voir plus bas). La leçon 3 utiliseis_acyclic(p), identique sur les trois. - Deux sorties pouvaient varier d’une exécution à l’autre : un
LOAD FROM … LIMIT 3sansORDER BY, et la première clé manquante signalée par unCOPYen échec, qui dépend du thread qui la trouve. Les scripts trient, et exécutentCALL threads = 1avant ceCOPY. - Une marche
LINKS_TO*sans borne sur le graphe du site a tourné plus de cinq minutes avant que je l’arrête ;* SHORTESTsur le même motif répond immédiatement.
2026-09-14 — Surprises en écrivant les leçons 1-4
Section intitulée « 2026-09-14 — Surprises en écrivant les leçons 1-4 »MERGEfait correspondre le motif entier : sans la clé primaire, il échoue à la liaison, et avec la clé et une autre propriété qui diffère, il tente de créer le nœud et échoue sur la clé en double.:schemaaffiche des instructionsCREATE, avecMANY_MANYsur chaque table de relations.- L’en-tête CSV est détecté sans
HEADER = true, et ses noms n’ont pas besoin de correspondre aux colonnes de la table. SKIP_DUPLICATE_PK = truefonctionne sur unCOPYdepuis un fichier (constants.h, ligne 111) mais ne figure pas dans la documentation, et n’est pas pris en charge sur unCOPYdepuis une sous-requête (bind_copy_from.cpp, ligne 149).- Le détecteur de format CSV lit les 256 premières lignes (
constants.h, ligne 144) ; un champ entre guillemets plus bas fait échouer leCOPY, sauf siQUOTEest précisé. - Les champs CSV vides deviennent
NULLpar défaut ;NULL_STRINGS = ['NA']les garde en''. split_part('/es/streeling/', '/', 1)renvoiees: la partie vide avant le séparateur initial est ignorée. DuckDB renvoie''.nodes(e)sur une liste de relations ne renvoie que les nœuds intermédiaires ; les listes sont indexées à partir de 1 ;ORDER BYsur unSTRING[]échoue, un cast enSTRINGfonctionne.- Les motifs de longueur variable sont des marches par défaut, donc deux relations
CHANGEDd’un même motif peuvent être la même. - La borne supérieure d’un motif de longueur variable est limitée à 30 (
client_config.h, ligne 32) jusqu’à unCALL var_length_extend_max_depth = …; un motif qui a besoin de plus ne renvoie rien, sans erreur. - Les nombres de chemins à 4 liens de la leçon 3 ont été vérifiés deux fois hors de LadybugDB : un script Python sur
links.csv(marches 1, 5, 12, 29 ; pistes 26 ; acycliques 1, 5, 9, 10), et une CTE récursive dans DuckDB (marches 1, 5, 12, 29), que la CI exécute.
2026-09-14 — Leçon 5 : les données de ga et le paquet C#
Section intitulée « 2026-09-14 — Leçon 5 : les données de ga et le paquet C# »data/ga/extract.pyn’a besoin que des fichiers de projet : un clone avec--filter=blob:noneet un sparse checkout de*.csproj,*.fsprojet*.slnxtélécharge les fichiers de projet de ga sans le reste de son contenu. Il ignore les copies situées dans des dossiers.claude.- 111 projets, 266 références de projet, 478 références de paquet, 136 paquets. Une référence pointe vers
reactapp1.client.esproj, un projet JavaScript que l’extraction ne garde pas : la leçon charge les références avecIGNORE_ERRORS. - La colonne
versionest ce qu’indique chaque fichier de projet. LeDirectory.Build.propsde ga redéfinit 14 paquets avecPackageReference Update, et l’extraction ne les applique pas ; les requêtes de la leçon évitent ces paquets. - Deux projets s’appellent
GaApi.Tests, dansTests/Apps/GaApi.TestsetTests/GaApi.Tests; le second n’est pas dansAllProjects.slnx. En tout, 38 projets n’y sont pas. - NuGet :
LadybugDBa trois versions,0.17.0-alpha.1,0.18.2et0.19.1; la 0.19.1 embarque le moteur 0.19.1, version de stockage 43. Le binding n’est pas ADO.NET et n’a pas de page sur docs.ladybugdb.com : j’ai lu son code source au commit0f58f1a. - Premier push sur la CI : le job C# a échoué sur les trois OS. L’identifiant interne de
GaApiétait0:11sous Windows et0:47sous Linux et macOS, à partir du même CSV ;SHORTESTa choisi un chemin passant parGA.Business.MLsur ma machine et parGA.Business.AIsur les runners, parmi deux de même longueur ; et la CLI du runner Linux affichaitWarning: failed to create directory: /home/runner/.lbdb/en ouvrant un fichier en lecture seule. Le programme compare désormais les identifiants au lieu de les afficher et liste les cheminsALL SHORTESTtriés ;check.shfiltre l’avertissement. - La CLI 0.20.4, en ouvrant en lecture-écriture un fichier de base de données 0.19.1, le réécrit en version de stockage 47 sans message ; le paquet 0.19.1 échoue ensuite avec
Failed to open Ladybug database at '…', sans la raison.--read_onlylaisse le fichier lisible par les deux. - Le binding n’expose pas
lbug_query_result_has_next_query_result,lbug_connection_interruptnilbug_connection_set_query_timeoutde l’API C : unQueryavec plusieurs instructions ne renvoie que le premier résultat, et une requête longue ne peut pas être arrêtée depuis C#.
2026-09-14 — Leçon 6 : le paquet Java
Section intitulée « 2026-09-14 — Leçon 6 : le paquet Java »com.ladybugdb:lbug0.20.4 sur Maven Central : un seul jar de 29 339 834 octets avec la bibliothèque native pourlinux_amd64,linux_arm64,osx_arm64etwindows_amd64, pasosx_amd64. Il tire 13 autres jars, 7 Mo :kotlin-stdlib2.3.20, Apache Arrow 18.2.0, Jackson, FlatBuffers, commons-codec et SLF4J.- Le jar des sources correspond au dépôt
ladybug-javaàf2fb39f, le commit du sous-module de LadybugDB v0.20.4. Les sources se trouvent dans un dossiercom/lbugdb, mais le package estcom.ladybugdb. - La page Java de la documentation montre encore
throws ObjectRefDestroyedException; la 0.20.4 lèveRuntimeExceptionavec des messages commeConnection has been destroyed. - Une requête en échec renvoie un résultat dont
isSuccess()vaut false ;getNext()sur ce résultat lèveRuntimeExceptionavec le même message. - Un
PreparedStatementgarde les valeurs de sa dernière exécution :execute(statement, Map.of("nom", 5))aprèsMap.of("n", 2.5)s’exécute avecn = 2.5et sans erreur. Sur une nouvelle instruction, la même map échoue avecParameter n not found. Value.getValue()lèveType of value is not supported in value_get_valuepourLISTetMAP, et ne gère pas non plusSTRUCT,NODE,RELniRECURSIVE_REL; les classesLbugList,LbugStruct,LbugMapetValue…Utilles lisent.interval('1 month 2 days')revient sous la formePT768H: le code JNI convertit l’intervalle en secondes avec des mois de 30 jours.setQueryTimeout(1)arrête les marches de la leçon 3 avecInterrupted., etgetNextQueryResult()lit la seconde instruction d’une requête : deux choses absentes du paquet .NET.- La bibliothèque native est copiée dans un nouveau fichier temporaire à chaque démarrage de la JVM, et
deleteOnExitne peut pas supprimer une DLL chargée sous Windows : 13 copies, 190 Mo, dans%TEMP%après les exécutions de cette leçon sur ma machine, 3 copies sur le runner Windows après trois exécutions, aucune sur les runners Linux et macOS. - Le premier push du job Java sur la CI a réussi sur les trois OS : la leçon 5 avait déjà rendu la sortie indépendante des identifiants internes et du chemin que choisit
SHORTEST.
2026-09-14 — Leçon 7 : extensions, algorithmes et recherche plein texte
Section intitulée « 2026-09-14 — Leçon 7 : extensions, algorithmes et recherche plein texte »- Avec la CLI 0.20.4,
INSTALL algotélécharge la build pour la 0.20.0, etLOAD algoéchoue sur les trois runners de la CI :libnetworkit.so: cannot open shared object filesous Linux,Library not loaded: @rpath/libnetworkit.dylibsous macOS, avec dans le message des chemins de la machine de build de LadybugDB, etThe specified module could not be found.sous Windows. L’issue #857 est ouverte. LOAD ftsfonctionne avec la 0.20.4 sur les trois runners, mais sur ma machine Windows la CLI se termine avec0xC0000409, sans rien afficher, au premierCREATE_FTS_INDEX, même sur une table d’une ligne. Sous Linux (WSL), le même script fonctionne avec la 0.20.4.- La CLI 0.19.1 télécharge les builds pour la 0.19.0 ;
algoetftsse chargent et donnent la même sortie sous Windows, Linux et macOS. La CI l’installe sous le nomlbug19;check.shaffiche le résultat deLOAD algoetLOAD ftsavec la 0.20.4 à chaque exécution, sans le comparer. - La CLI Windows, 0.19.1 et 0.20.4, importe
libssl-3-x64.dlletlibcrypto-3-x64.dll, absentes du fichier zip ; avec seulementC:\Windows\System32dans lePATH, elle se termine avec0xC0000135. Git for Windows fournit les deux DLL. La leçon 1 le dit désormais. project_graph_cyphern’est pas dans la documentation. Elle crée un graphe de typeCYPHER, etpage_ranketweakly_connected_componentssur ce graphe échouent avecBinder exception: AA:gds.cpp, ligne 72, toujours là en 0.20.4. Le test de la fonction dans le moteur ne fait que créer le graphe. Reproduction minimale, avec la 0.19.1 sous Windows :
LOAD algo;CREATE NODE TABLE P(id INT64 PRIMARY KEY, age INT64);CREATE REL TABLE E(FROM P TO P);CREATE (:P {id: 1, age: 5}), (:P {id: 2, age: 20}), (:P {id: 3, age: 7});MATCH (a:P {id: 1}), (b:P {id: 3}) CREATE (a)-[:E]->(b);CALL project_graph_cypher('G1', 'MATCH (n:P) WHERE n.age < 10 RETURN n');CALL page_rank('G1') RETURN node.id, rank;- Louvain sur le graphe de ga : 5 communautés de tailles
[27,20,17,14,14]dans un processus de la CLI,[25,20,18,18,11]dans les deux suivants, avecCALL threads = 1; trois appels dans un même processus donnent les mêmes tailles. Les nœuds sans aucune relation reçoiventlouvain_id-1. - Les rangs PageRank totalisent 0,3534 sur le graphe de ga : les 25 projets sans référence sortante ne transmettent pas leur rang. L’exercice 1 recalcule deux rangs avec
(1 - 0.85) / N + 0.85 × Σ rank / out_degree, à quatre décimales. RETURN n + sum(x), avecnissu deWITH count(*) AS n, échoue avecCannot evaluate expression with type AGGREGATE_FUNCTION.;RETURN n, n + sum(x)fonctionne, etRETURN 2 * sum(x)aussi.- FTS : le raciniseur anglais fait correspondre queries à query et Persistance à persistant ; le raciniseur français ne fait pas correspondre Persistence. La casse est ignorée, les accents non (
donneesne trouve rien). Les mots vides par défaut sont anglais quel que soit le raciniseur. Après unCREATE, l’index inclut le nouveau nœud et les autres scores changent.DROP_FTS_INDEXafficheTable 3_titles_fr_terms has been dropped., une table interne.
2026-09-14 — Leçon 8 : transactions, fichiers et processus
Section intitulée « 2026-09-14 — Leçon 8 : transactions, fichiers et processus »- Une erreur dans une transaction manuelle annule toute la transaction et y met fin, sans message qui le dise : les erreurs d’exécution (une clé en double), les erreurs de liaison, les erreurs de catalogue, un
BEGINdans une transaction etCHECKPOINTdans une transaction le font toutes. Les instructions qui suivent s’exécutent en auto-commit ;ROLLBACKetCOMMITéchouent ensuite avecNo active transaction. La seule exception trouvée : une écriture dansBEGIN TRANSACTION READ ONLYéchoue, et la transaction reste ouverte. La page sur les transactions promet que rien d’une transaction en échec ne persiste, et ne dit pas que les instructions après l’erreur s’exécutent seules. - Une seconde transaction d’écriture est refusée aussitôt,
Cannot start a new write transaction in the system. Only one write transaction at a time is allowed in the system., même quand elle touche d’autres nœuds ; il n’y a ni attente ni délai d’expiration. Une instruction en auto-commit refusée laisse la connexion utilisable. - Java, un
BEGIN TRANSACTIONrefusé casse la connexion : la requête suivante lèveRuntimeException: Unknown Errorsous Windows, et fait planter la JVM avecSIGSEGVsous Linux (CatalogSet::traverseVersionChainsForTransactionNoLock) et macOS (CatalogSet::containsEntry), code de sortie 134. UnBEGINqui réussit répare la connexion sous Windows ; unBEGINretenté ne plante sur aucun des trois runners (exercice 2 : 153, 155 et 24 refus). Reproduction minimale, uneDatabaseen mémoire et deux connexions :firstexécuteBEGIN TRANSACTION,secondexécuteBEGIN TRANSACTION(refusé),firstexécuteCOMMIT,secondexécute n’importe quelMATCH. Le paquet C# et les autres bindings n’ont pas été testés. - Les fichiers : le
.walapparaît au premier commit et disparaît au point de contrôle quand la base de données se ferme ; pas de.walpour une transaction non validée.kill -9après un commit laisse le.wal, rejoué à l’ouverture suivante ;kill -9dans une transaction perd la transaction. Fermer uneDatabaseJava avec une transaction ouverte l’annule. - Un second processus en lecture-écriture échoue avec
Could not set lock on file, suivi de(Error: 33)sous Windows et de(Error: Resource temporarily unavailable)sous Linux. Le code POSIX ferme le descripteur de fichier avantF_GETLK, donc son messageLock is held by PIDne peut pas apparaître (local_file_system.cpp, lignes 147-172). - Une base de données en lecture seule ne prend aucun verrou (
storage_manager.cpp, lignes 87-91). Un processus en lecture seule ouvre un fichier que tient un processus d’écriture sous Linux et macOS, et voit les modifications validées de ce dernier depuis le.wal; pas sous Windows, où leLockFileExdu processus d’écriture fait échouer la lecture avecError 33. Un processus d’écriture ouvre, modifie et fait un point de contrôle d’un fichier que tient un processus en lecture seule, sur les trois OS, et le lecteur continue de répondre depuis l’ancien état. La page sur la concurrence dit que cette combinaison n’est pas permise. - Dans un même processus Java, une seconde
Databaseen lecture-écriture, ou en lecture seule, sur un fichier que tient uneDatabaseen lecture-écriture : refusée sous Windows, ouverte sous Linux et macOS (les verrousfcntlappartiennent au processus). new Database(path)lèvejava.lang.Exception, une exception vérifiée qu’il ne déclare pas (lbug_java.cpp, lignes 618-636).EXPORT DATABASEen CSV écrit les macros dansschema.cypher; la page sur la migration mentionne un fichiermacro.cypherqui n’est pas créé. Les en-têtes CSV sont préfixés para.. L’ordre des options danscopy.cypherdiffère entre Windows et Linux ou macOS.IMPORT DATABASEdans une base de données non vide échoue avecProject already exists in catalog.- La 0.20.4 ouverte en lecture seule laisse un fichier 0.19.1 tel quel ; ouverte en lecture-écriture, elle le met à niveau de la version de stockage 43 à 47 sans message, et la CLI 0.19.1 affiche ensuite
Database file version: 47, Current build storage version: 43. - CI : la première exécution a échoué sur les trois jobs
cypher, parce quefiles.sh, exécuté sousset -e, renvoyait le code de sortie de la dernière CLI 0.19.1 ;check.shl’ignore désormais et compare la sortie. Les jobsjavasous Linux et macOS ont planté auBEGINrefusé, désormais dans un mode à part. La deuxième exécution a échoué sous macOS seulement :lstriaitProject.csvaprèscopy.cypher; le script trie avecLC_ALL=C. - Quand sa sortie est redirigée vers un fichier, la CLI n’écrit rien avant de se terminer :
files.shattend le fichier.wal, ou un temps fixe, plutôt qu’une ligne de sortie.
2026-09-14 — Bugs trouvés dans la 0.20.4
Section intitulée « 2026-09-14 — Bugs trouvés dans la 0.20.4 »Six bugs silencieux : aucune erreur, un résultat faux. Aucun n’avait d’issue sur le tracker de LadybugDB quand j’ai cherché le 2026-09-14 ; je ne les ai pas signalés. Chaque reproduction ci-dessous s’exécute dans une base de données en mémoire vide (lbug -m csv < repro.cypher) et n’a été exécutée que sous Windows, sauf mention contraire.
1. COPY depuis une sous-requête JSON avec MATCH relie les mauvais nœuds
Section intitulée « 1. COPY depuis une sous-requête JSON avec MATCH relie les mauvais nœuds »Avec r.json contenant [{"id":1,"k":"a"},{"id":2,"k":"b"},{"id":3,"k":"c"}] :
LOAD json;CREATE NODE TABLE K(k STRING PRIMARY KEY);CREATE NODE TABLE R(id INT64 PRIMARY KEY);CREATE REL TABLE ON_K(FROM R TO K);CREATE (:K {k: 'a'}), (:K {k: 'b'}), (:K {k: 'c'}), (:R {id: 1}), (:R {id: 2}), (:R {id: 3});COPY ON_K FROM (LOAD FROM 'r.json' WITH id, k MATCH (x:K) WHERE x.k = k RETURN id, k);MATCH (r:R)-[:ON_K]->(x:K) RETURN r.id, x.k ORDER BY x.k;3 tuples have been copied to the ON_K table.r.id,x.k1,a1,b1,cJ’attendais 1,a, 2,b, 3,c, ce que donne le même COPY depuis un fichier CSV avec les mêmes lignes. Avec (IGNORE_ERRORS = true) en plus, les trois relations vont plutôt vers a : 1,a, 2,a, 3,a. La leçon 4 l’a rencontré avec 123 exécutions : les 123 relations partaient toutes de la même exécution ; filtrer avec WHERE sans MATCH fonctionne.
IGNORE_ERRORS = true sur un COPY depuis une sous-requête, quand une clé manque, échoue avec Error: bad variant access au lieu d’ignorer la ligne.
2. count(DISTINCT …) avant sum(…) rend la somme NULL
Section intitulée « 2. count(DISTINCT …) avant sum(…) rend la somme NULL »CREATE NODE TABLE P(id INT64 PRIMARY KEY, g STRING, n INT64);CREATE (:P {id: 1, g: 'x', n: 10}), (:P {id: 2, g: 'x', n: 20}), (:P {id: 3, g: 'y', n: 5});MATCH (p:P) RETURN p.g, count(DISTINCT p) AS ps, sum(p.n) AS total ORDER BY p.g;MATCH (p:P) RETURN p.g, sum(p.n) AS total, count(DISTINCT p) AS ps ORDER BY p.g;p.g,ps,totalx,2,""y,1,""p.g,total,psx,30,2y,5,1Les mêmes agrégats, dans l’autre ordre, donnent les bons totaux.
La leçon 5 en a trouvé une forme plus générale, dans le moteur 0.19.1 du paquet NuGet aussi : avec une clé de regroupement, un agrégat placé après un agrégat DISTINCT dans la même projection est faux. RETURN k.name, collect(DISTINCT u.version) AS versions, count(*) AS projects donne 0 projet pour MongoDB.Driver au lieu de 14.
3. ACYCLIC garde des chemins qui répètent un nœud
Section intitulée « 3. ACYCLIC garde des chemins qui répètent un nœud »Trois nœuds, a et b reliés dans les deux sens, b relié à c :
CREATE NODE TABLE V(id STRING PRIMARY KEY);CREATE REL TABLE E(FROM V TO V);CREATE (:V {id: 'a'}), (:V {id: 'b'}), (:V {id: 'c'});MATCH (x:V {id: 'a'}), (y:V {id: 'b'}) CREATE (x)-[:E]->(y), (y)-[:E]->(x);MATCH (x:V {id: 'b'}), (y:V {id: 'c'}) CREATE (x)-[:E]->(y);MATCH p = (x:V {id: 'a'})-[e:E* ACYCLIC 1..4]->(y:V {id: 'c'}) RETURN properties(nodes(p), 'id') AS path;MATCH p = (x:V {id: 'a'})-[e:E*1..4]->(y:V {id: 'c'}) WHERE is_acyclic(p) RETURN properties(nodes(p), 'id') AS path;path"[a,b,a,b,c]""[a,b,c]"path"[a,b,c]"[a,b,a,b,c] passe deux fois par b. La vérification ACYCLIC se trouve dans output_writer.cpp, ligne 280 ; je n’ai pas trouvé la cause. Sur le graphe du site de la leçon 3, LINKS_TO* ACYCLIC 1..4 depuis l’index de GitHub Actions jusqu’à sa leçon 7 a renvoyé 14 chemins sous Windows, dont un qui passe deux fois par la même leçon, et 29, toutes les marches, sur les runners Linux et macOS. Une énumération en Python donne 25 chemins dont les nœuds intermédiaires sont distincts, et 10 avec la définition de is_acyclic, les deux extrémités comprises.
4. Un COPY en échec dans une table de nœuds la corrompt
Section intitulée « 4. Un COPY en échec dans une table de nœuds la corrompt »Chargée avec COPY, puis un COPY qui échoue sur une clé en double, avec n.csv contenant l’en-tête k et les lignes a, b, c :
CREATE NODE TABLE N(k STRING PRIMARY KEY);COPY N FROM 'n.csv' (HEADER = true);COPY N FROM (RETURN 'a' AS k);MATCH (n:N {k: 'b'}) RETURN count(*) AS b_by_key;CREATE (:N {k: 'b'});MATCH (n:N) RETURN n.k ORDER BY n.k;En mémoire :
Error: Copy exception: Found duplicated primary key value a, which violates the uniqueness constraint of the primary key column.b_by_key0n.kaabcb est là, mais sa clé ne trouve rien ; le CREATE d’une clé existante réussit, et le nouveau nœud contient a, la clé que le COPY en échec a rejetée, au lieu de b. Dans un fichier de base de données (lbug -m csv d/x.lbdb), cette séquence fonctionne : b_by_key vaut 1 et le CREATE échoue avec Runtime exception: Found duplicated primary key value b.
Quand les trois nœuds sont créés avec CREATE au lieu de COPY, le COPY en échec casse aussi un fichier de base de données : après lui, CREATE (:N {k: 'z', v: 26}) sur une table N(k STRING PRIMARY KEY, v INT64) ajoute un nœud qui se relit comme a,9, la ligne rejetée, en mémoire comme sur disque.
5. Grouper par une sous-requête COUNT fait disparaître le groupe de 0
Section intitulée « 5. Grouper par une sous-requête COUNT fait disparaître le groupe de 0 »CREATE NODE TABLE C(id INT64 PRIMARY KEY);CREATE REL TABLE PARENT(FROM C TO C);CREATE (:C {id: 1}), (:C {id: 2}), (:C {id: 3});MATCH (a:C {id: 2}), (b:C {id: 1}) CREATE (a)-[:PARENT]->(b);MATCH (a:C {id: 3}), (b:C {id: 2}) CREATE (a)-[:PARENT]->(b);MATCH (c:C) RETURN c.id, COUNT { MATCH (c)-[:PARENT]->(:C) } AS parents ORDER BY c.id;MATCH (c:C) RETURN COUNT { MATCH (c)-[:PARENT]->(:C) } AS parents, count(*) AS cs ORDER BY parents;c.id,parents1,02,13,1parents,cs1,2La sous-requête renvoie 0 pour le nœud 1, mais la requête groupée n’a pas de ligne 0,1. OPTIONAL MATCH avec count(…) donne les deux groupes (leçon 4).
6. timestamp() et CAST(… AS TIMESTAMP) ignorent le décalage
Section intitulée « 6. timestamp() et CAST(… AS TIMESTAMP) ignorent le décalage »RETURN timestamp('2026-09-14T09:30:00-04:00') AS f, CAST('2026-09-14T09:30:00-04:00' AS TIMESTAMP) AS c, CAST('2026-09-14T09:30:00-04:00' AS TIMESTAMP_TZ) AS tz;f,c,tz2026-09-14 09:30:00,2026-09-14 09:30:00,2026-09-14 13:30:00+00J’attendais 13:30 pour les deux premières aussi : COPY et LOAD FROM convertissent le même texte dans un fichier CSV en 13:30 UTC (leçon 4). Le décalage est abandonné sans être appliqué. timestamp(…) dans le paquet C# 0.19.1 renvoie 09:30 lui aussi (leçon 5).
2026-09-27 — Leçon 9 : un laboratoire de graphe
Section intitulée « 2026-09-27 — Leçon 9 : un laboratoire de graphe »Un laboratoire sur des pages inventées sous /lab/, pour prolonger les leçons 2 et 3 plutôt que les répéter. Les prédictions sont allées d’abord dans data/lab/preregistration.md, haché à 13:24 EDT (SHA-256 234e0639…040d) avant le chargement de tout jeu de données. Chaque fichier attendu en a été tiré à la main, avant l’exécution qu’il vérifie. Environnement : CLI LadybugDB 0.20.4 sous Windows, bases en mémoire, CALL threads = 1 avant le COPY comparé. Le script entier tourne en 1,5 à 6,5 s.
-
Traceur et extrémité absente : verts dès la première exécution. Après le
COPYéchoué dansLINKS_TO, il reste 2 liens et l’index de clé fonctionne. Le bug d’index de la leçon 2 concernait les tables de nœuds. -
Traductions : vertes dès la première exécution. L’égalité des titres donne 3 faux signalements sur 4.
-
La première exécution des cas de cycles différait du fichier attendu en deux points :
- Un défaut du script, le mien :
shortestest un mot-clé, et l’analyseur l’a refusé comme alias. - Un comportement amont : une piste fermée liste ses pages à contresens des flèches (ligne QA). Les pages et la longueur étaient conformes à la prédiction ; seul l’ordre ne l’était pas.
Une sonde sur le cycle de six pages, lancée avant la rédaction de l’addendum, a séparé les chemins ouverts (dans l’ordre) des fermés (inversés). Les deux points sont consignés dans un addendum daté ; les prédictions d’origine sont restées telles quelles.
- Un défaut du script, le mien :
-
Bornes : confirmé. Une borne de 5 manque un cycle de 6, et 6 le trouve. Une borne de 1 cache un prérequis à la profondeur 2.
Non exécuté : le check.sh complet sur cette machine. Son INSTALL json téléchargerait une extension, et ce laboratoire n’avait pas droit aux téléchargements. Seuls les deux scripts de la leçon 9 ont été comparés en local.
À vérifier
Section intitulée « À vérifier »- La leçon 9 sous Linux et macOS : le laboratoire n’a tourné que sous Windows.
ladybugdb-examples.ymlla compare sur les trois runners. - Si
nodes()sur un motif fermé est inversé volontairement (non documenté) ou par bug, et si c’est corrigé après la 0.20.4. Le suivi amont n’a pas été consulté. - Le script d’installation (
curl -s https://install.ladybugdb.com | bash) etbrew install ladybug: pas exécutés pour ce cours. - Les reproductions minimales n’ont été exécutées que sous Windows. Les scripts du cours qui montrent les bugs 1, 2, 4 et 5 sur les données du site donnent la même sortie sur les trois runners de la CI.
- Si ces bugs sont déjà corrigés sur la branche principale de LadybugDB, après la 0.20.4.
- Si les conflits de version majeure de l’exercice 2 de la leçon 5 (
MongoDB.Driver2 et 3,Microsoft.ML.Tokenizers1 et 2) cassent ga à l’exécution : je n’ai ni compilé ni exécuté ga. - Le programme C# sur
linux-arm64etosx-x64, et le programme Java surlinux_arm64: les runners de la CI sontlinux-x64,win-x64etosx-arm64. - Si
CREATE_FTS_INDEXfait aussi planter la CLI 0.20.4 sur le runner Windows : la CI n’exécute la leçon 7 qu’avec la 0.19.1. - Si les deux projets isolés de
Common,GA.Business.DSL.SourceGenetGA.Business.Core.Generated, sont utilisés dans ga autrement que par uneProjectReference. - Si un processus en lecture seule qui reste ouvert pendant qu’un processus d’écriture fait un point de contrôle renvoie des données fausses, et pas seulement anciennes, quand il lit des pages qu’il n’avait pas en cache (leçon 8).
- Sous Linux et macOS, si fermer la seconde
Databased’un processus libère le verroufcntlde la première, comme fermer n’importe quel descripteur d’un fichier libère les verrous du processus sur ce fichier, et permet à un autre processus d’ouvrir le fichier en lecture-écriture. - Si un
BEGINrefusé casse aussi une connexion du paquet C# et des autres bindings.