mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: report agent runtime hours usage in entitlements (#27985)
Populate `FeatureAgentRuntimeHours.Actual` on every entitlements refresh for licenses that grant the feature. A new `GetTotalUsageHBAgentRuntimeV1` query sums `runtime_ms` over the license's usage period, reading `usage_events` directly: `hb_agent_runtime_v1` is exactly one row per hourly bucket deployment-wide with `created_at` at the bucket start, enforced by the unique partial index introduced in #27983. The measurement reuses the shared `measureUsage` policy from #27984 through a new `AgentRuntimeMsFn` closure (usage publisher subject): failures publish the stable `LicenseAgentRuntimeUsageUnavailableErrorText` and log the cause. Usage is floored to whole hours, matching the unit of the `agent_runtime_hours_*` claims, and at most one warning is emitted per refresh: reaching the allocation supersedes the advisory soft limit. The dashboard renders the soft-limit advisory muted without a sales link and treats the runtime usage-unavailable text as a diagnostic. **Precise usage.** `Feature.ActualMs` (JSON `actual_ms`), set only for `agent_runtime_hours`, carries the exact stored milliseconds backing the floored `Actual` so clients can render fractional hours (e.g. `10.3`). It has the same freshness as `Actual`; the whole-hour warning thresholds are unchanged. **Unlimited licenses.** A license minted with the unlimited (`-1`) allocation decodes to an enabled feature with a nil `Limit` (#27984), so the warning write-back now guards the allocation dereference: no thresholds can exist for an unlimited license, so no runtime hours warning is ever emitted, while `Actual` is still measured and published. `Feature.Compare` is unchanged; for usage-period features the issued-at/end dates decide first, so a metered feature outranks an unlimited one only on an exact timestamp tie, an edge pinned by a `TestFeatureComparison` case and documented on `decodeAgentRuntimeHours`. **Grandfathered premium licenses.** Premium licenses without `agent_runtime_hours_*` claims are now granted the feature disabled with a zero limit over the license term, identical to an explicit `allocation: 0`: usage is measured and published for every Premium deployment, and chatd's pooled admission (#27902) caps concurrent agentic chats until a license with a positive allocation is added. The default carries a fixed early `UsagePeriod.IssuedAt` (2026-08-01, the same mechanism as the managed-agents default) so any license actually carrying the claims outranks it in the `AddFeature` merge regardless of the licenses' relative issue dates; the constant must stay earlier than the earliest legitimately issued claim-bearing license. Zero allocations (explicit or grandfathered) emit no deployment-wide warning banner: those deployments are steered by the in-page upgrade CTA and the concurrency cap. Enterprise licenses are unchanged. Part 3 of a 3-PR stack splitting up #27796 (see there for review history). Stack: #27983 → #27984 → this PR. Closes CODAGT-852.
This commit is contained in:
@@ -117,3 +117,28 @@ WHERE
|
||||
-- Parentheses are necessary to avoid sqlc from generating an extra
|
||||
-- argument.
|
||||
AND day BETWEEN date_trunc('day', (@start_date::timestamptz) AT TIME ZONE 'UTC')::date AND date_trunc('day', (@end_date::timestamptz) AT TIME ZONE 'UTC')::date;
|
||||
|
||||
-- name: GetTotalUsageHBAgentRuntimeV1 :one
|
||||
-- Gets the total Coder Agent runtime in milliseconds between two timestamps.
|
||||
-- The start bound is inclusive and the end bound is exclusive.
|
||||
--
|
||||
-- Unlike GetTotalUsageDCManagedAgentsV1 this reads usage_events directly
|
||||
-- rather than the usage_events_daily rollup: hb_agent_runtime_v1 is exactly
|
||||
-- one row per hourly bucket deployment-wide, with created_at at the bucket
|
||||
-- start, enforced by the unique partial index
|
||||
-- idx_usage_events_agent_runtime (which also keeps SUM from counting a
|
||||
-- bucket twice and serves this query). The result is bucket-granular: a
|
||||
-- bucket counts entirely against the period containing its start. See
|
||||
-- enterprise/coderd/usage/generator.go for what a bucket holds. If a
|
||||
-- usage_events retention policy ever lands, this must move to the daily
|
||||
-- rollup and accept day-granularity bounds.
|
||||
SELECT
|
||||
-- The first cast is necessary since you can't sum strings, and the second
|
||||
-- cast is necessary to make sqlc happy.
|
||||
COALESCE(SUM((event_data->>'runtime_ms')::bigint), 0)::bigint AS total_runtime_ms
|
||||
FROM
|
||||
usage_events
|
||||
WHERE
|
||||
event_type = 'hb_agent_runtime_v1'
|
||||
AND created_at >= @start_time::timestamptz
|
||||
AND created_at < @end_time::timestamptz;
|
||||
|
||||
Reference in New Issue
Block a user