@vtex/api 6.45.25-beta.0 → 6.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,10 +7,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [6.46.0] - 2023-10-25
11
+ ### Added
12
+ - Add disk cache steps and retry count to tracing
13
+
14
+ ### Changed
15
+ - Stop updating client cache when revalidated and still expired
16
+
10
17
  ## [6.45.24] - 2023-10-05
11
18
  ### Added
12
19
 
13
20
  - Allow disabling memoization for all requests of a client
21
+
14
22
  ## [6.45.23] - 2023-10-04
15
23
 
16
24
  ### Fixed
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const constants_1 = require("../../constants");
4
+ const tracing_1 = require("../../tracing");
4
5
  const RANGE_HEADER_QS_KEY = '__range_header';
5
6
  const cacheableStatusCodes = [200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501]; // https://tools.ietf.org/html/rfc7231#section-6.1
6
7
  exports.cacheKey = (config) => {
@@ -59,7 +60,7 @@ exports.cacheMiddleware = ({ type, storage }) => {
59
60
  if (!isLocallyCacheable(ctx.config, type)) {
60
61
  return await next();
61
62
  }
62
- const span = (_a = ctx.tracing) === null || _a === void 0 ? void 0 : _a.rootSpan;
63
+ const { rootSpan: span, tracer, logger } = (_a = ctx.tracing) !== null && _a !== void 0 ? _a : {};
63
64
  const key = exports.cacheKey(ctx.config);
64
65
  const segmentToken = ctx.config.headers[constants_1.SEGMENT_HEADER];
65
66
  const keyWithSegment = key + segmentToken;
@@ -69,8 +70,19 @@ exports.cacheMiddleware = ({ type, storage }) => {
69
70
  ["key" /* KEY */]: key,
70
71
  ["key-with-segment" /* KEY_WITH_SEGMENT */]: keyWithSegment,
71
72
  });
72
- const cacheHasWithSegment = await storage.has(keyWithSegment);
73
- const cached = cacheHasWithSegment ? await storage.get(keyWithSegment) : await storage.get(key);
73
+ const cacheReadSpan = createCacheSpan(cacheType, 'read', tracer, span);
74
+ let cached;
75
+ try {
76
+ const cacheHasWithSegment = await storage.has(keyWithSegment);
77
+ cached = cacheHasWithSegment ? await storage.get(keyWithSegment) : await storage.get(key);
78
+ }
79
+ catch (error) {
80
+ tracing_1.ErrorReport.create({ originalError: error }).injectOnSpan(cacheReadSpan);
81
+ logger === null || logger === void 0 ? void 0 : logger.warn({ message: 'Error reading from the HttpClient cache', error });
82
+ }
83
+ finally {
84
+ cacheReadSpan === null || cacheReadSpan === void 0 ? void 0 : cacheReadSpan.finish();
85
+ }
74
86
  if (cached && cached.response) {
75
87
  const { etag: cachedEtag, response, expiration, responseType, responseEncoding } = cached;
76
88
  if (type === CacheType.Disk && responseType === 'arraybuffer') {
@@ -149,26 +161,48 @@ exports.cacheMiddleware = ({ type, storage }) => {
149
161
  const cacheableData = type === CacheType.Disk && responseType === 'arraybuffer'
150
162
  ? data.toString(responseEncoding)
151
163
  : data;
152
- const expiration = Date.now() + (maxAge - currentAge) * 1000;
153
- storage.set(setKey, {
154
- etag,
155
- expiration,
156
- response: { data: cacheableData, headers, status },
157
- responseEncoding,
158
- responseType,
159
- });
160
- span === null || span === void 0 ? void 0 : span.log({
161
- event: "local-cache-saved" /* LOCAL_CACHE_SAVED */,
162
- ["cache-type" /* CACHE_TYPE */]: cacheType,
163
- ["key-set" /* KEY_SET */]: setKey,
164
- ["age" /* AGE */]: currentAge,
165
- ["etag" /* ETAG */]: etag,
166
- ["expiration-time" /* EXPIRATION_TIME */]: (expiration - Date.now()) / 1000,
167
- ["response-encoding" /* RESPONSE_ENCONDING */]: responseEncoding,
168
- ["response-type" /* RESPONSE_TYPE */]: responseType,
169
- });
164
+ const now = Date.now();
165
+ const expiration = now + (maxAge - currentAge) * 1000;
166
+ const alreadyExpired = expiration <= now;
167
+ const reusingRevalidatedCache = cached && (ctx.response === cached.response);
168
+ const shouldSkipCacheUpdate = alreadyExpired && reusingRevalidatedCache;
169
+ if (shouldSkipCacheUpdate) {
170
+ return;
171
+ }
172
+ const cacheWriteSpan = createCacheSpan(cacheType, 'write', tracer, span);
173
+ try {
174
+ await storage.set(setKey, {
175
+ etag,
176
+ expiration,
177
+ response: { data: cacheableData, headers, status },
178
+ responseEncoding,
179
+ responseType,
180
+ });
181
+ span === null || span === void 0 ? void 0 : span.log({
182
+ event: "local-cache-saved" /* LOCAL_CACHE_SAVED */,
183
+ ["cache-type" /* CACHE_TYPE */]: cacheType,
184
+ ["key-set" /* KEY_SET */]: setKey,
185
+ ["age" /* AGE */]: currentAge,
186
+ ["etag" /* ETAG */]: etag,
187
+ ["expiration-time" /* EXPIRATION_TIME */]: (expiration - Date.now()) / 1000,
188
+ ["response-encoding" /* RESPONSE_ENCONDING */]: responseEncoding,
189
+ ["response-type" /* RESPONSE_TYPE */]: responseType,
190
+ });
191
+ }
192
+ catch (error) {
193
+ tracing_1.ErrorReport.create({ originalError: error }).injectOnSpan(cacheWriteSpan);
194
+ logger === null || logger === void 0 ? void 0 : logger.warn({ message: 'Error writing to the HttpClient cache', error });
195
+ }
196
+ finally {
197
+ cacheWriteSpan === null || cacheWriteSpan === void 0 ? void 0 : cacheWriteSpan.finish();
198
+ }
170
199
  return;
171
200
  }
172
201
  span === null || span === void 0 ? void 0 : span.log({ event: "no-local-cache-save" /* NO_LOCAL_CACHE_SAVE */, ["cache-type" /* CACHE_TYPE */]: cacheType });
173
202
  };
174
203
  };
204
+ const createCacheSpan = (cacheType, operation, tracer, parentSpan) => {
205
+ if (tracer && tracer.isTraceSampled && cacheType === 'disk') {
206
+ return tracer.startSpan(`${operation}-disk-cache`, { childOf: parentSpan });
207
+ }
208
+ };
@@ -18,7 +18,7 @@ const exponentialDelay = (initialBackoffDelay, exponentialBackoffCoefficient, re
18
18
  // A lot of this code is based on:
19
19
  // https://github.com/softonic/axios-retry/blob/ffd4327f31d063522e58c525d28d4c5053d0ea7b/es/index.js#L109
20
20
  const onResponseError = (http) => (error) => {
21
- var _a, _b;
21
+ var _a, _b, _c, _d;
22
22
  if (error.config == null) {
23
23
  return Promise.reject(error);
24
24
  }
@@ -40,6 +40,10 @@ const onResponseError = (http) => (error) => {
40
40
  : config.timeout;
41
41
  config.transformRequest = [data => data];
42
42
  (_b = (_a = config.tracing) === null || _a === void 0 ? void 0 : _a.rootSpan) === null || _b === void 0 ? void 0 : _b.log({ event: "setup-request-retry" /* SETUP_REQUEST_RETRY */, ["retry-number" /* RETRY_NUMBER */]: config.retryCount, ["retry-in" /* RETRY_IN */]: delay });
43
+ (_d = (_c = config.tracing) === null || _c === void 0 ? void 0 : _c.rootSpan) === null || _d === void 0 ? void 0 : _d.addTags({
44
+ ["http.retry.count" /* HTTP_RETRY_COUNT */]: config.retryCount,
45
+ ["http.retry.error.code" /* HTTP_RETRY_ERROR_CODE */]: error.code,
46
+ });
43
47
  return new Promise(resolve => setTimeout(() => resolve(http(config)), delay));
44
48
  }
45
49
  return Promise.reject(error);
@@ -51,7 +51,9 @@ export declare const enum CustomHttpTags {
51
51
  HTTP_MEMORY_CACHE_RESULT = "http.cache.memory",
52
52
  HTTP_MEMOIZATION_CACHE_RESULT = "http.cache.memoization",
53
53
  HTTP_DISK_CACHE_RESULT = "http.cache.disk",
54
- HTTP_ROUTER_CACHE_RESULT = "http.cache.router"
54
+ HTTP_ROUTER_CACHE_RESULT = "http.cache.router",
55
+ HTTP_RETRY_ERROR_CODE = "http.retry.error.code",
56
+ HTTP_RETRY_COUNT = "http.retry.count"
55
57
  }
56
58
  export declare const UserlandTags: {
57
59
  SPAN_KIND: "span.kind";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/api",
3
- "version": "6.45.25-beta.0",
3
+ "version": "6.46.0",
4
4
  "description": "VTEX I/O API client",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",