retrace-sdk 0.16.6 → 0.16.7
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/dist/enforcement.js +1 -1
- package/dist/transport.d.ts +11 -0
- package/dist/transport.js +42 -5
- package/package.json +1 -1
package/dist/enforcement.js
CHANGED
|
@@ -16,7 +16,7 @@ function shortHash(input) {
|
|
|
16
16
|
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
17
17
|
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
18
18
|
const n = 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
19
|
-
return n.toString(16).padStart(
|
|
19
|
+
return n.toString(16).padStart(16, "0");
|
|
20
20
|
};
|
|
21
21
|
return (pass(0) + pass(0x9e3779b9)).slice(0, 32);
|
|
22
22
|
}
|
package/dist/transport.d.ts
CHANGED
|
@@ -71,8 +71,19 @@ export declare class WSTransport implements Transport {
|
|
|
71
71
|
export declare class HTTPTransport implements Transport {
|
|
72
72
|
private traceData;
|
|
73
73
|
private spans;
|
|
74
|
+
private backlog;
|
|
75
|
+
private readonly backlogCap;
|
|
76
|
+
private dropped;
|
|
74
77
|
send(eventType: string, data: Record<string, unknown>): void;
|
|
75
78
|
flush(): Promise<void>;
|
|
79
|
+
/** POST one complete trace payload. Returns "ok" | "reject" (non-retryable 4xx) | "retry"
|
|
80
|
+
* (429 / 5xx / network after retries exhausted — caller should retain the payload). */
|
|
81
|
+
private upload;
|
|
82
|
+
/** Retain a failed trace for a later retry; bounded — drop + count the oldest on overflow. */
|
|
83
|
+
private enqueueBacklog;
|
|
84
|
+
/** Retry previously-failed traces oldest-first; stop at the first still-failing one so a server
|
|
85
|
+
* that is still down isn't hammered. */
|
|
86
|
+
private drainBacklog;
|
|
76
87
|
private buildSpans;
|
|
77
88
|
close(): void;
|
|
78
89
|
/** HTTP is already the one-shot channel — just drain. */
|
package/dist/transport.js
CHANGED
|
@@ -349,6 +349,12 @@ export class WSTransport {
|
|
|
349
349
|
export class HTTPTransport {
|
|
350
350
|
traceData = null;
|
|
351
351
|
spans = [];
|
|
352
|
+
// Bounded offline backlog (parity with WSTransport's queue + the Python HTTPTransport): a trace
|
|
353
|
+
// that fails to upload after exhausting transient retries is RETAINED here and retried on a later
|
|
354
|
+
// flush instead of being silently dropped. Bounded so a long outage can't grow memory unbounded.
|
|
355
|
+
backlog = [];
|
|
356
|
+
backlogCap = 1000;
|
|
357
|
+
dropped = 0;
|
|
352
358
|
send(eventType, data) {
|
|
353
359
|
if (eventType === "trace_started") {
|
|
354
360
|
this.traceData = data;
|
|
@@ -363,15 +369,26 @@ export class HTTPTransport {
|
|
|
363
369
|
}
|
|
364
370
|
}
|
|
365
371
|
async flush() {
|
|
372
|
+
// Opportunistically retry anything stranded by a previous outage first.
|
|
373
|
+
if (this.backlog.length)
|
|
374
|
+
await this.drainBacklog();
|
|
366
375
|
if (!this.traceData)
|
|
367
376
|
return;
|
|
368
|
-
const cfg = getConfig();
|
|
369
|
-
const url = `${cfg.baseUrl}/api/v1/traces`;
|
|
370
377
|
const body = { ...this.traceData, spans: this.buildSpans() };
|
|
371
378
|
const payload = JSON.stringify(body);
|
|
372
379
|
// Clear first so a concurrent flush (e.g. trace_ended then shutdown drain) can't double-send.
|
|
373
380
|
this.traceData = null;
|
|
374
381
|
this.spans = [];
|
|
382
|
+
// On a transient failure, RETAIN the trace in the bounded backlog instead of dropping it — the
|
|
383
|
+
// previous code cleared the buffer unconditionally, losing the trace under sustained 429/5xx.
|
|
384
|
+
if ((await this.upload(payload)) === "retry")
|
|
385
|
+
this.enqueueBacklog(payload);
|
|
386
|
+
}
|
|
387
|
+
/** POST one complete trace payload. Returns "ok" | "reject" (non-retryable 4xx) | "retry"
|
|
388
|
+
* (429 / 5xx / network after retries exhausted — caller should retain the payload). */
|
|
389
|
+
async upload(payload) {
|
|
390
|
+
const cfg = getConfig();
|
|
391
|
+
const url = `${cfg.baseUrl}/api/v1/traces`;
|
|
375
392
|
// Retry up to 3 times with exponential backoff; awaited so shutdown can drain it.
|
|
376
393
|
for (let n = 1; n <= 3; n++) {
|
|
377
394
|
try {
|
|
@@ -381,13 +398,13 @@ export class HTTPTransport {
|
|
|
381
398
|
body: payload,
|
|
382
399
|
});
|
|
383
400
|
if (res.ok)
|
|
384
|
-
return;
|
|
401
|
+
return "ok";
|
|
385
402
|
const txt = await res.text().catch(() => "");
|
|
386
403
|
// 4xx (except 429) is a client/payload error that won't succeed on retry — surface
|
|
387
404
|
// it loudly and stop, rather than silently dropping the trace.
|
|
388
405
|
if (res.status < 500 && res.status !== 429) {
|
|
389
406
|
console.error(`[retrace] trace upload rejected (HTTP ${res.status}): ${txt.slice(0, 300)}`);
|
|
390
|
-
return;
|
|
407
|
+
return "reject";
|
|
391
408
|
}
|
|
392
409
|
// 5xx / 429 — transient; retry.
|
|
393
410
|
if (n === 3)
|
|
@@ -400,6 +417,26 @@ export class HTTPTransport {
|
|
|
400
417
|
if (n < 3)
|
|
401
418
|
await new Promise((r) => setTimeout(r, 1000 * n));
|
|
402
419
|
}
|
|
420
|
+
return "retry";
|
|
421
|
+
}
|
|
422
|
+
/** Retain a failed trace for a later retry; bounded — drop + count the oldest on overflow. */
|
|
423
|
+
enqueueBacklog(payload) {
|
|
424
|
+
this.backlog.push(payload);
|
|
425
|
+
while (this.backlog.length > this.backlogCap) {
|
|
426
|
+
this.backlog.shift();
|
|
427
|
+
this.dropped++;
|
|
428
|
+
console.error(`[retrace] offline buffer full (cap ${this.backlogCap}) — dropped oldest trace (total dropped: ${this.dropped})`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/** Retry previously-failed traces oldest-first; stop at the first still-failing one so a server
|
|
432
|
+
* that is still down isn't hammered. */
|
|
433
|
+
async drainBacklog() {
|
|
434
|
+
while (this.backlog.length) {
|
|
435
|
+
const r = await this.upload(this.backlog[0]);
|
|
436
|
+
if (r === "retry")
|
|
437
|
+
break; // still failing — keep the rest for next time
|
|
438
|
+
this.backlog.shift(); // "ok" (delivered) or "reject" (unrecoverable) → remove
|
|
439
|
+
}
|
|
403
440
|
}
|
|
404
441
|
buildSpans() {
|
|
405
442
|
const merged = new Map();
|
|
@@ -423,7 +460,7 @@ export class HTTPTransport {
|
|
|
423
460
|
await this.flush();
|
|
424
461
|
}
|
|
425
462
|
hasPendingData() {
|
|
426
|
-
return this.traceData !== null || this.spans.length > 0;
|
|
463
|
+
return this.traceData !== null || this.spans.length > 0 || this.backlog.length > 0;
|
|
427
464
|
}
|
|
428
465
|
}
|
|
429
466
|
export function createTransport(mode = "auto") {
|