pubsub: "Operation attempted on ended Span" when a modack is flushed after the message was acked
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 2/5
- 预计耗时
- 1-3 小时
- 新手友好度
- 82/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- node.js, typescript
- 领域
- backend, observability
调研方向
阅读 handwritten/pubsub/src/telemetry-tracing.ts 中 createAckRpcSpan 和 createModackRpcSpan 附近的内容,然后检查 handwritten/pubsub/src/message-queues.ts 和 subscriber.ts 中描述的队列流程。使用链接的 gist 或同等的针对性测试复现 span 已结束的情况。完成标准是:ack 和 modack tracing 不会调用已结束 span 的方法,并且测试确认 SDK 不再记录该错误。
由索引模型根据 Issue 内容生成。
描述
Please make sure you have searched for information in the following guides.
- Search the issues already opened: https://github.com/GoogleCloudPlatform/google-cloud-node/issues
- Check the API's issue tracker (related, but publisher-side only: googleapis/nodejs-pubsub#2110, googleapis/nodejs-pubsub#2119)
Library Name
@google-cloud/pubsub (reproduced on 6.0.1 and 6.4.1)
A screenshot that you have tested with "Try this API".
N/A. This is a client-side bug in the library's OpenTelemetry instrumentation. No API call is needed to reproduce it.
Link to the code that reproduces this issue. A link to a public Github Repository or gist with a minimal reproduction.
https://gist.github.com/tmair/b9c1cd2b558e40eb8423566580d0a244
A step-by-step description of how to reproduce the issue, based on the linked reproduction.
npm install && npm start- The repro registers an SDK
BasicTracerProvider, enables pubsub tracing, starts a subscribe span and ends it. This simulates a message that was acked while a modack for it was still queued. It then callsPubsubSpans.createModackRpcSpan([subscribeSpan], ...), which is whatModAckQueue._sendBatchdoes when the queue flushes. - The OpenTelemetry SDK logs:
Cannot execute the operation on ended Span {traceId: ..., spanId: ...} Error: Operation attempted on ended Span {traceId: ..., spanId: ...}
at SpanImpl._isSpanEnded (.../@opentelemetry/sdk-trace/build/src/Span.js:342:27)
at SpanImpl.addLink (.../@opentelemetry/sdk-trace/build/src/Span.js:170:18)
at .../@google-cloud/pubsub/build/src/telemetry-tracing.js:421:23
at Array.forEach (<anonymous>)
at PubsubSpans.createModackRpcSpan (...)
We see this in production on a subscriber created with enableOpenTelemetryTracing: true and @opentelemetry/sdk-node (sdk-trace 2.11.0). It happens for messages whose processing takes long enough to get a lease extension. The stack trace from production:
Error: Operation attempted on ended Span {traceId: 8d525b04ad08368f161db86c3d9c950d, spanId: 6419191f508e8c82}
at SpanImpl._isSpanEnded (/app/otel-instrumentation/node_modules/@opentelemetry/sdk-trace/build/src/Span.js:342:27)
at SpanImpl.addLink (/app/otel-instrumentation/node_modules/@opentelemetry/sdk-trace/build/src/Span.js:170:18)
at /app/node_modules/@google-cloud/pubsub/build/src/telemetry-tracing.js:420:23
at Array.forEach (<anonymous>)
at PubsubSpans.createModackRpcSpan (/app/node_modules/@google-cloud/pubsub/build/src/telemetry-tracing.js:418:26)
at /app/node_modules/@google-cloud/pubsub/build/src/message-queues.js:538:54
at Array.map (<anonymous>)
at ModAckQueue._sendBatch (/app/node_modules/@google-cloud/pubsub/build/src/message-queues.js:533:57)
at async ModAckQueue.flush (/app/node_modules/@google-cloud/pubsub/build/src/message-queues.js:262:29)
A clear and concise description of what the bug is, and what you expected to happen.
The ack and modack queues flush independently, but both keep a reference to the message's subscribe span:
- The lease manager extends the deadline of a message that is still being processed.
MessageQueue.addstoresmessage.parentSpanastracingSpanin the modack batch (message-queues.ts#L254). - The handler calls
message.ack().Subscriber.ackwaits only for the ack queue to flush and then callsmessage.endParentSpan()(subscriber.ts#L889-L892). - The modack queue flushes afterwards.
createModackRpcSpancallsm.addLink(...)on the subscribe span that has already ended (telemetry-tracing.ts#L602-L608).
The OpenTelemetry SDK rejects the addLink call and logs an error through diag. Nothing is thrown and the messages are acked correctly, but every affected message produces an error log entry, which shows up as an error group in Cloud Error Reporting. createAckRpcSpan uses the same pattern (telemetry-tracing.ts#L554-L560) and could hit the same problem for a span that ended in another way (e.g. nack or shutdown).
Expected: the library does not call methods on spans that it has already ended, so no errors are logged.
Suggested fix: skip spans that are no longer recording in createModackRpcSpan and createAckRpcSpan:
messageSpans.forEach(m => {
if (m && isSampled(m) && m.isRecording()) {
m.addLink({context: span.spanContext()});
}
});
An SDK span returns isRecording() === false once it has ended, so this skips exactly the failing case. The link from the subscribe span to the RPC span is lost for those messages, but the subscribe span has already been exported at that point anyway.
A clear and concise description WHY you expect this behavior, i.e., was it a recent change, there is documentation that points to this behavior, etc. **
The OpenTelemetry specification does not allow changes to a span after end() has been called ("Implementations SHOULD ignore all subsequent calls to End and any other Span methods"), and the SDK reports such calls as errors. The library ends the subscribe span itself in Subscriber.ack, so it should not modify that span afterwards. The same class of bug was reported for the publisher side in googleapis/nodejs-pubsub#2110 and googleapis/nodejs-pubsub#2119.
- 主要语言
- TypeScript
- 星标
- 3.2k
- 派生
- 732
- 平均合并
- 3 天 7 小时
- 30 天内合并 PR
- 117
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
googleapis/google-cloud-node 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 66/100
googleapis/google-cloud-node#9854 ·
维护者通常 1 天内回复
-
storage: V4 getSignedUrl sorts canonical headers with localeCompare, so they disagree with X-Goog-SignedHeaders可能已有人在做 @itsrayeedscodespace 于 6 天前认领。 未关闭api: storage
难度 2/5 1-3 小时 新手友好度 82/100
googleapis/google-cloud-node#9840 · 1 条评论 ·
维护者通常 1 天内回复
-
firestore: getPartitions() emits gax AutopaginateTrueWarning in every process可能已有人在做 @TheRealDanii 于 9 天前认领。 未关闭api: firestore
难度 2/5 1-3 小时 新手友好度 76/100
googleapis/google-cloud-node#9736 ·
维护者通常 1 天内回复
-
难度 1/5 1 小时以内 新手友好度 82/100
googleapis/google-cloud-node#9723 ·
维护者通常 1 天内回复
-
spanner: requiring @google-cloud/spanner stops the process from exiting on SIGTERM/SIGINT可能重新可做 @alkatrivedi 于 31 天前认领,目前没有进行中的 PR。 未关闭api: spanner
难度 2/5 1-3 小时 新手友好度 78/100
googleapis/google-cloud-node#9513 · 1 个 reaction · 已指派 1 人 ·
维护者通常 1 天内回复
查看 googleapis/google-cloud-node 的全部 Issue
相似的 Issue
-
bug UI
难度 2/5 1-3 小时 新手友好度 70/100
wardian-app/Wardian#1746 ·
维护者通常 1 天内回复
-
area: interop area: sip-core v3
难度 2/5 1-3 小时 新手友好度 62/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 62/100
-
难度 2/5 1-3 小时 新手友好度 68/100
维护者通常 1 天内回复
-
clawsweeper:fix-shape-clear clawsweeper:queueable-fix clawsweeper:source-repro impact:ux-friction issue-rating: 🦞 diamond lobster no-stale P2
难度 2/5 1-3 小时 新手友好度 72/100
openclaw/openclaw#198551 · 1 条评论 · 1 个 reaction ·
维护者通常 1 天内回复