Files
khadhroony-solana-project/deltas/0.1.2/pre.004-fix.001.md

4.3 KiB

Delta 0.1.2-pre.004-fix.001

Base requise

Livraison précédente :

0.1.2-pre.004

La base porte :

workspace.package.version = "0.1.2-pre.4"
Cargo.toml header version = 31

Validations remontées

Les validations utilisateur de pre.004 sont :

cargo fmt --all                         OK
cargo check --workspace                OK
cargo clippy --workspace --all-targets OK
cargo test --workspace                 ECHEC
cargo tree -p ksp-logging-lib          OK
cargo tree -p ksp-logging-lib -d       OK — aucun doublon
cargo tree -p ksp-logging-lib -e features inspecté

Tous les tests unitaires, de callsite et de façade publique passent. Le seul échec est :

global_runtime_supports_takeover_non_blocking_outputs_hot_reload_and_single_initialization

sur :

assertion failed: file_text.contains("file output marker")

Le test émet une ligne sur le sink fichier, retire immédiatement ce sink par hot reload, puis lit le fichier. Il constitue donc un test direct du contrat de drain/flush lors d'un reload.

Cause de lifecycle

pre.004 faisait conceptuellement :

reload_handle.reload(new_layers)
retire counters
replace outputs
drop(old WorkerGuard)

Le layer fmt retiré possède les clones NonBlocking utilisés pour alimenter le worker. KSP ne récupérait cependant pas explicitement l'ancien Vec de layers ; l'ordre entre la destruction effective de ces anciens layers et la destruction des WorkerGuard n'était donc pas exprimé dans notre lifecycle.

Pour un sink non bloquant, l'ordre voulu est explicite :

1. préparer complètement le nouveau runtime
2. remplacer le Vec actif et récupérer l'ancien Vec
3. mémoriser les dropped-line counters
4. remplacer les outputs actifs
5. détruire les anciens layers / NonBlocking senders
6. détruire les anciens WorkerGuard
7. retourner du reinitialize()

WorkerGuard envoie le signal de shutdown au worker et attend son drain/flush de manière bornée. Les anciens senders doivent donc être libérés avant cette étape lorsqu'un sink vient d'être retiré.

Correction

reinitialize() n'utilise plus :

Handle::reload(new_layers)

pour les changements de runtime.

Il utilise :

Handle::modify(... mem::replace(active_layers, new_layers) ...)

et récupère ainsi l'ancien RuntimeLayers.

Après succès du swap :

drop(retired_layers)
drop(retired_outputs)

est exécuté dans cet ordre.

Cette correction :

  • ne change pas l'API publique ;
  • conserve le subscriber global unique ;
  • conserve le takeover KSP ;
  • conserve la préparation transactionnelle des nouveaux sinks avant le swap ;
  • conserve l'ancienne configuration lorsqu'une validation ou une construction de sink échoue avant le swap ;
  • rend explicite le lifecycle de retrait des NonBlocking writers avant leurs WorkerGuard ;
  • évite d'ajouter un sleep ou un polling temporel au test.

Le test d'intégration qui a révélé le défaut reste inchangé et sert directement de test de non-régression.

Référence backend

tracing-appender 0.2.5 documente WorkerGuard comme responsable du flush des logs bufferisés à sa destruction. Son implémentation de Drop envoie un Msg::Shutdown au worker puis attend le signal de fin de drain de manière bornée. KSP doit donc contrôler clairement l'ordre de destruction des senders/layers et du guard au moment d'un hot reload.

Version technique

Ce correctif modifie du Rust. La version workspace devient :

workspace.package.version = "0.1.2-pre.4.fix.1"

et l'en-tête du Cargo.toml racine devient :

# version: 32

Fichiers du delta

Cargo.toml
crates/ksp-logging-lib/src/runtime.rs
docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md
deltas/0.1.2/pre.004-fix.001.md

Validations à exécuter

cargo fmt --all
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test --workspace

Le graphe Cargo/features de pre.004 a déjà été remonté sans doublon. Il pourra être réaudité dans pre.005 avec les validations d'intégration finales.

Si ces validations sont propres, la tranche suivante reste :

0.1.2-pre.005 — intégration + concurrence + saturation + audits