Skip to content

Conversation

@pieh
Copy link
Contributor

@pieh pieh commented Oct 29, 2025

Description

This introduces usage of conditional blob get requests if we fetched or stored given blob key on previous requests in given lambda instance. This allows to avoid fetching blob data if blob didn't change since last time given lambda.

This builds on top of memoization we already do within same request introduced in #2777

It does add global blobs map with weak refs to entries stored in our current LRU cache as a mechanism to offload memory management to what we already have and not having to do complex tracking of when global entries should be disposed.

Existing tests for memoized storage were updated and expanded to tests conditional blob gets scenario

@github-actions
Copy link
Contributor

github-actions bot commented Oct 29, 2025

📊 Package size report   0.08%↑

File Before (Size / Gzip) After (Size / Gzip)
dist/run/storage/request-scoped-in-memory-cache.cjs 47.4 kB / 10.9 kB 3%↑48.8 kB / 3%↑11.3 kB
dist/run/storage/storage.cjs 4.0 kB / 1.3 kB 24%↑5.0 kB / 18%↑1.6 kB
Total (Includes all files) 2.9 MB / 823.7 kB 0.08%↑2.9 MB / 0.07%↑824.3 kB
Tarball size 781.3 kB 0.07%↑781.9 kB
Unchanged files
File Size (Size / Gzip)
dist/build/advanced-api-routes.js 4.3 kB / 1.4 kB
dist/build/cache.js 1.0 kB / 416 B
dist/build/content/next-shims/telemetry-storage.cjs 1.6 kB / 659 B
dist/build/content/prerendered.js 11.1 kB / 3.1 kB
dist/build/content/server.js 11.9 kB / 3.6 kB
dist/build/content/static.js 4.2 kB / 1.4 kB
dist/build/functions/edge.js 24.9 kB / 6.6 kB
dist/build/functions/server.js 4.9 kB / 1.6 kB
dist/build/image-cdn.js 54.0 kB / 11.1 kB
dist/build/plugin-context.js 12.4 kB / 3.6 kB
dist/build/skew-protection.js 4.1 kB / 1.3 kB
dist/build/templates/handler-monorepo.tmpl.js 1.6 kB / 648 B
dist/build/templates/handler.tmpl.js 1.5 kB / 601 B
dist/build/verification.js 4.5 kB / 1.5 kB
dist/esm-chunks/chunk-5V5HA6YA.js 27.0 kB / 5.4 kB
dist/esm-chunks/chunk-6BT4RYQJ.js 1.9 kB / 862 B
dist/esm-chunks/chunk-TVEBGDAB.js 63.1 kB / 11.3 kB
dist/esm-chunks/chunk-YUXQHOYO.js 187.9 kB / 33.2 kB
dist/index.js 4.1 kB / 1.2 kB
dist/run/config.js 1.4 kB / 654 B
dist/run/constants.js 526 B / 319 B
dist/run/handlers/cache.cjs 16.5 kB / 4.0 kB
dist/run/handlers/request-context.cjs 5.3 kB / 1.7 kB
dist/run/handlers/server.js 142.7 kB / 33.5 kB
dist/run/handlers/tags-handler.cjs 8.9 kB / 2.8 kB
dist/run/handlers/tracer.cjs 35.7 kB / 6.7 kB
dist/run/handlers/use-cache-handler.js 48.8 kB / 11.0 kB
dist/run/handlers/wait-until.cjs 1.4 kB / 665 B
dist/run/headers.js 8.2 kB / 2.6 kB
dist/run/next.cjs 23.8 kB / 6.0 kB
dist/run/revalidate.js 1.0 kB / 479 B
dist/run/storage/regional-blob-store.cjs 27.8 kB / 7.2 kB
dist/shared/blob-types.cjs 1.6 kB / 648 B
dist/shared/blobkey.js 742 B / 400 B
dist/shared/cache-types.cjs 1.3 kB / 566 B
edge-runtime/lib/cjs.ts 10.7 kB / 3.1 kB
edge-runtime/lib/headers.ts 1.9 kB / 841 B
edge-runtime/lib/logging.ts 115 B / 121 B
edge-runtime/lib/middleware.ts 3.6 kB / 1.4 kB
edge-runtime/lib/next-request.ts 3.3 kB / 1.1 kB
edge-runtime/lib/response.ts 10.0 kB / 3.0 kB
edge-runtime/lib/routing.ts 15.3 kB / 4.0 kB
edge-runtime/lib/util.ts 3.7 kB / 1.3 kB
edge-runtime/matchers.json 3 B / 23 B
edge-runtime/middleware.ts 2.4 kB / 1.0 kB
edge-runtime/next.config.json 3 B / 23 B
edge-runtime/README.md 992 B / 509 B
edge-runtime/shim/edge.js 1.6 kB / 788 B
edge-runtime/shim/node.js 596 B / 347 B
edge-runtime/vendor.ts 1.1 kB / 360 B
edge-runtime/vendor/deno.land/std@0.175.0/_util/asserts.ts 854 B / 461 B
edge-runtime/vendor/deno.land/std@0.175.0/_util/os.ts 644 B / 355 B
edge-runtime/vendor/deno.land/std@0.175.0/async/abortable.ts 4.0 kB / 1.0 kB
edge-runtime/vendor/deno.land/std@0.175.0/async/deadline.ts 974 B / 544 B
edge-runtime/vendor/deno.land/std@0.175.0/async/debounce.ts 2.2 kB / 956 B
edge-runtime/vendor/deno.land/std@0.175.0/async/deferred.ts 1.5 kB / 798 B
edge-runtime/vendor/deno.land/std@0.175.0/async/delay.ts 1.8 kB / 845 B
edge-runtime/vendor/deno.land/std@0.175.0/async/mod.ts 465 B / 241 B
edge-runtime/vendor/deno.land/std@0.175.0/async/mux_async_iterator.ts 2.5 kB / 1.1 kB
edge-runtime/vendor/deno.land/std@0.175.0/async/pool.ts 3.2 kB / 1.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/async/retry.ts 2.4 kB / 1.0 kB
edge-runtime/vendor/deno.land/std@0.175.0/async/tee.ts 2.1 kB / 924 B
edge-runtime/vendor/deno.land/std@0.175.0/bytes/index_of_needle.ts 1.4 kB / 668 B
edge-runtime/vendor/deno.land/std@0.175.0/crypto/timing_safe_equal.ts 875 B / 442 B
edge-runtime/vendor/deno.land/std@0.175.0/datetime/to_imf.ts 1.3 kB / 681 B
edge-runtime/vendor/deno.land/std@0.175.0/encoding/base64.ts 2.5 kB / 1.0 kB
edge-runtime/vendor/deno.land/std@0.175.0/encoding/base64url.ts 2.0 kB / 872 B
edge-runtime/vendor/deno.land/std@0.175.0/flags/mod.ts 22.6 kB / 5.9 kB
edge-runtime/vendor/deno.land/std@0.175.0/fmt/colors.ts 12.4 kB / 2.7 kB
edge-runtime/vendor/deno.land/std@0.175.0/fmt/printf.ts 27.7 kB / 7.7 kB
edge-runtime/vendor/deno.land/std@0.175.0/http/cookie.ts 11.5 kB / 3.6 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/_core.ts 2.3 kB / 716 B
edge-runtime/vendor/deno.land/std@0.175.0/node/_events.d.ts 27.2 kB / 5.8 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/_events.mjs 28.0 kB / 7.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/_global.d.ts 1.7 kB / 650 B
edge-runtime/vendor/deno.land/std@0.175.0/node/_next_tick.ts 5.0 kB / 1.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/_process/exiting.ts 138 B / 138 B
edge-runtime/vendor/deno.land/std@0.175.0/node/_process/process.ts 3.8 kB / 1.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/_process/stdio.mjs 336 B / 233 B
edge-runtime/vendor/deno.land/std@0.175.0/node/_process/streams.mjs 4.0 kB / 1.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/_stream.d.ts 53.2 kB / 11.9 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/_stream.mjs 91.2 kB / 25.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/_util/_util_callbackify.ts 4.3 kB / 1.7 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/_utils.ts 5.9 kB / 2.0 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/assert.ts 23.1 kB / 4.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/assertion_error.ts 19.6 kB / 6.1 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/async_hooks.ts 7.7 kB / 2.1 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/buffer.ts 262 B / 204 B
edge-runtime/vendor/deno.land/std@0.175.0/node/events.ts 303 B / 221 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/_libuv_winerror.ts 7.8 kB / 1.9 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/_listen.ts 561 B / 342 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/_node.ts 443 B / 335 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/_utils.ts 2.4 kB / 938 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/_winerror.ts 354.4 kB / 64.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/#_timingsafeequal_bd5c8.ts 479 B / 268 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/ares.ts 2.4 kB / 1.1 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/async_wrap.ts 4.0 kB / 1.8 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/buffer.ts 3.5 kB / 1.3 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/cares_wrap.ts 15.2 kB / 3.9 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/config.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/connection_wrap.ts 2.6 kB / 1.3 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/constants.ts 21.5 kB / 5.1 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/contextify.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/credentials.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/crypto.ts 448 B / 244 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/errors.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/fs_dir.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/fs_event_wrap.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/fs.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/handle_wrap.ts 1.8 kB / 1.0 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/heap_utils.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/http_parser.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/icu.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/inspector.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/js_stream.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/messaging.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/mod.ts 3.1 kB / 955 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/module_wrap.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/native_module.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/natives.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/node_file.ts 2.9 kB / 1.5 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/node_options.ts 1.8 kB / 989 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/options.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/os.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/performance.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/pipe_wrap.ts 10.4 kB / 3.3 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/process_methods.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/report.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/serdes.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/signal_wrap.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/spawn_sync.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/stream_wrap.ts 9.3 kB / 2.8 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/string_decoder.ts 504 B / 261 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/symbols.ts 1.4 kB / 828 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/task_queue.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/tcp_wrap.ts 13.1 kB / 3.7 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/timers.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/tls_wrap.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/trace_events.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/tty_wrap.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/types.ts 5.7 kB / 1.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/udp_wrap.ts 12.4 kB / 3.6 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/url.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/util.ts 4.0 kB / 1.8 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/uv.ts 20.1 kB / 3.8 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/v8.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/worker.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal_binding/zlib.ts 87 B / 104 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/buffer.d.ts 73.6 kB / 12.1 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/buffer.mjs 66.1 kB / 10.6 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/crypto/_keys.ts 463 B / 262 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/crypto/constants.ts 252 B / 173 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/error_codes.ts 322 B / 250 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/errors.ts 78.9 kB / 17.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/fixed_queue.ts 4.4 kB / 1.2 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/hide_stack_frames.ts 550 B / 377 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/net.ts 3.1 kB / 1.5 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/normalize_encoding.mjs 2.1 kB / 500 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/options.ts 1.7 kB / 959 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/primordials.mjs 1.8 kB / 431 B
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/process/per_thread.mjs 7.8 kB / 2.3 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/readline/callbacks.mjs 3.8 kB / 1.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/readline/utils.mjs 14.3 kB / 3.7 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/streams/destroy.mjs 6.9 kB / 1.8 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/streams/end-of-stream.mjs 7.1 kB / 1.9 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/streams/utils.mjs 5.9 kB / 1.2 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/util.mjs 4.0 kB / 1.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/util/comparisons.ts 16.6 kB / 3.8 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/util/debuglog.ts 3.2 kB / 1.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/util/inspect.mjs 71.5 kB / 19.8 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/util/types.ts 3.7 kB / 1.3 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/internal/validators.mjs 8.0 kB / 2.1 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/process.ts 19.4 kB / 5.2 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/stream.ts 671 B / 346 B
edge-runtime/vendor/deno.land/std@0.175.0/node/string_decoder.ts 10.3 kB / 3.3 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/util.ts 7.8 kB / 2.2 kB
edge-runtime/vendor/deno.land/std@0.175.0/node/util/types.ts 199 B / 153 B
edge-runtime/vendor/deno.land/std@0.175.0/path/_constants.ts 2.0 kB / 727 B
edge-runtime/vendor/deno.land/std@0.175.0/path/_interface.ts 728 B / 369 B
edge-runtime/vendor/deno.land/std@0.175.0/path/_util.ts 5.0 kB / 1.6 kB
edge-runtime/vendor/deno.land/std@0.175.0/path/common.ts 1.2 kB / 607 B
edge-runtime/vendor/deno.land/std@0.175.0/path/glob.ts 12.7 kB / 3.9 kB
edge-runtime/vendor/deno.land/std@0.175.0/path/mod.ts 1.4 kB / 690 B
edge-runtime/vendor/deno.land/std@0.175.0/path/posix.ts 13.9 kB / 3.7 kB
edge-runtime/vendor/deno.land/std@0.175.0/path/separator.ts 259 B / 209 B
edge-runtime/vendor/deno.land/std@0.175.0/path/win32.ts 28.5 kB / 6.4 kB
edge-runtime/vendor/deno.land/std@0.175.0/streams/write_all.ts 2.2 kB / 598 B
edge-runtime/vendor/deno.land/std@0.175.0/testing/_diff.ts 11.6 kB / 3.6 kB
edge-runtime/vendor/deno.land/std@0.175.0/testing/_format.ts 705 B / 462 B
edge-runtime/vendor/deno.land/std@0.175.0/testing/asserts.ts 25.5 kB / 5.7 kB
edge-runtime/vendor/deno.land/std@0.175.0/types.d.ts 4.2 kB / 1.2 kB
edge-runtime/vendor/deno.land/x/htmlrewriter@v1.0.0/pkg/htmlrewriter_bg.wasm 573.2 kB / 262.7 kB
edge-runtime/vendor/deno.land/x/htmlrewriter@v1.0.0/pkg/htmlrewriter.js 31.0 kB / 4.7 kB
edge-runtime/vendor/deno.land/x/htmlrewriter@v1.0.0/src/index.ts 2.6 kB / 989 B
edge-runtime/vendor/deno.land/x/htmlrewriter@v1.0.0/src/types.d.ts 2.1 kB / 446 B
edge-runtime/vendor/deno.land/x/path_to_regexp@v6.2.1/index.ts 15.4 kB / 4.2 kB
edge-runtime/vendor/manifest.json 104 B / 119 B
edge-runtime/vendor/v1-7-0--edge-utils.netlify.app/logger/logger.ts 3.2 kB / 747 B
edge-runtime/vendor/v1-7-0--edge-utils.netlify.app/logger/mod.ts 29 B / 49 B
LICENSE 1.1 kB / 661 B
manifest.yml 31 B / 51 B
package.json 3.5 kB / 1.3 kB
README.md 2.8 kB / 1.2 kB

🤖 This report was automatically generated by pkg-size-action

@pieh pieh force-pushed the perf/conditional-blob-get branch 2 times, most recently from 375cbb7 to 5bc519f Compare October 29, 2025 12:23
},
],
'unicorn/filename-case': ['error', { case: 'kebabCase' }],
'unicorn/no-nested-ternary': 'off',
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because of sindresorhus/eslint-plugin-unicorn#2604 where prettier is getting rid of extra parenthesis needed to satisfy this rule

@pieh pieh force-pushed the perf/conditional-blob-get branch from 5bc519f to 346ed9f Compare October 29, 2025 12:37
Comment on lines +28 to +32
if (
memoizedValue?.conditional === false &&
typeof memoizedValue?.currentRequestValue !== 'undefined'
) {
return memoizedValue.currentRequestValue as T | null | Promise<T | null>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

inMemoryCache.get now will potentially return values from previous requests - the conditional property instruct wether we can just use it without checking blobs or if we need to to do roundtrip to blobs to check if we can reuse this via conditional store.getWithMetadata call

@pieh pieh force-pushed the perf/conditional-blob-get branch from 346ed9f to 89a536f Compare October 29, 2025 12:55
Comment on lines +63 to +64
staleAt: 123,
expireAt: 456,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

not related to this PR other than fact that I was touching this test and noticed types were no longer happy after TagManifest changes that were done in #3173 and this is just to satisfy types and otherwise doesn't have impact on actual tests

Comment on lines +136 to 197
it('does not automatically reuse in-memory values when running in request contexts', async () => {
const store = getMemoizedKeyValueStoreBackedByRegionalBlobStore()
await runWithRequestContext(createRequestContext(), async () => {
await store.get(TEST_KEY, OTEL_SPAN_TITLE)
const get1 = await runWithRequestContext(createRequestContext(), async () => {
return await store.get(TEST_KEY, OTEL_SPAN_TITLE)
})

await runWithRequestContext(createRequestContext(), async () => {
await store.get(TEST_KEY, OTEL_SPAN_TITLE)
const get2 = await runWithRequestContext(createRequestContext(), async () => {
return await store.get(TEST_KEY, OTEL_SPAN_TITLE)
})

expect(
mockedStore.get,
mockedStore.getWithMetadata,
'Blobs should be requested separately for each request context',
).toHaveBeenCalledTimes(2)

// first request context assertions
expect(get1, 'store.get in first request should return expected value').toEqual(
TEST_DEFAULT_VALUE,
)

expect(
mockedStore.getWithMetadata,
'On first request context, we should not provide etag as we do not have any yet',
).toHaveBeenNthCalledWith(1, expect.any(String), {
etag: undefined,
type: 'json',
})

expect(
mockedStore.getWithMetadata,
'should return full value from blobs as it is first time being requested',
).toHaveNthResolvedWith(
1,
expect.objectContaining({
data: TEST_DEFAULT_VALUE,
}),
)

// second request context assertions
expect(get2, 'store.get in second request should return expected value').toEqual(
TEST_DEFAULT_VALUE,
)

expect(
mockedStore.getWithMetadata,
'On second request context, we should provide an etag as first request fetched same blob',
).toHaveBeenNthCalledWith(2, expect.any(String), {
etag: expect.any(String),
type: 'json',
})

expect(
mockedStore.getWithMetadata,
'On second request context, we should not get blob value, just indication that we can reuse blob',
).toHaveNthResolvedWith(
2,
expect.objectContaining({
data: null,
etag: expect.any(String),
}),
)
})
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the expanded test for changes in this PR. Rest of changes in test suite are to fix types as mentioned in https://github.com/opennextjs/opennextjs-netlify/pull/3218/files#r2473214413 and to cover change from blobStore.get to blobStore.getWithMetadata

span?.setAttributes({ key, blobKey })
const blob = (await store.get(blobKey, { type: 'json' })) as T | null
inMemoryCache.set(key, blob)
span?.addEvent(blob ? 'Hit' : 'Miss')
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I converted from adding event to setting status attribute to make it easier for querying. The event was also always at the end of span anyway, so we don't get any extra value from using events in this case

span?.setAttributes({
etag: result?.etag,
reusingPreviouslyFetchedBlob: shouldReuseMemoizedBlob,
status: blob ? (shouldReuseMemoizedBlob ? 'Hit, no change' : 'Hit') : 'Miss',
Copy link
Contributor Author

@pieh pieh Oct 29, 2025

Choose a reason for hiding this comment

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

this is pretty much modeling 304, 200, 404 response status codes from underlying blobs response

hopefully this is temporary until we can nicely "merge" next-runtime produced span with blobs spans to avoid almost duplication (those spans below represent same thing)
image
without losing next.js specific context (primarly logical cache key vs blob key which is encoded and the context of blobs get - is it for tag manifest, static html blob or cachehandler entry)

@pieh pieh marked this pull request as ready for review October 29, 2025 14:04
@pieh pieh requested a review from a team as a code owner October 29, 2025 14:04
return extendedGlobalThis[IN_MEMORY_LRU_CACHE]
}

export function clearInMemoryLRUCacheForTesting() {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think ideally I would want this kind of helper to be in a test file rather than next to the real code, that said this is very much a preference and not an ask you have to actually do

await store.get('heavy-route-1', OTEL_SPAN_TITLE)
expect(mockedStore.get, 'Value should be read from memory').toHaveBeenCalledTimes(0)
mockedStore.get.mockClear()
expect(mockedStore.getWithMetadata, 'Value should be read from memory').toHaveBeenCalledTimes(
Copy link
Contributor

Choose a reason for hiding this comment

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

There's not.toHaveBeenCalled if you'd like

// operations in requestContext2 should result in evicting value for requestContext1
expect(mockedStore.get, 'Value should be read from blobs').toHaveBeenCalledTimes(1)
mockedStore.get.mockClear()
expect(mockedStore.getWithMetadata, 'Value should be read from blobs').toHaveBeenCalledTimes(
Copy link
Contributor

@mrstork mrstork Oct 31, 2025

Choose a reason for hiding this comment

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

There is a toHaveBeenCalledOnce if you'd like

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.

3 participants