diff --git a/src/js/16-bloblang-interactive.js b/src/js/16-bloblang-interactive.js index 6aceb4fc..81986159 100644 --- a/src/js/16-bloblang-interactive.js +++ b/src/js/16-bloblang-interactive.js @@ -72,6 +72,42 @@ ); } + /** + * Track Connect JSON URLs that recently returned an error response, so a + * URL that is known to 404 (for example, a version whose JSON was never + * generated) is not re-requested on every page view. + */ + const FETCH_FAILURE_KEY = 'connect-json-fetch-failures'; + const FETCH_FAILURE_TTL = 60 * 60 * 1000; // 1 hour + + function readFetchFailures() { + try { + const parsed = JSON.parse(localStorage.getItem(FETCH_FAILURE_KEY) || '{}'); + const now = Date.now(); + const fresh = {}; + Object.keys(parsed).forEach((url) => { + if (now - parsed[url] < FETCH_FAILURE_TTL) fresh[url] = parsed[url]; + }); + return fresh; + } catch (e) { + return {}; + } + } + + function hasRecentFetchFailure(url) { + return url in readFetchFailures(); + } + + function markFetchFailure(url) { + try { + const failures = readFetchFailures(); + failures[url] = Date.now(); + localStorage.setItem(FETCH_FAILURE_KEY, JSON.stringify(failures)); + } catch (e) { + // localStorage not available + } + } + /** * Parse a Bloblang snippet into mapping, input, and metadata sections. * Looks for # In: and # Meta: comment directives. @@ -196,11 +232,18 @@ if (!url) { url = `/redpanda-connect/components/_attachments/connect-${version}.json`; } + + if (hasRecentFetchFailure(url)) { + return null; + } } const response = await fetch(url); if (!response.ok) { + if (!isPreviewMode()) { + markFetchFailure(url); + } return null; } diff --git a/src/js/19-property-tooltips.js b/src/js/19-property-tooltips.js index e447c588..ef24a0ef 100644 --- a/src/js/19-property-tooltips.js +++ b/src/js/19-property-tooltips.js @@ -92,6 +92,7 @@ var CACHE_KEY = 'redpanda-properties-cache' var CACHE_TTL = 24 * 60 * 60 * 1000 // 24 hours + var FAILURE_CACHE_TTL = 60 * 60 * 1000 // 1 hour: retry failed fetches sooner so a fix deploy is picked up // Use latest-redpanda-tag meta tag for cache versioning var cacheVersion = getLatestRedpandaTag() || 'unknown' @@ -101,8 +102,11 @@ var cached = localStorage.getItem(CACHE_KEY) if (cached) { var parsed = JSON.parse(cached) - if (parsed.version === cacheVersion && Date.now() - parsed.timestamp < CACHE_TTL) { - propertiesData = parsed.data + var age = Date.now() - parsed.timestamp + if (parsed.version === cacheVersion && (parsed.failed ? age < FAILURE_CACHE_TTL : age < CACHE_TTL)) { + // failed entries resolve to an empty lookup so a URL known to 404 + // is not re-requested on every page view + propertiesData = parsed.failed ? {} : parsed.data propertiesLoading = false propertiesLoadQueue.forEach(function (resolve) { resolve(propertiesData) @@ -182,6 +186,20 @@ } console.warn('Property tooltips: Failed to load properties data:', error) + if (!isPreviewMode()) { + try { + localStorage.setItem( + CACHE_KEY, + JSON.stringify({ + version: cacheVersion, + timestamp: Date.now(), + failed: true, + }) + ) + } catch (cacheError) { + // localStorage full or unavailable + } + } propertiesLoading = false propertiesData = {} propertiesLoadQueue.forEach(function (resolve) {