Skip to content

v2.7.0

Choose a tag to compare

@github-actions github-actions released this 22 Aug 18:26
· 107 commits to main since this release
63acd42

A nest that declares [[calls]] can use --seal-direct again, a factory nest stops aborting its
backfill on a provider cap, and this page stops quoting performance numbers it cannot stand behind.

Drop-in. Swap the binary and restart; no config changes, no data migration.

A minor rather than a patch for one reason: 2.6.x refused --seal-direct on a nest with
[[calls]] and 2.7.0 does not.
That is new behaviour on an existing surface, and calling it a
patch would be the small dishonesty this release is otherwise about.

A factory nest no longer dies on a provider cap

uniswap-v2 on mainnet aborted its backfill outright:

Error: getLogs (children) 25791463..=25811399
  HTTP 400: Log response size exceeded

backfill_direct_factory fetches twice per chunk. Pass 1 - base contracts plus children already
known - caught an over-cap response and narrowed. Pass 2 - the children discovered in this very
chunk
- carried a bare ?. It was the only one of nine is_result_too_large sites in the file
treating a provider cap as fatal
, and it is the pass more likely to meet one, because a freshly
created child is exactly the contract whose first logs arrive in a burst.

Found in the field rather than by reading.

[[calls]] and --seal-direct are no longer mutually exclusive

2.6.0 shipped pinned eth_call (RFC-0023 tier 3) and 2.6.x refused --seal-direct whenever a nest
declared one, because a seal-direct run would sail past every sampled block and seal the range with
the calls table silently absent. The refusal was correct. It was also expensive: it forced the
entire range onto the slower path rather than only the blocks that needed a read.

All three seal-direct paths now resolve declared calls, so the refusal is gone and the sealed
segment matches what the streaming path would have produced.

Measured honestly, and the number is smaller than the one that prompted the work: on two identical
near-tip nests built from the same commit, one with a [[calls]] block and one without, the
difference was ~7% - not the 5.5x a single field report suggested. That field report was one run
on a 454-million-block nest with ~2,725 pinned reads, roughly 300x denser than the measurement.
Both are true of what they measured; only one of them is a measurement.

A declared event that had never fired took a whole view down

Declare an event a contract really emits, on a chain where it has not emitted yet, and no table was
created - so any view referencing it failed to load in its entirety. One view supplying fourteen
fields lost all fourteen because a single referenced table did not exist, and nothing in the logs
explained either state.

A declared-but-unfired table now resolves as an empty typed view. The nest reports which tables are
in that state at startup, so the two cases are distinguishable rather than silent.

Two narrower instances of the same staleness class are fixed alongside it: a schema.json that has
fallen behind the config no longer silently keeps its stale column set, and the skip that explains
why a hand-edited nuthatch.toml did not move schema.json now logs at info rather than debug,
where an operator running the default filter can actually see it.

Fewer requests, fewer retry storms

  • Tier-3 reads cost one round trip per sampled block instead of two. The second fetched a block
    hash that the batched header call already had.
  • block_timestamps no longer descends serially. A 454-million-block backfill produced six retry
    storms against an archive endpoint; the top-level split now runs its halves concurrently.
  • doctor probes with the nest's full contract set, not just the first declared contract. A
    multi-contract nest was getting advice measured against a narrower question than the one its
    backfill would ask, and the recommendation came out optimistic.

Operator and security hygiene

  • bench no longer prints a --state-rpc URL in full. Archive endpoints carry API keys, and
    that URL was going to a log.
  • main stopped going red at random. A test asserted an exact request count across a
    tokio::try_join! that cancels its losing branch, so the total was never a constant. It failed
    nine runs in ten on one machine while CI showed green.

This page no longer quotes numbers it cannot stand behind

The honest headline of this release, and the least comfortable part.

docs/benchmarks.md carried ~8.7x for seal-direct and ~20x for the pipeline. Both were ratios
over a 289 events/sec baseline measured two weeks before a fix to the benchmark harness itself -
it had been writing one database transaction and one fsync per row, which the indexer has never done.
The multipliers were never recomputed, and they had been quoted in a grant document since.

Re-measuring did not produce better multipliers. It produced this:

  • seal-direct came out at 0.92x - slower than the path it is supposed to beat - contradicting
    both the architecture and a 5.2x measured hours earlier at the same commit
  • a 3.8x spread inside a single arm in one session on one machine

So the page now says what is true: these figures measure a public endpoint's mood and the machine's
neighbours as much as they measure nuthatch, the discrepancy is published rather than resolved by
picking the flattering number, and a deterministic replay rig is being built before any multiplier is
quoted again.

The benchmark harness itself was also measuring the wrong thing: every seal-direct path passed an
empty [[calls]] slice regardless of what the nest declared, so bench could not measure the
feature this release is named for. Both arms now resolve declared calls, and bench refuses rather
than silently under-measuring when a nest declares [[calls]] and no --state-rpc is given.

Upgrading

Binary swap. On-disk state is forward-readable, dev flags are unchanged, no re-index. Minimum
supported Rust is 1.95 - measured, not asserted.