ops/bench/measure-node.sh takes two snapshots of a running xOne node, one before and one after a window you choose, and writes one JSON file and one markdown summary. It records RPC latency for seven read calls, block import times from the slow-block log, disk I/O from /proc, memory split into anonymous and file-backed pages, and the datadir size. It sends only read calls and reads only files. It came with PR #1056, merged on 2026-11-15, for the measurements that issues #259 (prefetch) and #200 (resource budgets) ask for.
This post describes the tool from its source and docs/ops/measure.md. It gives no results: the tool claims no number, and we have not run it for this post.
How do I run it?
ops/bench/measure-node.sh <datadir> <rpc-url> <minutes> [--pid PID] [--log FILE] [--out DIR] [--samples N]
| Argument | Meaning |
|---|---|
datadir |
The node's datadir. Its size is recorded before and after. |
rpc-url |
The node's HTTP RPC, for example http://127.0.0.1:8545. |
minutes |
The window between the two snapshots. 0 takes both at once. |
--pid or XONE_PID |
The exact pid of the node, for I/O and memory. Without it these rows say "skipped". |
--log or XONE_LOG |
The node's log file. Without it the import rows say "skipped". |
--out |
Output directory, default ./measure-out. |
--samples |
Calls per method and snapshot, default 50. |
For import times, start the node with --debug.logslowblock 0, so each imported block writes a Slow block line. See the receive and slow-block log fields.
Which RPC calls does it time?
Seven read calls, each --samples times in each snapshot:
eth_blockNumbereth_chainIdeth_getBlockByNumber("latest", false)eth_getBlockByNumber("latest", true)eth_getBlockReceipts("latest")eth_getBalanceof0x…0089atlatesteth_gasPrice
For each call it gives n, p50, p95, p99, max, mean and the number of errors, in milliseconds. The percentiles are nearest-rank. A failed call, for example one the node refuses under its RPC quota, counts as an error, and its time is still in the percentiles. Read the error column before you compare latencies.
What else does it record?
- Block import time. From the
Slow blocklines written during the window only; lines already in the log at the start are skipped. It gives the block count, the transactions and the gas, and p50, p95, p99 and max oftotal_ms,execution_ms,commit_msandverify_ms. - Disk I/O.
rchar,wchar,read_bytesandwrite_bytesfrom/proc/<pid>/io, before and after, the difference and the rate per second. - Memory.
VmRSS,RssAnon,RssFileand the peakVmHWMfrom/proc/<pid>/status. The doc says to report them as separate numbers: file-backed pages are page cache, not heap. See memory per XDC client for why this matters. - Datadir size. The sum of the file sizes under the datadir, before and after. This is the apparent size of each file (
st_size), which can differ from the disk blocks thatdureports. - Context. Host name, machine, CPU count and kernel;
web3_clientVersion; the head before and after.
What does the JSON look like?
One object with schema xone-measure-v1:
| Key | Content |
|---|---|
schema |
"xone-measure-v1" |
started_unix, window_s |
Start time and the window in seconds |
datadir, rpc, pid |
What was measured |
client_version, host |
web3_clientVersion; host node, machine, release, cpus |
head_before, head_after |
eth_blockNumber at the start and the end |
before, after |
Each: unix, rpc_ms (per call), proc (io, rss_bytes), datadir_bytes |
io_delta |
Differences of the I/O counters and per_second rates |
import |
blocks_logged, tx_total, gas_total and the four timing statistics |
The files are <out>/measure-<unix time>.json and .md. The host.node field holds the host name. Remove it before you post the file in a public issue if the name is private.
How do I use it for a before-and-after test?
The procedure in the doc, for the prefetch change of #259:
- Run a disk-state node (
--state.disk) on a datadir at a known height, with--debug.logslowblock 0. - Run the script for 30 minutes on the node as it is. This is "before".
- Stop the node by its exact pid, start the new build on the same datadir height and the same peers, and run the script again for the same window. This is "after".
- Run once more on a stretch that gives the change nothing to do, such as empty blocks, and report it too.
- Put both JSON files and both summaries in the issue.
For the resource budgets of #200, run it for 1,440 minutes (24 hours) on each node type and network from a fresh start, and take the memory rows and the datadir growth.
Is it safe to run on a production node?
It only reads. In each snapshot it sends seven read calls 50 times each, one at a time, and it reads /proc/<pid>/io, /proc/<pid>/status, the log file and the datadir file list. Two costs to plan for:
- The 350 calls per snapshot go through the node's RPC limits like any other client.
- The datadir walk reads the metadata of every file under the datadir, which takes time on a large archive datadir.
How is the tool tested?
ci/tests/test_ops_measure.py runs it against a stub RPC server, a fake /proc tree and a fixture log. It checks that the JSON and markdown are written, that old log lines are skipped, that only lines after the start offset count, that a missing pid or log is reported as skipped, and that bad arguments exit non-zero. shellcheck is clean on the shell wrapper.
Frequently asked questions
Does measure-node.sh change anything on the node?
No. It sends only read calls and reads files. It does not stop or restart the node.
Why do I need --debug.logslowblock 0?
Import times come from the Slow block lines. At 0 the node writes one for every imported block.
Why does it report RssAnon and RssFile separately?
RssFile is page cache of mapped files, such as the database, and the kernel can drop it. RssAnon is the process's own memory.
Where are results from this tool?
None are published yet. The doc names issues #259 and #200 as the places for them.
Sources
This article describes the source and evidence at its review date. Check the linked repository for current implementation and qualification status.
Explore the engineering evidenceEdit this page on GitHub opens the Markdown source (content/blog/measure-node-before-after/index.md). GitHub may ask you to sign in. Report a correction opens a prefilled GitHub issue for this page.