mirror of
				https://github.com/oarkflow/mq.git
				synced 2025-11-01 02:12:32 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			456 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			456 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package dag
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // WorkflowEngineAdapter implements the WorkflowEngine interface
 | |
| // This adapter bridges between the DAG system and the external workflow engine
 | |
| type WorkflowEngineAdapter struct {
 | |
| 	// External workflow engine import (when available)
 | |
| 	// workflowEngine *workflow.WorkflowEngine
 | |
| 
 | |
| 	// Configuration
 | |
| 	config             *WorkflowEngineAdapterConfig
 | |
| 	stateManager       *WorkflowStateManager
 | |
| 	persistenceManager *PersistenceManager
 | |
| 
 | |
| 	// In-memory state for when external engine is not available
 | |
| 	definitions map[string]*WorkflowDefinition
 | |
| 	executions  map[string]*ExecutionResult
 | |
| 
 | |
| 	// Thread safety
 | |
| 	mu sync.RWMutex
 | |
| 
 | |
| 	// Status
 | |
| 	running bool
 | |
| }
 | |
| 
 | |
| // WorkflowEngineAdapterConfig contains configuration for the adapter
 | |
| type WorkflowEngineAdapterConfig struct {
 | |
| 	UseExternalEngine   bool
 | |
| 	EnablePersistence   bool
 | |
| 	PersistenceType     string // "memory", "file", "database"
 | |
| 	PersistencePath     string
 | |
| 	EnableStateRecovery bool
 | |
| 	MaxExecutions       int
 | |
| }
 | |
| 
 | |
| // PersistenceManager handles workflow and execution persistence
 | |
| type PersistenceManager struct {
 | |
| 	config  *WorkflowEngineAdapterConfig
 | |
| 	storage PersistenceStorage
 | |
| 	mu      sync.RWMutex
 | |
| }
 | |
| 
 | |
| // PersistenceStorage interface for different storage backends
 | |
| type PersistenceStorage interface {
 | |
| 	SaveWorkflow(definition *WorkflowDefinition) error
 | |
| 	LoadWorkflow(id string) (*WorkflowDefinition, error)
 | |
| 	ListWorkflows() ([]*WorkflowDefinition, error)
 | |
| 	DeleteWorkflow(id string) error
 | |
| 
 | |
| 	SaveExecution(execution *ExecutionResult) error
 | |
| 	LoadExecution(id string) (*ExecutionResult, error)
 | |
| 	ListExecutions(workflowID string) ([]*ExecutionResult, error)
 | |
| 	DeleteExecution(id string) error
 | |
| }
 | |
| 
 | |
| // MemoryPersistenceStorage implements in-memory persistence
 | |
| type MemoryPersistenceStorage struct {
 | |
| 	workflows  map[string]*WorkflowDefinition
 | |
| 	executions map[string]*ExecutionResult
 | |
| 	mu         sync.RWMutex
 | |
| }
 | |
| 
 | |
| // NewWorkflowEngineAdapter creates a new workflow engine adapter
 | |
| func NewWorkflowEngineAdapter(config *WorkflowEngineAdapterConfig) *WorkflowEngineAdapter {
 | |
| 	if config == nil {
 | |
| 		config = &WorkflowEngineAdapterConfig{
 | |
| 			UseExternalEngine:   false,
 | |
| 			EnablePersistence:   true,
 | |
| 			PersistenceType:     "memory",
 | |
| 			EnableStateRecovery: true,
 | |
| 			MaxExecutions:       1000,
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	adapter := &WorkflowEngineAdapter{
 | |
| 		config:      config,
 | |
| 		definitions: make(map[string]*WorkflowDefinition),
 | |
| 		executions:  make(map[string]*ExecutionResult),
 | |
| 		stateManager: &WorkflowStateManager{
 | |
| 			stateStore: make(map[string]any),
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	// Initialize persistence manager if enabled
 | |
| 	if config.EnablePersistence {
 | |
| 		adapter.persistenceManager = NewPersistenceManager(config)
 | |
| 	}
 | |
| 
 | |
| 	return adapter
 | |
| }
 | |
| 
 | |
| // NewPersistenceManager creates a new persistence manager
 | |
| func NewPersistenceManager(config *WorkflowEngineAdapterConfig) *PersistenceManager {
 | |
| 	pm := &PersistenceManager{
 | |
| 		config: config,
 | |
| 	}
 | |
| 
 | |
| 	// Initialize storage backend based on configuration
 | |
| 	switch config.PersistenceType {
 | |
| 	case "memory":
 | |
| 		pm.storage = NewMemoryPersistenceStorage()
 | |
| 	case "file":
 | |
| 		// TODO: Implement file-based storage
 | |
| 		pm.storage = NewMemoryPersistenceStorage()
 | |
| 	case "database":
 | |
| 		// TODO: Implement database storage
 | |
| 		pm.storage = NewMemoryPersistenceStorage()
 | |
| 	default:
 | |
| 		pm.storage = NewMemoryPersistenceStorage()
 | |
| 	}
 | |
| 
 | |
| 	return pm
 | |
| }
 | |
| 
 | |
| // NewMemoryPersistenceStorage creates a new memory-based persistence storage
 | |
| func NewMemoryPersistenceStorage() *MemoryPersistenceStorage {
 | |
| 	return &MemoryPersistenceStorage{
 | |
| 		workflows:  make(map[string]*WorkflowDefinition),
 | |
| 		executions: make(map[string]*ExecutionResult),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // WorkflowEngine interface implementation
 | |
| func (a *WorkflowEngineAdapter) Start(ctx context.Context) error {
 | |
| 	a.mu.Lock()
 | |
| 	defer a.mu.Unlock()
 | |
| 
 | |
| 	if a.running {
 | |
| 		return fmt.Errorf("workflow engine adapter is already running")
 | |
| 	}
 | |
| 
 | |
| 	// Load persisted workflows if enabled
 | |
| 	if a.config.EnablePersistence && a.config.EnableStateRecovery {
 | |
| 		if err := a.recoverState(); err != nil {
 | |
| 			return fmt.Errorf("failed to recover state: %w", err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	a.running = true
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (a *WorkflowEngineAdapter) Stop(ctx context.Context) {
 | |
| 	a.mu.Lock()
 | |
| 	defer a.mu.Unlock()
 | |
| 
 | |
| 	if !a.running {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Save state before stopping
 | |
| 	if a.config.EnablePersistence {
 | |
| 		a.saveState()
 | |
| 	}
 | |
| 
 | |
| 	a.running = false
 | |
| }
 | |
| 
 | |
| func (a *WorkflowEngineAdapter) RegisterWorkflow(ctx context.Context, definition *WorkflowDefinition) error {
 | |
| 	a.mu.Lock()
 | |
| 	defer a.mu.Unlock()
 | |
| 
 | |
| 	if definition.ID == "" {
 | |
| 		return fmt.Errorf("workflow ID is required")
 | |
| 	}
 | |
| 
 | |
| 	// Store in memory
 | |
| 	a.definitions[definition.ID] = definition
 | |
| 
 | |
| 	// Persist if enabled
 | |
| 	if a.config.EnablePersistence && a.persistenceManager != nil {
 | |
| 		if err := a.persistenceManager.SaveWorkflow(definition); err != nil {
 | |
| 			return fmt.Errorf("failed to persist workflow: %w", err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (a *WorkflowEngineAdapter) ExecuteWorkflow(ctx context.Context, workflowID string, input map[string]any) (*ExecutionResult, error) {
 | |
| 	a.mu.RLock()
 | |
| 	definition, exists := a.definitions[workflowID]
 | |
| 	a.mu.RUnlock()
 | |
| 
 | |
| 	if !exists {
 | |
| 		return nil, fmt.Errorf("workflow %s not found", workflowID)
 | |
| 	}
 | |
| 
 | |
| 	// Create execution result
 | |
| 	execution := &ExecutionResult{
 | |
| 		ID:         generateExecutionID(),
 | |
| 		WorkflowID: workflowID,
 | |
| 		Status:     ExecutionStatusRunning,
 | |
| 		StartTime:  time.Now(),
 | |
| 		Input:      input,
 | |
| 		Output:     make(map[string]any),
 | |
| 	}
 | |
| 
 | |
| 	// Store execution
 | |
| 	a.mu.Lock()
 | |
| 	a.executions[execution.ID] = execution
 | |
| 	a.mu.Unlock()
 | |
| 
 | |
| 	// Execute asynchronously
 | |
| 	go a.executeWorkflowAsync(ctx, execution, definition)
 | |
| 
 | |
| 	return execution, nil
 | |
| }
 | |
| 
 | |
| func (a *WorkflowEngineAdapter) GetExecution(ctx context.Context, executionID string) (*ExecutionResult, error) {
 | |
| 	a.mu.RLock()
 | |
| 	defer a.mu.RUnlock()
 | |
| 
 | |
| 	execution, exists := a.executions[executionID]
 | |
| 	if !exists {
 | |
| 		return nil, fmt.Errorf("execution %s not found", executionID)
 | |
| 	}
 | |
| 
 | |
| 	return execution, nil
 | |
| }
 | |
| 
 | |
| // executeWorkflowAsync executes a workflow asynchronously
 | |
| func (a *WorkflowEngineAdapter) executeWorkflowAsync(ctx context.Context, execution *ExecutionResult, definition *WorkflowDefinition) {
 | |
| 	defer func() {
 | |
| 		if r := recover(); r != nil {
 | |
| 			execution.Status = ExecutionStatusFailed
 | |
| 			execution.Error = fmt.Sprintf("workflow execution panicked: %v", r)
 | |
| 		}
 | |
| 
 | |
| 		endTime := time.Now()
 | |
| 		execution.EndTime = &endTime
 | |
| 
 | |
| 		// Persist final execution state
 | |
| 		if a.config.EnablePersistence && a.persistenceManager != nil {
 | |
| 			a.persistenceManager.SaveExecution(execution)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	// Simple execution simulation
 | |
| 	// In a real implementation, this would execute the workflow nodes
 | |
| 	for i, node := range definition.Nodes {
 | |
| 		// Simulate node execution
 | |
| 		time.Sleep(time.Millisecond * 100) // Simulate processing time
 | |
| 
 | |
| 		// Update execution with node results
 | |
| 		if execution.NodeExecutions == nil {
 | |
| 			execution.NodeExecutions = make(map[string]any)
 | |
| 		}
 | |
| 
 | |
| 		execution.NodeExecutions[node.ID] = map[string]any{
 | |
| 			"status":     "completed",
 | |
| 			"started_at": time.Now().Add(-time.Millisecond * 100),
 | |
| 			"ended_at":   time.Now(),
 | |
| 			"output":     fmt.Sprintf("Node %s executed successfully", node.Name),
 | |
| 		}
 | |
| 
 | |
| 		// Check for cancellation
 | |
| 		select {
 | |
| 		case <-ctx.Done():
 | |
| 			execution.Status = ExecutionStatusCancelled
 | |
| 			execution.Error = "execution was cancelled"
 | |
| 			return
 | |
| 		default:
 | |
| 		}
 | |
| 
 | |
| 		// Simulate processing
 | |
| 		if i == len(definition.Nodes)-1 {
 | |
| 			// Last node - complete execution
 | |
| 			execution.Status = ExecutionStatusCompleted
 | |
| 			execution.Output = map[string]any{
 | |
| 				"result":         "workflow completed successfully",
 | |
| 				"nodes_executed": len(definition.Nodes),
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // recoverState recovers persisted state
 | |
| func (a *WorkflowEngineAdapter) recoverState() error {
 | |
| 	if a.persistenceManager == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	// Load workflows
 | |
| 	workflows, err := a.persistenceManager.ListWorkflows()
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("failed to load workflows: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	for _, workflow := range workflows {
 | |
| 		a.definitions[workflow.ID] = workflow
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // saveState saves current state
 | |
| func (a *WorkflowEngineAdapter) saveState() {
 | |
| 	if a.persistenceManager == nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Save all workflows
 | |
| 	for _, workflow := range a.definitions {
 | |
| 		a.persistenceManager.SaveWorkflow(workflow)
 | |
| 	}
 | |
| 
 | |
| 	// Save all executions
 | |
| 	for _, execution := range a.executions {
 | |
| 		a.persistenceManager.SaveExecution(execution)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // PersistenceManager methods
 | |
| func (pm *PersistenceManager) SaveWorkflow(definition *WorkflowDefinition) error {
 | |
| 	pm.mu.Lock()
 | |
| 	defer pm.mu.Unlock()
 | |
| 	return pm.storage.SaveWorkflow(definition)
 | |
| }
 | |
| 
 | |
| func (pm *PersistenceManager) LoadWorkflow(id string) (*WorkflowDefinition, error) {
 | |
| 	pm.mu.RLock()
 | |
| 	defer pm.mu.RUnlock()
 | |
| 	return pm.storage.LoadWorkflow(id)
 | |
| }
 | |
| 
 | |
| func (pm *PersistenceManager) ListWorkflows() ([]*WorkflowDefinition, error) {
 | |
| 	pm.mu.RLock()
 | |
| 	defer pm.mu.RUnlock()
 | |
| 	return pm.storage.ListWorkflows()
 | |
| }
 | |
| 
 | |
| func (pm *PersistenceManager) SaveExecution(execution *ExecutionResult) error {
 | |
| 	pm.mu.Lock()
 | |
| 	defer pm.mu.Unlock()
 | |
| 	return pm.storage.SaveExecution(execution)
 | |
| }
 | |
| 
 | |
| func (pm *PersistenceManager) LoadExecution(id string) (*ExecutionResult, error) {
 | |
| 	pm.mu.RLock()
 | |
| 	defer pm.mu.RUnlock()
 | |
| 	return pm.storage.LoadExecution(id)
 | |
| }
 | |
| 
 | |
| // MemoryPersistenceStorage implementation
 | |
| func (m *MemoryPersistenceStorage) SaveWorkflow(definition *WorkflowDefinition) error {
 | |
| 	m.mu.Lock()
 | |
| 	defer m.mu.Unlock()
 | |
| 
 | |
| 	// Deep copy to avoid reference issues
 | |
| 	data, err := json.Marshal(definition)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	var copy WorkflowDefinition
 | |
| 	if err := json.Unmarshal(data, ©); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	m.workflows[definition.ID] = ©
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *MemoryPersistenceStorage) LoadWorkflow(id string) (*WorkflowDefinition, error) {
 | |
| 	m.mu.RLock()
 | |
| 	defer m.mu.RUnlock()
 | |
| 
 | |
| 	workflow, exists := m.workflows[id]
 | |
| 	if !exists {
 | |
| 		return nil, fmt.Errorf("workflow %s not found", id)
 | |
| 	}
 | |
| 
 | |
| 	return workflow, nil
 | |
| }
 | |
| 
 | |
| func (m *MemoryPersistenceStorage) ListWorkflows() ([]*WorkflowDefinition, error) {
 | |
| 	m.mu.RLock()
 | |
| 	defer m.mu.RUnlock()
 | |
| 
 | |
| 	workflows := make([]*WorkflowDefinition, 0, len(m.workflows))
 | |
| 	for _, workflow := range m.workflows {
 | |
| 		workflows = append(workflows, workflow)
 | |
| 	}
 | |
| 
 | |
| 	return workflows, nil
 | |
| }
 | |
| 
 | |
| func (m *MemoryPersistenceStorage) DeleteWorkflow(id string) error {
 | |
| 	m.mu.Lock()
 | |
| 	defer m.mu.Unlock()
 | |
| 
 | |
| 	delete(m.workflows, id)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *MemoryPersistenceStorage) SaveExecution(execution *ExecutionResult) error {
 | |
| 	m.mu.Lock()
 | |
| 	defer m.mu.Unlock()
 | |
| 
 | |
| 	// Deep copy to avoid reference issues
 | |
| 	data, err := json.Marshal(execution)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	var copy ExecutionResult
 | |
| 	if err := json.Unmarshal(data, ©); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	m.executions[execution.ID] = ©
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *MemoryPersistenceStorage) LoadExecution(id string) (*ExecutionResult, error) {
 | |
| 	m.mu.RLock()
 | |
| 	defer m.mu.RUnlock()
 | |
| 
 | |
| 	execution, exists := m.executions[id]
 | |
| 	if !exists {
 | |
| 		return nil, fmt.Errorf("execution %s not found", id)
 | |
| 	}
 | |
| 
 | |
| 	return execution, nil
 | |
| }
 | |
| 
 | |
| func (m *MemoryPersistenceStorage) ListExecutions(workflowID string) ([]*ExecutionResult, error) {
 | |
| 	m.mu.RLock()
 | |
| 	defer m.mu.RUnlock()
 | |
| 
 | |
| 	executions := make([]*ExecutionResult, 0)
 | |
| 	for _, execution := range m.executions {
 | |
| 		if workflowID == "" || execution.WorkflowID == workflowID {
 | |
| 			executions = append(executions, execution)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return executions, nil
 | |
| }
 | |
| 
 | |
| func (m *MemoryPersistenceStorage) DeleteExecution(id string) error {
 | |
| 	m.mu.Lock()
 | |
| 	defer m.mu.Unlock()
 | |
| 
 | |
| 	delete(m.executions, id)
 | |
| 	return nil
 | |
| }
 | 
