mirror of
https://github.com/j3ssie/osmedeus.git
synced 2026-09-09 11:17:47 +02:00
- 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)
103 lines
2.7 KiB
Go
103 lines
2.7 KiB
Go
package executor
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"github.com/j3ssie/osmedeus/v5/internal/core"
|
|
)
|
|
|
|
// ResultCollector provides lock-free collection of step results for parallel execution.
|
|
// Uses pre-allocated slice with atomic index for O(1) appends without mutex contention.
|
|
type ResultCollector struct {
|
|
results []*core.StepResult
|
|
index int64
|
|
size int
|
|
}
|
|
|
|
// NewResultCollector creates a collector pre-allocated for the expected number of steps
|
|
func NewResultCollector(stepCount int) *ResultCollector {
|
|
return &ResultCollector{
|
|
results: make([]*core.StepResult, stepCount),
|
|
index: 0,
|
|
size: stepCount,
|
|
}
|
|
}
|
|
|
|
// Add atomically adds a step result to the collector.
|
|
// Thread-safe without mutex - uses atomic operations.
|
|
// Returns the index where the result was stored.
|
|
func (c *ResultCollector) Add(result *core.StepResult) int {
|
|
// Atomically get next index
|
|
idx := int(atomic.AddInt64(&c.index, 1) - 1)
|
|
|
|
// Bounds check (should never happen if stepCount is correct)
|
|
if idx < c.size {
|
|
c.results[idx] = result
|
|
}
|
|
|
|
return idx
|
|
}
|
|
|
|
// Results returns all collected results, filtering nil entries.
|
|
// Call this only after all goroutines have completed.
|
|
func (c *ResultCollector) Results() []*core.StepResult {
|
|
count := int(atomic.LoadInt64(&c.index))
|
|
if count > c.size {
|
|
count = c.size
|
|
}
|
|
|
|
// Filter out nil entries (from pre-allocation)
|
|
results := make([]*core.StepResult, 0, count)
|
|
for i := 0; i < count; i++ {
|
|
if c.results[i] != nil {
|
|
results = append(results, c.results[i])
|
|
}
|
|
}
|
|
return results
|
|
}
|
|
|
|
// Count returns the number of results added
|
|
func (c *ResultCollector) Count() int {
|
|
return int(atomic.LoadInt64(&c.index))
|
|
}
|
|
|
|
// OrderedResultCollector preserves insertion order using step name mapping.
|
|
// Useful when result order must match step execution order.
|
|
type OrderedResultCollector struct {
|
|
results map[string]*core.StepResult
|
|
mu sync.RWMutex
|
|
order []string
|
|
capacity int
|
|
}
|
|
|
|
// NewOrderedResultCollector creates a collector that preserves step ordering
|
|
func NewOrderedResultCollector(stepNames []string) *OrderedResultCollector {
|
|
return &OrderedResultCollector{
|
|
results: make(map[string]*core.StepResult, len(stepNames)),
|
|
order: stepNames,
|
|
capacity: len(stepNames),
|
|
}
|
|
}
|
|
|
|
// Add adds a result for a specific step
|
|
func (c *OrderedResultCollector) Add(stepName string, result *core.StepResult) {
|
|
c.mu.Lock()
|
|
c.results[stepName] = result
|
|
c.mu.Unlock()
|
|
}
|
|
|
|
// Results returns results in the original step order
|
|
func (c *OrderedResultCollector) Results() []*core.StepResult {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
|
|
results := make([]*core.StepResult, 0, len(c.order))
|
|
for _, name := range c.order {
|
|
if r, ok := c.results[name]; ok && r != nil {
|
|
results = append(results, r)
|
|
}
|
|
}
|
|
return results
|
|
}
|