Skip to content

fix: increase timeout robustness#93

Merged
sevenmachines merged 3 commits into
mainfrom
increase_timeout_robustness
Apr 15, 2026
Merged

fix: increase timeout robustness#93
sevenmachines merged 3 commits into
mainfrom
increase_timeout_robustness

Conversation

@sevenmachines
Copy link
Copy Markdown
Contributor

@sevenmachines sevenmachines commented Apr 15, 2026

  • Raise WS heartbeat + Node heap headroom

Description

Type

  • Bug fix
  • Feature
  • Breaking change
  • Documentation
  • Chore

Package

  • @parity/dotns-cli
  • Root/monorepo
  • Documentation

Related Issues

Fixes

Checklist

Code

  • Follows project style
  • bun run lint passes
  • bun run format passes
  • bun run typecheck passes

Documentation

  • README updated if needed
  • Types updated if needed

Breaking Changes

  • No breaking changes
  • Breaking changes documented below

Breaking changes:

Testing

How to test:

Notes

Two configuration tweaks that independently reduce the chance of a
bulletin upload failing on the polkadot-api WS heartbeat or hitting
the default Node heap ceiling. No behavioural change to upload code.

- packages/cli/src/bulletin/store.ts:
  Pass heartbeatTimeout=300_000 and timeout=10_000 to getWsProvider.
  The 40_000 ms ws-provider default is shorter than a single Bulletin
  chunk's worst-case best-block inclusion latency we measured (>50 s
  outliers under contention), so the transport was tearing down
  healthy WS connections while the chain was still acknowledging an
  in-flight extrinsic.

- .github/actions/bulletin/action.yml:
  Set NODE_OPTIONS=--max-old-space-size=4096 in both the authorize
  and upload steps. ubuntu-latest runners have ≥16 GiB RAM; default
  ~2 GiB Node heap leaves no headroom if a transient leak window
  opens. Cheap safety net.
Move chunk.bytes = null into a finally so a failed submission also
releases its 2 MiB. Today the buffer is only freed on success; on
failure (stall, retry, abort) it stays alive via the closure, pinning
memory for the lifetime of the upload. The payload is re-streamed
from disk on the next attempt, so dropping the in-memory copy is
always safe.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 15, 2026

CI Summary

Check Result
Lint Passed
Format Passed
Typecheck Passed
Build Passed
Release Passed
Deploy UI Passed
Deploy Example Passed
PR Title Passed
Labels Passed
Test Passed - 134 passed, 0 failed

Release - Passed

Test this PR

Download artifact (GitHub CLI required):

gh run download 24462300780 -n cli-release-0.0.0-pr.93 -R paritytech/dotns-sdk

Install globally:

npm install -g ./parity-dotns-cli-0.0.0-pr.93.tgz

Verify:

dotns --help
Deploy UI — Passed
Stage Status
✓ Build Build succeeded
✓ Deploy Deployed (cached)
Property Value
Domain pr93.dotns.dot
CID bafybeic2ajvmzb7ywywhhqzueedkurxv6ud45wsqeo5w2ppfoscyg3rdrm (cached)
URL (dot.li) https://dot.li/pr93.dotns.dot
URL (direct) https://pr93.dotns.dot

View run

Deploy Example — Passed
Stage Status
✓ Site validation Site validated
✓ Deploy Deployed (cached)
Property Value
Domain pr93.dotns-example-site.dot
CID bafybeicx555prsudvmqsdrap7keoilryrudwycdx3ody2rejwqwjwp52u4 (cached)
URL (dot.li) https://dot.li/pr93.dotns-example-site.dot
URL (direct) https://pr93.dotns-example-site.dot
Duration 46s

View run

Labels

pkg: cli, scope: bulletin

Test - Passed

134 passed, 0 failed across 134 tests.

View run

$ bun test tests/unit/
bun test v1.2.6 (8ebd5d53)

::group::tests/unit/text/textHelp.test.ts:
(pass) text --help lists subcommands and auth options [15.00ms]
(pass) text view --help shows name and key arguments [3.00ms]
(pass) text set --help shows name, key, and value arguments [3.00ms]

::endgroup::

::group::tests/unit/content/contentHelp.test.ts:
result:  {
  exitCode: 0,
  standardOutput: "Usage: dotns content [options] [command]\n\nManage domain content hashes\n\nOptions:\n  --rpc <wsUrl>               WebSocket RPC endpoint (env: DOTNS_RPC)\n  --keystore-path <path>      Keystore path (env: DOTNS_KEYSTORE_PATH)\n  --min-balance <pas>         Minimum balance in PAS (env:\n                              DOTNS_MIN_BALANCE_PAS)\n  --account <name>            Keystore account name (default: keystore default)\n  --password <pw>             Keystore password (env: DOTNS_KEYSTORE_PASSWORD)\n  -m, --mnemonic <phrase>     BIP39 mnemonic phrase (env: DOTNS_MNEMONIC)\n  -k, --key-uri <uri>         Substrate key URI (env: DOTNS_KEY_URI)\n  -h, --help                  display help for command\n\nCommands:\n  view [options] <name>       View domain content hash\n  set [options] <name> <cid>  Set domain content hash (IPFS CID)\n  help [command]              display help for command\n",
  standardError: "",
  combinedOutput: "Usage: dotns content [options] [command]\n\nManage domain content hashes\n\nOptions:\n  --rpc <wsUrl>               WebSocket RPC endpoint (env: DOTNS_RPC)\n  --keystore-path <path>      Keystore path (env: DOTNS_KEYSTORE_PATH)\n  --min-balance <pas>         Minimum balance in PAS (env:\n                              DOTNS_MIN_BALANCE_PAS)\n  --account <name>            Keystore account name (default: keystore default)\n  --password <pw>             Keystore password (env: DOTNS_KEYSTORE_PASSWORD)\n  -m, --mnemonic <phrase>     BIP39 mnemonic phrase (env: DOTNS_MNEMONIC)\n  -k, --key-uri <uri>         Substrate key URI (env: DOTNS_KEY_URI)\n  -h, --help                  display help for command\n\nCommands:\n  view [options] <name>       View domain content hash\n  set [options] <name> <cid>  Set domain content hash (IPFS CID)\n  help [command]              display help for command\n",
}
(pass) content --help lists subcommands and auth options [3.00ms]
(pass) content view --help shows name argument and options [2.00ms]
(pass) content set --help shows name and cid arguments [2.00ms]

::endgroup::

::group::tests/unit/auth/auth.test.ts:
(pass) auth set creates keystore and stores multiple accounts [289.00ms]
(pass) auth set accepts account names with special characters [402.00ms]
(pass) auth list reports missing keystore [3.00ms]
(pass) auth list shows all accounts and auth types [264.00ms]
(pass) auth use switches default account [409.00ms]
(pass) auth remove last account clears default [69.00ms]
(pass) auth remove preserves remaining accounts and reassigns default [199.00ms]
(pass) auth clear deletes all accounts [80.00ms]

::endgroup::

::group::tests/unit/auth/authHelp.test.ts:
(pass) root help lists auth command [2.00ms]
(pass) auth help shows options and subcommands [2.00ms]
(pass) auth set help shows all options [2.00ms]
(pass) auth list help shows options [1.00ms]
(pass) auth use help shows options [2.00ms]
(pass) auth remove help shows options [2.00ms]
(pass) auth clear help shows options [2.00ms]
(pass) auth parses keystore-path option [2.00ms]
(pass) auth parses password option [3.00ms]
(pass) auth set parses account option [2.00ms]
(pass) auth set parses mnemonic option [1.00ms]
(pass) auth set parses key-uri option [2.00ms]
(pass) auth help command shows help [2.00ms]
(pass) auth help set shows set command help [2.00ms]
(pass) auth help list shows list command help [1.00ms]
(pass) auth help use shows use command help [2.00ms]
(pass) auth help remove shows remove command help [1.00ms]
(pass) auth help clear shows clear command help [1.00ms]

::endgroup::

::group::tests/unit/auth/authRevert.test.ts:
(pass) auth set rejects account name with forward slash [2.00ms]
(pass) auth set rejects account name with backslash [2.00ms]
(pass) auth set rejects account name that is just a dot [1.00ms]
(pass) auth set rejects account name that is double dots [1.00ms]
(pass) auth set rejects account name starting with dot [2.00ms]
(pass) auth set rejects account name ending with dot [2.00ms]
(pass) auth set rejects account name with special characters [12.00ms]
(pass) auth set rejects account name that is too long [2.00ms]
(pass) auth use rejects non-existent account [71.00ms]
(pass) auth remove rejects non-existent account [79.00ms]

::endgroup::

::group::tests/unit/store/storeHelp.test.ts:
(pass) store --help lists subcommands [1.00ms]
(pass) store info --help shows auth options [2.00ms]
(pass) store list --help shows options [1.00ms]
(pass) store get --help shows key argument [1.00ms]
(pass) store set --help shows key and value arguments with auth [1.00ms]
(pass) store delete --help shows key argument with auth [1.00ms]
(pass) store check --help shows address argument [1.00ms]
(pass) store authorize --help shows address argument with auth [1.00ms]
(pass) store unauthorize --help shows address argument [1.00ms]
(pass) store authorize-controller --help shows address argument [1.00ms]
(pass) store unauthorize-controller --help shows address argument [1.00ms]
(pass) store ensure-auth --help shows description and auth options [2.00ms]

::endgroup::

::group::tests/unit/pop/setPopHelp.test.ts:
(pass) root help lists pop command [2.00ms]
(pass) pop help shows commands and description [1.00ms]
(pass) pop help shows auth options [2.00ms]
(pass) pop set help shows status parameter [1.00ms]
(pass) pop set help shows auth options [1.00ms]
(pass) pop info help shows description [1.00ms]
(pass) pop info help shows auth options [1.00ms]
(pass) pop set parses rpc option at pop level [2.00ms]
(pass) pop set parses rpc option at set level [1.00ms]
(pass) pop set parses keystore-path option [2.00ms]
(pass) pop set parses account option at pop level [1.00ms]
(pass) pop set parses account option at set level [2.00ms]
(pass) pop set parses password option [1.00ms]
(pass) pop set parses mnemonic option [1.00ms]
(pass) pop set parses key-uri option [1.00ms]
(pass) pop info parses auth options at pop level [2.00ms]
(pass) pop info parses auth options at info level [1.00ms]
(pass) pop set parses mixed options across levels [2.00ms]
(pass) pop help command shows pop help [2.00ms]
(pass) pop help set shows set help [2.00ms]
(pass) pop help info shows info help [1.00ms]

::endgroup::

::group::tests/unit/account/accountHelp.test.ts:
(pass) account --help lists subcommands including is-mapped, is-whitelisted, whitelist [2.00ms]
(pass) account is-mapped --help shows address argument and --json [2.00ms]
(pass) account is-whitelisted --help shows address argument and --json [1.00ms]
(pass) account whitelist --help shows address argument, --remove, and --json [1.00ms]
(pass) account is alias works for is-mapped
(pass) account iw alias works for is-whitelisted [1.00ms]

::endgroup::

::group::tests/unit/cli/reporter.test.ts:
(pass) cli reporter > stream reporter emits durable progress lines
(pass) cli reporter > withConsoleToStderr redirects console and stdout writes

::endgroup::

::group::tests/unit/bulletin/uploadManifest.test.ts:
(pass) upload manifest resume behavior > returns stale manifest when fingerprint does not match [2.00ms]
(pass) upload manifest resume behavior > deduplicates completed blocks by index [1.00ms]

::endgroup::

::group::tests/unit/bulletin/uploadProfiling.test.ts:
(pass) upload profiler > writes schema-complete profile report with peak aggregation [15.00ms]
(pass) upload profiler > default profile path is deterministic for a given fingerprint [1.00ms]

::endgroup::

::group::tests/unit/bulletin/bulletinHelp.test.ts:
(pass) root help lists bulletin command [1.00ms]
(pass) bulletin help shows commands and description [1.00ms]
(pass) bulletin upload help shows all options [2.00ms]
(pass) bulletin upload help shows default values [1.00ms]
(pass) bulletin authorize help shows all options [1.00ms]
(pass) bulletin authorize help shows default values [1.00ms]
(pass) bulletin history help shows options [1.00ms]
(pass) bulletin history:remove help shows usage [1.00ms]
(pass) bulletin history:clear help shows description [1.00ms]
(pass) bulletin help command shows bulletin help [1.00ms]
(pass) bulletin help upload shows upload help [1.00ms]
(pass) bulletin help authorize shows authorize help [1.00ms]
(pass) bulletin status help shows all options [1.00ms]
(pass) bulletin help status shows status help [1.00ms]
(pass) bulletin list alias works [1.00ms]
(pass) bulletin verify help shows usage [1.00ms]
(pass) bulletin help verify shows verify help [1.00ms]

::endgroup::

::group::tests/unit/register/registerHelp.test.ts:
(pass) root help lists register command [2.00ms]
(pass) register help shows subcommands [1.00ms]
(pass) register domain help shows options [1.00ms]
(pass) register subname help shows options [1.00ms]
(pass) register domain parses status none [1.00ms]
(pass) register domain parses status lite [1.00ms]
(pass) register domain parses status full [1.00ms]
(pass) register domain parses reverse flag [1.00ms]
(pass) register domain parses governance flag [1.00ms]
(pass) register domain parses owner option [1.00ms]
(pass) register domain parses transfer with destination [1.00ms]
(pass) register domain parses account option [1.00ms]
(pass) register domain parses keystore-path option [1.00ms]
(pass) register domain parses password option [1.00ms]
(pass) register domain parses mnemonic option [1.00ms]
(pass) register domain parses key-uri option [1.00ms]
(pass) register domain parses commitment-buffer option [1.00ms]
(pass) register domain parses commitment-buffer alias --cb [1.00ms]
(pass) register subname parses name and parent [2.00ms]
(pass) register subname parses owner option
(pass) getCommitmentBufferSeconds defaults to 6 when env is not set [1.00ms]
(pass) getCommitmentBufferSeconds reads from DOTNS_COMMITMENT_BUFFER env variable
(pass) COMMITMENT_POLL_INTERVAL_MS is 2000
(pass) COMMITMENT_POLL_TIMEOUT_MS is 30000

::endgroup::

::group::tests/unit/lookup/lookupHelp.test.ts:
(pass) lookup --help lists subcommands and auth options [2.00ms]
(pass) lookup name --help shows label argument and options [2.00ms]
(pass) lookup owner-of --help shows label argument and options [1.00ms]
(pass) lookup transfer --help shows label argument and destination option [1.00ms]
(pass) lookup transfer parses destination at transfer level [1.00ms]
(pass) lookup transfer parses auth options at lookup level [1.00ms]

::endgroup::

 134 pass
 0 fail
 511 expect() calls
Ran 134 tests across 14 files. [2.46s]

Comment thread packages/cli/src/bulletin/store.ts Outdated
// success it is already persisted; on failure it must not pin
// memory while the caller decides whether to retry — the
// payload is re-streamed from disk on the next attempt.
chunk.bytes = null as unknown as Uint8Array;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we want only null if there is success?

We could also restructure so in-wave failures reread the bytes from disk, otherwise that is only happening at the wave boundaries now if I am not mistaken (via runWaveWithRetries).

With this change as is, transient failures won't recover on retry

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

happy to seperate this bit just now and focus on the timeouts. I took this bit out of a bigger error handling piece which might be needed for it to make sense. The timeout stuff should at least help.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted it to focus on the timout orchestration in the first instance. Error handling/memory release I'll move to a seperate pr (if we want it)

@andrew-ifrita andrew-ifrita changed the title increase timeout robustness fix: increase timeout robustness Apr 15, 2026
Copy link
Copy Markdown
Collaborator

@andrew-ifrita andrew-ifrita left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@sevenmachines sevenmachines merged commit f32539e into main Apr 15, 2026
25 of 27 checks passed
@sevenmachines sevenmachines deleted the increase_timeout_robustness branch April 15, 2026 15:43
@sevenmachines
Copy link
Copy Markdown
Contributor Author

lets see what the affect is. should be at least better

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants