We have a situation where we call from one of our front end lambda into another one which does some work for us. This work doesn't need to be waited for, and as it can take some time (0.5 - 10s), we'd rather the user didn't have to wait for it either.
All well and good, this is pretty easy to do with 2 lambda, just call them with the InvocationType
of Event
const params: Lambda.InvocationRequest = {
FunctionName: config.lambdaSmsProviderFunctionName,
InvocationType: 'Event',
Payload: JSON.stringify({
message,
phoneNumber,
}),
};
const result = await o11y.withEvent('sms.lambda', {phoneNumber, message}, async span => {
return await lambda.invoke(params).promise();
});
The problem is, we end up with 2 distinct traces in Honeycomb. This is also "fine", but I'd rather have them as one.
Hooking them up, it turns out, is fairly easy. In the calling lambda, you extract the traceId
and patentSpanId
which are in the trace context:
const context = beeline.marshalTraceContext(o11y.bee.getTraceContext());
const params: Lambda.InvocationRequest = {
FunctionName: config.lambdaSmsProviderFunctionName,
InvocationType: 'Event',
Payload: JSON.stringify({
message,
phoneNumber,
honeycombContext: context,
}),
};
I'm putting it into the Payload
because lambda, for some reason, doesn't pass over the ClientContext
when you use the Event
invocation type.
On the other end, you pull it out, and when starting your initial trace, pass it in as the initial ids:
const {context, event} = handler;
if (event.honeycombContext) {
const {traceId, parentSpanId} = beeline.unmarshalTraceContext(event.honeycombContext);
span = beeline.startTrace(
{name: 'lambda', awsRequestId: context.awsRequestId, resource: event.resource},
traceId,
parentSpanId,
);
} else {
span = beeline.startTrace({name: 'lambda', awsRequestId: context.awsRequestId, resource: event.resource});
}
And... we have embedded traces: