Skip to content

Commit 28cf632

Browse files
committed
compute cache hit for getItems
1 parent 7d0f2ac commit 28cf632

2 files changed

Lines changed: 39 additions & 6 deletions

File tree

packages/nitro/src/runtime/hooks/captureStorageEvents.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
import {
99
flushIfServerless,
1010
GLOBAL_OBJ,
11+
isObjectLike,
1112
SEMANTIC_ATTRIBUTE_CACHE_HIT,
1213
SEMANTIC_ATTRIBUTE_CACHE_KEY,
1314
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
@@ -37,7 +38,7 @@ const TRACED_OPERATIONS = [
3738

3839
type TracedOperation = (typeof TRACED_OPERATIONS)[number];
3940

40-
const CACHE_HIT_OPERATIONS = new Set<TracedOperation>(['hasItem', 'getItem', 'getItemRaw']);
41+
const CACHE_HIT_OPERATIONS = new Set<TracedOperation>(['hasItem', 'getItem', 'getItemRaw', 'getItems']);
4142

4243
/**
4344
* Maps each unstorage operation to a convention cache op. Reads (including existence and key
@@ -107,8 +108,7 @@ function setupStorageTracingChannel(operation: TracedOperation): void {
107108
if (!('error' in data)) {
108109
const result = (data as { result?: unknown }).result;
109110
if (CACHE_HIT_OPERATIONS.has(operation)) {
110-
const hit = operation === 'hasItem' ? Boolean(result) : isCacheHit(data.keys?.[0], result);
111-
span.setAttribute(SEMANTIC_ATTRIBUTE_CACHE_HIT, hit);
111+
span.setAttribute(SEMANTIC_ATTRIBUTE_CACHE_HIT, resolveCacheHit(operation, data.keys?.[0], result));
112112
}
113113
}
114114

@@ -118,6 +118,23 @@ function setupStorageTracingChannel(operation: TracedOperation): void {
118118
);
119119
}
120120

121+
/**
122+
* Resolves the `cache.hit` value for a read operation. `hasItem` returns a boolean directly,
123+
* `getItems` returns a `{ key, value }[]` where a hit means at least one entry has a value,
124+
* and single-key reads fall back to the value-based `isCacheHit` check.
125+
*/
126+
function resolveCacheHit(operation: TracedOperation, key: unknown, result: unknown): boolean {
127+
if (operation === 'hasItem') {
128+
return Boolean(result);
129+
}
130+
131+
if (operation === 'getItems') {
132+
return Array.isArray(result) && result.some(item => isObjectLike(item) && item.value != null);
133+
}
134+
135+
return isCacheHit(key, result);
136+
}
137+
121138
interface CacheEntry<T = unknown> {
122139
value?: T;
123140
expires?: number;

packages/nuxt/src/runtime/utils/instrumentStorage.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ type DriverMethod = keyof Driver;
5858
/**
5959
* Methods that should have an attribute to indicate a cache hit.
6060
*/
61-
const CACHE_HIT_METHODS = new Set<DriverMethod>(['hasItem', 'getItem', 'getItemRaw']);
61+
const CACHE_HIT_METHODS = new Set<DriverMethod>(['hasItem', 'getItem', 'getItemRaw', 'getItems']);
6262

6363
/**
6464
* Maps each unstorage method to a convention cache op. Reads (including existence and key
@@ -181,8 +181,7 @@ function createMethodWrapper(
181181
span.setStatus({ code: SPAN_STATUS_OK });
182182

183183
if (CACHE_HIT_METHODS.has(methodName)) {
184-
const hit = methodName === 'hasItem' ? Boolean(result) : isCacheHit(args[0], result);
185-
span.setAttribute(SEMANTIC_ATTRIBUTE_CACHE_HIT, hit);
184+
span.setAttribute(SEMANTIC_ATTRIBUTE_CACHE_HIT, resolveCacheHit(methodName, args[0], result));
186185
}
187186

188187
return result;
@@ -283,6 +282,23 @@ function normalizeKey(key: unknown, prefix: string): string {
283282

284283
const CACHED_FN_HANDLERS_RE = /^nitro:(functions|handlers):/i;
285284

285+
/**
286+
* Resolves the `cache.hit` value for a read method. `hasItem` returns a boolean directly,
287+
* `getItems` returns a `{ key, value }[]` where a hit means at least one entry has a value,
288+
* and single-key reads fall back to the value-based `isCacheHit` check.
289+
*/
290+
function resolveCacheHit(methodName: DriverMethod, key: unknown, result: unknown): boolean {
291+
if (methodName === 'hasItem') {
292+
return Boolean(result);
293+
}
294+
295+
if (methodName === 'getItems') {
296+
return Array.isArray(result) && result.some(item => isObjectLike(item) && item.value != null);
297+
}
298+
299+
return isCacheHit(key, result);
300+
}
301+
286302
/**
287303
* Since Nitro's cache may not utilize the driver's TTL, it is possible that the value is present in the cache but won't be used by Nitro.
288304
* The maxAge and expires values are serialized by Nitro in the cache entry. This means the value presence does not necessarily mean a cache hit.

0 commit comments

Comments
 (0)