Skip to content

fix: negative-cache property/Connect JSON fetch failures - #407

Open
JakeSCahill wants to merge 2 commits into
mainfrom
fix/json-fetch-negative-cache
Open

fix: negative-cache property/Connect JSON fetch failures#407
JakeSCahill wants to merge 2 commits into
mainfrom
fix/json-fetch-negative-cache

Conversation

@JakeSCahill

Copy link
Copy Markdown
Contributor

Problem

Two of the top 404 sources on docs.redpanda.com come from tooltip data fetches that retry a missing file on every page view:

  • 19-property-tooltips.js caches successful properties-JSON fetches for 24h in localStorage, but failures are never cached — when the referenced attachment doesn't exist (release tag drifted ahead of the generated JSON), every streaming page view fires a guaranteed 404 (~17.6k/day for redpanda-properties-v26.1.14.json).
  • 16-bloblang-interactive.js builds the Connect JSON URL from latest-connect-version and, on failure, walks a hardcoded fallback-version list — up to 6 404s per page view (~6.4k/day for connect-4.102.0.json), with no caching of failures.

Fix

  • Property tooltips: store a failed marker in the existing localStorage cache entry (1h TTL vs 24h for successes, so a fix deploy is picked up quickly; still versioned by latest-redpanda-tag). While fresh, resolve to an empty lookup without fetching.
  • Bloblang: track per-URL failure timestamps in localStorage (1h TTL); skip URLs that recently returned an error response. Only deterministic HTTP errors are marked — transient network errors still retry.
  • Preview mode (localhost / docs-ui.netlify.app) is unaffected: failures there are never marked, and property-tooltip cache reads were already skipped in preview.

This is defense-in-depth for the storm; the root cause (meta tags referencing JSON that was never generated) is fixed at build time by redpanda-data/docs-extensions-and-macros#224.

Testing

  • node --check and npx eslint on both files (only pre-existing max-len warning remains)

🤖 Generated with Claude Code

The property-tooltip script caches successful JSON fetches in
localStorage for 24 hours but never caches failures, so when the
referenced attachment does not exist (version drift between release
tags and generated JSON), every page view re-requests a URL that is
guaranteed to 404 (~17k requests/day). The Bloblang script has the same
problem, plus a hardcoded fallback-version chain that multiplies the
misses.

Property tooltips now store a failure marker (1 hour TTL, versioned by
latest-redpanda-tag) and resolve to an empty lookup while it is fresh.
The Bloblang loader tracks per-URL failures for 1 hour and skips URLs
that recently returned an error response. Preview mode is unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@netlify

netlify Bot commented Jul 28, 2026

Copy link
Copy Markdown

Deploy Preview for docs-ui ready!

Name Link
🔨 Latest commit cc3c4e8
🔍 Latest deploy log https://app.netlify.com/projects/docs-ui/deploys/6a70d7be4191700008570fdd
😎 Deploy Preview https://deploy-preview-407--docs-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 30 (🟢 up 1 from production)
Accessibility: 89 (no change from production)
Best Practices: 92 (no change from production)
SEO: 89 (no change from production)
PWA: -
View the detailed breakdown and full score reports
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: da8b819e-edca-4b50-a98a-423b73c9cd02

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The changes add localStorage-backed failure caching for Connect JSON and property JSON fetches. Connect requests skip URLs recorded as recently failed and record non-OK responses outside preview mode. Property data cache entries now distinguish failed fetches from successful results, using a one-hour failure TTL instead of the 24-hour success TTL, and failed fetches store version and timestamp metadata.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant tryFetchConnectJSON
  participant localStorage
  participant ConnectJSONEndpoint
  tryFetchConnectJSON->>localStorage: Read recent URL failures
  localStorage-->>tryFetchConnectJSON: Return failure status
  tryFetchConnectJSON->>ConnectJSONEndpoint: Fetch URL when not recently failed
  ConnectJSONEndpoint-->>tryFetchConnectJSON: Return non-OK response
  tryFetchConnectJSON->>localStorage: Record URL failure
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly names the main change: negative-caching failed property and Connect JSON fetches.
Description check ✅ Passed The description directly matches the changes, problem statement, fix, and testing for cached JSON fetch failures.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/json-fetch-negative-cache

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@micheleRP

Copy link
Copy Markdown
Contributor

Right idea — an uncacheable failure retried on every page view is the actual mechanism behind the 404 storm, and negative caching is the correct defense-in-depth alongside redpanda-data/docs-extensions-and-macros#224. The Bloblang half looks good. The property-tooltips half has a problem worth fixing before merge.

Fix before merge

The failure marker writes to the same key as the successful cache, and it's written for far more than 404s.

localStorage.setItem(
  CACHE_KEY,
  JSON.stringify({ version: cacheVersion, timestamp: Date.now(), failed: true })
)

Two things compound here:

  1. It clobbers good data. CACHE_KEY is the same entry that holds parsed.data for successful fetches, so writing the marker discards a valid 24-hour cache.
  2. It fires on transient failures. This sits in the .catch() of the fetch chain, and the chain throws new Error('HTTP ' + response.status) for any non-ok response. So the catch — and therefore the marker — is reached for a 503, a 429, a user who is briefly offline, and a malformed-JSON parse error, not just the deterministic 404 this PR is targeting.

Together that means one transient blip does this: valid 24h cache destroyed, and for the next hour every page sharing that latest-redpanda-tag resolves to propertiesData = {} — property tooltips silently dead site-wide for that user, with nothing in the UI to indicate why.

This also doesn't match the PR body, which says "Only deterministic HTTP errors are marked — transient network errors still retry." That's accurate for 16-bloblang-interactive.js (which checks !response.ok and never marks on a thrown error), but not for 19-property-tooltips.js.

Two changes fix it:

  • Store the marker under its own key (e.g. redpanda-properties-fetch-failed) so a failure can't evict cached data. The Bloblang side already does the equivalent by keying failures per URL in a separate connect-json-fetch-failures entry.
  • Mark only on statuses that won't fix themselves — 404/410, or status >= 400 && status < 500 — and let 5xx, 429, and thrown network errors retry. That needs the status plumbed into the catch, or the marking moved to where the response is still in hand.

Suggestion

Bloblang marks on any !response.ok too, which includes 429 and 5xx:

if (!response.ok) {
  if (!isPreviewMode()) markFetchFailure(url)
  return null
}

Less severe than above — the failure is scoped to one URL and can't evict anything else — but a transient CDN 5xx still disables Bloblang tooltips for an hour. Same narrowing to 404/410 applies.

Verified

  • The hasRecentFetchFailure guard sits inside the non-preview branch after the meta-tag lookup and the fallback assignment, so it covers both the primary URL and the hardcoded fallback. That's the ~6.4k/day path, and it's correctly covered.
  • Preview mode is excluded on both sides: Bloblang never marks and takes the static connect.json path; property tooltips already skipped cache reads in preview.
  • readFetchFailures prunes expired entries on every read and markFetchFailure persists the pruned set, so the failure map self-cleans and can't grow without bound.
  • Keying the failure TTL by latest-redpanda-tag means a fix that bumps the tag invalidates immediately, and the 1h-vs-24h split is a sensible asymmetry. Worth keeping.

Minor note

The Bloblang fallback URL is built as /redpanda-connect/components/_attachments/connect-${version}.json, but the site serves that content at /connect/... (head-meta.hbs uses component='connect'). Pre-existing and unrelated to this change, but it means the fallback path depends on a 301 — and with this PR that redirect chain now gets negative-cached under the pre-redirect URL. Worth a look while you're in the file.

Review findings: the failure marker shared CACHE_KEY with successful
data and was written from the generic catch, so one transient blip
(5xx, offline, parse error) wiped a valid 24h cache and left tooltips
dead for an hour for that user.

- The marker now lives under its own key (redpanda-properties-missing)
  and can never overwrite cached data. Valid data is preferred on read.
- It is written only for HTTP 404/410 (the resource does not exist for
  this version); transient failures are not cached and simply retry on
  the next page view.
- A successful load clears the marker.
@JakeSCahill

Copy link
Copy Markdown
Contributor Author

Fixed per the review: the missing-resource marker now lives under its own key (redpanda-properties-missing) so it can never clobber a valid cached dataset, it's written only when the fetch failed with HTTP 404/410 (the error now carries status), transient failures (5xx, offline, parse) aren't cached at all and retry on the next view, and a successful load clears the marker. Bloblang half untouched.

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.

2 participants