Skip to content

fix(core): revalidate the models.dev catalog with its ETag - #58813

Merged
rekram1-node merged 2 commits into
devfrom
models-dev-etag
Oct 25, 2026
Merged

rekram1-node merged 2 commits into
devfrom
models-dev-etag

Conversation

@rekram1-node

@rekram1-node rekram1-node commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The models.dev refresh runs every 5 minutes. Each tick reads and parses the ~6 MB cached catalog from KV just to check its age, then downloads the whole catalog (about 6 MB raw, ~413 KB brotli) and hashes it, even though models.opencode.ai serves an ETag and answers revalidation with an empty 304. When the body matched the cache it returned early without recording the check, so the next process or tick never saw it as fresh.

Change

I moved the freshness info (updatedAt, digest, etag) into its own small KV key next to the catalog, and the refresh tick only reads that key.

 refresh()
-  read + parse 6 MB catalog entry   (age + digest)
-  GET api.json                      -> 200, full body
-  same digest?  return              (check not recorded)
+  read ~150 B meta entry            (age + digest + etag)
+  GET api.json  If-None-Match: <etag>
+  304 or same digest?  write meta only, publish nothing
   otherwise     decode, write catalog + meta, invalidate, publish Refreshed
  • The catalog entry keeps its current shape, so get() and older builds read it as before. It's only written when the catalog actually changes.
  • The meta key is written after the catalog write succeeds, so a backend that rejects the big value (Durable Objects) never ends up with an ETag pointing at a catalog it doesn't have.
  • If there's no meta key yet (entries from older builds), it falls back to updatedAt/digest from the catalog entry once.
  • Removing a corrupt catalog entry also removes its meta key.
  • Forced refreshes skip If-None-Match, and responses without an ETag behave like before.

No migration and no public schema change.

Out of scope

Durable Object deployments still can't cache the catalog at all (it's over their 2 MB value limit), so they keep fetching the full body.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I ran it against the live models.opencode.ai. On the base, both the forced and the stale refresh downloaded the full catalog and the entry stayed stale. With this change, the stale refresh sent the stored ETag, got a 304 (which the retry layer doesn't retry), and the entry's updatedAt was updated. test/models.test.ts passes, and typecheck:tsc is clean (the typecheck script's bun check isn't available in this Bun).

Public API: unchanged. The ETag is an optional field inside the internal KV cache entry, and older builds ignore it when reading.

No blocking concerns.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new commit looks good. I re-ran it against the live models.opencode.ai. The forced refresh stored the ETag in the meta entry. A stale refresh then got a 304, the meta entry's updatedAt was refreshed, and the catalog entry was left untouched. test/models.test.ts passes, and typecheck:tsc is clean.

Public API: unchanged. The extra :meta KV key is internal, and older builds still read the catalog entry as before.

No new concerns.

@rekram1-node
rekram1-node merged commit 0b7f4c4 into dev Oct 25, 2026
21 of 22 checks passed
@rekram1-node
rekram1-node deleted the models-dev-etag branch October 25, 2026 04:34
@amrkmn amrkmn mentioned this pull request Oct 27, 2026
1 task
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