|
| 1 | +# lib-cmd-queue-redis |
| 2 | + |
| 3 | +Asynchronous command queue for executing long-running tasks with persistent state tracking and automatic status polling. |
| 4 | + |
| 5 | +## Installation |
| 6 | + |
| 7 | +Add this dependency to your `build.gradle`: |
| 8 | + |
| 9 | +```gradle |
| 10 | +dependencies { |
| 11 | + implementation 'io.seqera:lib-cmd-queue-redis:0.1.0' |
| 12 | +} |
| 13 | +``` |
| 14 | + |
| 15 | +## Features |
| 16 | + |
| 17 | +- Fire-and-forget command submission |
| 18 | +- Typed parameters and results with JSON serialization |
| 19 | +- Status transitions: `SUBMITTED` → `RUNNING` → `SUCCEEDED`/`FAILED`/`CANCELLED` |
| 20 | +- Automatic timeout handling for long-running commands |
| 21 | +- Periodic status checking for async commands |
| 22 | +- Command cancellation support |
| 23 | +- Persistent storage using Redis or in-memory backend |
| 24 | + |
| 25 | +## Usage |
| 26 | + |
| 27 | +### Define Command Parameters and Result |
| 28 | + |
| 29 | +```java |
| 30 | +// Command parameters - must have default constructor for Jackson |
| 31 | +public class ProcessingParams { |
| 32 | + private String datasetId; |
| 33 | + private List<String> steps; |
| 34 | + // Getters, setters, constructors... |
| 35 | +} |
| 36 | + |
| 37 | +// Command result |
| 38 | +public class ProcessingResult { |
| 39 | + private int recordsProcessed; |
| 40 | + private long durationMs; |
| 41 | + // Getters, setters, constructors... |
| 42 | +} |
| 43 | +``` |
| 44 | + |
| 45 | +### Implement a Command Handler |
| 46 | + |
| 47 | +For **synchronous** commands that complete quickly: |
| 48 | + |
| 49 | +```java |
| 50 | +@Singleton |
| 51 | +public class ProcessingHandler implements CommandHandler<ProcessingParams, ProcessingResult> { |
| 52 | + |
| 53 | + @Override |
| 54 | + public String type() { return "data-processing"; } |
| 55 | + |
| 56 | + @Override |
| 57 | + public CommandResult<ProcessingResult> execute(Command<ProcessingParams> command) { |
| 58 | + var params = command.params(); |
| 59 | + // Do the work... |
| 60 | + var result = new ProcessingResult(1000, 5000L); |
| 61 | + return CommandResult.success(result); |
| 62 | + } |
| 63 | +} |
| 64 | +``` |
| 65 | + |
| 66 | +For **asynchronous** long-running commands: |
| 67 | + |
| 68 | +```java |
| 69 | +@Singleton |
| 70 | +public class AsyncProcessingHandler implements CommandHandler<ProcessingParams, ProcessingResult> { |
| 71 | + |
| 72 | + @Inject ExternalService externalService; |
| 73 | + |
| 74 | + @Override |
| 75 | + public String type() { return "async-processing"; } |
| 76 | + |
| 77 | + @Override |
| 78 | + public CommandResult<ProcessingResult> execute(Command<ProcessingParams> command) { |
| 79 | + // Start async job |
| 80 | + externalService.startJob(command.id(), command.params()); |
| 81 | + return CommandResult.running(); // checkStatus() will be called later |
| 82 | + } |
| 83 | + |
| 84 | + @Override |
| 85 | + public CommandResult<ProcessingResult> checkStatus(Command<ProcessingParams> command, CommandState state) { |
| 86 | + var status = externalService.getStatus(command.id()); |
| 87 | + if (status.isComplete()) return CommandResult.success(status.getResult()); |
| 88 | + if (status.isFailed()) return CommandResult.failure(status.getError()); |
| 89 | + return CommandResult.running(); // Still running, check again later |
| 90 | + } |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +### Submit Commands |
| 95 | + |
| 96 | +```java |
| 97 | +@Inject |
| 98 | +private CommandService commandService; |
| 99 | + |
| 100 | +// Register handler at startup |
| 101 | +commandService.registerHandler(new ProcessingHandler()); |
| 102 | + |
| 103 | +// Submit a command |
| 104 | +var command = new ProcessingCommand("cmd-123", params); |
| 105 | +String commandId = commandService.submit(command); |
| 106 | + |
| 107 | +// Check status |
| 108 | +Optional<CommandState> state = commandService.getState(commandId); |
| 109 | + |
| 110 | +// Get result when complete |
| 111 | +ProcessingResult result = commandService.getResult(commandId, ProcessingResult.class).orElseThrow(); |
| 112 | +``` |
| 113 | + |
| 114 | +## Command Status Flow |
| 115 | + |
| 116 | +``` |
| 117 | +submit() ──▶ SUBMITTED ──pickup──▶ RUNNING ─┬─success──▶ SUCCEEDED |
| 118 | + ├─error────▶ FAILED |
| 119 | + └─cancel───▶ CANCELLED |
| 120 | +``` |
| 121 | + |
| 122 | +## Testing |
| 123 | + |
| 124 | +```bash |
| 125 | +./gradlew :lib-cmd-queue-redis:test |
| 126 | +``` |
| 127 | + |
| 128 | +## License |
| 129 | + |
| 130 | +Apache License 2.0 |
0 commit comments