Skip to content

add credit leases, reservations, and preflight checks - #65

Open
bpapillon wants to merge 19 commits into
mainfrom
bpapillon/sch-7510-schematic-ruby-credit-leases-reservations-and-preflight
Open

bpapillon wants to merge 19 commits into
mainfrom
bpapillon/sch-7510-schematic-ruby-credit-leases-reservations-and-preflight

Conversation

@bpapillon

@bpapillon bpapillon commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Brings the Ruby SDK to parity with Node, Go, and Python on credit leases and reservations.

check with a usage now reserves credits: in client mode against a lease held locally (in memory, or in Redis so a fleet shares one lease), in server mode with one check-and-reserve call. track_with_reservation settles the reservation and release refunds an unused one. The plain check takes usage, event_usage, and credit_cost preflight options. Identify can prewarm a lease.

The Redis key layout and Lua scripts are Node's, byte for byte, so a mixed-language fleet shares leases. conformance/ is copied verbatim from schematic-node and the runner passes every vector on both the in-memory and Redis stores. Extend and check-and-reserve each send a fresh idempotency key per call and keep the client's default retries. Acquire sends none: the API has no such field and returns the slot's active lease on a repeat.

New code lives under lib/schematic/credits/leases/, which is in .fernignore. This also lists test/rules_engine_clock_test.rb there; it is hand-written and a regen would have deleted it.

@bpapillon
bpapillon force-pushed the bpapillon/sch-7510-schematic-ruby-credit-leases-reservations-and-preflight branch from 56a6003 to 8642f6a Compare September 17, 2026 18:16
@bpapillon
bpapillon changed the base branch from fern-bot/2026-09-17_06-09-26_193 to main September 17, 2026 18:16
A client-mode fallback honours the caller's default value, a zero
prewarm timeout reads the cache instead of skipping, fractional
quantities round up on integer wire fields instead of truncating, and
the check result's entitlement has one shape in both modes. The README
says the per-check timeout is not applied, because the generated
transport ignores it.
Only custom.test.rb ran, so the lease, conformance, and rules engine
clock tests never ran on a pull request. Rake's glob misses
custom.test.rb, so both steps stay.
The prewarm flush moves onto the background thread so identify stays
a buffer push, drain recomputes its budget per thread so the total
wait is bounded, a dropped lease takes its mutex with it, and the
REST flag check sends the preflight and skips the flag cache for it.
A zero usage has no effect on the API's verdict, but sending it as a
preflight made every such check skip the flag cache.
An unrecognised failure mode or lease mode warns and takes the safe
default instead of silently reading as fail-open or auto. Local
release shares the shutdown budget, and the sweeper is joined before
it is killed so a claim in progress still refunds the lease.
…config

Prewarm reads the cache when the socket is down and caps its wait at
the resolve budget instead of the transport's 30 seconds. A reservation
is sized from the rounded-up usage so it matches what the settle
bills, wire amounts round up, a check spawns an extend thread only
when one is due, stop takes the flight lock, and bad knobs raise.
A lease that lands after stop is left to the drain or server expiry,
as in node; releasing it refunded a lease sibling processes share. Hold
sizing and the settle debit go back to the spec's usage times rate.
Also validates plain-path usage, rejects infinite knobs, spawns no
extend on a store error, and runs every test file explicitly in CI.
check sized the hold with a bare ceil while the track event's quantity
went through wire_quantity, which shaves float noise first. A usage of
3.0000000000000004 debited the lease for four units and billed three,
against the invariant both comments state and node holds by using one
rounding everywhere (schematic-node src/credits/check.ts:221 and
src/credits/track.ts:47, 75).
check_flag_with_entitlement built get_default for the offline, API and
error branches but the datastream branch reached past it to the
registered default, so default_value was ignored on exactly the path
that answers most checks. engine_options rounds through wire_quantity
too, so a preflight passed straight in is asked the same question a
lease check asks.
The generated gemspec drops test/ but not conformance/, so 148K of
cross-SDK fixtures shipped to every install. schematic-node's npm
allowlist already excludes them.
The expiry index is the only way the sweeper reaches a surviving
byCredit field, so dropping it first and then failing on the field left
the field orphaned and reserved_credits reading high for that tenant for
good. Matches schematic-node 923dee5.
The plain check stands in the caller's default for the same nil, so the
two branches read it differently and only one said why. Matches
schematic-node src/credits/check.ts:364.
The check ran after the event buffer's flush thread and the DataStream
socket were already up, so a rejected knob raised out of the constructor
and leaked both: the caller never gets a client to close. Matches
schematic-csharp 94d772c.
@bpapillon bpapillon self-assigned this Sep 21, 2026
@bpapillon
bpapillon requested a review from a team September 21, 2026 22:33
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