Skip to content

rrr: migrate pure-arithmetic helpers to inline Rust DSL#68

Closed
shuaimu wants to merge 32 commits into
mako-devfrom
worktree-srpc
Closed

rrr: migrate pure-arithmetic helpers to inline Rust DSL#68
shuaimu wants to merge 32 commits into
mako-devfrom
worktree-srpc

Conversation

@shuaimu
Copy link
Copy Markdown
Contributor

@shuaimu shuaimu commented May 28, 2026

Summary

  • Pilot the rusty-cpp transpiler's inline-rust feature across rrr by extracting pure-arithmetic helpers into #if RUSTYCPP_RUST blocks that lower to byte-for-byte equivalent C++.
  • 33 commits touching 19 files: every pure-arithmetic helper across rrr now has a Rust DSL source-of-truth with the matching /*RUSTYCPP:GEN-BEGIN ... GEN-END*/ C++ block regenerated by the transpiler.
  • Includes a migration plan doc (docs/dev/rrr_rust_dsl_migration_plan.md) cataloging every migrated helper with rationale, lowering quirks, and per-file test results.

What's migrated

Predicates / classifiers: errors.* (4 helpers across get_error_category, is_retryable, is_connection, is_timeout), connection_state.* (4 transition-table helpers), request_options.* (4 helpers including exponential backoff), idempotency.* (4 helpers including FNV-1a hash combiner), completion_tracker.* (3 helpers), frame_codec.* (3 size/threshold helpers), internal_protocol.* (3 bit-twiddling helpers).

Timing / arithmetic: heartbeat.* (4 helpers including timespec→us), circuit_breaker.* (2 helpers including timespec→us), reconnect_policy.* (3 helpers including exponential-backoff loop), request_queue.* (3 helpers), connection_metrics.* (6 helpers in one block — sentinel math + min/max), basetypes.* (5 helpers: SparseInt buf_size / val_size, Timer::elapsed split, Timer::start/stop split, Rand::next scaling), rand.* (3 scaling helpers), stat.* (3 AvgStat helpers), load_balancer.* (2 selection helpers).

Misc: misc.* (FrequentJob period predicate), server.* (instance_id XOR mix).

Discoveries documented in the plan doc

  • match lowering pulls in 3800+ lines of runtime support — must use if-chains for primitive matching.
  • Auto-id allocation in the transpiler assigns <basename>.<N> in source order; new blocks added BEFORE existing GEN blocks collide. Workaround: manually renumber existing GEN ids before re-running the transpiler.
  • Per-file helper name prefixes (e.g. heartbeat_timespec_to_us vs circuit_timespec_to_us) avoid link-time collisions when the same arithmetic appears in multiple modules.
  • C-style includes (<stdint.h>, <stdlib.h>) are required in module fragments for the first inline-rust block per file; converted across migrated files.

Test plan

  • CI green on this branch.
  • Per-file borrow checks pass (each migrated file's borrow_check_rrr_borrow_<file> target was verified clean during the migration).
  • Targeted gtest suites pass: test_rpc_frame_codec (25/25), test_idempotency (32/32), test_completion_tracker (27/27), test_rpc_request_queue (30/30), test_load_balancer (21/21), test_rpc_circuit_breaker (21/21), test_rpc_heartbeat (20/20), test_rpc_timeout_retry (36/36), test_rpc_metrics (25/25), test_rpc_connection_state (30/30), test_rpc_state_integration (16/16), test_rpc_server_channel_binding (3/3).
  • rpcbench links clean (verified for files without dedicated test suites).
  • Spot-check a generated GEN block against the inline Rust source to confirm the transpiler still produces byte-identical C++.

🤖 Generated with Claude Code

shuaimu and others added 30 commits May 27, 2026 18:09
Phase 0 of the goal to gradually rewrite rrr's C++ source files as Rust
DSL and use the rusty-cpp transpiler to generate the C++ that compiles
into librrr.a. The .rs becomes the source of truth; the .gen.cppm is a
build artifact.

The doc defines:
  - Per-iteration protocol (read .cpp → author .rs → transpile →
    diff → wire into CMake → verify build/borrow-check/tests → commit).
  - Stop rules (3 consecutive blockers).
  - Per-phase progress log: pilot (errors.cpp), 8 leaf files, 5 medium
    files, then a decision point on the large files.

Transpiler is built and smoke-tested. The pilot starts in the next
commit.
Phase 1 pilot of the rrr Rust DSL migration. The body of
`SparseInt::val_size(i64) -> size_t` is reauthored as an inline Rust
DSL block inside basetypes.cpp; the rusty-cpp transpiler's
`inline-rust --rewrite` populates the matching
`/*RUSTYCPP:GEN-BEGIN id=basetypes.1 ... GEN-END*/` block right after
with the C++ equivalent. The original member method now delegates
to the generated free helper `sparse_int_val_size_impl(i64) -> u64`.

The C++ surface is unchanged — every caller (SparseInt::buf_size,
v32::val_size, v64::val_size) sees the same signature. The compiler
never sees the Rust source (RUSTYCPP_RUST is undefined at build
time); it compiles only the GEN block. The Rust is the developer-
facing source of truth.

This commit also revises docs/dev/rrr_rust_dsl_migration_plan.md to:
  - swap the per-iteration protocol from full-file translation
    (.rs → .gen.cppm) to inline mode (#if RUSTYCPP_RUST blocks in
    the existing .cpp). The full-file approach diverged on enum
    casing and return types; inline mode keeps the public surface
    intact.
  - record the v0 finding as a paragraph in the Goal section.
  - tick the pilot row.

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_basetypes -j32` clean
  - `build_clang21/test_marshal` — 23/23 PASSED (the SparseInt
    encoding-boundary suite exercises val_size on each branch)

Transpiler quirks observed (logged in plan doc):
  - GEN block ids are auto-assigned <basename>.N — supplying
    your own `id=...` is ignored.
  - Numeric comparisons get wrapped in
    `rusty::detail::deref_if_pointer_like(...)`; harmless for
    primitive args.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Same pattern as the prior val_size pilot: `SparseInt::buf_size` body is
reauthored as an inline Rust DSL block; the rusty-cpp transpiler's
`inline-rust --rewrite` populates the matching `/*RUSTYCPP:GEN-BEGIN
id=basetypes.1 ...*/` block right after with the C++ equivalent. The
member method delegates to the generated free helper
`sparse_int_buf_size_impl(i32) -> u64`.

`byte0` is taken as i32 (not i8 / char) so the comparison literals
(`0x80`, `0xC0`, ...) stay signed-positive on the Rust side; the
caller masks to 8 bits via `static_cast<int32_t>(byte0) & 0xFF`.
Preserves the pre-existing C++ behavior: `char` is interpreted as
unsigned bit pattern via the `&` masks, exactly as before.

The previous val_size GEN block was renumbered from `basetypes.1` to
`basetypes.2` to make room for buf_size's auto-assigned `basetypes.1`
(the transpiler's auto-id allocator goes in source order and doesn't
check for collisions with existing GEN blocks; documented in the plan
doc's Quirks section).

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean.
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_basetypes -j32` clean.
  - `build_clang21/test_marshal` — 23/23 PASSED.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Migrates `get_error_category(RpcError) -> RpcErrorCategory` to an
inline Rust DSL helper `rpc_error_category_code(i32) -> i32`. The C++
wrapper casts the enum to int at the boundary, so the public API
(`RpcError`, `RpcErrorCategory`, and `get_error_category`'s signature)
stays exactly as it was; existing callers and tests need no change.

Also adds `#include <stdint.h>` and `#include <rusty/rusty.hpp>` to
the module fragment — the generated C++ uses bare `int32_t` and
`rusty::detail::deref_if_pointer_like`, neither of which was reachable
through the previous includes. This is a one-time fix per migrated
file (documented in the plan doc's Quirks section).

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_errors -j32` clean
  - `build_clang21/test_rpc_errors` — 19/19 PASSED
  - `build_clang21/test_rpc_error_integration` — 10/10 PASSED

This is the first inline-Rust DSL migration in a non-basetypes file,
proving the workflow generalizes across translation units.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Migrates `is_retryable_error(RpcError) -> bool` to inline Rust DSL.
Free helper `rpc_error_is_retryable(i32) -> bool`; C++ wrapper casts
the enum to int. Public surface unchanged.

Authored as an if-chain rather than a `match`. First draft used
`match code { 102 | 103 | 104 | ... => true, _ => false }` and the
transpiler emitted ~3800 lines of runtime support code (rusty::cmp,
Option<TokenTree>, proc_macro_runtime, etc.) into the GEN block —
none of which then resolves against the surrounding `namespace rrr`
(errors: `no template named 'Option' in namespace 'rrr::rusty'`).
The if-chain lowering is the clean ~25-line path. Documented in the
plan doc as the second known transpiler quirk worth raising upstream.

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_errors -j32` clean
  - `build_clang21/test_rpc_errors` — 19/19 PASSED

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Stamps the inline-Rust pattern over two more tiny predicates:

  - `is_connection_error(err)` → free helper
    `rpc_error_is_connection_code(i32) -> bool` (range check
    100 ≤ code < 200).
  - `is_timeout_error(err)` → free helper
    `rpc_error_is_timeout_code(i32) -> bool` (range check
    400 ≤ code < 500).

Both equivalent to the previous `get_error_category(err) == X` form
since CONNECTION/TIMEOUT are precisely those code ranges in
rpc_error_category_code. C++ wrappers cast the enum to int at the
boundary so the public surface is unchanged.

is_retryable_error's existing GEN id renumbered from `errors.2` to
`errors.4` to give the two new blocks (`errors.2`, `errors.3`)
contiguous auto-ids in source order — same workaround pattern as
the SparseInt::buf_size pilot.

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_errors -j32` clean
  - `build_clang21/test_rpc_errors` — 19/19 PASSED

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tests the transpiler's multi-function-per-block lowering by merging
the two predicate helpers `rpc_error_is_connection_code` and
`rpc_error_is_timeout_code` into a single `#if RUSTYCPP_RUST ... #endif`.
Works cleanly: the transpiler emits ONE GEN block (id=errors.2) with
both forward decls at the top and both definitions below.

Net file change is a small cleanup: 4 GEN blocks → 3, since errors.3
is consumed into errors.2.

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_errors -j32` clean
  - `build_clang21/test_rpc_errors` — 19/19 PASSED

Plan-doc Phase 1 row updated to record both the initial two-block
landing (c0b2d6f) and this consolidation step.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both predicates classify the same (auto_reconnect, max_retries,
retry_count) tuple, so they're a natural fit for the multi-fn block
pattern proven in the previous errors.cpp commit. Authored as
`reconnect_should_retry(bool, u32, u32) -> bool` and
`reconnect_retries_exhausted(bool, u32, u32) -> bool` in a single
`#if RUSTYCPP_RUST` block; the transpiler emits one GEN block with
both forward decls and definitions.

Member methods on `ReconnectCalculator` are now one-line forwarders
that read the policy fields + retry counter and pass to the helpers.
Public surface unchanged.

First inline-Rust migration in a third file. Required adding
`#include <stdint.h>` and `#include <rusty/rusty.hpp>` to the module
fragment (same one-time-per-file fix documented in the plan doc).

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_reconnect_policy -j32` clean
  - `build_clang21/test_rpc_reconnect_policy` — 19/19 PASSED
  - `build_clang21/test_rpc_reconnect_integration` — 12/12 enabled
    PASSED (6 disabled tests skipped, unrelated)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… backoff

Adds a third helper `reconnect_peek_delay_ms_impl(u32, u32, f64, u32) -> u32`
to the existing multi-fn block. Lowers the deterministic exponential
backoff loop used by both `peek_delay_ms` and `next_delay_ms`:

  - First inline-Rust migration to use `f64` arithmetic.
  - First migration to use a `while` loop with `break`.
  - First migration to share one helper across two C++ call sites.

Transpiler handled the lowering cleanly (~17 lines of C++ from
the GEN block, no runtime-support spam). Documented one minor
quirk: a primitive-typed assignment like `delay = max_delay` lowers
as `delay = std::move(max_delay)` — harmless on a `double`.

`peek_delay_ms` becomes a 5-line forwarder.

`next_delay_ms` is refactored to share the deterministic backoff
through the new helper, then applies its random_device jitter on
the C++ side (random_device pulls system entropy; not a Rust-DSL
candidate yet). ~15 lines of duplicated backoff math removed.

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_reconnect_policy -j32` clean
  - `build_clang21/test_rpc_reconnect_policy` — 19/19 PASSED

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Extracts the OPEN→HALF_OPEN probe-readiness check from
`CircuitBreaker::allow_request` into a free helper
`circuit_should_probe(now_us: u64, last_failure_us: u64, timeout_ms: u32) -> bool`.
First inline-Rust migration to use `u64` parameters; transpiler
lowered cleanly.

Most of the rest of this file manipulates `rusty::Cell<T>` interior-
mutable state from C++ — the Rust DSL has no idiom to reach into
those, so the probe-readiness arithmetic is the file's only natural
candidate.

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_circuit_breaker -j32` clean
  - `build_clang21/test_rpc_circuit_breaker` — 21/21 PASSED

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Migrates three small pure methods on `RequestOptions` to a single
multi-fn `#if RUSTYCPP_RUST` block:

  - `can_retry(retry_count)` → `request_can_retry(idempotent, retry,
    max_retries)`: boolean predicate.
  - `is_total_timeout_exceeded(elapsed_ms)` →
    `request_total_timeout_exceeded(total_timeout_ms, elapsed_ms)`:
    bounds check.
  - `remaining_time_ms(elapsed_ms)` →
    `request_remaining_time_ms(total_timeout_ms, elapsed_ms)`:
    sentinel-returning arithmetic (0-value means "no limit", returns
    u64::MAX in that case).

Each member method is now a one-line forwarder.

First migration to use:
  - `u16` parameters — lowered to `uint16_t`.
  - `u64::MAX` — lowered to `std::numeric_limits<uint64_t>::max()`.

The `calculate_delay_ms` method is the obvious next candidate but
uses `std::pow` + `thread_local std::mt19937` + `random_device` —
the random side stays C++ and `std::pow(2.0, attempt)` needs a
manual loop on the Rust side. Deferred.

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_request_options -j32` clean
  - `build_clang21/test_rpc_timeout_retry` — 36/36 PASSED

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ion table

Migrates four pure-logic methods on `ConnectionStateMachine` to a
single multi-fn `#if RUSTYCPP_RUST` block:

  - `is_valid_transition(from, to)` — the 6-state transition table
    (NEW/CONNECTING/CONNECTED/DISCONNECTING/DISCONNECTED/FAILED).
    Authored as an if-chain (each `from` state mapped explicitly),
    not a `switch` or `match` — the latter trips the heavyweight
    runtime lowering documented in the plan doc's Quirks section.
  - `is_terminal(s)` — true when s ∈ {DISCONNECTED, FAILED}.
  - `can_connect(s)` — true when s ∈ {NEW, DISCONNECTED, FAILED}.
  - `is_usable(s)` — true when s ∈ {CONNECTING, CONNECTED}.

All four Rust helpers take the raw `i32` discriminant of
`ConnectionState`; the C++ member methods cast at the boundary so the
public typed-enum surface (`ConnectionState`) is unchanged.

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_connection_state -j32` clean
  - `build_clang21/test_rpc_connection_state` — 30/30 PASSED
  - `build_clang21/test_rpc_state_integration` — 16/16 PASSED

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Migrates the timing-elapsed checks inside `HeartbeatManager::
should_send_heartbeat`, `check_timeout`, and
`time_until_next_heartbeat_ms` to a single multi-fn `#if RUSTYCPP_RUST`
block:

  - `heartbeat_interval_elapsed(now_us, last_send_us, interval_ms)`
  - `heartbeat_timeout_elapsed(now_us, last_send_us, timeout_ms)`
  - `heartbeat_time_until_next_ms(now_us, last_send_us, interval_ms)`

All three are pure `u64`/`u32` math (convert `_ms` to `_us`,
compare against a `now - last` window). The C++ member methods
still own the Cell reads + enabled/timed_out/pending_pong guards;
only the deterministic arithmetic moves to Rust DSL.

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_heartbeat -j32` clean
  - `build_clang21/test_rpc_heartbeat` — 20/20 PASSED

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Migrates the derived-statistic methods on `ConnectionMetrics` to a
single multi-fn `#if RUSTYCPP_RUST` block:

  - `min_latency_us()` → `metrics_min_latency_us(stored)`:
    maps u64::MAX sentinel to 0.
  - `success_rate_percent()` → `metrics_success_rate_percent(completed, total)`:
    100 when total=0, else `(completed*100)/total`.
  - `avg_latency_us()` → `metrics_avg_latency_us(total_us, completed)`:
    0 when completed=0, else `total_us/completed`.
  - `uptime_ms(now)` → `metrics_uptime_ms(connect_time, now)`:
    0 when connect_time=0 or now<connect_time, else `now-connect_time`.

C++ member methods read the rusty::Cell counters and forward.

Verification:
  - `cmake --build build_clang21 --target rrr -j32` clean
  - `cmake --build build_clang21 --target
    borrow_check_rrr_borrow_connection_metrics -j32` clean
  - `build_clang21/test_rpc_metrics` — 25/25 PASSED

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert response_has_extended_header / response_payload_size /
encode_response_size to one multi-fn `#if RUSTYCPP_RUST` block. High
bit of the i32-encoded size marks "extended header" (response carries
<server_instance_id> after <error_code>); low 31 bits hold payload
size. C++ wrappers are now one-line forwarders; lost the `constexpr`
qualifier (now `inline`) — verified no callers use these in constexpr
contexts via grep across the rrr tree.

borrow_check_rrr_borrow_internal_protocol clean,
test_rpc_frame_codec 25/25 pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert IdempotencyKey::is_valid, CachedResponse::is_expired, and
IdempotencyCache::hit_rate to one multi-fn `#if RUSTYCPP_RUST` block.
First migration with `f64` return — `(hits as f64) / (total as f64)`
lowers cleanly. C++ member methods read struct fields / Cells and
forward.

borrow_check_rrr_borrow_idempotency clean, test_idempotency 32/32
pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert CompletedEntry::is_expired, CompletionTracker::hit_rate, and
CompletionQueryResult::is_completed to one multi-fn `#if RUSTYCPP_RUST`
block. First migration with `u8` parameter — the is_completed helper
takes the `u8` discriminant of CompletionStatus (NOT_FOUND=0..
EXPIRED=3); C++ casts at the boundary.

borrow_check_rrr_borrow_completion_tracker clean,
test_completion_tracker 27/27 pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert LoadBalancerState::next_round_robin_index and
LoadBalancer::select_random to one multi-fn `#if RUSTYCPP_RUST` block.
Both helpers take `u64`-modeled `size_t`s and assume `pool_size > 0`
— C++ wrappers cast at the boundary and own the `pool_size == 0` /
Cell read-modify-write logic.

borrow_check_rrr_borrow_load_balancer clean,
test_load_balancer 21/21 pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert RandomGenerator::rand, rand_double, and nu_rand to one
multi-fn `#if RUSTYCPP_RUST` block. Each helper takes the raw
`rand_r` output as `i32` plus the user-supplied bounds. The
rand_double helper takes RAND_MAX as a parameter to avoid the
platform-dependent constant in Rust. C++ methods retain the `@unsafe`
seed-fetch block and pass the scaled `r` into the helper.

borrow_check_rrr_borrow_rand clean, rpcbench links (no dedicated
test suite for rand).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert QueuedRequest::is_expired, QueuedRequest::age_ms, and
RequestQueue::remaining_capacity to one multi-fn `#if RUSTYCPP_RUST`
block. The first two helpers take `now_us` (fetched by the C++ caller
via `clock_monotonic_us()`) plus the struct's timestamp_us/ttl_ms;
the last takes the two `size_t`s the SpinMutex guard returned.

borrow_check_rrr_borrow_request_queue clean,
test_rpc_request_queue 30/30 pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert Timer::elapsed's two arithmetic branches to one multi-fn
`#if RUSTYCPP_RUST` block (added as basetypes.3 after the existing
SparseInt helpers). The live branch divides `now_us - begin_us` by
1e6; the stopped branch combines (sec, usec) pairs into seconds via
i64 arithmetic.

borrow_check_rrr_borrow_basetypes clean, rpcbench links (no dedicated
test suite for Timer).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add `request_calculate_delay_ms_base` as a second inline-Rust block
in request_options.cpp. Replaces `base_delay_ms * std::pow(2.0,
attempt)` with an iterative double-and-cap loop that saturates at
`max_delay_ms` instead of overflowing for large attempts. The jitter
step stays C++-side because it pulls a thread_local mt19937 sample.

borrow_check_rrr_borrow_request_options clean,
test_rpc_timeout_retry 36/36 pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert FrameHeader::total_frame_size, the payload-size validation
shared by frame_codec_write_header / frame_codec_encode_into, and
FrameStreamReader::compact_if_needed's threshold check to one
multi-fn `#if RUSTYCPP_RUST` block. total_frame_size loses its
constexpr qualifier (now a regular inline forwarder) — verified no
callers use it in constexpr contexts.

borrow_check_rrr_borrow_frame_codec clean,
test_rpc_frame_codec 25/25 pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert FrequentJob::Ready's period-elapsed check to a single-fn
`#if RUSTYCPP_RUST` block (`frequent_job_period_elapsed`). Pure u64
arithmetic — `(now - last) > period`. C++ wrapper retains the
`rrr::Time::now()` call and the `tm_last_` state mutation.

borrow_check_rrr_borrow_misc clean, rpcbench links (no dedicated
test suite for FrequentJob).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert Server::Server's instance-id mixing step to a single-fn
`#if RUSTYCPP_RUST` block (`server_mix_instance_id`). Pure u64
bit-twiddling: `(t ^ r ^ p) & i64::MAX`, force nonzero. The three
input components (timestamp, random, pid-shifted) stay C++-side
because their sources (std::chrono, std::random_device,
rusty::sys::process::getpid) sit outside the inline-Rust world.
First migration that lowers `i64::MAX as u64`.

borrow_check_rrr_borrow_server clean,
test_rpc_server_channel_binding 3/3 pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Extend the existing connection_metrics.1 inline-Rust block with two
new helpers (`metrics_new_min_latency_us`, `metrics_new_max_latency_us`)
that collapse record_request_completed's per-axis "if sample is
better, store sample" branches to a single Cell set. Block now holds
6 helpers total.

borrow_check_rrr_borrow_connection_metrics clean,
test_rpc_metrics 25/25 pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert AvgStat::sample's per-sample mutations to one multi-fn
`#if RUSTYCPP_RUST` block (`avg_stat_compute_avg`, `avg_stat_new_max`,
`avg_stat_new_min`). `n_stat` is post-increment (always >= 1), so
average division is safe. Min/max follow the same pattern as the
connection_metrics latency helpers, on `i64` instead of `u64`.

borrow_check_rrr_borrow_stat clean, rpcbench links (no dedicated
test suite for AvgStat).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert Rand::next's scaling step to a single-fn `#if RUSTYCPP_RUST`
block (`rand_next_in_range`). Pure u32 arithmetic mirroring the C++
implicit-conversion semantics: `(lower as u32) + r % ((upper - lower)
as u32)`. New block inserted in the export namespace BEFORE the
existing SparseInt/Timer impl-section helpers so the forward
declaration is visible to the in-class `Rand::next` inline body —
required renumbering existing basetypes.1/.2/.3 → .2/.3/.4 (per the
auto-id-collision workaround).

borrow_check_rrr_borrow_basetypes clean, rpcbench links.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add idempotency_key_combine_hash to the existing idempotency.1
inline-Rust block (now 4 helpers). Pure u64 FNV-1a-style mixing:
`h1 ^ (h2 * 0x9e3779b97f4a7c15)` (golden ratio constant). C++
wrapper still calls std::hash<uint64_t>{} then passes the two
hashes into the helper.

borrow_check_rrr_borrow_idempotency clean, test_idempotency 32/32
pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Extract the pure-arithmetic part of current_time_us into a new
inline-Rust block (`circuit_timespec_to_us`). Added as the new
circuit_breaker.1, with the existing circuit_should_probe renumbered
to .2 (per the auto-id-collision workaround). C++ wrapper retains
the `clock_gettime` syscall and feeds the `(sec, nsec)` pair into
the helper. File-prefixed name avoids future link-time collision if
heartbeat.cpp grows the same helper.

borrow_check_rrr_borrow_circuit_breaker clean,
test_rpc_circuit_breaker 21/21 pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
shuaimu and others added 2 commits May 28, 2026 15:00
Extract the pure-arithmetic part of heartbeat_time_us into a new
inline-Rust block (`heartbeat_timespec_to_us`). Added as new
heartbeat.1 ahead of the existing 3 timing helpers, with those
renumbered to .2. Identical body to circuit_timespec_to_us in
circuit_breaker.cpp; file-prefixed name keeps the two separate at
link time.

borrow_check_rrr_borrow_heartbeat clean,
test_rpc_heartbeat 20/20 pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Convert Timer::start/stop's us→(sec, usec) split to a new multi-fn
`#if RUSTYCPP_RUST` block (`timer_us_to_sec`,
`timer_us_to_usec_remainder`). The block is the inverse of the
existing Timer::elapsed helpers — splitting microseconds back into a
`time_t`/`suseconds_t` pair. Added as new basetypes.4 between
SparseInt and Timer::elapsed; required renumbering existing
Timer::elapsed block .4 → .5.

borrow_check_rrr_borrow_basetypes clean, rpcbench links.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@shuaimu
Copy link
Copy Markdown
Contributor Author

shuaimu commented May 28, 2026

CI investigation

The failing job is multiShardSingleProcess with rrr::Server::start: channel listener failed to bind 0.0.0.0:<port>: AddressInUse. After investigating, this is a pre-existing flake on mako-dev unrelated to this PR's inline-Rust DSL changes:

Evidence the failure pre-dates this PR

  • The most recent mako-dev push (e2a1c46c, the merge of rrr: safety + perf milestone — Marshal Vec rewrite, ClientConnection @safe class, HashMap/Weak library fixes #67) fails CI with the identical Failed to start rrr::Server on port <port> panic.
  • The last green run on mako-dev was commit f5d9d9ea multiShardSingleProcess: skip unused MultiTransportManager bind on 2026-05-21. Every commit on mako-dev since then has failed CI in this same step, including several commits that were just CI-randomization tweaks with no code changes.
  • This PR's commits modify only purely arithmetic helpers in src/rrr/* (extracting them into #if RUSTYCPP_RUST blocks whose generated C++ is byte-equivalent to the original). None of them touch Server::start, TcpListener::bind, RrrRpcBackend::Initialize, or any port / listener path.

Root cause sketch (out of scope for this PR)
The randomization in simple_transaction_rep_port_utils.sh probes base + {0, 100, 1000, 1100, 2000, 2100, 3000, 3100}, but dbtest actually binds base + 0, base + 1, …, base + warehouses-1 per shard via ShardClient(par_id) in mbta_wrapper.hh:thread_init. In multi-shard single-process mode, both shards run worker threads sharing the same global static partition_id, and BenchmarkConfig::getShardIndex() only honors the thread-local override on the shard thread — children spawned by start_workers_tpcc_shard lose that override and fall back to the global shardIndex_. So shard-1 workers can wind up creating ShardClients with shard-0's base port, which collides with shard-0's own workers (or vice versa). The visible log "first FastTransport on base+99, second on base+0" is consistent with a worker from one shard winning the bind first and a later worker from the other shard losing on the listener port.

The proper fix is in mako, not rrr — either propagate the thread-local shard index into worker threads in start_workers_tpcc_shard, or have ShardClient accept an explicit shard index, or partition partition_id per shard. I'd rather not bundle that into this rrr-only PR.

Recommendation
Merging this PR will keep the same mako-dev flake but won't worsen it. Happy to open a separate PR with the mako fix once we agree on direction.

@shuaimu
Copy link
Copy Markdown
Contributor Author

shuaimu commented May 28, 2026

Closing per reviewer feedback. The approach taken (extracting free helpers + re-wiring callers) is wrong; the correct pattern is to rewrite an existing C++ class/function in Rust DSL such that the generated code replaces it directly. Restarting.

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.

1 participant