v0.1.2-pre.002-fix

This commit is contained in:
2026-08-14 18:10:38 +02:00
parent 7b4444fb07
commit 697527675a
7 changed files with 153 additions and 20 deletions

View File

@@ -1,12 +1,12 @@
# file: Cargo.toml # file: Cargo.toml
# version: 27 # version: 28
[workspace] [workspace]
resolver = "3" resolver = "3"
members = ["crates/ksp-core-lib", "crates/ksp-logging-lib"] members = ["crates/ksp-core-lib", "crates/ksp-logging-lib"]
[workspace.package] [workspace.package]
version = "0.1.2-pre.2" version = "0.1.2-pre.2.fix.1"
edition = "2024" edition = "2024"
license = "MIT" license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-logging-lib/src/settings.rs // file: crates/ksp-logging-lib/src/settings.rs
// version: 1 // version: 2
/// Runtime filter level used by KSP logging settings. /// Runtime filter level used by KSP logging settings.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -216,13 +216,13 @@ impl LoggingSettings {
); );
} }
} }
if let std::option::Option::Some(file) = self.file.as_ref() { if let std::option::Option::Some(file) = self.file.as_ref()
if file.file_name_prefix().is_empty() { && file.file_name_prefix().is_empty()
return std::result::Result::Err( {
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_SETTINGS, "file name prefix must not be empty") return std::result::Result::Err(
.with_context("field", "file.file_name_prefix"), ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_SETTINGS, "file name prefix must not be empty")
); .with_context("field", "file.file_name_prefix"),
} );
} }
return std::result::Result::Ok(()); return std::result::Result::Ok(());
} }

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-logging-lib/src/span.rs // file: crates/ksp-logging-lib/src/span.rs
// version: 1 // version: 2
/// KSP-owned handle to a tracing span. /// KSP-owned handle to a tracing span.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@@ -28,8 +28,7 @@ impl Span {
/// Instruments an asynchronous future with a KSP span. /// Instruments an asynchronous future with a KSP span.
/// ///
/// The span is entered whenever the future is polled and exited whenever it yields, so no enter guard is held across an `.await` point. /// The span is entered whenever the future is polled or dropped and exited when that operation returns, so no enter guard is held across an `.await` point.
#[must_use]
pub fn instrument<F>(span: crate::Span, future: F) -> impl std::future::Future<Output = F::Output> pub fn instrument<F>(span: crate::Span, future: F) -> impl std::future::Future<Output = F::Output>
where where
F: std::future::Future, F: std::future::Future,

View File

@@ -1,5 +1,7 @@
// file: crates/ksp-logging-lib/tests/callsite.rs // file: crates/ksp-logging-lib/tests/callsite.rs
// version: 1 // version: 2
//! Integration tests for KSP logging callsite and async span instrumentation behavior.
const TEST_TARGET: &str = "ksp-logging-lib"; const TEST_TARGET: &str = "ksp-logging-lib";
@@ -137,7 +139,7 @@ fn span_macro_preserves_consumer_callsite() {
} }
#[test] #[test]
fn async_instrumentation_enters_and_exits_span_during_poll() { fn async_instrumentation_enters_and_exits_span_during_poll_and_drop() {
let captured = std::sync::Arc::new(std::sync::Mutex::new(std::vec::Vec::new())); let captured = std::sync::Arc::new(std::sync::Mutex::new(std::vec::Vec::new()));
let enters = std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0)); let enters = std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0));
let exits = std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0)); let exits = std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0));
@@ -150,8 +152,12 @@ fn async_instrumentation_enters_and_exits_span_during_poll() {
let mut context = std::task::Context::from_waker(waker); let mut context = std::task::Context::from_waker(waker);
let poll = std::future::Future::poll(future.as_mut(), &mut context); let poll = std::future::Future::poll(future.as_mut(), &mut context);
assert_eq!(poll, std::task::Poll::Ready(42_u32)); assert_eq!(poll, std::task::Poll::Ready(42_u32));
assert_eq!(enters.load(std::sync::atomic::Ordering::Relaxed), 1);
assert_eq!(exits.load(std::sync::atomic::Ordering::Relaxed), 1);
std::mem::drop(future);
assert_eq!(enters.load(std::sync::atomic::Ordering::Relaxed), 2);
assert_eq!(exits.load(std::sync::atomic::Ordering::Relaxed), 2);
return; return;
}); });
assert_eq!(enters.load(std::sync::atomic::Ordering::Relaxed), 1); assert_eq!(enters.load(std::sync::atomic::Ordering::Relaxed), exits.load(std::sync::atomic::Ordering::Relaxed));
assert_eq!(exits.load(std::sync::atomic::Ordering::Relaxed), 1);
} }

View File

@@ -1,5 +1,7 @@
// file: crates/ksp-logging-lib/tests/public_api.rs // file: crates/ksp-logging-lib/tests/public_api.rs
// version: 1 // version: 2
//! Integration tests for the public crate-root surface of `ksp-logging-lib`.
const TEST_TARGET: &str = "ksp-logging-lib"; const TEST_TARGET: &str = "ksp-logging-lib";

View File

@@ -0,0 +1,125 @@
<!-- file: deltas/0.1.2/pre.002-fix.001.md -->
<!-- version: 1 -->
# Delta 0.1.2-pre.002-fix.001
## Base requise
Livraison précédente :
```text
0.1.2-pre.002
```
Ce correctif traite uniquement les résultats de validation remontés après `pre.002`. Il ne modifie pas le périmètre fonctionnel de la prerelease et n'ouvre pas `pre.003`.
## Résultats de validation à corriger
Les commandes exécutées sur le workspace de développement ont montré :
- `cargo fmt --all` : exécuté sans erreur ;
- `cargo check --workspace` : réussi ;
- `cargo clippy --workspace --all-targets` : terminé avec quatre catégories de warnings à nettoyer dans Logging/tests ;
- `cargo test --workspace` : tous les tests Core et les tests unitaires Logging réussissent, mais `async_instrumentation_enters_and_exits_span_during_poll` échoue avec `enters = 2` au lieu de l'attente `1`.
## Cause du test async
Le test `pre.002` supposait qu'une future instrumentée n'entrait dans son span que pendant son unique `poll`.
Le contrat de `tracing::Instrument` est plus précis : la future instrumentée entre dans le span lors de chaque `poll` **et lors de son `Drop`**. Pour `std::future::ready(42_u32)`, le test observe donc :
```text
poll -> enter + exit
Drop -> enter + exit
```
Le compteur final `2` est donc conforme au comportement de `tracing`; c'est l'attente du test qui était incorrecte.
Le test corrigé vérifie séparément :
1. une paire `enter` / `exit` immédiatement après le `poll` ;
2. une deuxième paire après destruction explicite de la future instrumentée ;
3. l'équilibre final entre le nombre d'entrées et de sorties.
Le plan actif documente désormais explicitement cette sémantique afin qu'un futur test async ne réintroduise pas l'hypothèse erronée d'une seule paire `enter` / `exit` sur toute la durée de vie d'une future.
## Nettoyage Clippy
### `collapsible_if`
La validation du préfixe de fichier utilise désormais un `if let` avec condition chaînée compatible Rust 2024 au lieu de deux `if` imbriqués.
### `double_must_use`
L'attribut `#[must_use]` explicite de `ksp_logging_lib::instrument(...)` est supprimé : la fonction retourne déjà un type `Future`, lui-même marqué `must_use` par son contrat standard.
## Documentation des tests d'intégration
Les crates de tests d'intégration :
```text
crates/ksp-logging-lib/tests/callsite.rs
crates/ksp-logging-lib/tests/public_api.rs
```
reçoivent chacune une documentation crate-root `//! ...` afin de satisfaire `missing_docs = "warn"` lorsque les tests sont compilés comme crates séparées.
## Version Cargo
La version reste :
```text
0.1.2-pre.2
```
Aucune dépendance et aucun manifest ne sont modifiés.
L'identifiant de livraison de ce correctif est :
```text
0.1.2-pre.002-fix.001
```
## Fichiers modifiés
- `crates/ksp-logging-lib/src/settings.rs`
- `crates/ksp-logging-lib/src/span.rs`
- `crates/ksp-logging-lib/tests/callsite.rs`
- `crates/ksp-logging-lib/tests/public_api.rs`
- `docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md`
## Fichier ajouté
- `deltas/0.1.2/pre.002-fix.001.md`
## Validations statiques exécutées lors de la préparation
- contrôle des headers `file:` / `version:` des fichiers du correctif ;
- contrôle que `Cargo.toml` n'est pas inclus dans le delta ;
- contrôle que la version Cargo de la base reste `0.1.2-pre.2` ;
- contrôle de l'absence de nouvelle dépendance ;
- contrôle que le correctif ne contient aucun ajout `unwrap`, `expect`, `panic` ou opérateur `?` dans le code production modifié ;
- contrôle que l'archive contient uniquement les cinq fichiers modifiés et le nouveau delta.
## Validations à réexécuter sur le workspace
```bash
cargo fmt --all
cargo check --workspace
cargo test --workspace
cargo clippy --workspace --all-targets
```
Puis, pour compléter les validations prévues pour `pre.002` si elles ne l'ont pas encore été :
```bash
cargo tree -p ksp-logging-lib
cargo tree -p ksp-logging-lib -d
cargo tree -p ksp-logging-lib -e features
```
Aucune validation Cargo non exécutable dans l'environnement de préparation n'est déclarée réussie par ce delta.
## Suite
Une fois ce correctif validé, `0.1.2-pre.002` peut être considérée propre et la session peut passer à `0.1.2-pre.003` pour le subscriber runtime, le takeover, le filtering, la console non bloquante et la fondation du hot reload.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md --> <!-- file: docs/plans/004-V0_1_2_LOGGING_FOUNDATION_PLAN.md -->
<!-- version: 3 --> <!-- version: 4 -->
# Plan KSP 0.1.2 — Logging foundation # Plan KSP 0.1.2 — Logging foundation
@@ -223,7 +223,7 @@ La façade ne doit pas obliger les consumers à importer `tracing::Span` ou `tra
Pour le code synchrone, la surface doit permettre d'associer un scope d'exécution au span sans déplacer le callsite. Pour le code synchrone, la surface doit permettre d'associer un scope d'exécution au span sans déplacer le callsite.
Pour le code async, la surface doit instrumenter la `Future` elle-même. Un guard issu de `Span::enter()` ne doit pas être maintenu à travers un `.await`, car cela produit des traces incorrectes lorsque l'exécution change de tâche/thread ou que la future yield. Pour le code async, la surface doit instrumenter la `Future` elle-même. Un guard issu de `Span::enter()` ne doit pas être maintenu à travers un `.await`, car cela produit des traces incorrectes lorsque l'exécution change de tâche/thread ou que la future yield. La primitive `tracing::Instrument` entre dans le span à chaque `poll` **et lors du `Drop`** de la future instrumentée ; les tests KSP doivent donc distinguer explicitement ces deux opérations au lieu de supposer une seule paire `enter`/`exit` sur toute la durée de vie de la future.
### Mesure de durée ### Mesure de durée
@@ -791,6 +791,7 @@ Cette arborescence reste ajustable si une séparation plus petite suffit. Aucun
- target explicite conservé ; - target explicite conservé ;
- scope synchrone correctement associé au span ; - scope synchrone correctement associé au span ;
- future async instrumentée par la façade KSP ; - future async instrumentée par la façade KSP ;
- entrée/sortie du span vérifiée pendant chaque `poll` pertinent et lors du `Drop` de la future instrumentée ;
- aucun enter guard conservé à travers `.await` dans l'API recommandée ; - aucun enter guard conservé à travers `.await` dans l'API recommandée ;
- `SpanEvents::NewAndClose` produit les événements lifecycle attendus ; - `SpanEvents::NewAndClose` produit les événements lifecycle attendus ;
- `CLOSE` contient les temps `busy`/`idle` lorsque les timestamps sont actifs ; - `CLOSE` contient les temps `busy`/`idle` lorsque les timestamps sont actifs ;