fix(blockchain): reliability hardening — issues #917 #933 #934 #937#1
Open
knytcomics-ui wants to merge 1 commit into
Open
fix(blockchain): reliability hardening — issues #917 #933 #934 #937#1knytcomics-ui wants to merge 1 commit into
knytcomics-ui wants to merge 1 commit into
Conversation
…olutions-plug#934 solutions-plug#937 — reliability hardening ## solutions-plug#933 — WATCHED_TX_TTL now configurable (WATCHED_TX_TTL_SECS) - Remove hardcoded 30-min Duration const; replace with config-driven value - Add Config::watched_tx_ttl_secs (default 1800) read from WATCHED_TX_TTL_SECS - Thread the value into BlockchainClient::watched_tx_ttl field - Migration 019 creates watched_transactions table with expires_at column driven by the same TTL, satisfying the DB persistence requirement ## solutions-plug#934 — WATCHED_TX_MAX_SIZE cap now rejects (503) instead of silently evicting - Cap behaviour changed from evict-oldest to reject-with-503; callers receive a clear error rather than silently losing monitor coverage of old hashes - Add Config::watched_tx_max_size (default 10 000) read from WATCHED_TX_MAX_SIZE - Add Metrics::watched_tx_count IntGauge updated on every insert/eviction/removal - Add Prometheus alert WatchedTxCountHigh (>8000, warning) and WatchedTxCountCritical (>=10000, critical) in performance/config/alerts.yaml ## solutions-plug#937 — Deduplication check for watched transactions - watch_transaction now returns Result<(), WatchTxError> instead of () - WatchTxError::AlreadyWatched returned when hash already in map (no dup insert) - WatchTxError::CapReached returned when map is full (propagates to 503 in handler) - blockchain_tx_status handler matches on the error variants: AlreadyWatched is treated as idempotent (status still returned), CapReached returns 503 SERVICE_UNAVAILABLE with a clear message - Migration 019 adds UNIQUE constraint on tx_hash for DB-layer dedup ## solutions-plug#917 — Stellar RPC reachability probe at startup & /health/ready endpoint - validate_network_passphrase now differentiates dev vs production: PREDICTIQ_ENV=production → process::exit(1) on failure/mismatch all other envs → log warning and continue - Add probe_stellar_ready() for per-request liveness checking - Add GET /health/ready readiness endpoint returning 200/503 + JSON body with ready bool and stellar_rpc field ("ok" | "unreachable") - Route registered in main.rs public_routes ## Docs & config - services/api/README.md: document WATCHED_TX_TTL_SECS, WATCHED_TX_MAX_SIZE, PREDICTIQ_ENV, /health/ready endpoint, watched_tx_count metric - services/api/.env.example: add commented-out examples for new variables - Config test structs updated with new required fields
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resolves four blockchain reliability issues in a single cohesive change. All modifications are in
services/api/src/(Rust/Axum), with supporting changes to config, metrics, alert rules, a new DB migration, and README.Closes
Changes
solutions-plug#933 — WATCHED_TX_TTL configurable via
WATCHED_TX_TTL_SECSconst WATCHED_TX_TTL: Duration = 30 minConfig::watched_tx_ttl_secs(default1800) read fromWATCHED_TX_TTL_SECSenv varBlockchainClientnow carries awatched_tx_ttl: Durationfield populated at construction019_create_watched_transactions.sqladds anexpires_at TIMESTAMPTZcolumn that mirrors the same TTL for any persisted approachsolutions-plug#934 — WATCHED_TX_MAX_SIZE cap + Prometheus gauge + alert rule
Config::watched_tx_max_size(default10_000) read fromWATCHED_TX_MAX_SIZEMetrics::watched_tx_countIntGauge updated on every insert, eviction, and removalperformance/config/alerts.yaml:WatchedTxCountHigh: fires whenwatched_tx_count > 8000(80% of default cap) for 5 min — severity warningWatchedTxCountCritical: fires whenwatched_tx_count >= 10000for 1 min — severity criticalsolutions-plug#937 — Deduplication of watched transactions
watch_transactionnow returnsResult<(), WatchTxError>instead of()WatchTxError::AlreadyWatched— hash already in map; no second entry insertedWatchTxError::CapReached— map full; caller responsible for 503blockchain_tx_statushandler matches on both variants:AlreadyWatchedis idempotent — status is still returned to the callerCapReachedreturns503 SERVICE_UNAVAILABLEwith a clear messagesync_onceignoresAlreadyWatched(benign) and logs a warning onCapReached019addsUNIQUE (tx_hash)constraint for DB-layer dedup enforcementsolutions-plug#917 — Stellar RPC reachability probe at startup +
/health/readyendpointvalidate_network_passphrasenow differentiates environments viaPREDICTIQ_ENV:PREDICTIQ_ENV=production→process::exit(1)on RPC unreachable or passphrase mismatchBlockchainClient::probe_stellar_ready() -> boolfor on-demand liveness checkingGET /health/readyreadiness endpoint:200 OK+{ "ready": true, "stellar_rpc": "ok" }when RPC is reachable and passphrase matches503 Service Unavailable+{ "ready": false, "stellar_rpc": "unreachable" }on failuremain.rspublic routes alongside/healthFiles Changed
src/blockchain.rsWatchTxErrorenum;watch_transactionreturnsResult; configurable TTL/cap;probe_stellar_ready; dev/prod passphrase behaviour; gauge updatessrc/config.rswatched_tx_ttl_secs,watched_tx_max_size,is_productionfields; test struct fixessrc/metrics.rswatched_tx_countIntGauge;set_watched_tx_countmethodsrc/handlers.rsblockchain_tx_statushandlesWatchTxError; newhealth_readyhandlersrc/main.rs/health/readyroute registereddatabase/migrations/019_create_watched_transactions.sqlUNIQUE (tx_hash)andexpires_atdatabase/migrations/rollbacks/019_create_watched_transactions_down.sqlperformance/config/alerts.yamlWatchedTxCountHighandWatchedTxCountCriticalalert rulesREADME.md/health/ready, andwatched_tx_countmetric.env.exampleTesting
Unit tests updated in
blockchain.rsto cover:watch_transaction_dedup_returns_already_watched— second registration of same hash returnsAlreadyWatchedand does not insert a duplicatewatch_transaction_cap_returns_cap_reached— insertion when map is full returnsCapReachedand map size stays at capwatched_txs_ttl_evicts_stale_entries— entries older than TTL are removed on next writewatch_tx_error_variants_are_distinct— enum variants are distinguishableMetrics test updated to assert
watched_tx_count 42appears in rendered output.