broccoli-workers 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. broccoli_workers-0.1.0/LICENSE +21 -0
  2. broccoli_workers-0.1.0/PKG-INFO +594 -0
  3. broccoli_workers-0.1.0/README.md +546 -0
  4. broccoli_workers-0.1.0/broccoli/__init__.py +0 -0
  5. broccoli_workers-0.1.0/broccoli/cli.py +637 -0
  6. broccoli_workers-0.1.0/broccoli/core/__init__.py +15 -0
  7. broccoli_workers-0.1.0/broccoli/core/chain/chain.py +45 -0
  8. broccoli_workers-0.1.0/broccoli/core/chain/chain_mixin.py +49 -0
  9. broccoli_workers-0.1.0/broccoli/core/chain/chain_queue.py +299 -0
  10. broccoli_workers-0.1.0/broccoli/core/chain/task_chain.py +201 -0
  11. broccoli_workers-0.1.0/broccoli/core/health.py +20 -0
  12. broccoli_workers-0.1.0/broccoli/core/redis_controller.py +35 -0
  13. broccoli_workers-0.1.0/broccoli/core/result.py +78 -0
  14. broccoli_workers-0.1.0/broccoli/core/task/task.py +58 -0
  15. broccoli_workers-0.1.0/broccoli/core/task/task_queue.py +300 -0
  16. broccoli_workers-0.1.0/broccoli/core/task/task_registry.py +47 -0
  17. broccoli_workers-0.1.0/broccoli/workers/__init__.py +15 -0
  18. broccoli_workers-0.1.0/broccoli/workers/async_worker.py +157 -0
  19. broccoli_workers-0.1.0/broccoli/workers/base_worker.py +339 -0
  20. broccoli_workers-0.1.0/broccoli/workers/chain_worker.py +94 -0
  21. broccoli_workers-0.1.0/broccoli/workers/hybrid_worker.py +269 -0
  22. broccoli_workers-0.1.0/broccoli/workers/threaded_worker.py +154 -0
  23. broccoli_workers-0.1.0/broccoli/workers/worker_pool.py +99 -0
  24. broccoli_workers-0.1.0/broccoli_workers.egg-info/PKG-INFO +594 -0
  25. broccoli_workers-0.1.0/broccoli_workers.egg-info/SOURCES.txt +29 -0
  26. broccoli_workers-0.1.0/broccoli_workers.egg-info/dependency_links.txt +1 -0
  27. broccoli_workers-0.1.0/broccoli_workers.egg-info/entry_points.txt +2 -0
  28. broccoli_workers-0.1.0/broccoli_workers.egg-info/requires.txt +9 -0
  29. broccoli_workers-0.1.0/broccoli_workers.egg-info/top_level.txt +1 -0
  30. broccoli_workers-0.1.0/pyproject.toml +106 -0
  31. broccoli_workers-0.1.0/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Broccoli Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,594 @@
1
+ Metadata-Version: 2.4
2
+ Name: broccoli-workers
3
+ Version: 0.1.0
4
+ Summary: Distributed task queue with priorities, dependencies, and chains, backed by Redis
5
+ Author-email: Success Oguntuyi <oguntuyisuccess@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Broccoli Contributors
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Classifier: Development Status :: 4 - Beta
29
+ Classifier: Intended Audience :: Developers
30
+ Classifier: License :: OSI Approved :: MIT License
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: Programming Language :: Python :: 3.11
33
+ Classifier: Programming Language :: Python :: 3.12
34
+ Classifier: Programming Language :: Python :: 3.13
35
+ Classifier: Operating System :: OS Independent
36
+ Requires-Python: >=3.11
37
+ Description-Content-Type: text/markdown
38
+ License-File: LICENSE
39
+ Requires-Dist: redis>=4.0.0
40
+ Provides-Extra: dev
41
+ Requires-Dist: pytest>=7.0; extra == "dev"
42
+ Requires-Dist: pytest-cov; extra == "dev"
43
+ Requires-Dist: black>=23.0; extra == "dev"
44
+ Requires-Dist: isort>=5.0; extra == "dev"
45
+ Requires-Dist: mypy>=1.0; extra == "dev"
46
+ Requires-Dist: flake8; extra == "dev"
47
+ Dynamic: license-file
48
+
49
+ # Broccoli
50
+
51
+ A lightweight Redis-backed task queue with priority scheduling, dependency chaining, and multiple worker execution models.
52
+
53
+ ---
54
+
55
+ ## Requirements
56
+
57
+ - Python 3.10+
58
+ - Redis 6+
59
+ - `redis-py`
60
+
61
+ ---
62
+
63
+ ## Quick start
64
+
65
+ ```python
66
+ from broccoli.core.task.task import Task
67
+ from broccoli.core.task.task_queue import TaskQueue
68
+ from broccoli.workers.base_worker import BaseWorker
69
+
70
+ # Register a task handler
71
+ class MyWorker(BaseWorker):
72
+ pass
73
+
74
+ worker = MyWorker(redis_url="redis://localhost:6379")
75
+ worker.registry.register("send_email", lambda payload: send(payload["to"]))
76
+
77
+ # Push a task
78
+ queue = TaskQueue(redis_url="redis://localhost:6379")
79
+ task = Task(task_type="send_email", payload={"to": "user@example.com"})
80
+ queue.push(task)
81
+
82
+ # Start processing (blocking)
83
+ worker.start()
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Worker types
89
+
90
+ Choose the worker that fits your workload.
91
+
92
+ ### BaseWorker — simple, single-threaded
93
+
94
+ Processes one task at a time in a blocking loop. Good for low-volume workloads or when you want the simplest possible setup.
95
+
96
+ ```python
97
+ from broccoli.workers.base_worker import BaseWorker
98
+
99
+ class VideoWorker(BaseWorker):
100
+ pass
101
+
102
+ worker = VideoWorker(redis_url="redis://localhost:6379")
103
+ worker.registry.register("transcode", transcode_video)
104
+ worker.start()
105
+ ```
106
+
107
+ ### ThreadedWorker — concurrent, CPU-friendly
108
+
109
+ Runs a configurable thread pool. Good for I/O-bound tasks or when you want multiple tasks processing in parallel without asyncio.
110
+
111
+ ```python
112
+ from broccoli.workers.threaded_worker import ThreadedWorker
113
+
114
+ worker = ThreadedWorker(redis_url="redis://localhost:6379", max_workers=8)
115
+ worker.registry.register("resize_image", resize)
116
+ worker.start()
117
+ ```
118
+
119
+ ### AsyncWorker — high-concurrency async
120
+
121
+ Dispatches tasks as asyncio coroutines. Good for large numbers of I/O-bound tasks (HTTP calls, DB queries) where thread overhead would be significant.
122
+
123
+ ```python
124
+ from broccoli.workers.async_worker import AsyncWorker
125
+
126
+ worker = AsyncWorker(redis_url="redis://localhost:6379", max_concurrent=50)
127
+ worker.registry.register("fetch_url", fetch)
128
+ worker.start()
129
+ ```
130
+
131
+ ### HybridWorker — async dispatch + thread execution
132
+
133
+ Combines asyncio concurrency control with a ThreadPoolExecutor for the actual handler. The right choice when you want high throughput and your handlers are not async-native.
134
+
135
+ ```python
136
+ from broccoli.workers.hybrid_worker import HybridWorker
137
+
138
+ worker = HybridWorker(
139
+ redis_url="redis://localhost:6379",
140
+ thread_workers=4,
141
+ async_tasks=20,
142
+ result_ttl=3600, # seconds before result expires in Redis
143
+ )
144
+ worker.registry.register("process_batch", process)
145
+ worker.start()
146
+ ```
147
+
148
+ ### WorkerPool — run multiple workers
149
+
150
+ Spawns N workers of any type, each in its own daemon thread.
151
+
152
+ ```python
153
+ from broccoli.workers.worker_pool import WorkerPool
154
+ from broccoli.workers.threaded_worker import ThreadedWorker
155
+
156
+ pool = WorkerPool(
157
+ worker_type=ThreadedWorker,
158
+ num_workers=4,
159
+ redis_url="redis://localhost:6379",
160
+ )
161
+ pool.start() # blocks until pool.stop() or SIGTERM
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Task options
167
+
168
+ ```python
169
+ from broccoli.core.task.task import Task
170
+
171
+ task = Task(
172
+ task_type="my_task",
173
+ payload={"key": "value"},
174
+ max_retries=3, # default: 0 (no retry)
175
+ depends_on="<task_id>", # optional: block until this task completes
176
+ )
177
+ ```
178
+
179
+ ### Priority
180
+
181
+ Lower number = higher priority. Default is `0`.
182
+
183
+ ```python
184
+ queue.push(urgent_task, priority=0) # processed first
185
+ queue.push(normal_task, priority=1)
186
+ queue.push(low_task, priority=5)
187
+ ```
188
+
189
+ Tasks with the same priority are processed in FIFO order.
190
+
191
+ ### Dependencies
192
+
193
+ A task with `depends_on` will not run until its parent task completes.
194
+
195
+ ```python
196
+ step1 = Task(task_type="extract", payload={...})
197
+ step2 = Task(task_type="transform", payload={...}, depends_on=step1.task_id)
198
+ step3 = Task(task_type="load", payload={...}, depends_on=step2.task_id)
199
+
200
+ queue.push(step1)
201
+ queue.push(step2)
202
+ queue.push(step3)
203
+ # step2 runs only after step1 completes; step3 only after step2
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Lifecycle hooks
209
+
210
+ Register functions to run at specific points in a task's lifecycle. All registration methods return `self` for chaining.
211
+
212
+ ```python
213
+ worker = MyWorker(redis_url="redis://localhost:6379")
214
+
215
+ # Method-style registration
216
+ worker.add_completion_handler(lambda task, result: print(f"Done: {result}"))
217
+ worker.add_failure_handler(lambda task, err: alert(str(err)))
218
+ worker.add_pre_process_handler(lambda task: task.payload.get("enabled", True))
219
+ worker.add_post_process_handler(lambda task, success: metrics.record(success))
220
+
221
+ # Chained registration
222
+ worker \
223
+ .add_completion_handler(notify_slack) \
224
+ .add_failure_handler(notify_pagerduty)
225
+
226
+ # Decorator-style registration
227
+ @worker.on_complete
228
+ def on_done(task, result):
229
+ print(f"Task {task.task_id} finished with: {result}")
230
+
231
+ @worker.on_failure
232
+ def on_fail(task, error):
233
+ logger.error(f"Task {task.task_id} failed: {error}")
234
+ ```
235
+
236
+ Pre-process handlers can abort a task by returning `False`:
237
+
238
+ ```python
239
+ @worker.on_pre_process
240
+ def gate(task):
241
+ if task.payload.get("dry_run"):
242
+ return False # skip this task
243
+ return True
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Chain tasks
249
+
250
+ For multi-step pipelines, use `ChainWorker`. Each step in the chain carries a `__chain_id` in its payload; the final result is stored atomically when the last step completes.
251
+
252
+ ```python
253
+ from broccoli.workers.chain_worker import ChainWorker
254
+
255
+ worker = ChainWorker(redis_url="redis://localhost:6379")
256
+ worker.registry.register("step_a", do_a)
257
+ worker.registry.register("step_b", do_b)
258
+ worker.start()
259
+ ```
260
+
261
+ ---
262
+
263
+ ## Crash recovery
264
+
265
+ If a worker process dies mid-task, the task remains in the `tasks:processing` sorted set indefinitely. Call `recover_stalled` on startup (or on a schedule) to re-enqueue any tasks that have been in-flight longer than expected:
266
+
267
+ ```python
268
+ queue = TaskQueue(redis_url="redis://localhost:6379")
269
+ recovered = queue.recover_stalled(timeout_seconds=3600)
270
+ print(f"Re-enqueued {recovered} stalled tasks")
271
+ ```
272
+
273
+ ---
274
+
275
+ ## Logging
276
+
277
+ Broccoli uses the standard `logging` module under the `broccoli` namespace. Configure it in your application:
278
+
279
+ ```python
280
+ import logging
281
+ logging.basicConfig(level=logging.INFO)
282
+ ```
283
+
284
+ # Command-Line Interface (CLI)
285
+
286
+ Broccoli ships with a powerful CLI for starting workers, inspecting queues, managing failed tasks, and monitoring chains – all from the terminal.
287
+
288
+ ---
289
+
290
+ ## Installation
291
+
292
+ Make the CLI executable and available on your `PATH`:
293
+
294
+ ```bash
295
+ chmod +x broccoli/cli.py
296
+ # Optionally symlink it:
297
+ ln -s $(pwd)/broccoli/cli.py /usr/local/bin/broccoli
298
+ ```
299
+
300
+ Or run it directly via Python:
301
+
302
+ ```bash
303
+ python -m broccoli.cli --help
304
+ ```
305
+
306
+ ---
307
+
308
+ ## Global Options
309
+
310
+ | Flag | Description |
311
+ |------|-------------|
312
+ | `-v`, `--verbose` | Increase logging verbosity (`-v` for INFO, `-vv` for DEBUG). |
313
+ | `--help` | Show help for any command or subcommand. |
314
+
315
+ All subcommands also respect environment variables for common settings:
316
+
317
+ | Env Variable | Default | Used For |
318
+ |--------------|---------|----------|
319
+ | `BROCCOLI_REDIS_URL` | `redis://localhost:6379` | Redis connection string |
320
+ | `BROCCOLI_QUEUE_NAME` | `tasks:queue` | Regular task queue name |
321
+ | `BROCCOLI_CHAIN_QUEUE_NAME` | `chain_tasks:queue` | Chain task queue name |
322
+ | `BROCCOLI_TASK_PREFIX` | `task` | Redis key prefix for task hashes |
323
+
324
+ ---
325
+
326
+ ## Worker Management
327
+
328
+ ### Start a single worker
329
+
330
+ ```bash
331
+ # Threaded worker with 8 threads
332
+ broccoli worker start --type threaded --concurrency 8
333
+
334
+ # Async worker with 20 concurrent tasks
335
+ broccoli worker start --type async --concurrency 20
336
+
337
+ # Hybrid worker (threads for CPU + asyncio for I/O)
338
+ broccoli worker start --type hybrid --thread-workers 4 --async-tasks 10
339
+
340
+ # Chain worker (for task chains)
341
+ broccoli worker start --type chain
342
+ ```
343
+
344
+ ### Start a worker pool
345
+
346
+ ```bash
347
+ # Pool of 4 threaded workers
348
+ broccoli worker start --type threaded --pool --num-workers 4
349
+
350
+ # Pool of 3 async workers
351
+ broccoli worker start --type async --pool --num-workers 3
352
+ ```
353
+
354
+ ### Recover stalled tasks on startup
355
+
356
+ ```bash
357
+ # Re-enqueue any tasks that have been in-flight for > 3600 seconds
358
+ broccoli worker start --type threaded --recover-stalled 3600
359
+ ```
360
+
361
+ ### Advanced: custom queue names
362
+
363
+ ```bash
364
+ broccoli worker start --type threaded \
365
+ --queue-name myapp:queue \
366
+ --chain-queue-name myapp:chain \
367
+ --task-prefix myapp
368
+ ```
369
+
370
+ ---
371
+
372
+ ## Queue Inspection
373
+
374
+ ### View queue statistics
375
+
376
+ ```bash
377
+ broccoli queue stats
378
+ ```
379
+
380
+ Output:
381
+ ```
382
+ runnable: 12
383
+ processing: 3
384
+ dead_letter: 2
385
+ ```
386
+
387
+ With JSON output:
388
+ ```bash
389
+ broccoli queue stats --format json
390
+ ```
391
+
392
+ ### List tasks by status
393
+
394
+ ```bash
395
+ # Pending tasks (ready to run)
396
+ broccoli queue list --status pending --limit 10
397
+
398
+ # In-progress tasks
399
+ broccoli queue list --status in_progress
400
+
401
+ # All tasks (scan all task hashes – use with care on large queues)
402
+ broccoli queue list --status all --limit 20
403
+ ```
404
+
405
+ ### Get a specific task
406
+
407
+ ```bash
408
+ broccoli queue get <task_id>
409
+ ```
410
+
411
+ Returns full task metadata (payload, result, error, retries, etc.).
412
+
413
+ ### Show tasks waiting for a parent
414
+
415
+ ```bash
416
+ # See which tasks are blocked on a particular task
417
+ broccoli queue waiting <parent_task_id>
418
+ ```
419
+
420
+ This is invaluable for debugging dependency deadlocks.
421
+
422
+ ---
423
+
424
+ ## Dead-Letter Management
425
+
426
+ When tasks exhaust all retries, they are moved to the dead‑letter set for manual inspection and retry.
427
+
428
+ ### List dead-letter tasks
429
+
430
+ ```bash
431
+ broccoli dead list
432
+ ```
433
+
434
+ Shows task IDs and the timestamp when they failed.
435
+
436
+ ### Requeue a dead task
437
+
438
+ ```bash
439
+ broccoli dead requeue <task_id>
440
+ ```
441
+
442
+ This resets the retry count to `0` and pushes the task back to the runnable queue.
443
+
444
+ ---
445
+
446
+ ## Chain Inspection
447
+
448
+ ### Get chain status
449
+
450
+ ```bash
451
+ broccoli chain status <chain_id>
452
+ ```
453
+
454
+ Returns:
455
+ ```json
456
+ {
457
+ "chain_id": "abc-123",
458
+ "total_tasks": 5,
459
+ "completed_tasks": 3,
460
+ "current_task": 3,
461
+ "status": "in_progress",
462
+ "failed": false
463
+ }
464
+ ```
465
+
466
+ ### List all tasks in a chain
467
+
468
+ ```bash
469
+ broccoli chain tasks <chain_id>
470
+ ```
471
+
472
+ Shows the full task configuration for every step in the chain, including payloads and task IDs.
473
+
474
+ ---
475
+
476
+ ## Health Check
477
+
478
+ Useful for monitoring systems (e.g., Kubernetes liveness probes).
479
+
480
+ ```bash
481
+ broccoli health
482
+ ```
483
+
484
+ - Exits with `0` if Redis is reachable and basic queue operations work.
485
+ - Exits with `1` and prints an error on failure.
486
+
487
+ Example with `curl`‑style usage:
488
+ ```bash
489
+ if broccoli health; then
490
+ echo "Broccoli is ready"
491
+ else
492
+ echo "Broccoli is unhealthy" >&2
493
+ exit 1
494
+ fi
495
+ ```
496
+
497
+ ---
498
+
499
+ ## Output Formats
500
+
501
+ All inspection commands support two output formats:
502
+
503
+ - **`table`** (default) – human‑readable, aligned columns.
504
+ - **`json`** – machine‑readable, ideal for scripting and APIs.
505
+
506
+ ```bash
507
+ # Human‑readable
508
+ broccoli queue stats
509
+
510
+ # Machine‑readable
511
+ broccoli queue stats --format json
512
+ ```
513
+
514
+ ---
515
+
516
+ ## Complete Usage Examples
517
+
518
+ ### Development workflow
519
+
520
+ Start a single threaded worker with verbose logging:
521
+ ```bash
522
+ broccoli -v worker start --type threaded --concurrency 4
523
+ ```
524
+
525
+ ### Production deployment (systemd / supervisor)
526
+
527
+ ```bash
528
+ # Start a pool of 8 async workers, recovering stalled tasks
529
+ broccoli worker start --type async --pool --num-workers 8 \
530
+ --concurrency 10 --recover-stalled 3600
531
+ ```
532
+
533
+ ### Debugging a stuck workflow
534
+
535
+ 1. Check if tasks are waiting:
536
+ ```bash
537
+ broccoli queue waiting <parent_id>
538
+ ```
539
+
540
+ 2. Inspect the task that isn't progressing:
541
+ ```bash
542
+ broccoli queue get <task_id>
543
+ ```
544
+
545
+ 3. If a task failed permanently, requeue it:
546
+ ```bash
547
+ broccoli dead list
548
+ broccoli dead requeue <failed_task_id>
549
+ ```
550
+
551
+ 4. Monitor chain progress:
552
+ ```bash
553
+ broccoli chain status <chain_id>
554
+ ```
555
+
556
+ ### Automation / scripting
557
+
558
+ Export settings once:
559
+ ```bash
560
+ export BROCCOLI_REDIS_URL="redis://prod-cluster:6379"
561
+ export BROCCOLI_QUEUE_NAME="video:queue"
562
+ ```
563
+
564
+ Then run commands without repeating arguments:
565
+ ```bash
566
+ broccoli queue stats --format json
567
+ broccoli dead list
568
+ ```
569
+
570
+ ---
571
+
572
+ ## CLI Reference
573
+
574
+ | Command | Description |
575
+ |---------|-------------|
576
+ | `worker start` | Start a worker (or pool) with the specified type and concurrency. |
577
+ | `queue stats` | Show runnable, processing, and dead‑letter counts. |
578
+ | `queue list` | List tasks filtered by status (pending, in_progress, etc.). |
579
+ | `queue get <id>` | Fetch and display a task's full metadata. |
580
+ | `queue waiting <parent_id>` | Show tasks blocked on a given parent. |
581
+ | `dead list` | List all permanently failed tasks. |
582
+ | `dead requeue <id>` | Re‑enqueue a failed task (retry). |
583
+ | `chain status <id>` | Show completion progress of a task chain. |
584
+ | `chain tasks <id>` | List all steps in a chain. |
585
+ | `health` | Check Redis connectivity and queue health. |
586
+
587
+ For detailed options on any command, use `--help`:
588
+
589
+ ```bash
590
+ broccoli worker start --help
591
+ broccoli queue list --help
592
+ ```
593
+
594
+ ---