Skip to content

Commit e6299fe

Browse files
Fix negative duration on aws.apigateway.authorizer span (#751)
1 parent 4096318 commit e6299fe

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

src/trace/span-inferrer.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,6 +1414,29 @@ describe("Authorizer Spans", () => {
14141414
]);
14151415
});
14161416

1417+
it("clamps authorizer span endTime to startTime when clock skew causes negative duration", () => {
1418+
// Simulate the case where requestTimeEpoch + integrationLatency < parentSpanFinishTime
1419+
// (i.e., API Gateway's reported end is before the authorizer lambda's self-reported finish).
1420+
// This 1ms discrepancy would produce a negative duration.
1421+
const skewedEvent = JSON.parse(JSON.stringify(apiGatewayV1RequestAuthorizer));
1422+
const authorizerHeaders = JSON.parse(
1423+
Buffer.from(skewedEvent.requestContext.authorizer._datadog, "base64").toString(),
1424+
);
1425+
const parentSpanFinishTimeMs = authorizerHeaders["x-datadog-parent-span-finish-time"] / 1e6;
1426+
const { requestTimeEpoch } = skewedEvent.requestContext;
1427+
1428+
// Set integrationLatency so that requestTimeEpoch + integrationLatency < parentSpanFinishTimeMs
1429+
skewedEvent.requestContext.authorizer.integrationLatency = parentSpanFinishTimeMs - requestTimeEpoch - 1;
1430+
1431+
const inferrer = new SpanInferrer(mockWrapperWithFinish as unknown as TracerWrapper);
1432+
inferrer.createInferredSpan(skewedEvent, {} as any, {} as SpanContext);
1433+
1434+
// The second apigateway span must not start before the first one ends,
1435+
// otherwise the authorizer span gets a negative duration.
1436+
const secondApiGatewayStartTime = (mockWrapperWithFinish.startSpan.mock.calls[1] as any)[1].startTime;
1437+
expect(secondApiGatewayStartTime).toBeGreaterThanOrEqual(parentSpanFinishTimeMs);
1438+
});
1439+
14171440
it("No inferred span for API Gateway Websocket Message event with traced authorizers [Request Type]", () => {
14181441
const inferrer = new SpanInferrer(mockWrapperWithFinish as unknown as TracerWrapper);
14191442
inferrer.createInferredSpan(apiGatewayWSSRequestAuthorizerMessage, {} as any, {} as SpanContext);

src/trace/span-inferrer.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@ export class SpanInferrer {
170170
this.traceWrapper.startSpan("aws.apigateway.authorizer", upstreamSpanOptions),
171171
{ isAsync: false },
172172
);
173-
const endTime = event.requestContext.requestTimeEpoch + event.requestContext.authorizer.integrationLatency;
173+
const endTime = Math.max(
174+
startTime,
175+
event.requestContext.requestTimeEpoch + event.requestContext.authorizer.integrationLatency,
176+
);
174177
upstreamAuthorizerSpan.finish(endTime);
175178
options.startTime = endTime; // For the main function's inferred span
176179
}

0 commit comments

Comments
 (0)