Files
khadhroony-solana-project/deltas/0.3.3/pre.006.md
2026-08-30 13:06:33 +02:00

312 lines
8.0 KiB
Markdown

<!-- file: deltas/0.3.3/pre.006.md -->
<!-- version: 1 -->
# Delta `0.3.3-pre.006` — pagination keyset et cursor V1 RawTransaction
## 1. Base et gate d'entrée
Base opérateur obligatoire :
```text
0.3.3-pre.5
```
Le gate opérateur fourni le 2026-08-30 est entièrement vert :
```text
cargo fmt --all PASS
scripts/audit_rust_workspace_rules.py PASS
scripts/audit_markdown_tables.py PASS
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS
cargo test -p ksp-store-api PASS
cargo test -p ksp-store-lib PASS
cargo test -p ksp-store-postgres-lib PASS (30 unit backend, live foundation ignoré)
cargo test -p ksp-config-lib PASS (128 unit + ownership/public API)
cargo check -p ksp-store-lib --no-default-features PASS
```
La tranche peut donc ouvrir la pagination sans rouvrir l'écriture atomique acquise en `pre.005`.
## 2. Version
```text
workspace.package.version = 0.3.3-pre.6
```
## 3. Scope exact
Cette tranche ajoute uniquement la navigation backend PostgreSQL des transactions RAW :
```text
PostgresBackend::list_raw_transactions
RawTransactionQuery
RawPage<RawTransactionReference>
RawPageCursor backend-private V1
```
Ne sont pas ouverts :
```text
transitions Full -> Archived -> Purged
compare-and-transition de rétention
implémentations complètes des six traits RawTransaction*
dispatch ksp-store-lib
RawAccountState
worker/job policy
```
## 4. Ordre physique et keyset
L'ordre total reste celui figé en `pre.001` :
```text
Ascending = (slot ASC, signature ASC)
Descending = (slot DESC, signature DESC)
```
Les deux requêtes privées utilisent :
```text
retention_state <> 'purged'
slot >= start_inclusive si présent
slot <= end_inclusive si présent
(slot, signature) > cursor pour Ascending
(slot, signature) < cursor pour Descending
LIMIT requested + 1
```
Aucun `OFFSET` n'est utilisé. La signature 64 octets constitue le tie-breaker déterministe pour plusieurs transactions au même slot.
Le prédicat et l'ordre correspondent à l'index V001 existant :
```text
ix_ksp_raw_transactions_slot_signature
ON ksp_raw_transactions (slot, signature)
WHERE retention_state <> 'purged'
```
Aucune migration n'est modifiée.
## 5. Cursor V1
Le cursor est implémenté dans un sous-module privé au domaine `raw_transaction`.
Format fixe :
```text
magic 4 bytes = KSPT
version 1 byte = 1
last_slot 8 bytes = u64 big-endian
last_signature 64 bytes
binding_digest 32 bytes = SHA-256
--------------------------------------
total 109 bytes
```
Le digest utilise le domaine séparé :
```text
KSP/raw-transaction-cursor/v1
```
Il lie de façon non ambiguë :
```text
longueur du network + network
direction
flag start + start slot si présent
flag end + end slot si présent
last_slot
last_signature
```
Le digest n'est ni un secret ni un mécanisme d'autorisation. Il sert uniquement à détecter corruption et replay sous un autre contexte de navigation.
Le decode rejette avant `pool.get()` :
```text
taille != 109
magic incorrect
version inconnue
digest divergent
network différent
range différente
direction différente
last_slot hors range
bytes hostiles bornés par RawPageCursor
```
Ces cas sont classés `PostgresBackendErrorKind::QueryInvalid` avec phase statique uniquement.
## 6. Limite physique réelle
`RawPageLimit` reste sans cap métier KSP.
Le backend doit obtenir une row supplémentaire pour déterminer la présence d'une continuation :
```text
LIMIT requested + 1
```
La borne physique exacte retenue sur PostgreSQL est donc :
```text
requested <= i64::MAX - 1
```
Une valeur supérieure produit :
```text
PostgresBackendErrorKind::PageLimitUnsupported
```
Aucun clamp vers `100`, `500`, `1000` ou une autre taille de worker/job n'est introduit.
## 7. Décodage des pages
Toutes les rows retournées par le statement, y compris la row de probe `limit+1`, passent par le mapping fallible :
```text
signature BYTEA -> [u8; 64]
slot NUMERIC(20,0) -> texte décimal -> u64
```
Une corruption physique n'est donc pas masquée simplement parce qu'elle se trouve sur la row supplémentaire.
Si plus de `requested` rows sont valides :
1. la row de probe est retirée du résultat ;
2. le cursor est construit depuis la dernière row réellement retournée ;
3. la page contient exactement au plus `requested` références.
Les tombstones `Purged` ne sont jamais listés.
## 8. Concurrence et snapshot
Chaque page est cohérente au niveau de son statement PostgreSQL, mais la navigation ne prétend pas fournir un snapshot inter-pages.
Une mutation concurrente qui insère une clé ordonnée avant le cursor déjà consommé peut ne pas être vue par cette navigation. Cette propriété est désormais explicitement documentée et n'est pas confondue avec une garantie de replay transactionnel.
## 9. Erreurs sûres
Deux kinds backend supplémentaires sont matérialisés :
```text
PageLimitUnsupported
QueryInvalid
```
Comme les autres erreurs physiques, ils ne conservent que :
```text
kind
phase &'static str
```
Aucun cursor, network hostile, signature, query SQL, bind, SQLSTATE ou texte PostgreSQL n'est rendu.
## 10. Tests et canaris
Les tests unitaires ajoutés couvrent :
```text
round-trip cursor V1 exact
109 bytes exacts
magic/version exacts
replay autre network
replay autre direction
replay autre range
last_slot hors range
taille hostile 1/108/109/110/4096
mutation magic/version/digest
borne page i64::MAX - 1
rejet i64::MAX
```
Les canaris d'intégration figent :
```text
keyset > / <
ASC/DESC total
pas d'OFFSET
exclusion Purged
index V001 compatible
pas de cap 500/1000
pas de DELETE
pas de transition de rétention
pas d'implémentation complète de trait prématurée
```
La pagination PostgreSQL réelle multi-page avec ties/ranges reste réservée au gate live opt-in `pre.009`.
## 11. Migrations
Les migrations sont byte-identiques à `pre.005` :
```text
V000 d29068b8c13b9dc0cc9ef6aaadd0fa12d41e0fe4c56541a1118c4bfc846a1450
V001 31488cda2f08f3f46c4cdbdbb6c18c243662fada02eac4487040c8735d72cc51
```
V001 conserve exactement :
```text
4 tables
35 contraintes
1 index
40 ressources
```
## 12. Fichiers ajoutés/modifiés
```text
Cargo.toml
crates/ksp-store-postgres-lib/README.md
crates/ksp-store-postgres-lib/USAGE.md
crates/ksp-store-postgres-lib/src/error.rs
crates/ksp-store-postgres-lib/src/lib.rs
crates/ksp-store-postgres-lib/src/raw_transaction.rs
crates/ksp-store-postgres-lib/src/raw_transaction/cursor.rs
crates/ksp-store-postgres-lib/src/runtime.rs
crates/ksp-store-postgres-lib/tests/dependency_boundary.rs
crates/ksp-store-postgres-lib/tests/hardening_completeness.rs
crates/ksp-store-postgres-lib/tests/public_api.rs
crates/ksp-store-postgres-lib/unit_tests/raw_transaction.rs
docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md
docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md
deltas/0.3.3/pre.006.md
```
Aucun fichier n'est supprimé.
## 13. Audits exécutables dans l'environnement de génération
```text
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.3
```
Les commandes Cargo/rustfmt ne sont pas disponibles dans l'environnement de génération. Elles doivent rester `NON EXÉCUTÉES` jusqu'au gate opérateur ; elles ne doivent jamais être présentées comme PASS sans sortie réelle.
## 14. Gate opérateur
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.3
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-store-api
cargo test -p ksp-store-lib
cargo test -p ksp-store-postgres-lib
cargo test -p ksp-config-lib
cargo check -p ksp-store-lib --no-default-features
```
## 15. Suite après gate vert
```text
0.3.3-pre.007 — archive/purge/tombstone/compare-and-transition + Compacted unsupported stable
```