diff --git a/trace/KNOWN_ISSUES.md b/trace/KNOWN_ISSUES.md new file mode 100644 index 00000000..fdaa86b1 --- /dev/null +++ b/trace/KNOWN_ISSUES.md @@ -0,0 +1,107 @@ +# Trace Module - Known Issues + +This document tracks known issues in the Trace module that are scheduled for refactoring. + +## Goroutine Leak + +### 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 + +### Evidence + +``` +Goroutine growth by function: +Function Initial Final Growth +-------------------------------------------------------------------------- +github.com/yaoapp/yao/trace. 0 10 10 +github.com/yaoapp/yao/trace/pubsub. 0 10 10 +``` + +### Root Cause + +- Goroutines exit when `Release()` closes their channels +- Exit is **asynchronous** (goroutine needs to reach select statement) +- Go runtime needs time to schedule and cleanup +- In rapid iterations, new goroutines are created before old ones fully exit + +### Current Behavior + +- **NOT a true leak**: Goroutines eventually exit (channels are closed) +- **No unbounded growth**: They will be GC'd eventually +- **Typical pattern**: Async cleanup in Go + +### Impact on Tests + +Memory leak tests use a 20KB/iteration threshold to accommodate this overhead: + +| Test | Actual Growth | Threshold | +| ----------------- | -------------- | --------- | +| StandardMode | ~11-15 KB/iter | 20 KB | +| BusinessScenarios | ~13-16 KB/iter | 20 KB | +| NestedCalls | ~13 KB/iter | 20 KB | + +## Memory Growth + +### Symptom + +Linear memory growth during trace operations: + +``` +Batch | Iterations | HeapAlloc (MB) | Growth/iter (bytes) +------|------------|----------------|-------------------- + 1 | 1000 | 23.28 | 12014.42 + 2 | 2000 | 37.06 | 13229.02 + 3 | 3000 | 50.63 | 13562.39 + 4 | 4000 | 64.54 | 13819.49 + 5 | 5000 | 78.15 | 13910.01 +``` + +### Root Cause + +Trace-related objects are not fully released during `ctx.Release()`: + +- State machine data +- PubSub subscriptions +- Trace node references + +### Workaround + +The 20KB threshold in memory leak tests accommodates this known overhead while still detecting severe leaks (50KB+ growth would indicate a real problem). + +## Planned Refactoring + +The Trace module is scheduled for refactoring to address: + +1. **Synchronous cleanup**: Ensure goroutines exit before `Release()` returns +2. **Memory management**: Properly release all trace-related objects +3. **Resource pooling**: Consider reusing trace resources to reduce allocation overhead + +## 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) + +## Related Files + +- `trace/manager.go` - State machine and goroutine management +- `trace/pubsub/pubsub.go` - PubSub forwarding goroutine +- `trace/trace.go` - Release() implementation +- `agent/assistant/hook/create_mem_test.go` - Memory leak tests + +--- + +_Last updated: December 2025_