Skip to content

yesod-core: compute max-expires per call instead of a 24h worker thread - #1923

Draft
parsonsmatt wants to merge 1 commit into
yesodweb:masterfrom
MercuryTechnologies:mattp/max-expires-no-worker-thread
Draft

parsonsmatt wants to merge 1 commit into
yesodweb:masterfrom
MercuryTechnologies:mattp/max-expires-no-worker-thread

Conversation

@parsonsmatt

Copy link
Copy Markdown
Collaborator

Summary

getGetMaxExpires cached its value behind a mkAutoUpdate worker on a 24-hour refresh cycle. Because the refresh interval is a full day, the worker thread lingers for up to 24h after its last use before it can notice it's idle and exit.

Constructing a YesodRunnerEnv per request — which test harnesses like hspec-yesod do — therefore accumulates dormant worker threads proportional to the request count. (The session date cacher and logger time cache don't have this problem: their mkAutoUpdate workers self-terminate quickly because their refresh intervals are seconds, which is also why clientSessionDateCacher's "closer" is already a documented no-op.)

The cached value is just formatTime of now + 1 year — a single cheap call. Caching it behind a thread is a premature optimization; runFakeHandler already computes it inline. This PR recomputes it on each call and drops the worker entirely.

getGetMaxExpires :: IO (IO Text)
getGetMaxExpires = pure getCurrentMaxExpiresRFC1123   -- was: mkAutoUpdate { ...updateFreq = 24h... }

Compatibility

No API changes — patch release (1.7.0.0 → 1.7.0.1):

  • getGetMaxExpires :: IO (IO Text) — signature unchanged.
  • yreGetMaxExpires field — unchanged.
  • Both YesodRunnerEnv construction sites (mkYesodRunnerEnv, toWaiAppLogger) and any direct caller benefit automatically.
  • Only other change: dropped the now-unused Control.AutoUpdate import in Dispatch.hs (the dependency stays — clientSessionDateCacher still uses it).

Behavior change: the max-expires timestamp now advances continuously instead of jumping once per day. It's a far-future (+1yr) value used for cache/expiry headers, so this has no practical effect.

Testing

cabal build yesod-core compiles clean. This unblocks per-request YesodRunnerEnv construction in test harnesses without leaking worker threads.

🤖 Generated with Claude Code

getGetMaxExpires cached its value (a far-future RFC1123 timestamp) behind
a mkAutoUpdate worker on a 24-hour refresh cycle. Because the refresh
interval is a full day, the worker thread lingers for up to 24h after its
last use before it can notice it is idle and exit. Constructing a
YesodRunnerEnv per request (as test harnesses such as hspec-yesod do)
therefore piles up dormant worker threads proportional to the request
count.

The value is a single formatTime of (now + 1 year), so caching it behind
a thread is a premature optimization; runFakeHandler already computes it
inline. Recompute it on each call and drop the worker entirely.

No API changes: getGetMaxExpires keeps its 'IO (IO Text)' type and the
yreGetMaxExpires field is unchanged, so this is a patch release (1.7.0.1).
Both YesodRunnerEnv construction sites (mkYesodRunnerEnv, toWaiAppLogger)
benefit, as does any direct caller.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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