409 lines
10 KiB
Markdown
409 lines
10 KiB
Markdown
|
# 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**
|
||
|
> - See [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md) for implementation status
|
||
|
> - See [Foundation Integration Guide](foundation_integration_guide.md) for integration patterns
|
||
|
> - See [Testing Guide](testing_guide.md) for testing approaches
|
||
|
> - See [Getting Started Guide](getting_started.md) for development setup
|
||
|
> - See [Events Package Specification](events_package_specification.md) for process events
|
||
|
> - See [Queue Package Specification](queue_package_specification.md) for background processing
|
||
|
|
||
|
## Core Features
|
||
|
|
||
|
### 1. Process Manager
|
||
|
|
||
|
```dart
|
||
|
/// 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
|
||
|
|
||
|
```dart
|
||
|
/// 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
|
||
|
|
||
|
```dart
|
||
|
/// 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
|
||
|
|
||
|
```dart
|
||
|
/// 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
|
||
|
```dart
|
||
|
// 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
|
||
|
```dart
|
||
|
// 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
|
||
|
```dart
|
||
|
// 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
|
||
|
|
||
|
```dart
|
||
|
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](getting_started.md)
|
||
|
2. Check [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
|
||
|
3. Follow [Testing Guide](testing_guide.md)
|
||
|
4. Use [Foundation Integration Guide](foundation_integration_guide.md)
|
||
|
5. Review [Events Package Specification](events_package_specification.md)
|
||
|
6. Review [Queue Package Specification](queue_package_specification.md)
|
||
|
|
||
|
### 2. Implementation Process
|
||
|
For each process feature:
|
||
|
1. Write tests following [Testing Guide](testing_guide.md)
|
||
|
2. Implement following Laravel patterns
|
||
|
3. Document following [Getting Started Guide](getting_started.md#documentation)
|
||
|
4. Integrate following [Foundation Integration Guide](foundation_integration_guide.md)
|
||
|
|
||
|
### 3. Quality Requirements
|
||
|
All implementations must:
|
||
|
1. Pass all tests (see [Testing Guide](testing_guide.md))
|
||
|
2. Meet Laravel compatibility requirements
|
||
|
3. Follow integration patterns (see [Foundation Integration Guide](foundation_integration_guide.md))
|
||
|
4. Support event integration (see [Events Package Specification](events_package_specification.md))
|
||
|
5. Support queue integration (see [Queue Package Specification](queue_package_specification.md))
|
||
|
|
||
|
### 4. Integration Considerations
|
||
|
When implementing process features:
|
||
|
1. Follow patterns in [Foundation Integration Guide](foundation_integration_guide.md)
|
||
|
2. Ensure Laravel compatibility per [Laravel Compatibility Roadmap](laravel_compatibility_roadmap.md)
|
||
|
3. Use testing approaches from [Testing Guide](testing_guide.md)
|
||
|
4. Follow development setup in [Getting Started Guide](getting_started.md)
|
||
|
|
||
|
### 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](laravel_compatibility_roadmap.md#performance-benchmarks)
|
||
|
|
||
|
### 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](testing_guide.md)
|
||
|
|
||
|
### 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](getting_started.md#documentation)
|