0.3.15-pre.015
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 631
|
# version: 632
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-raw-transaction-ingest-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api", "crates/ksp-worker-raw-transaction-ingest-lib"]
|
members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-raw-transaction-ingest-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api", "crates/ksp-worker-raw-transaction-ingest-lib"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.3.15-pre.14.fix.3"
|
version = "0.3.15-pre.15"
|
||||||
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"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: crates/ksp-onchain-transport-lib/README.md -->
|
<!-- file: crates/ksp-onchain-transport-lib/README.md -->
|
||||||
<!-- version: 24 -->
|
<!-- version: 25 -->
|
||||||
|
|
||||||
# `ksp-onchain-transport-lib`
|
# `ksp-onchain-transport-lib`
|
||||||
|
|
||||||
@@ -239,7 +239,9 @@ YellowstoneGrpcSubscribeSnapshot
|
|||||||
|
|
||||||
Les sept unary standards retenus sont `SubscribeReplayInfo`, `Ping`, `GetLatestBlockhash`, `GetBlockHeight`, `GetSlot`, `IsBlockhashValid` et `GetVersion`. `Subscribe` couvre accounts, slots, transactions, transaction status, blocks, block metadata et entries, avec `commitment`, `accounts_data_slice`, `ping` et `from_slot`. `SubscribeDeshred` reste hors scope de la foundation standard.
|
Les sept unary standards retenus sont `SubscribeReplayInfo`, `Ping`, `GetLatestBlockhash`, `GetBlockHeight`, `GetSlot`, `IsBlockhashValid` et `GetVersion`. `Subscribe` couvre accounts, slots, transactions, transaction status, blocks, block metadata et entries, avec `commitment`, `accounts_data_slice`, `ping` et `from_slot`. `SubscribeDeshred` reste hors scope de la foundation standard.
|
||||||
|
|
||||||
Le stream bidirectionnel est borné : request/update queues, tailles inbound/outbound, half-close, close timeout et reconnect budget sont explicites. Après reconnect, KSP rejoue la dernière requête complète acceptée et avance prudemment `from_slot` selon le dernier slot observé et `SubscribeReplayInfo.first_available`. Les compteurs de gap et duplicate sont de l’observabilité ; ils ne constituent jamais une garantie exactly-once ou lossless.
|
Le stream bidirectionnel est borné : request/update queues, tailles inbound/outbound, half-close, close timeout et reconnect budget sont explicites. La queue d'updates Yellowstone reste bornée mais sa saturation locale n'est plus un fault : l'acteur attend asynchronement qu'une place soit rendue et cesse donc de poller le stream Tonic, ce qui propage la backpressure via gRPC/HTTP/2 sans drop silencieux ni queue non bornée. Le shutdown préempte cette attente. La queue de mutations de requête reste au contraire synchrone/fail-fast : un caller qui la sature reçoit toujours `grpc_backpressure_overflow`.
|
||||||
|
|
||||||
|
Après reconnect, KSP rejoue la dernière requête complète acceptée et avance prudemment `from_slot` selon le dernier slot observé et `SubscribeReplayInfo.first_available`. Les compteurs de gap et duplicate sont de l’observabilité ; ils ne constituent jamais une garantie exactly-once ou lossless.
|
||||||
|
|
||||||
Config Transport V3 peut mapper des `grpc_endpoints` vers ces settings sans inverser la dépendance. `protocol = solana_yellowstone` décrit le wire standard, tandis que `provider` reste un descripteur distinct. Les profils committés `publicnode_mainnet` et `publicnode_testnet` utilisent le standard sans façade PublicNode spécifique. Leur personal token est injecté comme metadata secrète `x-token` depuis Config ; Transport ne lit jamais les variables KSP.
|
Config Transport V3 peut mapper des `grpc_endpoints` vers ces settings sans inverser la dépendance. `protocol = solana_yellowstone` décrit le wire standard, tandis que `provider` reste un descripteur distinct. Les profils committés `publicnode_mainnet` et `publicnode_testnet` utilisent le standard sans façade PublicNode spécifique. Leur personal token est injecté comme metadata secrète `x-token` depuis Config ; Transport ne lit jamais les variables KSP.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-onchain-transport-lib/src/grpc_stream.rs
|
// file: crates/ksp-onchain-transport-lib/src/grpc_stream.rs
|
||||||
// version: 5
|
// version: 6
|
||||||
|
|
||||||
use tonic_prost::prost::Message; // rust-rules: trait-import
|
use tonic_prost::prost::Message; // rust-rules: trait-import
|
||||||
|
|
||||||
@@ -296,8 +296,9 @@ impl SolanaYellowstoneGrpcSubscribeSession {
|
|||||||
|
|
||||||
/// Receives the next decoded standard Yellowstone update.
|
/// Receives the next decoded standard Yellowstone update.
|
||||||
///
|
///
|
||||||
/// Transient remote stream loss is hidden while KSP performs a bounded reconnect. Terminal remote statuses after the reconnect budget, malformed updates
|
/// Transient remote stream loss is hidden while KSP performs a bounded reconnect. A full bounded update queue applies asynchronous backpressure to the
|
||||||
/// and overflow surface a safe KSP error. A normal close returns `Ok(None)`.
|
/// gRPC stream instead of dropping an update or terminating locally; terminal remote statuses after the reconnect budget and malformed updates still
|
||||||
|
/// surface a safe KSP error. A normal close returns `Ok(None)`.
|
||||||
pub async fn next_update(&mut self) -> ksp_core_lib::Result<std::option::Option<crate::YellowstoneSubscribeUpdate>> {
|
pub async fn next_update(&mut self) -> ksp_core_lib::Result<std::option::Option<crate::YellowstoneSubscribeUpdate>> {
|
||||||
return match self.update_rx.recv().await {
|
return match self.update_rx.recv().await {
|
||||||
std::option::Option::Some(std::result::Result::Ok(update)) => std::result::Result::Ok(std::option::Option::Some(update)),
|
std::option::Option::Some(std::result::Result::Ok(update)) => std::result::Result::Ok(std::option::Option::Some(update)),
|
||||||
@@ -635,23 +636,30 @@ async fn run_subscribe_actor(
|
|||||||
}
|
}
|
||||||
tracker.observe(&update, &mut snapshot);
|
tracker.observe(&update, &mut snapshot);
|
||||||
snapshot_tx.send_replace(snapshot);
|
snapshot_tx.send_replace(snapshot);
|
||||||
match update_tx.try_send(std::result::Result::Ok(update)) {
|
let delivery = tokio::select! {
|
||||||
std::result::Result::Ok(()) => {},
|
biased;
|
||||||
std::result::Result::Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
|
shutdown_changed = shutdown_rx.changed() => {
|
||||||
ksp_logging_lib::warn!(
|
let deadline = shutdown_deadline(&shutdown_rx, shutdown_changed);
|
||||||
target: crate::TRACING_TARGET,
|
snapshot.state = crate::YellowstoneGrpcSubscribeState::Closing;
|
||||||
endpoint_name = endpoint_name.as_str(),
|
snapshot_tx.send_replace(snapshot);
|
||||||
provider = provider.as_str(),
|
clear_request_sender(&request_state);
|
||||||
cluster = cluster.as_str(),
|
finish_client_half_close(
|
||||||
"Yellowstone subscribe update queue overflowed"
|
&endpoint_name,
|
||||||
);
|
&provider,
|
||||||
fail_actor(crate::ERROR_CODE_GRPC_BACKPRESSURE_OVERFLOW, &request_state, &mut snapshot, &snapshot_tx);
|
&cluster,
|
||||||
|
&mut incoming,
|
||||||
|
deadline,
|
||||||
|
&mut snapshot,
|
||||||
|
&snapshot_tx,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
return;
|
return;
|
||||||
},
|
}
|
||||||
std::result::Result::Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
|
result = update_tx.send(std::result::Result::Ok(update)) => result,
|
||||||
|
};
|
||||||
|
if delivery.is_err() {
|
||||||
close_actor(&request_state, &mut snapshot, &snapshot_tx);
|
close_actor(&request_state, &mut snapshot, &snapshot_tx);
|
||||||
return;
|
return;
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
std::result::Result::Ok(std::option::Option::None) => {
|
std::result::Result::Ok(std::option::Option::None) => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-onchain-transport-lib/tests/release_completeness.rs
|
// file: crates/ksp-onchain-transport-lib/tests/release_completeness.rs
|
||||||
// version: 47
|
// version: 48
|
||||||
|
|
||||||
//! Release-level completeness canaries for staged HTTP and WebSocket Transport coverage.
|
//! Release-level completeness canaries for staged HTTP and WebSocket Transport coverage.
|
||||||
|
|
||||||
@@ -1412,3 +1412,25 @@ fn release_v0_3_15_pre_002_websocket_capabilities_are_endpoint_declared_typed_an
|
|||||||
assert!(!settings_source.contains("provider().as_str() == \"helius\""));
|
assert!(!settings_source.contains("provider().as_str() == \"helius\""));
|
||||||
assert!(!settings_source.contains("provider().as_str() == \"publicnode\""));
|
assert!(!settings_source.contains("provider().as_str() == \"publicnode\""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn release_v0_3_15_pre_015_yellowstone_update_delivery_backpressures_without_local_overflow() {
|
||||||
|
let stream_source = include_str!("../src/grpc_stream.rs");
|
||||||
|
let stream_tests = include_str!("../unit_tests/grpc_stream.rs");
|
||||||
|
let actor_start = stream_source.find("async fn run_subscribe_actor(").expect("Subscribe actor must remain present");
|
||||||
|
let actor_tail = &stream_source[actor_start..];
|
||||||
|
let actor_end = actor_tail
|
||||||
|
.find(
|
||||||
|
"
|
||||||
|
enum ReconnectOutcome",
|
||||||
|
)
|
||||||
|
.expect("Subscribe actor boundary must remain present");
|
||||||
|
let actor = &actor_tail[..actor_end];
|
||||||
|
assert!(actor.contains("result = update_tx.send(std::result::Result::Ok(update)) => result"));
|
||||||
|
assert!(actor.contains("shutdown_changed = shutdown_rx.changed()"));
|
||||||
|
assert!(actor.contains("biased;"));
|
||||||
|
assert!(!actor.contains("Yellowstone subscribe update queue overflowed"));
|
||||||
|
assert!(stream_source.contains("ERROR_CODE_GRPC_BACKPRESSURE_OVERFLOW"));
|
||||||
|
assert!(!stream_source.contains("unbounded_channel"));
|
||||||
|
assert!(stream_tests.contains("yellowstone_slow_receiver_applies_bounded_backpressure_without_terminal_overflow"));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-onchain-transport-lib/unit_tests/grpc_stream.rs
|
// file: crates/ksp-onchain-transport-lib/unit_tests/grpc_stream.rs
|
||||||
// version: 6
|
// version: 7
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
enum FixtureMode {
|
enum FixtureMode {
|
||||||
@@ -501,21 +501,32 @@ async fn yellowstone_session_drop_best_effort_half_closes_request_stream() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "current_thread")]
|
#[tokio::test(flavor = "current_thread")]
|
||||||
async fn yellowstone_slow_receiver_overflow_is_terminal_and_observable() {
|
async fn yellowstone_slow_receiver_applies_bounded_backpressure_without_terminal_overflow() {
|
||||||
let server = FixtureServer::start(FixtureMode::Flood).await;
|
let server = FixtureServer::start(FixtureMode::Flood).await;
|
||||||
let defaults = crate::YellowstoneGrpcSessionSettings::default();
|
let defaults = crate::YellowstoneGrpcSessionSettings::default();
|
||||||
let settings = fixture_settings(server.endpoint_url.as_str(), 8, 2, defaults.max_inbound_message_size_bytes(), defaults.max_outbound_message_size_bytes());
|
let settings = fixture_settings(server.endpoint_url.as_str(), 8, 2, defaults.max_inbound_message_size_bytes(), defaults.max_outbound_message_size_bytes());
|
||||||
let channel = crate::YellowstoneGrpcChannel::connect(&settings).await.expect("fixture channel must connect");
|
let channel = crate::YellowstoneGrpcChannel::connect(&settings).await.expect("fixture channel must connect");
|
||||||
let mut session = channel.open_standard_subscribe(initial_request()).await.expect("fixture Subscribe stream must open");
|
let mut session = channel.open_standard_subscribe(initial_request()).await.expect("fixture Subscribe stream must open");
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
|
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
|
||||||
assert!(session.next_update().await.expect("first queued update must remain readable").is_some());
|
assert_eq!(session.snapshot().state(), crate::YellowstoneGrpcSubscribeState::Active);
|
||||||
assert!(session.next_update().await.expect("second queued update must remain readable").is_some());
|
assert_eq!(session.snapshot().terminal_error_code(), std::option::Option::None);
|
||||||
let error = session.next_update().await.expect_err("overflow must become terminal after bounded queued updates drain");
|
for expected_slot in 1_000_u64..1_003_u64 {
|
||||||
assert_eq!(error.code(), crate::ERROR_CODE_GRPC_BACKPRESSURE_OVERFLOW);
|
let update = tokio::time::timeout(std::time::Duration::from_secs(1), session.next_update())
|
||||||
let rendered = format!("{error:?} {session:?}");
|
.await
|
||||||
assert!(!rendered.contains("GRPC-STREAM-SECRET-CANARY"));
|
.expect("bounded backpressure must resume after consumer capacity returns")
|
||||||
assert!(!rendered.contains(server.endpoint_url.as_str()));
|
.expect("slow consumer must not receive a local overflow error")
|
||||||
let _ = session.close().await;
|
.expect("flood fixture must continue delivering queued updates");
|
||||||
|
match update {
|
||||||
|
crate::YellowstoneSubscribeUpdate::Slot(value) => assert_eq!(value.slot(), expected_slot),
|
||||||
|
_ => panic!("flood fixture must deliver slot updates"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
|
||||||
|
assert_eq!(session.snapshot().terminal_error_code(), std::option::Option::None);
|
||||||
|
tokio::time::timeout(std::time::Duration::from_secs(1), session.close())
|
||||||
|
.await
|
||||||
|
.expect("shutdown must preempt a blocked bounded update delivery")
|
||||||
|
.expect("backpressured session must close cleanly");
|
||||||
server.stop().await;
|
server.stop().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: crates/ksp-worker-raw-transaction-ingest-lib/README.md -->
|
<!-- file: crates/ksp-worker-raw-transaction-ingest-lib/README.md -->
|
||||||
<!-- version: 15 -->
|
<!-- version: 16 -->
|
||||||
|
|
||||||
# ksp-worker-raw-transaction-ingest-lib
|
# ksp-worker-raw-transaction-ingest-lib
|
||||||
|
|
||||||
@@ -86,7 +86,9 @@ Le runtime-resource aggregate public accepte une collection validée de 1 à 32
|
|||||||
|
|
||||||
Le supervisor possède toutes les tâches source. Une perte de source n'est plus assimilée automatiquement à un fault : lorsqu'elle porte une plage de continuité bornée, le Worker l'inscrit dans son ledger run-local puis n'autorise la continuation des siblings que si la coverage passée de cette perte est réconciliée et si les sources encore actives couvrent explicitement tout le `TargetCoverage` futur. Une perte sans plage sûre, une coverage insuffisante ou un gap encore ouvert reste terminal. Le Worker ne respawn jamais lui-même une source Transport.
|
Le supervisor possède toutes les tâches source. Une perte de source n'est plus assimilée automatiquement à un fault : lorsqu'elle porte une plage de continuité bornée, le Worker l'inscrit dans son ledger run-local puis n'autorise la continuation des siblings que si la coverage passée de cette perte est réconciliée et si les sources encore actives couvrent explicitement tout le `TargetCoverage` futur. Une perte sans plage sûre, une coverage insuffisante ou un gap encore ouvert reste terminal. Le Worker ne respawn jamais lui-même une source Transport.
|
||||||
|
|
||||||
Pour un abonnement Yellowstone `Block` pur, la notification gRPC est un trigger de slot et ne bloque plus la boucle de réception pendant l'hydration HTTP. Le slot entre dans un coordinateur privé borné ; les `getBlock` puis l'admission complète du bloc s'exécutent dans au plus le quota `in-flight` attribué à cette source, tandis que le nombre de slots en attente reste borné par son quota `pending`. La boucle Yellowstone continue donc à consommer `next_update()` tant qu'une capacité pending existe. Aucun `unbounded_channel`, aucun task détaché et aucun silent drop n'est introduit ; si le débit aval reste durablement inférieur au débit de chaîne jusqu'à saturation de toutes les bornes, la politique reste fail-closed.
|
Pour un abonnement Yellowstone `Block` pur, la notification gRPC est un trigger de slot et ne bloque plus la boucle de réception pendant l'hydration HTTP. Le slot entre dans un coordinateur privé borné ; les `getBlock` puis l'admission complète du bloc s'exécutent dans au plus le quota `in-flight` attribué à cette source, tandis que le nombre de slots en attente reste borné par son quota `pending`. La boucle Yellowstone continue donc à consommer `next_update()` tant qu'une capacité pending existe.
|
||||||
|
|
||||||
|
Lorsque ce quota pending est plein, le Worker suspend temporairement `next_update()` au lieu d'allouer davantage. Transport conserve lui aussi une queue d'updates bornée ; lorsqu'elle est pleine, son acteur Yellowstone attend asynchronement de la capacité et arrête de poller le stream gRPC jusqu'à reprise du consumer. La pression remonte donc au flux HTTP/2 sans `unbounded_channel`, task détaché, silent drop ni `grpc_backpressure_overflow` local pour la seule saturation de la queue d'updates. Une rupture distante réelle reste soumise au reconnect/replay borné de Transport et ne constitue toujours pas une garantie lossless ou exactly-once.
|
||||||
|
|
||||||
Un inventaire privé `source_key -> latest processing/source state`, borné à 32 entrées, agrège la projection run-local. La frontier agrégée reste conservative : elle n'expose un `processing_frontier_slot` que lorsque toutes les sources en possèdent un, choisit le minimum des frontiers connus et le plus ancien pending. Les sources reference-bearing partagent en plus un registre global d'hydration borné : une même clé `(network, signature, commitment)` ne déclenche qu'un leader HTTP, puis chaque signal source conserve sa propre provenance lors de la finalisation.
|
Un inventaire privé `source_key -> latest processing/source state`, borné à 32 entrées, agrège la projection run-local. La frontier agrégée reste conservative : elle n'expose un `processing_frontier_slot` que lorsque toutes les sources en possèdent un, choisit le minimum des frontiers connus et le plus ancien pending. Les sources reference-bearing partagent en plus un registre global d'hydration borné : une même clé `(network, signature, commitment)` ne déclenche qu'un leader HTTP, puis chaque signal source conserve sa propre provenance lors de la finalisation.
|
||||||
|
|
||||||
|
|||||||
167
deltas/0.3.15/pre.015.md
Normal file
167
deltas/0.3.15/pre.015.md
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
<!-- file: deltas/0.3.15/pre.015.md -->
|
||||||
|
<!-- version: 1 -->
|
||||||
|
|
||||||
|
# Delta v0.3.15-pre.015 — Yellowstone sustained gRPC backpressure
|
||||||
|
|
||||||
|
## Base
|
||||||
|
|
||||||
|
```text
|
||||||
|
ksp-general-0.3.15-pre.014-fix.003.zip
|
||||||
|
SHA-256: 44700585e23456a2d2bd22504d44bc6d726d04f195b113f22e5fce9aeee2914c
|
||||||
|
```
|
||||||
|
|
||||||
|
Le gate opérateur de `pre.014-fix.003` ferme `pre.014` : audits Rust/Markdown, `cargo check --workspace`, Clippy strict, suites Store/Worker/Desk ciblées et `cargo test --workspace --all-targets --all-features` passent. Le correctif de compatibilité `logMessages` tronqués est donc conservé tel quel.
|
||||||
|
|
||||||
|
Le live prolongé précédent a toutefois reproduit une saturation Yellowstone distincte : `Yellowstone subscribe update queue overflowed` survient à `22:09:54`, alors que le Stop opérateur n'est demandé qu'à `22:12:03`. `content_conflict_total=0` au terminal. Le défaut appartient au couloir acquisition et ouvre cette prerelease conformément à `VER-LIFECYCLE-010`.
|
||||||
|
|
||||||
|
## Cause durable
|
||||||
|
|
||||||
|
`pre.013` a supprimé le couplage séquentiel entre réception Block et `getBlock`, mais toutes les bornes restent volontairement finies :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Worker Yellowstone Block pending : <= 256 par défaut
|
||||||
|
Worker hydration in-flight : <= 8 par défaut
|
||||||
|
Transport gRPC update queue : <= 256 par défaut
|
||||||
|
```
|
||||||
|
|
||||||
|
Lorsque le débit aval moyen reste inférieur au débit de chaîne assez longtemps :
|
||||||
|
|
||||||
|
```text
|
||||||
|
pending Worker plein
|
||||||
|
-> Worker suspend session.next_update()
|
||||||
|
-> queue Transport update se remplit
|
||||||
|
-> ancien update_tx.try_send(...) retourne Full
|
||||||
|
-> grpc_backpressure_overflow local
|
||||||
|
-> source_failed / Faulted
|
||||||
|
```
|
||||||
|
|
||||||
|
Le problème n'est donc pas une capacité insuffisante à augmenter, mais la traduction incorrecte d'une pression locale bornée en faute terminale.
|
||||||
|
|
||||||
|
## Correction
|
||||||
|
|
||||||
|
L'acteur `Subscribe` Yellowstone conserve la même queue d'updates bornée mais remplace uniquement la livraison réussie non bloquante :
|
||||||
|
|
||||||
|
```text
|
||||||
|
update_tx.try_send(Ok(update))
|
||||||
|
```
|
||||||
|
|
||||||
|
par une attente asynchrone bornée par la capacité existante :
|
||||||
|
|
||||||
|
```text
|
||||||
|
update_tx.send(Ok(update)).await
|
||||||
|
```
|
||||||
|
|
||||||
|
Cette attente est placée dans un `tokio::select!` `biased` dont le signal de shutdown est prioritaire. Ainsi :
|
||||||
|
|
||||||
|
```text
|
||||||
|
queue update pleine
|
||||||
|
-> acteur suspend la livraison
|
||||||
|
-> stream Tonic n'est plus pollé
|
||||||
|
-> backpressure propagée au flux gRPC / HTTP/2
|
||||||
|
-> consumer libère une place
|
||||||
|
-> livraison reprend
|
||||||
|
```
|
||||||
|
|
||||||
|
Un Stop reçu pendant cette attente déclenche immédiatement le chemin Closing/half-close existant. Aucun update n'est dropé en fonctionnement normal, aucune queue non bornée n'est ajoutée et aucune capacité n'est augmentée.
|
||||||
|
|
||||||
|
`grpc_backpressure_overflow` reste un code public valide. Les queues de mutations de requête Yellowstone restent synchrones/fail-fast avec `try_send`, et les bornes de taille outbound/ping continuent d'utiliser ce code. Seule la saturation de la queue d'updates reçues cesse d'être un terminal local.
|
||||||
|
|
||||||
|
## Frontières
|
||||||
|
|
||||||
|
Aucun changement de :
|
||||||
|
|
||||||
|
```text
|
||||||
|
YellowstoneGrpcSessionSettings capacities
|
||||||
|
Worker pending / in-flight quotas
|
||||||
|
Worker public API
|
||||||
|
Store / schema / RAW canonical identity
|
||||||
|
compatibilité logMessages de pre.014-fix.001
|
||||||
|
Config
|
||||||
|
Desk
|
||||||
|
WebSocket backpressure policy
|
||||||
|
Backfill
|
||||||
|
public Transport API
|
||||||
|
```
|
||||||
|
|
||||||
|
La correction ne revendique ni `lossless` ni `exactly-once`. Une vraie rupture distante reste soumise au reconnect/replay borné existant et à ses preuves de continuité conservatrices.
|
||||||
|
|
||||||
|
## Canaris
|
||||||
|
|
||||||
|
```text
|
||||||
|
yellowstone_slow_receiver_applies_bounded_backpressure_without_terminal_overflow
|
||||||
|
release_v0_3_15_pre_015_yellowstone_update_delivery_backpressures_without_local_overflow
|
||||||
|
```
|
||||||
|
|
||||||
|
Le premier fixture impose une queue d'updates de capacité `2`, laisse le producer la saturer, puis exige que les updates suivants reprennent après drainage sans terminal `grpc_backpressure_overflow`. Il exige également qu'un Stop préempte une livraison bloquée.
|
||||||
|
|
||||||
|
Le canari release vérifie que l'acteur de production utilise l'envoi asynchrone borné, conserve la branche shutdown prioritaire, ne contient plus le WARN d'overflow local des updates, n'introduit aucune queue non bornée et conserve le code public d'overflow pour les autres responsabilités.
|
||||||
|
|
||||||
|
## Plan
|
||||||
|
|
||||||
|
L'insertion de cette correction acquisition décale les responsabilités de clôture :
|
||||||
|
|
||||||
|
```text
|
||||||
|
pre.015 Yellowstone sustained gRPC backpressure
|
||||||
|
pre.016 gate technique/live final
|
||||||
|
pre.017 réconciliation documentaire finale
|
||||||
|
pre.018 préparation publication
|
||||||
|
rel.001 stable mécanique
|
||||||
|
```
|
||||||
|
|
||||||
|
## Version
|
||||||
|
|
||||||
|
```text
|
||||||
|
workspace.package.version : 0.3.15-pre.15
|
||||||
|
root Cargo header counter : 632
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation attendue
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt --all
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
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.15
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
cargo test -p ksp-onchain-transport-lib --all-targets --all-features
|
||||||
|
cargo test -p ksp-worker-raw-transaction-ingest-lib --all-targets --all-features
|
||||||
|
cargo test -p ksp-app-raw-transaction-ingest-desk --all-targets --all-features
|
||||||
|
cargo test --workspace --all-targets --all-features
|
||||||
|
(cd crates/ksp-app-raw-transaction-ingest-desk && cargo tauri dev)
|
||||||
|
```
|
||||||
|
|
||||||
|
Live obligatoire :
|
||||||
|
|
||||||
|
```text
|
||||||
|
1. Yellowstone Mainnet seul pendant au moins 10 minutes.
|
||||||
|
2. Vérifier absence de "Yellowstone subscribe update queue overflowed" et de grpc_backpressure_overflow.
|
||||||
|
3. Vérifier route Running/Healthy puis Stop ciblé -> Stopped.
|
||||||
|
4. Yellowstone + HTTP Block Polling en parallèle pendant au moins 10 minutes.
|
||||||
|
5. Vérifier les deux routes productives, sans overflow local Yellowstone.
|
||||||
|
6. Stop ciblé HTTP puis Yellowstone ; chaque route doit finir proprement.
|
||||||
|
7. Une vraie rupture distante éventuelle doit rester classée par le reconnect/Transport existant, pas par une saturation locale.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inventaire exact du delta
|
||||||
|
|
||||||
|
Ajout :
|
||||||
|
|
||||||
|
```text
|
||||||
|
deltas/0.3.15/pre.015.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Modifications :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Cargo.toml
|
||||||
|
crates/ksp-onchain-transport-lib/README.md
|
||||||
|
crates/ksp-onchain-transport-lib/src/grpc_stream.rs
|
||||||
|
crates/ksp-onchain-transport-lib/tests/release_completeness.rs
|
||||||
|
crates/ksp-onchain-transport-lib/unit_tests/grpc_stream.rs
|
||||||
|
crates/ksp-worker-raw-transaction-ingest-lib/README.md
|
||||||
|
docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md
|
||||||
|
docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Suppressions : aucune.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md -->
|
<!-- file: docs/plans/036-V0_3_15_RAW_TRANSACTION_INGEST_DESK_PLAN.md -->
|
||||||
<!-- version: 23 -->
|
<!-- version: 24 -->
|
||||||
|
|
||||||
# Plan v0.3.15 — Raw Transaction Ingest Desk
|
# Plan v0.3.15 — Raw Transaction Ingest Desk
|
||||||
|
|
||||||
@@ -342,7 +342,7 @@ La documentation Helius actuelle indique que `blockSubscribe` n'est pas support
|
|||||||
| Store open / network / partage `Arc<Store>` | aucun gap | Store + app | `ksp-store-lib` suffit |
|
| Store open / network / partage `Arc<Store>` | aucun gap | Store + app | `ksp-store-lib` suffit |
|
||||||
| Lifecycle / stop / snapshots Worker | aucun gap | Worker + app | handles publics existants |
|
| Lifecycle / stop / snapshots Worker | aucun gap | Worker + app | handles publics existants |
|
||||||
| Backfill / historical repair | hors scope | Job Backfill | aucune orchestration automatique |
|
| Backfill / historical repair | hors scope | Job Backfill | aucune orchestration automatique |
|
||||||
| Taille release | pas de split de release | plan | forecast étendu jusqu’à `pre.017` |
|
| Taille release | pas de split de release | plan | forecast étendu jusqu’à `pre.018` |
|
||||||
|
|
||||||
La release n'est pas rescindée. Le gap WS est petit, clairement propriétaire et testable. En revanche, le forecast doit être étendu pour ne pas fusionner Transport, Config, Worker et scaffold UI dans une même tranche.
|
La release n'est pas rescindée. Le gap WS est petit, clairement propriétaire et testable. En revanche, le forecast doit être étendu pour ne pas fusionner Transport, Config, Worker et scaffold UI dans une même tranche.
|
||||||
|
|
||||||
@@ -668,7 +668,7 @@ Le build Tauri final reste un gate tardif et ne remplace pas les tests runtime.
|
|||||||
|
|
||||||
## 20. Forecast recalibré — prereleases souples
|
## 20. Forecast recalibré — prereleases souples
|
||||||
|
|
||||||
Le forecast initial `pre.002 -> pre.013` est désormais étendu jusqu'à `pre.017`. La cause n'est pas une inflation UI : c'est la découverte d'un contrat WS capability manquant qui doit être fermé proprement dans Transport, Config puis Worker avant consommation par la Desk.
|
Le forecast initial `pre.002 -> pre.013` est désormais étendu jusqu'à `pre.018`. La cause n'est pas une inflation UI : c'est la découverte d'un contrat WS capability manquant qui doit être fermé proprement dans Transport, Config puis Worker avant consommation par la Desk.
|
||||||
|
|
||||||
Le forecast reste volontairement souple. Chaque prerelease possède une sous-section directement éditable ; lorsqu'un correctif est nécessaire, il est ajouté sous la prerelease concernée avec un titre `#### pre.NNN-fix.MMM`. Les responsabilités de clôture `gate technique/live -> réconciliation documentaire -> préparation de publication` restent séparées.
|
Le forecast reste volontairement souple. Chaque prerelease possède une sous-section directement éditable ; lorsqu'un correctif est nécessaire, il est ajouté sous la prerelease concernée avec un titre `#### pre.NNN-fix.MMM`. Les responsabilités de clôture `gate technique/live -> réconciliation documentaire -> préparation de publication` restent séparées.
|
||||||
|
|
||||||
@@ -849,15 +849,15 @@ Budget cible : une tranche hardening.
|
|||||||
|
|
||||||
### `pre.013` — Yellowstone Block hydration backpressure
|
### `pre.013` — Yellowstone Block hydration backpressure
|
||||||
|
|
||||||
**État : implémenté, gate opérateur requis.** Le live de `pre.012-fix.004` a reproduit deux `grpc_backpressure_overflow` Mainnet environ 79 s puis 81 s après démarrage Yellowstone. Les WARN Transport précèdent les Stop opérateur de plusieurs secondes : la cause est donc le couplage historique `Block update -> getBlock HTTP -> admission de toutes les transactions -> lecture de l'update suivante`, et non la sémantique Stop. Conformément à `VER-LIFECYCLE-010`, ce défaut du couloir acquisition antérieur ouvre une nouvelle prerelease dédiée au lieu d'un `pre.012-fix.005`.
|
**État : implémenté ; amélioration confirmée, saturation prolongée rouverte en `pre.015`.** Le live de `pre.012-fix.004` a reproduit deux `grpc_backpressure_overflow` Mainnet environ 79 s puis 81 s après démarrage Yellowstone. Les WARN Transport précèdent les Stop opérateur de plusieurs secondes : la cause initiale est donc le couplage historique `Block update -> getBlock HTTP -> admission de toutes les transactions -> lecture de l'update suivante`, et non la sémantique Stop. Conformément à `VER-LIFECYCLE-010`, ce défaut du couloir acquisition antérieur a ouvert une prerelease dédiée au lieu d'un `pre.012-fix.005`.
|
||||||
|
|
||||||
Le mode Yellowstone Block reçoit désormais une partition déterministe des budgets d'hydration déjà possédés par le Worker. Les slots gRPC sont placés dans un coordinateur privé borné ; plusieurs `getBlock` peuvent progresser concurremment jusqu'au quota `in-flight`, et chaque tâche porte aussi l'admission complète de son bloc avant settlement du slot. La boucle source continue de consommer `session.next_update()` tant qu'une capacité pending existe. Aucun changement de capacité Transport, aucune queue non bornée et aucun drop silencieux ne masquent une surcharge durable.
|
Le mode Yellowstone Block reçoit désormais une partition déterministe des budgets d'hydration déjà possédés par le Worker. Les slots gRPC sont placés dans un coordinateur privé borné ; plusieurs `getBlock` peuvent progresser concurremment jusqu'au quota `in-flight`, et chaque tâche porte aussi l'admission complète de son bloc avant settlement du slot. La boucle source continue de consommer `session.next_update()` tant qu'une capacité pending existe. Cette correction supprime le couplage strictement séquentiel, mais un live plus long a ensuite prouvé qu'une saturation durable des bornes Worker puis Transport restait possible ; cette seconde responsabilité est isolée en `pre.015`.
|
||||||
|
|
||||||
Budget cible : une tranche corrective acquisition/throughput, suivie d'un live Mainnet prolongé.
|
Budget cible : une tranche corrective acquisition/throughput, suivie du complément Transport de `pre.015`.
|
||||||
|
|
||||||
### `pre.014` — convergence inter-provider + completeness/security cross-layer
|
### `pre.014` — convergence inter-provider + completeness/security cross-layer
|
||||||
|
|
||||||
**État : implémenté, gate/live opérateur requis.** Le gate de `pre.013` compile et teste proprement le workspace et le live ne reproduit plus `grpc_backpressure_overflow`. Un unique `content_conflict` Mainnet réapparaît toutefois entre HTTP Block Polling (`solana_mainnet_public`) et Yellowstone hydraté par HTTP (`publicnode_solana_mainnet_rpc`), tous deux en `confirmed`. Le diagnostic `pre.012-fix.004` prouve que `slot`, `block_time`, transaction wire, version et `transactionIndex` sont identiques ; seule la valeur `meta.logMessages` diverge.
|
**État : implémenté et gate opérateur PASS après `pre.014-fix.003`.** Le premier live après `pre.013` n'a pas reproduit immédiatement `grpc_backpressure_overflow`, mais un unique `content_conflict` Mainnet est apparu entre HTTP Block Polling (`solana_mainnet_public`) et Yellowstone hydraté par HTTP (`publicnode_solana_mainnet_rpc`), tous deux en `confirmed`. Le diagnostic `pre.012-fix.004` a prouvé que `slot`, `block_time`, transaction wire, version et `transactionIndex` étaient identiques ; seule la valeur `meta.logMessages` divergeait.
|
||||||
|
|
||||||
La tranche ne normalise ni n'ignore `logMessages`. Elle ajoute un diagnostic de convergence sûr permettant de distinguer un fork `confirmed` d'une divergence de représentation/provider : chaque `getBlock` réussi des deux routes journalise le slot et un fingerprint SHA-256 irréversible de l'identité de bloc (`blockhash`, `previousBlockhash`, `parentSlot`, `blockHeight`) sans exposer les blockhash. En cas de conflit RAW, Store journalise désormais les slots exacts et une forme bornée de la divergence `logMessages` : état absent/null/array, cardinalités, premier index divergent, type statique de la première ligne divergente, longueur, relation de préfixe et présence d'un marqueur de troncature. Aucun texte de log distant n'est copié.
|
La tranche ne normalise ni n'ignore `logMessages`. Elle ajoute un diagnostic de convergence sûr permettant de distinguer un fork `confirmed` d'une divergence de représentation/provider : chaque `getBlock` réussi des deux routes journalise le slot et un fingerprint SHA-256 irréversible de l'identité de bloc (`blockhash`, `previousBlockhash`, `parentSlot`, `blockHeight`) sans exposer les blockhash. En cas de conflit RAW, Store journalise désormais les slots exacts et une forme bornée de la divergence `logMessages` : état absent/null/array, cardinalités, premier index divergent, type statique de la première ligne divergente, longueur, relation de préfixe et présence d'un marqueur de troncature. Aucun texte de log distant n'est copié.
|
||||||
|
|
||||||
@@ -867,19 +867,29 @@ Les canaris de clôture continuent de vérifier dependency firewalls, API publiq
|
|||||||
|
|
||||||
Budget cible : une tranche de convergence/completeness cross-layer, sans modification de la politique fail-closed.
|
Budget cible : une tranche de convergence/completeness cross-layer, sans modification de la politique fail-closed.
|
||||||
|
|
||||||
### `pre.015` — gate technique/live final
|
### `pre.015` — Yellowstone sustained gRPC backpressure
|
||||||
|
|
||||||
|
**État : en cours.** Le live prolongé de `pre.014-fix.001` a reproduit `Yellowstone subscribe update queue overflowed` à `22:09:54`, alors que le Stop opérateur n'est demandé qu'à `22:12:03`. `pre.013` a donc amélioré le débit sans fermer le défaut durable : lorsque les `256` slots pending Worker sont pleins, le Worker suspend `next_update()` ; la queue Transport bornée de `256` updates finit ensuite par se remplir et son ancien `try_send` transforme cette pression locale en faute terminale `grpc_backpressure_overflow`.
|
||||||
|
|
||||||
|
La correction conserve toutes les bornes et n'augmente aucune queue. L'acteur Yellowstone Transport remplace uniquement l'envoi non bloquant de l'update décodée par un `send().await` sélectionné avec le signal de shutdown prioritaire. Une queue pleine suspend ainsi le polling du stream Tonic et propage la backpressure au flux gRPC/HTTP/2 ; lorsque le consumer libère une place, la livraison reprend. Aucun update n'est dropé et aucune allocation non bornée n'est introduite. `grpc_backpressure_overflow` reste un code public valide pour les queues de mutations de requête synchrones/fail-fast et les bornes de message ; seule la saturation de la queue d'updates reçues cesse d'être un terminal local.
|
||||||
|
|
||||||
|
Le gate live doit dépasser la durée de reproduction précédente : Yellowstone Mainnet seul puis Yellowstone + HTTP Block Polling doivent chacun rester actifs au moins 10 minutes, sans WARN `Yellowstone subscribe update queue overflowed`, sans `grpc_backpressure_overflow` et avec Stop ciblé terminant `Stopped`. Une vraie rupture distante reste traitée par le reconnect/replay borné existant.
|
||||||
|
|
||||||
|
Budget cible : une tranche corrective Transport/acquisition, sans croissance d'API publique.
|
||||||
|
|
||||||
|
### `pre.016` — gate technique/live final
|
||||||
|
|
||||||
Exécuter le workspace complet, Clippy strict, suites ciblées, graphes/duplicates, smokes réellement accessibles et build frontend/Tauri final. Aucun nouveau scope métier.
|
Exécuter le workspace complet, Clippy strict, suites ciblées, graphes/duplicates, smokes réellement accessibles et build frontend/Tauri final. Aucun nouveau scope métier.
|
||||||
|
|
||||||
Budget cible : une tranche technique.
|
Budget cible : une tranche technique.
|
||||||
|
|
||||||
### `pre.016` — réconciliation documentaire finale
|
### `pre.017` — réconciliation documentaire finale
|
||||||
|
|
||||||
Réconcilier plan, validation, README/USAGE et documents durables réellement affectés. Ne pas modifier CHANGELOG, ROADMAP ni le prompt suivant.
|
Réconcilier plan, validation, README/USAGE et documents durables réellement affectés. Ne pas modifier CHANGELOG, ROADMAP ni le prompt suivant.
|
||||||
|
|
||||||
Budget cible : une tranche documentaire.
|
Budget cible : une tranche documentaire.
|
||||||
|
|
||||||
### `pre.017` — préparation de publication
|
### `pre.018` — préparation de publication
|
||||||
|
|
||||||
Préparer le prompt suivant, CHANGELOG, ROADMAP et la mécanique minimale de version/delta. Aucun rattrapage fonctionnel ou documentaire durable.
|
Préparer le prompt suivant, CHANGELOG, ROADMAP et la mécanique minimale de version/delta. Aucun rattrapage fonctionnel ou documentaire durable.
|
||||||
|
|
||||||
@@ -922,7 +932,7 @@ aucun gap Store / HTTP / lifecycle Worker essentiel
|
|||||||
capability WS fine = gap lower-layer réel à fermer avant UI
|
capability WS fine = gap lower-layer réel à fermer avant UI
|
||||||
Helius standard WS != Helius transactionSubscribe
|
Helius standard WS != Helius transactionSubscribe
|
||||||
aucune route rendue disponible à partir d'un exemple Config ou du provider name
|
aucune route rendue disponible à partir d'un exemple Config ou du provider name
|
||||||
forecast recalibré jusqu'à pre.017
|
forecast recalibré jusqu'à pre.018
|
||||||
```
|
```
|
||||||
|
|
||||||
`pre.002` doit donc commencer par le contrat Transport de capabilities WS, et non par le scaffold Tauri.
|
`pre.002` doit donc commencer par le contrat Transport de capabilities WS, et non par le scaffold Tauri.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md -->
|
<!-- file: docs/validation/032-V0_3_15_RAW_TRANSACTION_INGEST_DESK.md -->
|
||||||
<!-- version: 31 -->
|
<!-- version: 32 -->
|
||||||
|
|
||||||
# Validation v0.3.15 — Raw Transaction Ingest Desk
|
# Validation v0.3.15 — Raw Transaction Ingest Desk
|
||||||
|
|
||||||
@@ -276,7 +276,7 @@ Aucune de ces informations Web n'est convertie directement en route sélectionna
|
|||||||
| Store open / network / partage `Arc<Store>` | aucun gap | Store + app | `ksp-store-lib` suffit |
|
| Store open / network / partage `Arc<Store>` | aucun gap | Store + app | `ksp-store-lib` suffit |
|
||||||
| Lifecycle / stop / snapshots Worker | aucun gap | Worker + app | handles publics existants |
|
| Lifecycle / stop / snapshots Worker | aucun gap | Worker + app | handles publics existants |
|
||||||
| Backfill / historical repair | hors scope | Job Backfill | aucune orchestration automatique |
|
| Backfill / historical repair | hors scope | Job Backfill | aucune orchestration automatique |
|
||||||
| Taille release | pas de split de release | plan | forecast étendu jusqu’à `pre.016` |
|
| Taille release | pas de split de release | plan | forecast étendu jusqu’à `pre.018` |
|
||||||
|
|
||||||
Résultat : `0.3.15` reste une release cohérente. Aucun split vers `0.3.16` n'est nécessaire avant implémentation, mais le forecast interne est élargi.
|
Résultat : `0.3.15` reste une release cohérente. Aucun split vers `0.3.16` n'est nécessaire avant implémentation, mais le forecast interne est élargi.
|
||||||
|
|
||||||
@@ -465,19 +465,23 @@ Races, shutdown app, sécurité IPC, hardening hostile/stale inventory.
|
|||||||
|
|
||||||
Découplage borné réception gRPC / `getBlock` / admission pour la route Yellowstone Block, conformément à `VER-LIFECYCLE-010`.
|
Découplage borné réception gRPC / `getBlock` / admission pour la route Yellowstone Block, conformément à `VER-LIFECYCLE-010`.
|
||||||
|
|
||||||
### `pre.014` — completeness
|
### `pre.014` — convergence inter-provider + completeness
|
||||||
|
|
||||||
Cross-layer completeness + canaris système sans expansion de frontières.
|
Diagnostic puis compatibilité étroite des `logMessages` explicitement tronqués, avec canaris cross-layer et sans expansion de frontières publiques.
|
||||||
|
|
||||||
### `pre.015` — gate technique/live
|
### `pre.015` — Yellowstone sustained gRPC backpressure
|
||||||
|
|
||||||
|
La saturation locale de la queue d'updates Yellowstone doit appliquer une backpressure asynchrone bornée au stream gRPC au lieu de produire un terminal `grpc_backpressure_overflow`.
|
||||||
|
|
||||||
|
### `pre.016` — gate technique/live
|
||||||
|
|
||||||
Gate technique/live accessible + frontend/Tauri + build final de validation technique.
|
Gate technique/live accessible + frontend/Tauri + build final de validation technique.
|
||||||
|
|
||||||
### `pre.016` — réconciliation documentaire
|
### `pre.017` — réconciliation documentaire
|
||||||
|
|
||||||
Plan/validation/README/USAGE uniquement selon les surfaces réellement affectées.
|
Plan/validation/README/USAGE uniquement selon les surfaces réellement affectées.
|
||||||
|
|
||||||
### `pre.017` — préparation publication
|
### `pre.018` — préparation publication
|
||||||
|
|
||||||
Prompt suivant + CHANGELOG + ROADMAP + mécanique minimale de publication.
|
Prompt suivant + CHANGELOG + ROADMAP + mécanique minimale de publication.
|
||||||
|
|
||||||
@@ -1005,3 +1009,46 @@ Le gate opérateur de `pre.014-fix.002` confirme la correction de typage : les a
|
|||||||
|
|
||||||
Non-claim : ce fix ne traite pas le `grpc_backpressure_overflow` Yellowstone prolongé déjà identifié ; ce défaut ouvre toujours la prochaine prerelease acquisition dédiée après fermeture du gate `pre.014`.
|
Non-claim : ce fix ne traite pas le `grpc_backpressure_overflow` Yellowstone prolongé déjà identifié ; ce défaut ouvre toujours la prochaine prerelease acquisition dédiée après fermeture du gate `pre.014`.
|
||||||
|
|
||||||
|
### `pre.015` — backpressure gRPC Yellowstone soutenue sans overflow local
|
||||||
|
|
||||||
|
Le gate opérateur de `pre.014-fix.003` ferme la prerelease `pre.014` : audits Rust/Markdown propres, `cargo check --workspace`, Clippy strict, `ksp-store-postgres-lib` (`75/75` unitaires et `16/16` hardening completeness), Worker (`161/161` unitaires et suites externes), Raw Transaction Ingest Desk et `cargo test --workspace --all-targets --all-features` passent. Le canari de non-réécriture canonique resserré par `fix.003` est donc validé.
|
||||||
|
|
||||||
|
Le défaut acquisition restant provient du live prolongé précédent et ne relève pas du Store. Le WARN `Yellowstone subscribe update queue overflowed` apparaît à `22:09:54`, tandis que le Stop Yellowstone n'est demandé qu'à `22:12:03`. `content_conflict_total=0` au terminal. La chaîne de saturation est désormais établie :
|
||||||
|
|
||||||
|
```text
|
||||||
|
Worker Yellowstone Block pending plein (borne 256)
|
||||||
|
-> Worker suspend session.next_update()
|
||||||
|
-> queue Transport update bornée (borne 256) se remplit
|
||||||
|
-> ancien update_tx.try_send(...) retourne Full
|
||||||
|
-> grpc_backpressure_overflow local
|
||||||
|
-> source_failed / Faulted
|
||||||
|
```
|
||||||
|
|
||||||
|
`pre.015` ne change ni les valeurs `256/8`, ni le Store, ni le payload RAW, ni le reconnect budget. L'acteur Subscribe Yellowstone conserve sa queue d'updates bornée mais attend désormais asynchronement sa capacité avec `update_tx.send(...).await`. Le même `tokio::select!` donne priorité au shutdown afin qu'un Stop reste préemptif même lorsque le consumer ne draine plus la queue. Pendant l'attente, le stream Tonic n'est plus pollé ; la pression est donc propagée au transport gRPC/HTTP/2 au lieu d'être transformée en erreur locale ou en drop.
|
||||||
|
|
||||||
|
Canaris requis :
|
||||||
|
|
||||||
|
```text
|
||||||
|
yellowstone_slow_receiver_applies_bounded_backpressure_without_terminal_overflow
|
||||||
|
release_v0_3_15_pre_015_yellowstone_update_delivery_backpressures_without_local_overflow
|
||||||
|
```
|
||||||
|
|
||||||
|
Non-claims : aucune garantie lossless/exactly-once, aucune queue non bornée, aucun agrandissement de capacité, aucune modification WebSocket, aucun changement de la queue de mutations Yellowstone qui reste synchrone/fail-fast, aucune suppression du code public `grpc_backpressure_overflow`, aucune nouvelle API publique.
|
||||||
|
|
||||||
|
Gate opérateur attendu :
|
||||||
|
|
||||||
|
```text
|
||||||
|
cargo fmt --all
|
||||||
|
cargo fmt --all -- --check
|
||||||
|
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.15
|
||||||
|
cargo check --workspace
|
||||||
|
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||||
|
cargo test -p ksp-onchain-transport-lib --all-targets --all-features
|
||||||
|
cargo test -p ksp-worker-raw-transaction-ingest-lib --all-targets --all-features
|
||||||
|
cargo test -p ksp-app-raw-transaction-ingest-desk --all-targets --all-features
|
||||||
|
cargo test --workspace --all-targets --all-features
|
||||||
|
```
|
||||||
|
|
||||||
|
Gate live Mainnet : Yellowstone seul pendant au moins 10 minutes, puis Yellowstone + HTTP Block Polling pendant au moins 10 minutes. Dans les deux cas, aucune occurrence de `Yellowstone subscribe update queue overflowed` ou `grpc_backpressure_overflow` n'est admise ; la route Yellowstone doit rester active/saine jusqu'au Stop ciblé, puis terminer `Stopped`. Une interruption réseau distante réellement observée doit rester classée par les codes Transport/reconnect existants et ne doit pas être confondue avec ce gate de saturation locale.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user