yao/trace/KNOWN_ISSUES.md
Max ccb1a9404c Refactor Trace module to mitigate goroutine leaks and reduce memory growth
- Update KNOWN_ISSUES.md to reflect the mitigation of goroutine leaks and reduced memory growth, detailing the current status and residual behaviors.
- Enhance the state worker lifecycle in manager.go to ensure proper command processing and cleanup.
- Implement a safe three-step shutdown sequence in trace.go for the Release function, improving resource management and preventing premature exits.
- Adjust memory leak test thresholds in KNOWN_ISSUES.md to accommodate improved performance metrics.
2026-02-22 20:37:02 +08:00

3.5 KiB

Trace Module - Known Issues

This document tracks known issues in the Trace module that are scheduled for refactoring.

Goroutine Leak (Mitigated)

Symptom

Each trace creation starts 2 goroutines that accumulate during rapid iterations:

  1. trace/pubsub.(*PubSub).forward() - PubSub event forwarding
  2. trace.(*manager).startStateWorker() - State machine worker

Current Status

Fixed in Feb 2026: State worker now uses for range on the command channel, which exits cleanly when Release() closes the channel. The three-step shutdown sequence (atomic flag -> cancel -> close) ensures:

  • No new commands are accepted after the flag is set
  • In-flight safeSend calls unblock via ctx.Done
  • Worker drains remaining buffered commands before exiting

Residual Behavior

  • PubSub forward() goroutine still exits asynchronously after Stop() closes its channel
  • In rapid create/release loops, there may be a brief overlap where old goroutines haven't exited before new ones start. This is normal Go async cleanup behavior.
  • NOT a true leak: goroutines eventually exit (channels are closed)

Impact on Tests

Memory leak tests use a 20KB/iteration threshold to accommodate this overhead:

Test Actual Growth Threshold
StandardMode ~15 bytes/iter 20 KB
BusinessScenarios ~80 bytes/iter 20 KB
NestedCalls ~13 KB/iter 20 KB

Memory Growth (Reduced)

Symptom

Linear memory growth during trace operations.

Current Status

Improved in Feb 2026: The state worker lifecycle fix eliminates the scenario where the worker exits prematurely while the channel remains open, which could cause command objects to accumulate in the buffer without being consumed.

The three-step shutdown ensures all buffered commands are processed before the worker exits, reducing memory retention from unconsumed channel entries.

Residual Growth Sources

  • PubSub subscription objects (cleaned up on Stop)
  • Driver I/O buffers (transient, GC-eligible)
  • Trace node references in memory state (released on worker exit)

Workaround

The 20KB threshold in memory leak tests accommodates known overhead while still detecting severe leaks (50KB+ growth would indicate a real problem).

Planned Refactoring

The Trace module is scheduled for further refactoring:

  1. Global Event Service: Decouple event broadcasting from trace manager into a process-level daemon (separate plan)
  2. Resource pooling: Consider reusing trace resources to reduce allocation overhead
  3. PubSub synchronous cleanup: Ensure forward() goroutine exits before Stop() returns

Testing Notes

When running memory leak tests:

  • TestMemoryLeakStandardMode: 20KB threshold
  • TestMemoryLeakBusinessScenarios: 20KB threshold
  • TestMemoryLeakNestedCalls: 20KB threshold
  • TestMemoryLeakNestedConcurrent: 25KB threshold (concurrent + DB operations)

These thresholds are intentionally higher than actual growth to:

  1. Accommodate CI environment variations
  2. Allow for GC timing differences
  3. Still catch severe leaks (50KB+ would be concerning)
  • trace/manager.go - State machine and goroutine management
  • trace/state.go - Channel-based state worker and safeSend
  • trace/trace.go - Release() three-step shutdown
  • trace/pubsub/pubsub.go - PubSub forwarding goroutine
  • trace/trace_lifecycle_test.go - Boundary condition tests for shutdown races
  • trace/BUGFIX.md - Detailed bug analysis and fix documentation

Last updated: February 2026