Skip to content

Commit 8f0228b

Browse files
BridgeARrochdev
authored andcommitted
perf(pubsub): trim per-message allocations in publish hot path (#8374)
Three per-message allocations that compound on a typical batch: 1. `bindStart` called `String(messageCount)` and `String(Math.floor(batchSpan._startTime))` on every iteration despite both being loop-invariant — on a 100-message batch that's 200 redundant string allocations per send. Hoist both above the loop. 2. The per-message `Object.assign({...})` over seven attribute fields rebuilt a fresh object every iteration. Assign per-key so V8 keeps `msg.attributes` on its existing hidden class. 3. The parent-from-message-0 span-link extraction (`messages.slice(1).map(...).filter(Boolean)`) allocated three intermediate arrays per call. Replace with a single `for` loop.
1 parent 0c71281 commit 8f0228b

1 file changed

Lines changed: 27 additions & 18 deletions

File tree

  • packages/datadog-plugin-google-cloud-pubsub/src

packages/datadog-plugin-google-cloud-pubsub/src/producer.js

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,13 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
5454
* - Inject batch span context + metadata into all message attributes for downstream
5555
* consumers to reconstruct the trace and understand batch relationships
5656
*/
57-
const spanLinkData = hasTraceContext
58-
? messages.slice(1).map(msg => this.#extractSpanLink(msg.attributes)).filter(Boolean)
59-
: []
57+
const spanLinkData = []
58+
if (hasTraceContext) {
59+
for (let i = 1; i < messageCount; i++) {
60+
const link = this.#extractSpanLink(messages[i].attributes)
61+
if (link) spanLinkData.push(link)
62+
}
63+
}
6064

6165
const firstAttrs = messages[0]?.attributes
6266
const parentData = firstAttrs?.['x-datadog-trace-id'] && firstAttrs['x-datadog-parent-id']
@@ -107,35 +111,40 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
107111
))
108112
}
109113

110-
for (let i = 0; i < messages.length; i++) {
114+
const messageCountStr = String(messageCount)
115+
const startTimeStr = String(Math.floor(batchSpan._startTime))
116+
const dsmEnabled = this.config.dsmEnabled
117+
118+
for (let i = 0; i < messageCount; i++) {
111119
const msg = messages[i]
112-
msg.attributes ??= {}
120+
const attributes = msg.attributes ??= {}
113121

114122
if (!hasTraceContext) {
115-
this.tracer.inject(batchSpan, 'text_map', msg.attributes)
123+
this.tracer.inject(batchSpan, 'text_map', attributes)
116124
}
117125

118-
Object.assign(msg.attributes, {
119-
'_dd.pubsub_request.trace_id': batchTraceIdHex,
120-
'_dd.pubsub_request.span_id': batchSpanIdHex,
121-
'_dd.batch.size': String(messageCount),
122-
'_dd.batch.index': String(i),
123-
'gcloud.project_id': projectId,
124-
'pubsub.topic': topic,
125-
'x-dd-publish-start-time': String(Math.floor(batchSpan._startTime)),
126-
})
126+
// Assign keys one-by-one rather than via `Object.assign({...})` so V8
127+
// keeps the attributes object on its existing hidden class instead of
128+
// allocating a fresh literal per message.
129+
attributes['_dd.pubsub_request.trace_id'] = batchTraceIdHex
130+
attributes['_dd.pubsub_request.span_id'] = batchSpanIdHex
131+
attributes['_dd.batch.size'] = messageCountStr
132+
attributes['_dd.batch.index'] = String(i)
133+
attributes['gcloud.project_id'] = projectId
134+
attributes['pubsub.topic'] = topic
135+
attributes['x-dd-publish-start-time'] = startTimeStr
127136

128137
if (batchTraceIdUpper) {
129-
msg.attributes['_dd.pubsub_request.p.tid'] = batchTraceIdUpper
138+
attributes['_dd.pubsub_request.p.tid'] = batchTraceIdUpper
130139
}
131140

132-
if (this.config.dsmEnabled) {
141+
if (dsmEnabled) {
133142
const dataStreamsContext = this.tracer.setCheckpoint(
134143
['direction:out', `topic:${topic}`, 'type:google-pubsub'],
135144
batchSpan,
136145
getHeadersSize(msg)
137146
)
138-
DsmPathwayCodec.encode(dataStreamsContext, msg.attributes)
147+
DsmPathwayCodec.encode(dataStreamsContext, attributes)
139148
}
140149
}
141150

0 commit comments

Comments
 (0)