Files
osmedeus/internal/executor/result_collector_test.go
j3ssie 7de3e7b2b2 feat: add performance optimizations and robustness improvements
- Add lock-free ResultCollector for parallel execution with atomic operations, eliminating mutex contention for pre-allocated slices
- Implement circuit breaker pattern (internal/retry/circuit_breaker) with configurable thresholds and half-open recovery state for fault tolerance
- Introduce json-iterator replacement (internal/json) for 2-6x faster JSON operations while maintaining stdlib compatibility
- Add lazy template rendering (RenderLazy) with variable reference caching for 50-80% faster rendering on large contexts
- Implement memory-efficient buffer pooling (bufpool) with 10MB pre-allocated reusable buffers for reduced GC pressure
- Add LoadFlowWithModules for parallel module pre-loading using errgroup, improving startup time for complex flows
- Add VarRefCache with LRU eviction for variable extraction caching
- Add streaming output support for foreach loops to process large datasets without memory accumulation
- Fix json import compatibility in llm_executor and db_functions
- Update test fixtures with correct YAML field names (call→function, run→command)
2026-01-31 01:26:28 +07:00

97 lines
2.1 KiB
Go

package executor
import (
"sync"
"testing"
"time"
"github.com/j3ssie/osmedeus/v5/internal/core"
)
func TestResultCollectorConcurrent(t *testing.T) {
stepCount := 100
collector := NewResultCollector(stepCount)
var wg sync.WaitGroup
for i := 0; i < stepCount; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
result := &core.StepResult{
StepName: "step-" + string(rune('a'+idx%26)),
Status: core.StepStatusSuccess,
}
collector.Add(result)
}(i)
}
wg.Wait()
results := collector.Results()
if len(results) != stepCount {
t.Errorf("expected %d results, got %d", stepCount, len(results))
}
if collector.Count() != stepCount {
t.Errorf("expected count %d, got %d", stepCount, collector.Count())
}
}
func TestResultCollectorOrder(t *testing.T) {
names := []string{"step-a", "step-b", "step-c"}
collector := NewOrderedResultCollector(names)
// Add in reverse order
collector.Add("step-c", &core.StepResult{StepName: "step-c"})
collector.Add("step-a", &core.StepResult{StepName: "step-a"})
collector.Add("step-b", &core.StepResult{StepName: "step-b"})
results := collector.Results()
// Should be in original order
for i, r := range results {
if r.StepName != names[i] {
t.Errorf("position %d: expected %s, got %s", i, names[i], r.StepName)
}
}
}
func BenchmarkResultCollectorLockFree(b *testing.B) {
b.Run("lock-free", func(b *testing.B) {
collector := NewResultCollector(b.N)
var wg sync.WaitGroup
for i := 0; i < b.N; i++ {
wg.Add(1)
go func() {
defer wg.Done()
collector.Add(&core.StepResult{
StepName: "test",
Status: core.StepStatusSuccess,
StartTime: time.Now(),
})
}()
}
wg.Wait()
})
b.Run("mutex", func(b *testing.B) {
var mu sync.Mutex
results := make([]*core.StepResult, 0, b.N)
var wg sync.WaitGroup
for i := 0; i < b.N; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
results = append(results, &core.StepResult{
StepName: "test",
Status: core.StepStatusSuccess,
StartTime: time.Now(),
})
mu.Unlock()
}()
}
wg.Wait()
})
}