Repository navigation
feat(runtime): add freshness-gated persisted run state gauges (issue #331) - #361
Merged
yuluo-yx merged 1 commit intoNov 8, 2026
Conversation
…gentic-ai-java#331) - PersistedRunStateReader: the bounded aggregate read contract — one query (namespace scope plus states of interest) answered with per-state counts computed by the adapter's own storage; the core never scans a table and never opens a database connection - PersistedRunStateSnapshot: per-state stock, collectedAt advanced only by a successful collection, and an explicit stale marker instead of fabricated zeros - FreshnessGatedRunStateAggregator: success advances the snapshot, failure keeps the last known values and marks them stale since the first outage; a fresh process has no snapshot at all, so a restart cannot present process counters or zeros as persisted stock - MemoryRunStore doubles as the reference reader; multi-replica semantics are documented and tested: shared-store instances observe one snapshot (never summed), sharded instances own their namespace and the consumer declares the cross-namespace sum - labels stay bounded: RunState values only, run/tenant identifiers are scope parameters per issue agentic-ai-java#318
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Describe what this PR does / why we need it
Issue #331 asks for persisted-run state gauges with collection freshness: an authorized read of the persisted Run/Task stock, delivered together with the time it was collected and an explicit staleness marker. This PR adds that as a contract plus a reference implementation plus contract tests, entirely inside
argi-runtime-core's newrun/metrics/package — no micrometer, no JDBC, no Redis, no table scans in the core. Storage implementations stay on the adapter side, per the persistence-ownership boundary.Three kinds of measurement, kept apart
The package javadoc states the separation the issue requires. A process transition counter counts what this JVM did and dies with the process (issue #317's scope). An audit history is the persisted journal of every transition. The persisted state gauge delivered here is the current stock per
RunStateas read from the persistence — and a freshly built aggregator has no snapshot at all (current()is empty until the first successful collection), so a restarted process can never present local counters or zeros as persisted stock.The reading contract
PersistedRunStateReaderanswers onePersistedRunStateQuery— aRunNamespacescope plus the states of interest — with per-state counts computed by the adapter's own storage. The contract is aggregate-only: implementations never hand out row lists for the caller to count, and the core never opens a database connection. The namespace is the aggregation scope; run and tenant identifiers are scope parameters, never metric dimensions, keeping gauge cardinality bounded per issue #318.Freshness gating
FreshnessGatedRunStateAggregatorwraps any reader. A successfulrefresh()advances the snapshot and itscollectedAt. A failed one keeps the last known values and marks them stale since the first failure of the outage — the counts are the previous ones, never fabricated zeros, so a collection outage cannot disguise a real backlog as an empty store. A first failure with no previous snapshot propagates instead of inventing an empty one.Multi-replica semantics
Two instances sharing one store observe the same snapshot: summing them would double every run, and the package javadoc plus a test state that a shared-store global view must not be summed. A sharded deployment instead lets each instance own its namespace, and the consumer declares the cross-namespace sum as the global aggregate — also covered by a test.
Vocabulary note
The neutral
RunStatehas noCREATED; the issue's "CREATED → RUNNING → terminal" maps toPENDING → RUNNING → terminal, and the lifecycle test follows the real vocabulary.Testing
PersistedRunStateReaderContractTestis abstract so durable adapters inherit the same four cases with their own reader (lifecycle stock, state-subset filtering, empty-store zeros, namespace as scope);MemoryRunStoreReaderTestruns it against the reference store.FreshnessGatedRunStateAggregatorTestadds 8 clock-driven cases covering the freshness and multi-replica semantics above. The full argi-runtime-core module is 139/139 green, checkstyle clean, and the downstream framework regression shows zero failures.Fixes #331