CLI¶
The taskq console entry point (Typer).
cli ¶
taskq CLI entry point.
The CLI is intentionally thin today — only the commands needed to bootstrap a database. Worker and client commands will be added as those subsystems land.
Usage::
taskq migrate status
taskq migrate up [--phase pre|post] [--target VERSION] [--max-steps N]
app
module-attribute
¶
app = typer.Typer(
name="taskq",
no_args_is_help=True,
help="TaskQ — async Postgres-backed background jobs.",
)
migrate_app
module-attribute
¶
health_app
module-attribute
¶
workgroup_app
module-attribute
¶
workgroup_app = typer.Typer(
no_args_is_help=True,
help="Manage a multi-worker process group (supervisor).",
)
actor_config_app
module-attribute
¶
actor_config_app = typer.Typer(
no_args_is_help=True,
help="Inspect and tune stored actor_config capacity fields on a live deployment.",
)
queues_app
module-attribute
¶
queues_app = typer.Typer(
no_args_is_help=True,
help="Inspect and configure queue dispatch mode and per-queue concurrency caps.",
)
worker ¶
worker(
actors: str = typer.Option(
...,
"--actors",
help="Module:attr reference to the actor registry (e.g. myapp.actors:registry). Resolves at startup to a Mapping[str, ActorRef] or Iterable[ActorRef].",
),
force_update_actor_config: bool = typer.Option(
False,
"--force-update-actor-config",
help="Allow sync_actor_config to overwrite a stored actor_config row whose queue or metadata differ from the registered values. Use for one deploy to deliberately re-route an actor, then unset. Capacity fields (max_concurrent / max_pending / result_ttl) are unaffected — use `taskq actor-config set` for those. Equivalent to env var TASKQ_FORCE_UPDATE_ACTOR_CONFIG=true.",
),
queues: list[str] | None = typer.Option(
None,
"--queues",
help="Queue names to consume from (repeat the flag once per queue). Overrides TASKQ_QUEUES.",
),
max_concurrency: int | None = typer.Option(
None,
"--max-concurrency",
help="Upper bound on concurrent jobs. Overrides TASKQ_MAX_CONCURRENCY.",
),
poll_interval: float | None = typer.Option(
None,
"--poll-interval",
help="Producer loop fallback polling cadence in seconds. Overrides TASKQ_POLL_INTERVAL.",
),
worker_group: str | None = typer.Option(
None,
"--worker-group",
help="Consumer group name for observability spans. Overrides TASKQ_WORKER_GROUP.",
),
worker_label: str | None = typer.Option(
None,
"--worker-label",
help="Human-readable label stored in the workers table for correlation with workgroup supervisors and external monitoring.",
),
workgroup_instance: str | None = typer.Option(
None,
"--workgroup-instance",
help="UUIDv7 identifying the workgroup orchestrator that launched this worker. Used for cross-process correlation and health checking.",
),
health_socket_path: str | None = typer.Option(
None,
"--health-socket-path",
help="Unix socket path for the health server. Overrides TASKQ_HEALTH_SOCKET_PATH. Use unique paths when running multiple workers on the same host.",
),
until_idle: bool = typer.Option(
False,
"--until-idle",
help="Run until all subscribed queues are drained, then exit. Exit 0 if all jobs succeeded, 3 if any failed, 4 if idle-max-runtime was exceeded. Incompatible with cron-driven workloads.",
),
idle_settle_window: float | None = typer.Option(
None,
"--idle-settle-window",
help="Seconds to wait after queues appear empty before declaring drained. Overrides TASKQ_IDLE_SETTLE_WINDOW. Default 2.0. Only used with --until-idle.",
),
idle_poll_interval: float | None = typer.Option(
None,
"--idle-poll-interval",
help="How often to check queue depth. Overrides TASKQ_IDLE_POLL_INTERVAL. Default 1.0. Only used with --until-idle.",
),
idle_max_runtime: float | None = typer.Option(
None,
"--idle-max-runtime",
help="Maximum wall-clock seconds before forcing exit (code 4). Overrides TASKQ_IDLE_MAX_RUNTIME. Only used with --until-idle.",
),
pg_credential_provider: str | None = typer.Option(
None,
"--pg-credential-provider",
help=f"Module:attr reference to a PgCredentialProvider (e.g. {_PROVIDER_EXAMPLE}) — an instance, a zero-arg factory returning one, or the provider class. Every Postgres pool and dedicated connection is then built through it, so SIGHUP / TASKQ_RELOAD_INTERVAL rotate real credentials. Overrides TASKQ_PG_CREDENTIAL_PROVIDER (inherited by workgroup-supervised workers) via dotenvmodel.",
),
redis_credential_provider: str | None = typer.Option(
None,
"--redis-credential-provider",
help="Module:attr reference to a RedisCredentialProvider, in the same shapes as --pg-credential-provider. Requires TASKQ_REDIS_URL. Overrides TASKQ_REDIS_CREDENTIAL_PROVIDER via dotenvmodel.",
),
) -> None
Start a TaskQ worker consuming from the given actor registry.
Source code in src/taskq/cli.py
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 | |
dev_watch ¶
dev_watch(
actors: Annotated[
str,
Argument(help="Import path: dotted.module:attr"),
],
watch: Annotated[
list[str] | None,
Option(
--watch,
help="Path to watch (repeatable). Default: cwd.",
),
] = None,
grace_period: Annotated[
int,
Option(
--grace - period,
min=0,
help="Seconds before SIGKILL. Default: 5.",
),
] = 5,
) -> None
Run a worker in dev mode with auto-reload on file changes.
Source code in src/taskq/cli.py
migrate_status ¶
migrate_status(
pg_credential_provider: str | None = typer.Option(
None,
"--pg-credential-provider",
help=f"Module:attr reference to a PgCredentialProvider (e.g. {_PROVIDER_EXAMPLE}). The connection is opened through it instead of the DSN's static password. Overrides TASKQ_PG_CREDENTIAL_PROVIDER.",
),
) -> None
Show applied and pending migrations.
Source code in src/taskq/cli.py
migrate_up ¶
migrate_up(
phase: Phase | None = typer.Option(
None, "--phase", help="Restrict to 'pre' or 'post'."
),
target: str | None = typer.Option(
None,
"--target",
help="Stop after this version (inclusive). E.g. 01.00.00_01",
),
max_steps: int | None = typer.Option(
None, "--max-steps", help="Cap number of applies."
),
pg_credential_provider: str | None = typer.Option(
None,
"--pg-credential-provider",
help=f"Module:attr reference to a PgCredentialProvider (e.g. {_PROVIDER_EXAMPLE}). The connection is opened through it instead of the DSN's static password. Overrides TASKQ_PG_CREDENTIAL_PROVIDER.",
),
) -> None
Apply pending migrations.
Source code in src/taskq/cli.py
actor_config_list ¶
actor_config_get ¶
Show the stored actor_config row for one actor.
Source code in src/taskq/cli.py
actor_config_set ¶
actor_config_set(
actor: Annotated[str, Argument(help="Actor name.")],
max_concurrent: Annotated[
int | None,
Option(
--max - concurrent,
min=0,
help="New fleet-wide concurrency cap. Takes effect on the next dispatch cycle (no worker restart) — the dispatch query re-reads this column every cycle.",
),
] = None,
clear_max_concurrent: Annotated[
bool,
Option(
--clear - max - concurrent,
help="Set max_concurrent back to unlimited.",
),
] = False,
max_pending: Annotated[
int | None,
Option(
--max - pending,
min=0,
help="New queue-depth backpressure cap. Takes effect within seconds on every enqueue-side process (bounded by each client's capacity-cache TTL, default 5s) — no redeploy, no worker restart.",
),
] = None,
clear_max_pending: Annotated[
bool,
Option(
--clear - max - pending,
help="Clear the stored override; enforcement reverts to the @actor(max_pending=...) literal.",
),
] = False,
result_ttl: Annotated[
float | None,
Option(
--result - ttl,
min=0,
help="New result TTL in seconds. Takes effect for jobs completing after this change (no worker restart) — the terminal-write UPDATE re-reads this column for every job.",
),
] = None,
clear_result_ttl: Annotated[
bool,
Option(
--clear - result - ttl,
help="Set result_ttl back to unset.",
),
] = False,
) -> None
Update capacity fields on an existing actor_config row.
Only flags actually passed are changed. An actor must already have a stored row (created by a worker startup that registered it) before its capacity can be tuned here.
All three fields are live: --max-concurrent is re-read by the
dispatch query every cycle and --result-ttl by the terminal-write
path on every job completion (both immediate); --max-pending is
re-read by every enqueue-side process through a TTL-bounded cache
(default 5s staleness). No redeploy and no worker restart for any of
them. Use taskq actor-config diff to see the stored value, the
code literal, and which one the engine currently enforces.
Source code in src/taskq/cli.py
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 | |
actor_config_deregister ¶
actor_config_deregister(
actor: Annotated[
str, Argument(help="Actor name to deregister.")
],
force: Annotated[
bool,
Option(
--force,
help="Cancel pending/scheduled jobs and disable enabled cron schedules instead of refusing. Running jobs still block deregistration.",
),
] = False,
purge_queue: Annotated[
bool,
Option(
--purge - queue,
help="Also delete the orphaned queues row if no other actor_config references the same queue.",
),
] = False,
) -> None
Deregister an actor: delete its actor_config row with safety checks.
By default refuses if non-terminal jobs or enabled cron schedules reference the actor. Use --force to cancel pending/scheduled jobs and disable schedules. Running jobs always block (force or not). Use --purge-queue to also delete the queues row if no other actor uses it.
Exit codes: 0 success, 2 refusal (active jobs/schedules or invalid schema), 3 not found.
Source code in src/taskq/cli.py
actor_config_diff ¶
actor_config_diff(
actors: Annotated[
str,
Option(
--actors,
help="Module:attr reference to the actor registry (e.g. myapp.actors:registry). Stored rows are compared against these code literals.",
),
],
) -> None
Diff stored actor_config rows against the code literals in a registry.
Per actor and field, shows the @actor(...) literal, the stored value,
and the value the engine actually enforces right now ("effective").
Reach for this when debugging "why is my change not taking effect":
a capacity literal that differs from the stored row is IGNORED at
runtime — the stored value wins; tune it with taskq actor-config
set — while a queue/metadata mismatch blocks the next worker
startup with ActorConfigDriftList.
Source code in src/taskq/cli.py
health_live ¶
health_ready ¶
health_metrics ¶
ui_serve ¶
ui_serve(
pg_dsn: str | None = typer.Option(
None,
"--pg-dsn",
help="Postgres DSN. Falls back to TASKQ_PG_DSN via dotenvmodel.",
),
schema: str | None = typer.Option(
None,
"--schema",
help="Postgres schema name. Falls back to TASKQ_SCHEMA_NAME via dotenvmodel.",
),
redis_url: str | None = typer.Option(
None,
"--redis-url",
help="Redis URL for real-time mode. Falls back to TASKQ_REDIS_URL via dotenvmodel.",
),
host: str | None = typer.Option(
None,
"--host",
help="Bind address. Falls back to TASKQ_ADMIN_HOST via dotenvmodel.",
),
port: int | None = typer.Option(
None,
"--port",
help="Bind port. Falls back to TASKQ_ADMIN_PORT via dotenvmodel.",
),
run_migrate: bool = typer.Option(
False,
"--migrate",
help="Apply pending migrations before starting. Aborts startup if migrations fail.",
),
pg_credential_provider: str | None = typer.Option(
None,
"--pg-credential-provider",
help=f"Module:attr reference to a PgCredentialProvider (e.g. {_PROVIDER_EXAMPLE}). The admin pool (and --migrate) authenticate through it instead of the DSN's static password. Overrides TASKQ_PG_CREDENTIAL_PROVIDER.",
),
redis_credential_provider: str | None = typer.Option(
None,
"--redis-credential-provider",
help="Module:attr reference to a RedisCredentialProvider for the real-time mode client. Overrides TASKQ_REDIS_CREDENTIAL_PROVIDER.",
),
) -> None
Start the admin UI server on the given host:port.
Source code in src/taskq/cli.py
1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 | |
main ¶
workgroup_start ¶
workgroup_start(
config: Annotated[
Path,
Argument(
help="Path to the workgroup TOML configuration file."
),
],
) -> None
Start a workgroup supervisor that manages multiple worker processes.
The supervisor spawns one taskq worker subprocess per [[workers]]
entry in the config file, monitors their health, restarts them on crash,
and propagates shutdown signals.
Source code in src/taskq/cli.py
workgroup_validate ¶
workgroup_validate(
config: Annotated[
Path,
Argument(
help="Path to the workgroup TOML configuration file."
),
],
) -> None
Validate a workgroup TOML config without starting any workers.
Source code in src/taskq/cli.py
queues_list ¶
List every configured queue row.
Queues absent from this list are not missing -- queues are implicit and
are created by enqueueing onto them. An absent queue runs on the
defaults: strict_fifo ordering (so fairness_key has no effect) and no
concurrency cap.
Source code in src/taskq/cli.py
queues_get ¶
Show one queue's stored configuration.
queues_set_mode ¶
queues_set_mode(
name: Annotated[str, Argument(help="Queue name.")],
mode: Annotated[
str,
Argument(
help=f"Dispatch ordering mode. One of: {join(QUEUE_MODES)}."
),
],
) -> None
Set a queue's dispatch ordering mode, creating the row if needed.
round_robin is what makes fairness_key do anything: on the default
strict_fifo the key is accepted, stored, and ignored. Takes effect on
the next dispatch cycle -- no worker restart.
Source code in src/taskq/cli.py
queues_set_max_concurrent ¶
queues_set_max_concurrent(
name: Annotated[str, Argument(help="Queue name.")],
max_concurrent: Annotated[
int | None,
Option(
--max - concurrent,
min=1,
help="New per-queue leased-slot cap (>= 1; pass --clear for uncapped).",
),
] = None,
clear: Annotated[
bool,
Option(--clear, help="Remove the cap (unlimited)."),
] = False,
) -> None
Set or clear a queue's fleet-wide leased-slot concurrency cap.
Unlike actor-config set --max-concurrent, this is read once at worker
startup, so it needs a worker restart to take effect. There is no 0
state: NULL (via --clear) is uncapped, and an emergency drain to 0
belongs to actor-config set --max-concurrent 0, which is per-actor.