platform/doc/process_package_specification.md
2024-11-25 20:33:45 -07:00

10 KiB

Process Package Specification

Overview

The Process package provides a robust system process handling system that matches Laravel's process functionality. It supports process execution, input/output handling, process pools, and signal handling while integrating with our Event and Queue packages.

Related Documentation

Core Features

1. Process Manager

/// Core process manager implementation
class ProcessManager implements ProcessContract {
  /// Container instance
  final Container _container;
  
  /// Active processes
  final Map<int, Process> _processes = {};
  
  /// Process event dispatcher
  final EventDispatcherContract _events;
  
  ProcessManager(this._container, this._events);
  
  /// Starts a process
  Future<Process> start(
    String command, [
    List<String>? arguments,
    ProcessOptions? options
  ]) async {
    options ??= ProcessOptions();
    
    var process = await Process.start(
      command,
      arguments ?? [],
      workingDirectory: options.workingDirectory,
      environment: options.environment,
      includeParentEnvironment: options.includeParentEnvironment,
      runInShell: options.runInShell
    );
    
    _processes[process.pid] = process;
    await _events.dispatch(ProcessStarted(process));
    
    return process;
  }
  
  /// Runs a process to completion
  Future<ProcessResult> run(
    String command, [
    List<String>? arguments,
    ProcessOptions? options
  ]) async {
    var process = await start(command, arguments, options);
    var result = await process.exitCode;
    
    await _events.dispatch(ProcessCompleted(
      process,
      result
    ));
    
    return ProcessResult(
      process.pid,
      result,
      await _readOutput(process.stdout),
      await _readOutput(process.stderr)
    );
  }
  
  /// Kills a process
  Future<void> kill(int pid, [ProcessSignal signal = ProcessSignal.sigterm]) async {
    var process = _processes[pid];
    if (process == null) return;
    
    process.kill(signal);
    await _events.dispatch(ProcessKilled(process));
    _processes.remove(pid);
  }
  
  /// Gets active processes
  List<Process> get activeProcesses => List.from(_processes.values);
  
  /// Reads process output
  Future<String> _readOutput(Stream<List<int>> stream) async {
    var buffer = StringBuffer();
    await for (var data in stream) {
      buffer.write(String.fromCharCodes(data));
    }
    return buffer.toString();
  }
}

2. Process Pool

/// Process pool for parallel execution
class ProcessPool {
  /// Maximum concurrent processes
  final int concurrency;
  
  /// Process manager
  final ProcessManager _manager;
  
  /// Active processes
  final Set<Process> _active = {};
  
  /// Pending commands
  final Queue<PendingCommand> _pending = Queue();
  
  ProcessPool(this._manager, {this.concurrency = 5});
  
  /// Starts a process in the pool
  Future<ProcessResult> start(
    String command, [
    List<String>? arguments,
    ProcessOptions? options
  ]) async {
    var pending = PendingCommand(
      command,
      arguments,
      options
    );
    
    _pending.add(pending);
    await _processQueue();
    
    return await pending.future;
  }
  
  /// Processes pending commands
  Future<void> _processQueue() async {
    while (_active.length < concurrency && _pending.isNotEmpty) {
      var command = _pending.removeFirst();
      await _startProcess(command);
    }
  }
  
  /// Starts a process
  Future<void> _startProcess(PendingCommand command) async {
    var process = await _manager.start(
      command.command,
      command.arguments,
      command.options
    );
    
    _active.add(process);
    
    process.exitCode.then((result) {
      _active.remove(process);
      command.complete(ProcessResult(
        process.pid,
        result,
        '',
        ''
      ));
      _processQueue();
    });
  }
}

3. Process Events

/// Process started event
class ProcessStarted {
  /// The started process
  final Process process;
  
  ProcessStarted(this.process);
}

/// Process completed event
class ProcessCompleted {
  /// The completed process
  final Process process;
  
  /// Exit code
  final int exitCode;
  
  ProcessCompleted(this.process, this.exitCode);
}

/// Process killed event
class ProcessKilled {
  /// The killed process
  final Process process;
  
  ProcessKilled(this.process);
}

/// Process failed event
class ProcessFailed {
  /// The failed process
  final Process process;
  
  /// Error details
  final Object error;
  
  ProcessFailed(this.process, this.error);
}

4. Process Options

/// Process execution options
class ProcessOptions {
  /// Working directory
  final String? workingDirectory;
  
  /// Environment variables
  final Map<String, String>? environment;
  
  /// Include parent environment
  final bool includeParentEnvironment;
  
  /// Run in shell
  final bool runInShell;
  
  /// Process timeout
  final Duration? timeout;
  
  /// Idle timeout
  final Duration? idleTimeout;
  
  /// Retry attempts
  final int retryAttempts;
  
  /// Retry delay
  final Duration retryDelay;
  
  ProcessOptions({
    this.workingDirectory,
    this.environment,
    this.includeParentEnvironment = true,
    this.runInShell = false,
    this.timeout,
    this.idleTimeout,
    this.retryAttempts = 0,
    this.retryDelay = const Duration(seconds: 1)
  });
}

Integration Examples

1. Basic Process Execution

// Run process
var result = await processManager.run('ls', ['-la']);
print('Output: ${result.stdout}');
print('Exit code: ${result.exitCode}');

// Start long-running process
var process = await processManager.start('server', ['--port=8080']);
await process.exitCode; // Wait for completion

2. Process Pool

// Create process pool
var pool = ProcessPool(processManager, concurrency: 3);

// Run multiple processes
await Future.wait([
  pool.start('task1'),
  pool.start('task2'),
  pool.start('task3'),
  pool.start('task4') // Queued until slot available
]);

3. Process Events

// Listen for process events
events.listen<ProcessStarted>((event) {
  print('Process ${event.process.pid} started');
});

events.listen<ProcessCompleted>((event) {
  print('Process ${event.process.pid} completed with code ${event.exitCode}');
});

// Start process
await processManager.start('long-task');

Testing

void main() {
  group('Process Manager', () {
    test('runs processes', () async {
      var manager = ProcessManager(container, events);
      
      var result = await manager.run('echo', ['Hello']);
      
      expect(result.exitCode, equals(0));
      expect(result.stdout, contains('Hello'));
    });
    
    test('handles process failure', () async {
      var manager = ProcessManager(container, events);
      
      expect(
        () => manager.run('invalid-command'),
        throwsA(isA<ProcessException>())
      );
    });
  });
  
  group('Process Pool', () {
    test('limits concurrent processes', () async {
      var pool = ProcessPool(manager, concurrency: 2);
      var started = <String>[];
      
      events.listen<ProcessStarted>((event) {
        started.add(event.process.pid.toString());
      });
      
      await Future.wait([
        pool.start('task1'),
        pool.start('task2'),
        pool.start('task3')
      ]);
      
      expect(started.length, equals(3));
      expect(started.take(2).length, equals(2));
    });
  });
}

Next Steps

  1. Implement core process features
  2. Add process pool
  3. Add process events
  4. Add retry handling
  5. Write tests
  6. Add benchmarks

Development Guidelines

1. Getting Started

Before implementing process features:

  1. Review Getting Started Guide
  2. Check Laravel Compatibility Roadmap
  3. Follow Testing Guide
  4. Use Foundation Integration Guide
  5. Review Events Package Specification
  6. Review Queue Package Specification

2. Implementation Process

For each process feature:

  1. Write tests following Testing Guide
  2. Implement following Laravel patterns
  3. Document following Getting Started Guide
  4. Integrate following Foundation Integration Guide

3. Quality Requirements

All implementations must:

  1. Pass all tests (see Testing Guide)
  2. Meet Laravel compatibility requirements
  3. Follow integration patterns (see Foundation Integration Guide)
  4. Support event integration (see Events Package Specification)
  5. Support queue integration (see Queue Package Specification)

4. Integration Considerations

When implementing process features:

  1. Follow patterns in Foundation Integration Guide
  2. Ensure Laravel compatibility per Laravel Compatibility Roadmap
  3. Use testing approaches from Testing Guide
  4. Follow development setup in Getting Started Guide

5. Performance Guidelines

Process system must:

  1. Handle concurrent processes efficiently
  2. Manage system resources
  3. Support process pooling
  4. Scale with process count
  5. Meet performance targets in Laravel Compatibility Roadmap

6. Testing Requirements

Process tests must:

  1. Cover all process operations
  2. Test concurrent execution
  3. Verify event handling
  4. Check resource cleanup
  5. Follow patterns in Testing Guide

7. Documentation Requirements

Process documentation must:

  1. Explain process patterns
  2. Show pool examples
  3. Cover error handling
  4. Include performance tips
  5. Follow standards in Getting Started Guide