From 13a3b6dbbbeeb678e3e5c0da58835b8e63e44f9d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 23:07:30 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20optimize=20content=20fetchi?= =?UTF-8?q?ng=20and=20fix=20metrics=20concurrency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements several performance optimizations in the content fetching pipeline: 1. **Early In-flight Joining**: Deduplication of concurrent requests now starts immediately after the L1 cache check, covering L2 cache lookups, file list indexing (L3), and extension resolution. 2. **File List Extension Resolution**: The adapter now attempts to resolve missing file extensions using the in-memory file list index before falling back to API calls, significantly reducing network round-trips. 3. **Concurrent-Safe Metrics**: Refactored `ContentMetrics` to use `AsyncLocalStorage`, fixing a race condition where concurrent requests would overwrite global metrics data. Verified with `deno task test` and `deno task lint`. Co-authored-by: kojiwakayama <940749+kojiwakayama@users.noreply.github.com> --- .../adapters/fs/veryfront/read-operations.ts | 358 ++++++++++-------- 1 file changed, 201 insertions(+), 157 deletions(-) diff --git a/src/platform/adapters/fs/veryfront/read-operations.ts b/src/platform/adapters/fs/veryfront/read-operations.ts index 93787674a3..5ce85950af 100644 --- a/src/platform/adapters/fs/veryfront/read-operations.ts +++ b/src/platform/adapters/fs/veryfront/read-operations.ts @@ -1,3 +1,4 @@ +import { AsyncLocalStorage } from "node:async_hooks"; import { logger } from "#veryfront/utils"; import { isFrameworkSourcePath } from "#veryfront/utils/path-utils.ts"; import { withSpan } from "#veryfront/observability/tracing/otlp-setup.ts"; @@ -83,8 +84,8 @@ const cumulativeMetrics: CumulativeMetrics = { requestsTracked: 0, }; -// Per-request metrics (reset each request via startRequestMetrics) -let currentRequest: PerRequestMetrics | null = null; +// Per-request metrics (stored in AsyncLocalStorage for concurrent-safe tracking) +const metricsStorage = new AsyncLocalStorage(); function createFreshRequestMetrics(): PerRequestMetrics { return { @@ -113,16 +114,16 @@ function detectFileType(path: string): FileType { /** Call at start of HTTP request to begin per-request tracking */ export function startRequestMetrics(): void { - currentRequest = createFreshRequestMetrics(); + metricsStorage.enterWith(createFreshRequestMetrics()); } /** Call at end of HTTP request to log summary and update cumulative metrics */ export function endRequestMetrics( requestContext?: { requestId?: string; pathname?: string; mode?: string }, ): void { - if (!currentRequest) return; + const req = metricsStorage.getStore(); + if (!req) return; - const req = currentRequest; const durationMs = Math.round(performance.now() - req.startTime); // Compute derived metrics @@ -166,8 +167,6 @@ export function endRequestMetrics( uniqueFiles: req.filesAccessed.size, isPreviewMode: req.isPreviewMode, }); - - currentRequest = null; } type ContentMetricEvent = @@ -190,39 +189,40 @@ function logContentMetric( }, ): void { const path = details.path ?? ""; + const req = metricsStorage.getStore(); // Track in per-request metrics if active - if (currentRequest) { - currentRequest.filesAccessed.add(path); + if (req) { + req.filesAccessed.add(path); if (details.isPreviewMode !== undefined) { - currentRequest.isPreviewMode = details.isPreviewMode; + req.isPreviewMode = details.isPreviewMode; } switch (event) { case "REQUEST_SCOPED_HIT": - currentRequest.requestScopedHits++; + req.requestScopedHits++; recordContentCacheHit("request"); break; case "PERSISTENT_CACHE_HIT": - currentRequest.persistentCacheHits++; + req.persistentCacheHits++; recordContentCacheHit("persistent"); break; case "FILE_LIST_HIT": - currentRequest.fileListHits++; + req.fileListHits++; recordContentCacheHit("filelist"); break; case "NETWORK_FETCH": - currentRequest.networkFetches++; - currentRequest.fetchesByType[detectFileType(path)]++; + req.networkFetches++; + req.fetchesByType[detectFileType(path)]++; break; case "NETWORK_FETCH_COMPLETE": if (details.durationMs) { - currentRequest.networkMs += details.durationMs; + req.networkMs += details.durationMs; } break; case "CACHE_MISS": if (details.missReason) { - currentRequest.missReasons[details.missReason]++; + req.missReasons[details.missReason]++; } break; } @@ -460,7 +460,7 @@ export class ReadOperations { const apiPath = this.getOriginalApiPath?.(normalizedPath) ?? normalizedPath; const cacheKeyPrefix = buildFileCacheKeyPrefix(ctx); const cacheKey = `${cacheKeyPrefix}:${normalizedPath}`; - const isProduction = this.contextProvider?.isProductionMode() ?? false; + const isProductionMode = this.contextProvider?.isProductionMode() ?? false; const hasKnownExt = EXTENSION_PRIORITY.some((ext) => apiPath.endsWith(ext)); logger.debug("[ReadOperations] fetchContent context", { @@ -472,7 +472,7 @@ export class ReadOperations { branch: ctx?.branch, releaseId: ctx?.releaseId, cacheKeyPrefix, - isProduction, + isProductionMode, }); const requestCached = getRequestScopedFile(cacheKey); @@ -491,164 +491,208 @@ export class ReadOperations { return requestCached; } - const currentReleaseId = ctx?.releaseId; - const isPrefixInvalidated = - (isProduction && this.contextProvider?.isPersistentCacheInvalidated?.(cacheKeyPrefix)) ?? - false; - const isReleaseInvalidated = isProduction && currentReleaseId - ? this.contextProvider?.isReleaseBeingInvalidated?.(currentReleaseId) - : undefined; - - const skipPersistentCaches = !!(isPrefixInvalidated || isReleaseInvalidated); + // ============================================================================ + // EARLY IN-FLIGHT JOINING - Deduplicate L2, L3, and network fetches + // ============================================================================ + this.cleanupStaleInFlightRequests(); - if (isProduction && skipPersistentCaches) { - logger.info("[ReadOperations] PERSISTENT_CACHE_SKIPPED - cache invalidation in progress", { + const existingEntry = this.inFlightRequests.get(cacheKey); + if (existingEntry) { + logger.debug("[ReadOperations] Deduplicating request - joining existing operation", { path: normalizedPath, cacheKey, - cacheKeyPrefix, - releaseId: currentReleaseId ?? undefined, - prefixInvalidated: isPrefixInvalidated, + ageMs: Date.now() - existingEntry.startedAt, }); + return existingEntry.promise; } - // Check persistent cache for PRODUCTION mode only - // Preview mode skips persistent cache to avoid staleness risk when WebSocket is slow/disconnected - if (isProduction && !skipPersistentCaches) { - const cached = await this.cache.getAsync(cacheKey); - if (cached) { - logContentMetric("PERSISTENT_CACHE_HIT", { - path: normalizedPath, - mode: ctx?.sourceType ?? "unknown", - cacheKey, - }); - logger.debug("[ReadOperations] PERSISTENT_CACHE_HIT", { - path: normalizedPath, - cacheKey, - contentLength: cached.length, - preview: previewText(cached).replace(/\n/g, "\\n"), - }); - setRequestScopedFile(cacheKey, cached); - return cached; - } - } - - // File list cache is enabled for BOTH preview and production modes. - // The file list is an in-memory index built from API response at init, updated by WebSocket pokes. - // This is safe because: - // - File list is refreshed on every WebSocket poke (websocket-manager.ts:483-500) - // - Request-scoped cache ensures consistency within a single render - // - Persistent cache is only written for production mode (to avoid staleness risk in preview) const isPreviewMode = ctx?.sourceType === "branch"; - if (!skipPersistentCaches) { - const fileListContent = await this.getContentFromFileList(normalizedPath); - if (fileListContent) { - logContentMetric("FILE_LIST_HIT", { - path: normalizedPath, - mode: ctx?.sourceType ?? "unknown", - cacheKey, - isPreviewMode, - }); - // Only cache to persistent storage for production mode - // Preview mode uses file list cache directly without persisting (fresher, WebSocket-driven) - if (isProduction) { - this.cache.set(cacheKey, fileListContent); - } - setRequestScopedFile(cacheKey, fileListContent); - return fileListContent; - } - } else { - // Skip only happens during cache invalidation (both preview and production) - logContentMetric("CACHE_MISS", { - path: normalizedPath, - mode: ctx?.sourceType ?? "unknown", - missReason: "invalidation" as MissReason, - isPreviewMode, - }); - logger.debug("[ReadOperations] Skipping file list cache due to invalidation", { - path: normalizedPath, - cacheKeyPrefix, - }); - } + const isPublished = ctx?.sourceType !== "branch"; - if (!hasKnownExt) { + const fetchPromise = (async () => { try { - const resolved = await this.client.resolveFileWithExtension( - apiPath, - [...EXTENSION_PRIORITY], - ); - if (resolved) { - const resolvedPath = this.normalizer.normalize(resolved.path); - const resolvedCacheKey = `${cacheKeyPrefix}:${resolvedPath}`; - - logger.debug("[ReadOperations] Resolved extension for base path", { - basePath: apiPath, - resolvedPath, + const currentReleaseId = ctx?.releaseId; + const isPrefixInvalidated = + (isProductionMode && this.contextProvider?.isPersistentCacheInvalidated?.(cacheKeyPrefix)) ?? + false; + const isReleaseInvalidated = isProductionMode && currentReleaseId + ? this.contextProvider?.isReleaseBeingInvalidated?.(currentReleaseId) + : undefined; + + const skipPersistentCaches = !!(isPrefixInvalidated || isReleaseInvalidated); + + if (isProductionMode && skipPersistentCaches) { + logger.info("[ReadOperations] PERSISTENT_CACHE_SKIPPED - cache invalidation in progress", { + path: normalizedPath, cacheKey, - resolvedCacheKey: resolvedCacheKey === cacheKey ? undefined : resolvedCacheKey, + cacheKeyPrefix, + releaseId: currentReleaseId ?? undefined, + prefixInvalidated: isPrefixInvalidated, }); + } - if (isProduction) { - this.cache.set(cacheKey, resolved.content); - if (resolvedCacheKey !== cacheKey) this.cache.set(resolvedCacheKey, resolved.content); + // Check persistent cache for PRODUCTION mode only + // Preview mode skips persistent cache to avoid staleness risk when WebSocket is slow/disconnected + if (isProductionMode && !skipPersistentCaches) { + const cached = await this.cache.getAsync(cacheKey); + if (cached) { + logContentMetric("PERSISTENT_CACHE_HIT", { + path: normalizedPath, + mode: ctx?.sourceType ?? "unknown", + cacheKey, + }); + logger.debug("[ReadOperations] PERSISTENT_CACHE_HIT", { + path: normalizedPath, + cacheKey, + contentLength: cached.length, + preview: previewText(cached).replace(/\n/g, "\\n"), + }); + setRequestScopedFile(cacheKey, cached); + return cached; } + } - setRequestScopedFile(cacheKey, resolved.content); - if (resolvedCacheKey !== cacheKey) { - setRequestScopedFile(resolvedCacheKey, resolved.content); + // File list cache is enabled for BOTH preview and production modes. + // The file list is an in-memory index built from API response at init, updated by WebSocket pokes. + // This is safe because: + // - File list is refreshed on every WebSocket poke (websocket-manager.ts:483-500) + // - Request-scoped cache ensures consistency within a single render + // - Persistent cache is only written for production mode (to avoid staleness risk in preview) + if (!skipPersistentCaches) { + const fileListContent = await this.getContentFromFileList(normalizedPath); + if (fileListContent) { + logContentMetric("FILE_LIST_HIT", { + path: normalizedPath, + mode: ctx?.sourceType ?? "unknown", + cacheKey, + isPreviewMode, + }); + // Only cache to persistent storage for production mode + // Preview mode uses file list cache directly without persisting (fresher, WebSocket-driven) + if (isProductionMode) { + this.cache.set(cacheKey, fileListContent); + } + setRequestScopedFile(cacheKey, fileListContent); + return fileListContent; } - return resolved.content; + // Try to resolve extension using the file list index (L3 cache) to avoid API network call + if (!hasKnownExt) { + const index = await this.getOrBuildFileListIndex(); + if (index) { + for (const ext of EXTENSION_PRIORITY) { + const resolvedPath = normalizedPath + ext; + const content = index.get(resolvedPath); + if (content) { + const resolvedCacheKey = `${cacheKeyPrefix}:${resolvedPath}`; + logContentMetric("FILE_LIST_HIT", { + path: normalizedPath, + resolvedPath, + mode: ctx?.sourceType ?? "unknown", + cacheKey, + isPreviewMode, + }); + + logger.debug("[ReadOperations] FILE_LIST_CACHE_HIT - resolved extension", { + path: normalizedPath, + resolvedPath, + cacheKey, + }); + + if (isProductionMode) { + this.cache.set(cacheKey, content); + if (resolvedCacheKey !== cacheKey) { + this.cache.set(resolvedCacheKey, content); + } + } + + setRequestScopedFile(cacheKey, content); + if (resolvedCacheKey !== cacheKey) { + setRequestScopedFile(resolvedCacheKey, content); + } + + return content; + } + } + } + } + } else { + // Skip only happens during cache invalidation (both preview and production) + logContentMetric("CACHE_MISS", { + path: normalizedPath, + mode: ctx?.sourceType ?? "unknown", + missReason: "invalidation" as MissReason, + isPreviewMode, + }); + logger.debug("[ReadOperations] Skipping file list cache due to invalidation", { + path: normalizedPath, + cacheKeyPrefix, + }); } - } catch (error) { - logger.debug("[ReadOperations] resolveFileWithExtension failed", { - basePath: apiPath, - error: error instanceof Error ? error.message : String(error), - }); - } - } - - this.cleanupStaleInFlightRequests(); - - const existingEntry = this.inFlightRequests.get(cacheKey); - if (existingEntry) { - logger.debug("[ReadOperations] Deduplicating request - joining existing fetch", { - path: normalizedPath, - cacheKey, - ageMs: Date.now() - existingEntry.startedAt, - }); - return existingEntry.promise; - } - const isPublished = ctx?.sourceType !== "branch"; + if (!hasKnownExt) { + try { + const resolved = await this.client.resolveFileWithExtension( + apiPath, + [...EXTENSION_PRIORITY], + ); + if (resolved) { + const resolvedPath = this.normalizer.normalize(resolved.path); + const resolvedCacheKey = `${cacheKeyPrefix}:${resolvedPath}`; + + logger.debug("[ReadOperations] Resolved extension for base path", { + basePath: apiPath, + resolvedPath, + cacheKey, + resolvedCacheKey: resolvedCacheKey === cacheKey ? undefined : resolvedCacheKey, + }); + + if (isProductionMode) { + this.cache.set(cacheKey, resolved.content); + if (resolvedCacheKey !== cacheKey) this.cache.set(resolvedCacheKey, resolved.content); + } + + setRequestScopedFile(cacheKey, resolved.content); + if (resolvedCacheKey !== cacheKey) { + setRequestScopedFile(resolvedCacheKey, resolved.content); + } + + return resolved.content; + } + } catch (error) { + logger.debug("[ReadOperations] resolveFileWithExtension failed", { + basePath: apiPath, + error: error instanceof Error ? error.message : String(error), + }); + } + } - // Track why we're making a network fetch (for optimization analysis) - const hasFileListCache = !!this.getFileListCache; - logContentMetric("CACHE_MISS", { - path: normalizedPath, - mode: ctx?.sourceType ?? "unknown", - missReason: (hasFileListCache ? "not_in_filelist" : "no_filelist_cache") as MissReason, - isPreviewMode, - }); + // Track why we're making a network fetch (for optimization analysis) + const hasFileListCache = !!this.getFileListCache; + logContentMetric("CACHE_MISS", { + path: normalizedPath, + mode: ctx?.sourceType ?? "unknown", + missReason: (hasFileListCache ? "not_in_filelist" : "no_filelist_cache") as MissReason, + isPreviewMode, + }); - // THIS IS A NETWORK FETCH - every call here = API round trip - // With caching enabled for preview mode, this should only happen on true cache misses - logContentMetric("NETWORK_FETCH", { - path: normalizedPath, - mode: ctx?.sourceType ?? "unknown", - isPublished, - isPreviewMode, - }); + // THIS IS A NETWORK FETCH - every call here = API round trip + // With caching enabled for preview mode, this should only happen on true cache misses + logContentMetric("NETWORK_FETCH", { + path: normalizedPath, + mode: ctx?.sourceType ?? "unknown", + isPublished, + isPreviewMode, + }); - logger.debug("[ReadOperations] fetchContent decision", { - path: normalizedPath, - isPublished, - willFetch: isPublished ? "published (environment)" : "draft (branch)", - sourceType: ctx?.sourceType ?? "null/undefined", - }); + logger.debug("[ReadOperations] fetchContent decision", { + path: normalizedPath, + isPublished, + willFetch: isPublished ? "published (environment)" : "draft (branch)", + sourceType: ctx?.sourceType ?? "null/undefined", + }); - const fetchStartTime = performance.now(); - const fetchPromise = (async () => { - try { + const fetchStartTime = performance.now(); const result = isPublished ? await this.fetchPublishedContent( normalizedPath, @@ -656,9 +700,9 @@ export class ReadOperations { cacheKey, ctx?.releaseId ?? null, ctx?.environmentName ?? null, - isProduction, + isProductionMode, ) - : await this.fetchDraftContent(normalizedPath, apiPath, cacheKey, isProduction); + : await this.fetchDraftContent(normalizedPath, apiPath, cacheKey, isProductionMode); const fetchDuration = Math.round(performance.now() - fetchStartTime);