Derive Secrets Manager trial state on read
What
Cache CDot's raw GET /trials body instead of the parsed
SecretsManagerTrialResponse, and parse on read.
Why
state is derived by comparing the trial's expires_at to Time.current.
Caching the parsed object freezes that comparison for the cache TTL, so a
cached :trial keeps reporting :trial after the trial has actually ended.
Entitlement::Resolver#blocked_reason_from then labels the resulting denial
:credits_exhausted instead of :trial_expired.
Access is unaffected — permits_access? covers both :trial and :paid.
What is wrong is the reported state: GraphQL entitlement_state and denial
telemetry.
How
RawTrialPayload(raw body +cache_ttl) is what goes intoRails.cache.SecretsManagerTrialResponseis built inside theSafeRequestStoreblock, so it is parsed once per request and re-derived on the next one.derive_secrets_manager_trial_stateis unchanged and remains the only place the clock is consulted.
Re-parsing costs ~14 µs more per read than deserializing the pre-derived object, against a Redis round-trip of hundreds of µs.
Note on merge order
No cache-key version bump is needed because CDot currently sends
Cache-Control: max-age=0, so nothing is written to Rails.cache today.
That stops being true once
customers-gitlab-com!17034
ships a real max-age — this MR should merge first, otherwise the cache
fills with the old shape and the new reader breaks for up to one TTL.