perf(wallet): bring createAction under remote latency budgets - #426
Conversation
e1eba93 to
56132aa
Compare
56132aa to
8659dfb
Compare
E2E test report — real mainnet deploymentDeployed this branch (head Performance vs main: confirmed ✅A/B on identical hardware: two server instances against byte-identical copies of the same production DB backup, localhost, matching client/server toolbox versions per side. Op:
The <100 ms p50 claim reproduces on a direct connection. Over TLS + reverse proxy our remote p50 was ~400 ms (transport-dominated), so budgets hold at the storage layer, not necessarily WAN paths. (Our snapshot had 82 spendable custom outputs, so the many-input test is 82 inputs rather than 153 — identical on both sides.) Cross-version spot-check: a cold 2.4.22 client performed a real broadcast against the upgraded 2.5.0 server successfully. noSend / batch reservation lifecycle: verified working ✅Confirmed the full prepared-action flow E2E on mainnet: Two issues worth tracking (both reproduce on main, not introduced here)
Operational note for anyone upgrading from ≤2.4.2 deployments: 2.4.22+ requires Update: re-verified at head
|
|
BraydenLangley
left a comment
There was a problem hiding this comment.
I ran all the changes through E2E integration testing, and everything seems good to merge now.
…baseAccess (#444) StorageKnex.readSettings took no trx parameter and called this.toDb() with no argument, so it always ran on the pool. verifyReadyForDatabaseAccess(trx) then dropped its own trx when lazily populating the settings cache: this._settings ??= await this.readSettings() knex forces {min:1,max:1} on the sqlite dialect, so when that cache is cold and the first access happens inside a caller's transaction, the settings read asks for a second connection that can never be granted: the transaction will not release until the query returns, and the query cannot run until the transaction releases. It fails with "KnexTimeoutError: Timeout acquiring a connection. The pool is probably full. Are you missing a .transacting(trx) call?". verifyReadyForDatabaseAccess is on every write path, so any StorageKnex whose first database access is transactional self-deadlocks. It was already using trx correctly for the PRAGMA two lines below. This is the same defect as ef710c3 (#426) fixed in getProvenOrRawTx, and the last remaining toDb() call in the file that cannot receive a caller's transaction. The abstract already declared the parameter -- StorageReader.readSettings(trx?: sdk.TrxToken) -- and StorageIdb, StorageMySQLDojoReader and getBeefForTxid all accept it. Only StorageKnex dropped it, so this restores the existing contract rather than changing an API. Adds a regression test with acquireConnectionTimeout lowered to 5s so a regression fails in seconds rather than the 60s default. Verified it fails without the fix (both cases, with the KnexTimeoutError above) and passes with it. #426 corrected getProvenOrRawTx without locking the behaviour down; this covers both call sites. Co-authored-by: Imran Munir <imran.munir@bsvassociation.org>



Program and scope
createActionlatency program (no separate issue).createActionroutinely took multiple seconds. The successful path repeated proof reads/merges, key derivation, verification serialization, output writes, and authenticated session persistence instead of batching the work.e7d8f93ee769d51aa0082234baabf79be5ba925f.Impact
Affected packages/services and intended patch versions (publication occurs only through the release workflow after approval):
@bsv/sdk2.3.0: additive batch BEEF/key-derivation APIs and optimized validated Merkle/P2PKH paths.@bsv/wallet-toolbox2.5.0: atomic set-basedcreateActionpersistence, proof batching, signing/verification batching, storage indexes, session-touch coalescing, and privacy-safe spans.@bsv/wallet-toolbox-client2.5.0 and@bsv/wallet-toolbox-mobile2.5.0: coordinated compatible candidates and measured platform budgets.Review follow-up
noSendbatching remains the default. No opt-in or interface change was introduced. This preserves the intentional feat(wallet-toolbox): plan noSend batches locally and commit atomically #289 behavior: intermediate workspaces are session-scoped and the finalsendWithoperation is the atomic durability boundary; explicitlegacymode remains available only as a rollback/comparison path.actionBatchMode, proves the default isauto, proves the intermediate action is visible throughlistNoSendActionsbut not prematurely persisted, and provessendWithatomically commits it. A second regression proves the listed txid can be passed toabortActionand aborts the staged batch without a persisted transaction.listActionskeeps its existing SDK response contract. No nonstandardreferencefield was added:abortActionalready accepts either an action reference or txid, and the staged-batch regression proves the listed txid path.Verification
e7d8f93ee769d51aa0082234baabf79be5ba925f..interfaces.tsdeclarations are excluded because they emit no Istanbul entry; fail-closed executable-file behavior remains tested.Security and dependencies
Quality Gate passedverdict alone is not merge evidenceDependency evidence
@bsv/verifastis a workspace-only devDependency used by the retained benchmark. No runtime dependency or peer range changed. Additive SDK APIs retain older compatible peer behavior through the sequential proof-merge fallback.fast-urito 3.1.5 andip-addressto patched releases; the existing governedbrace-expansionoverride moves from 5.0.8 to 5.0.9 across the root, codegen, message-box, UHRP basic/cloud, and WAB workspaces. No new broad override or advisory dismissal was added.Release and operations
The exact governed Node 24.18.0 release path staged, checksum/SBOM-verified, and dry-run-published all four candidate tarballs. Actual publication remains exclusively in the approved release workflow. Knex migrations are reversible and passed fresh up/down/up tests; IndexedDB advances additively to schema version 3. Rollback is the prior package set plus the down migration. No service image or live deployment is part of this PR.
Completion evidence