Skip to content

Add Rust implementation alongside Go, with shared conformance and Prometheus integration - #15

Open
angles-n-daemons wants to merge 8 commits into
mainfrom
bdillmann/rust-port
Open

angles-n-daemons wants to merge 8 commits into
mainfrom
bdillmann/rust-port

Conversation

@angles-n-daemons

@angles-n-daemons angles-n-daemons commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Adds a Rust implementation alongside Go, with shared conformance tests and optional Prometheus integrations. Existing Go consumers keep importing github.com/cockroachdb/goodhistogram.

  • Keeps go.mod and the public Go API at the repository root. The implementation lives in internal/, exposed through type aliases, presets, and constructor wrappers. Rust stays in rust/.
  • Adds the Rust histogram implementation, quantile APIs, and OpenMetrics exposition helpers. The default crate has no dependencies.
  • Adds shared datatest/ scenarios and preset fixtures. Go owns the golden output; Rust replays it to check bucket counts, sums, schemas, quantiles, and preset compatibility. Regenerate with go test ./internal/ -run 'TestDatatest|TestPresets' -rewrite.
  • Adds optional prometheus and prometheus-client integrations for duration histograms that observe seconds, store nanoseconds, and export seconds with the full bucket set.
  • Adds Rust CI and a Go consumer test covering the original import path, exported types, constructors, presets, and Prometheus registration.

Validation: Go build, vet, and tests; module tidy unchanged; Rust default and all-feature tests, including shared conformance; Rust formatting.

Native/exponential exposition conformance, a metrics-facade recorder, and adoption in basalt remain follow-up work.

angles-n-daemons and others added 7 commits August 27, 2026 17:10
Relocate the Go sources into go/ so the repository can host the Rust
implementation alongside it under rust/. The Go module path is unchanged
(github.com/cockroachdb/goodhistogram), so the package import path becomes
github.com/cockroachdb/goodhistogram/go; update the README accordingly.

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
Add a Rust implementation of goodhistogram under rust/, kept in the same
repository as the Go source so the two stay in lockstep. The crate has no
dependencies: recording is a couple of atomics and std bit ops. Includes the
core histogram, quantile estimation, and OpenMetrics exposition helpers
(conventional_buckets, to_openmetrics), unit tests, a CI workflow, and a README.

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
Add datatest/, one self-contained scenario per file: a hand-authored input
section (params + values) and a golden section generated from the Go
implementation (OpenMetrics exposition + quantiles). The Go suite owns the
golden (go test ./go/ -run TestDatatest -rewrite); the Rust suite replays the
same files and checks it reproduces them, so the two implementations can never
silently drift. Includes simple scenarios plus edge cases and a README.

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
Add two optional, off-by-default features that expose goodhistogram to the Rust
metrics ecosystem as a near-drop-in duration histogram (observe seconds, store
nanoseconds, export seconds), both exporting the full bucket set:

  - prometheus:        tikv prometheus crate Collector (PromHistogram,
                       PromHistogramVec). Takes cumulative bucket counts and
                       synthesizes +Inf from the sample count.
  - prometheus-client: OpenMetrics-native Collector (HistogramCollector).
                       Takes per-bucket counts and accumulates them itself.

The default build stays dependency-free. CI now also runs clippy and tests with
--all-features.

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
The Go datatest check regenerated the golden text and string-compared it, but
the golden contains floats from math.Pow/Sqrt whose last bit differs across
platforms (the arm64 dev machines vs the amd64 CI runners), so CI failed on
boundary/quantile text that was numerically identical.

Parse and replay instead, mirroring the Rust suite: counts and sum exact,
boundaries and quantiles within a small relative tolerance. -rewrite still
regenerates. (The Rust CI already passed on amd64 with exact count comparison
against the arm64-generated golden, confirming the counts are platform-stable.)

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
Bring the Rust crate's public surface in line with the Go implementation:

  - Match Go's default: an unset error_bound now resolves to 0.10 (schema 3)
    via DEFAULT_ERROR_BOUND, not STANDARD_ERROR_BOUND. (Go and Rust had
    diverged on the zero-value default.)
  - Add the 9 Params presets (COARSE/STANDARD/FINE + HIRES_LATENCY/IO_LATENCY/
    RESPONSE_TIME/LONG_RUNNING/DATA_SIZE/MEMORY_USAGE), matching Go's values.
  - Store the schema in Config; add Snapshot::schema() and Histogram::schema().
  - Add Snapshot::values_at_quantiles (batch) and Histogram::values_at_quantiles_into
    (buffer-reusing live reads), plus Snapshot::merge/sub for windowed views.
  - Add a core HistogramVec (labeled histogram map) mirroring Go's vec.go,
    minus the prometheus Collector (that lives in the prometheus feature).

Native prometheus-histogram export is intentionally not added: neither the tikv
prometheus 0.13 proto nor prometheus-client 0.22 supports native encoding.

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
Verify the new parity surface cross-language:

  - Add a schema line to the datatest golden (Go emits Snapshot.Schema(); the
    Go and Rust suites both assert it).
  - Add a presets conformance fixture (datatest/presets.golden) generated from
    Go; rust/tests/presets.rs asserts the Rust preset consts reproduce each
    preset's lo/hi/error_bound/schema/num_buckets. TestPresets also verifies it
    from the Go side.
  - Add Go quantile-parity tests pinning that ValuesAtQuantiles and
    ValuesAtQuantilesInto match ValueAtQuantile per quantile, which is the
    assumption the Rust map-based port relies on.

Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
@RaduBerinde

Copy link
Copy Markdown
Member

It's not great to have an import path github.com/cockroachdb/goodhistogram/go with package name goodhistogram that doesn't match the last path component.

I would keep the go.mod in the root, move the go code to ./pkg/ or ./internal and have a small wrapper .go file that reexports the internals (e.g. type Histogram = internal.Histogram, etc). I believe rust doesn't care about paths, so putting all rust code inside ./rust will work fine (and won't require updating any import paths).

If not, at least make it github.com/cockroachdb/goodhistogram/goodhistogram-go.

@angles-n-daemons

Copy link
Copy Markdown
Contributor Author

@RaduBerinde that's a good point, I'll revert that - it makes a lot of sense (at least for the public api)

Address Radu’s review by retaining the original root import path. Move the Go implementation to internal and expose its types, presets, and constructors through a root package. Update conformance commands and add consumer tests for the public API.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants