8.4 KiB
Handoff v0.3.16 — RAW resilience, variantes et gestion des conflits
1. Origine de la version
Le live 0.3.15-pre.014 a démontré qu'une même transaction du même bloc peut être renvoyée avec une qualité de représentation différente selon le nœud RPC. Le cas observé n'était pas un fork : même slot, même identité de bloc, même transaction et mêmes métadonnées hors logMessages. Une acquisition contenait les logs complets ; l'autre contenait un marqueur Log truncated.
0.3.15-pre.014-fix.001 ferme uniquement le cas où la version complète est déjà canonique et où une version tronquée strictement compatible arrive ensuite. La généralisation ci-dessous appartient à 0.3.16.
2. Principe directeur
une divergence de données != une panne d'acquisition
Le pipeline ne doit plus arrêter un Worker simplement parce qu'un N-ième RAW d'identité (network, signature) diffère du canonique. Store doit d'abord classifier la divergence, préserver les données et seulement exposer un conflit durable lorsqu'aucune convergence automatique sûre n'est démontrée.
Un provider n'est jamais déclaré « toujours correct ». La préférence porte sur la qualité prouvée de la représentation, indépendamment de l'ordre d'arrivée et de la provenance.
3. Classification de convergence
Le comparateur partagé vise au minimum :
Exact
CompatibleLessComplete
CompatibleMoreComplete
Conflict
Règles :
Exact: contenu canonique identique ; observation idempotente/nouvelle ;CompatibleLessComplete: conserver le canonique plus complet et l'observation entrante ;CompatibleMoreComplete: promotion atomique vers la représentation plus complète, sans détruire l'ancienne variante ;Conflict: conserver le canonique courant, conserver intégralement la variante entrante et ouvrir/compléter un dossier de conflit.
La comparaison est champ-spécifique. Il est interdit d'utiliser une règle générique « valeur la plus longue gagne ». logMessages peut utiliser une relation de troncature explicitement prouvée ; les autres champs ne deviennent enrichissables que lorsque leur contrat permet de distinguer absence informationnelle et contradiction.
4. Modèle Store cible
Le modèle conceptuel devient :
RawTransaction identity (network, signature)
|
+-- Variant A <- canonical current
| +-- observations/provenances
|
+-- Variant B
| +-- observations/provenances
|
+-- Variant C
+-- observations/provenances
Conflict case
+-- status
+-- canonical variant
+-- classification
+-- resolution history
Les noms physiques exacts restent à décider en 0.3.16-pre.001, mais les responsabilités doivent couvrir : identité canonique, variantes de contenu, observations rattachées à la variante réellement reçue, dossier de conflit et historique de résolution/promotion.
Une résolution ou promotion n'efface jamais l'ancienne valeur canonique. Un A -> B doit pouvoir être suivi plus tard d'un B -> A sans reconstruire les bytes depuis une source externe.
5. Résolution automatique
Le cas logMessages sert de première preuve :
FULL + TRUNCATED compatible
-> garder FULL
TRUNCATED + FULL compatible
-> promouvoir FULL
TRUNCATED A + TRUNCATED B compatibles
-> promouvoir uniquement si la relation de qualité est formellement démontrée
Les champs scalaires présents avec des valeurs différentes restent des conflits. Les listes ne sont jamais fusionnées par longueur seule. Une absence peut devenir enrichissable uniquement si le contrat RPC/source prouve que l'absence signifie « non fourni » et non une valeur métier différente.
6. Conflit durable et santé Worker
Un conflit durablement quarantiné n'est pas terminal :
content conflict
-> variant persisted
-> conflict case unresolved
-> Worker Running
-> health Degraded
Les snapshots doivent pouvoir exposer au minimum des compteurs source-neutral et sans identité métier, par exemple unresolved_content_conflict_count, auto_resolved_conflict_count et canonical_promotion_count. Les signatures, payloads et variantes restent Store-owned.
La complétude doit distinguer :
acquisition_complete
= toute donnée attendue est durablement capturée, variantes comprises
canonical_complete
= aucune obligation de réconciliation canonique n'est ouverte
Un conflit non résolu ne doit pas bloquer l'acquisition des autres transactions. Le passage RAW -> STRUCTURAL décidera séparément comment traiter une identité encore conflictuelle.
7. Store indisponible, retry et backpressure
Un Store temporairement indisponible est différent d'un content conflict. Le Worker ne doit ni perdre silencieusement les données ni continuer à consommer indéfiniment sans durabilité.
Le contrat 0.3.16 doit prévoir une politique Store bornée : pause/backpressure des admissions, retry, délais/backoff configurables dans la couche propriétaire appropriée, observabilité Degraded/Blocked, puis terminal seulement si la politique explicite est épuisée ou si l'erreur est structurellement irréconciliable.
8. Reconnexion Transport
La reconnexion Transport doit être explicitement configurable et distincte de la politique Store. Paramètres conceptuels à auditer en pre.001 :
initial_delay
max_delay
backoff_multiplier
max_attempts
reset_after_stable_duration
jitter borné si retenu
Une perte de connexion ne doit pas provoquer un arrêt immédiat tant que la politique de reconnexion n'est pas épuisée. Les gaps éventuels continuent à utiliser les mécanismes de continuité/repair du Worker ; aucune reconnexion ne vaut preuve de couverture.
9. ksp-app-store-desk
0.3.16 étend Store Desk avec une ou plusieurs vues dédiées, au minimum autour de deux responsabilités : conflits ouverts et historique de résolution.
Pour une identité, l'UI doit pouvoir présenter le canonique, les variantes, leurs différences structurées et leurs provenances sans charger arbitrairement tout le RAW dans les tables. Actions visées : promouvoir une variante, conserver le canonique et résoudre, fusion assistée uniquement pour des champs dont la compatibilité est définie, rouvrir/restaurer une résolution antérieure.
Une fusion manuelle produit une nouvelle variante explicitement marquée comme synthétique/manuelle avec ses parents ; elle ne prétend jamais être une observation provider native. Aucune action utilisateur ne doit rendre les anciennes variantes irrécupérables.
10. Frontières de crates
ksp-store-api
-> DTO/contrats backend-neutral variantes, conflits, résolutions
ksp-store-lib
-> façade unique et moteur de convergence exposé aux producteurs
ksp-store-postgres-lib
-> atomicité physique, schema/migrations, variantes et historique
ksp-worker-raw-transaction-ingest-lib
-> continue sur conflits locaux durables, retry/reconnect selon contrats existants
ksp-app-store-desk
-> inspection/résolution via ksp-store-lib uniquement
Aucun Worker ou Job ne dépend directement du backend PostgreSQL. Le moteur de convergence est partagé afin que les futurs producteurs n'implémentent pas chacun leur propre arbitrage.
11. Versions suivantes
La trajectoire retenue est désormais :
0.3.16 RAW resilience / conflict variants / Store Desk conflict management
0.3.17 ksp-job-backfill-lib multi-route / multi-stratégie
0.3.18 ksp-app-backfill-desk adapté au Job multi-route
0.3.17 conserve le Job borné et paramétré, mais lui permet de composer plusieurs routes/stratégies d'acquisition sur la même convergence Store 0.3.16. Il ne dépend pas du Worker.
0.3.18 adapte le Desk Backfill à ce Job : inventaire/composition de routes, sélection, supervision, progression et résultats, en réutilisant les patterns applicatifs prouvés par ksp-app-raw-transaction-ingest-desk sans dupliquer la logique métier du Job.
12. Hors périmètre de 0.3.15-pre.014-fix.001
Le fix courant ne crée aucune table de variante, ne promeut pas un canonique tronqué vers une version plus complète, ne modifie pas le health model Worker, n'ajoute pas de retry Store et ne change pas la politique de reconnexion Transport. Il ferme seulement le cas live démontré où une observation entrante tronquée et strictement compatible arrive après un canonique complet.