fix: improve otel span map cleanup performance#1200
Conversation
Previously the span map was holding all span information in a tree structure. When a span finished, MarkFinished acquired a global write lock and recursively traversed the tree to clean up. The fix usees the already existin spanRecorder to keep track of the spans in the transaction. Instead of linking each child, we keep track of the root transaction and override the ParentSpanID so that the spanRecorder reconstructs the span hierarchy.
Semver Impact of This PR🟢 Patch (bug fixes) 📋 Changelog PreviewThis is how your changes will appear in the changelog. Breaking Changes 🛠
New Features ✨
Bug Fixes 🐛
Internal Changes 🔧Deps
Other
🤖 This preview updates automatically when you update the PR. |
d018672 to
d4f8f67
Compare
| if entry.activeCount.Add(-1) <= 0 { | ||
| // CompareAndSwap(CAS) is used to prevent a race between Set and MarkFinished. | ||
| // The race has two windows: | ||
| // 1. MarkFinished decremented activeCount to 0 but hasn't CAS'd yet -> Set Adds(1), and CAS fails keeping the | ||
| // transaction, since we just added a new span. | ||
| // 2. MarkFinished already CAS'd -> Set will store on the transaction marked for deletion (better than | ||
| // creating a new orphaned span). | ||
| if entry.activeCount.CompareAndSwap(0, -1) { | ||
| ssm.transactions.CompareAndDelete(traceID, entry) | ||
| } |
There was a problem hiding this comment.
@sl0thentr0py @Litarnus would appreciate if you can verify my logic here. Added the fix due to this comment and I think it makes sense.
There was a problem hiding this comment.
It looks good to me, your comments describe it accurately imo
| if entry.activeCount.Add(-1) <= 0 { | ||
| // CompareAndSwap(CAS) is used to prevent a race between Set and MarkFinished. | ||
| // The race has two windows: | ||
| // 1. MarkFinished decremented activeCount to 0 but hasn't CAS'd yet -> Set Adds(1), and CAS fails keeping the | ||
| // transaction, since we just added a new span. | ||
| // 2. MarkFinished already CAS'd -> Set will store on the transaction marked for deletion (better than | ||
| // creating a new orphaned span). | ||
| if entry.activeCount.CompareAndSwap(0, -1) { | ||
| ssm.transactions.CompareAndDelete(traceID, entry) | ||
| } |
There was a problem hiding this comment.
It looks good to me, your comments describe it accurately imo
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
HasSpan should be enough to distinguish if the span should be a child of an active span
Description
Previously the span map was holding all span information in a tree structure. When a span finished, MarkFinished acquired a global write lock and recursively traversed the tree to clean up. The fix usees the already existin spanRecorder to keep track of the spans in the transaction. Instead of linking each child, we keep track of the root transaction and override the ParentSpanID so that the spanRecorder reconstructs the span hierarchy.
Issues
Changelog Entry Instructions
To add a custom changelog entry, uncomment the section above. Supports:
For more details: custom changelog entries
Reminders
feat:,fix:,ref:,meta:)