Google Cloud Run Jobs Execution¶
CloudRunExecutionBackend (configured via CloudRunExecutionConfig) executes each queued task inside an isolated, one-shot Google Cloud Run Job container that scales to zero immediately upon exit.
Runnable Configuration Example¶
"""Configuring CloudRunExecutionConfig with execution profiles and schedules_on_enqueue."""
from litestar_queues import QueueConfig, WorkerConfig, task
from litestar_queues.backends.redis import RedisBackendConfig
from litestar_queues.execution.cloudrun import CloudRunExecutionConfig
@task("reports.render_annual_pdf", queue="reports", execution_backend="cloudrun", execution_profile="high-memory")
async def render_annual_pdf(organization_id: str) -> str:
"""Render a large PDF inside a high-memory Cloud Run Job execution."""
return f"pdf:{organization_id}"
def build_cloud_run_config() -> QueueConfig:
"""Configure Cloud Run Jobs execution with profile mapping and direct enqueue dispatch."""
execution_config = CloudRunExecutionConfig(
project_id="my-gcp-project",
region="us-central1",
job_name="litestar-worker-standard",
profiles={"default": "litestar-worker-standard", "high-memory": "litestar-worker-highmem"},
schedules_on_enqueue=True,
)
return QueueConfig(
queue_backend=RedisBackendConfig(url="redis://redis.internal:6379/0"),
execution_backend=execution_config,
worker=WorkerConfig(placement="external"),
)
async def main() -> None:
"""Verify Cloud Run profile resolution and task routing."""
config = build_cloud_run_config()
assert isinstance(config.execution_backend, CloudRunExecutionConfig)
assert config.execution_backend.schedules_on_enqueue is True
assert config.execution_backend.resolve_job_name("high-memory") == "litestar-worker-highmem"
assert config.execution_backend.resolve_job_name(None) == "litestar-worker-standard"
assert render_annual_pdf.execution_profile == "high-memory"
Cloud Run Deployment Topologies¶
| Topology | CloudRunExecutionConfig / WorkerConfig |
Always-On Instances | Delayed Task Support | Best For |
|---|---|---|---|---|
| Direct Enqueue Dispatch (Default) | schedules_on_enqueue=True, WorkerConfig(placement="external") |
0 (web tier scales to zero) |
Immediate tasks dispatched on enqueue(); delayed tasks dispatched by periodic maintenance (--phase external) |
Scale-to-zero web apps running batch or heavy tasks on demand |
| Hybrid Cloud Tasks + Cloud Run Jobs | execution_backends=(cloud_tasks, cloud_run), WorkerConfig(placement="external") |
0 (full scale to zero) |
Cloud Tasks handles < 30m & delayed tasks; Cloud Run Jobs handles > 30m batch tasks |
Serverless applications needing both sub-second delayed push and multi-hour batch jobs |
External dispatcher (schedules_on_enqueue=False) |
schedules_on_enqueue=False, litestar queues run worker service (--min-instances 1 --no-cpu-throttling) |
1 lightweight dispatcher |
Polls storage continuously and launches Cloud Run Jobs as delayed tasks become due | High-volume delayed Cloud Run Job workloads without external maintenance crons |
In-process worker (placement="asgi") |
execution_backend="local", WorkerConfig(placement="asgi") (--min-instances 1 --no-cpu-throttling) |
1+ always-on service instances |
Full local polling and push wakeups | Traditional always-on Cloud Run services without Cloud Run Jobs |
| Eventarc | Not implemented | N/A | Not implemented (use Cloud Tasks or Pub/Sub instead) | Not implemented as a litestar-queues execution backend |
Job Container Command & Environment Contract¶
Configure your Cloud Run Job container entrypoint to invoke the one-shot task consumer CLI (litestar queues run-task or python -m litestar_queues.consumer):
Or without loading a Litestar HTTP application object, point QUEUES_CONFIG_FACTORY to a callable returning QueueConfig:
ENV QUEUES_CONFIG_FACTORY="my_app.queues:build_queue_config"
ENV LITESTAR_QUEUES_TASK_MODULES="my_app.tasks"
ENTRYPOINT ["python", "-m", "litestar_queues.consumer"]
When CloudRunExecutionBackend triggers run_job(), it passes the ID-only override environment variable QUEUES_TASK_ID=<uuid> (plus any static CloudRunExecutionConfig.extra_env variables) to the container overrides. Task arguments and metadata are never passed in container environment overrides; run-task reads QUEUES_TASK_ID and claims the authoritative record from your storage backend.
Execution Profiles & Hardware Sizing¶
Map logical profile names to distinct Cloud Run Job resources (for example standard CPU vs high-memory vs GPU jobs) using CloudRunExecutionConfig.profiles. When resolving which Cloud Run Job to launch for a QueuedTaskRecord, resolve_job_name() evaluates in strict order:
profiles[record.execution_profile]whenrecord.execution_profileis set and present inprofiles.CloudRunExecutionConfig.job_namewhen configured.profiles["default"]when present.
If none of the three resolves a job name, QueueConfigurationError is raised.
Required IAM Permissions & Cloud SQL Connectivity¶
Grant the service account executing QueueService.enqueue() (and QueueMaintenanceService) permission to launch, inspect, and cancel Cloud Run Job executions:
run.jobs.runandrun.jobs.runWithOverrides(included inroles/run.developer)run.executions.get(used byreconcile_external_tasks()to inspect execution completion or failure)run.executions.cancel(used byQueueService.cancel_task()to terminate running job executions)
When connecting both the web service and Cloud Run Jobs to Cloud SQL or Memorystore Redis/Valkey, attach the Cloud SQL Auth Proxy volume (--add-cloudsql-instances) or Direct VPC Egress (--network / --subnet) to both the Cloud Run Service and every target Cloud Run Job definition.
Dispatch Failure Handling & Deterministic Exit Codes¶
- Fallback Backend (
fallback_execution_backend): IfCloudRunExecutionConfig.fallback_execution_backendis set (for example"local"), dispatch failures fall back to the named execution backend; whenNone(default), dispatch errors leave the record eligible for boundedreconcile_external_tasks()repair. - Deterministic Container Exit Codes (
TaskExitCode):litestar queues run-taskandpython -m litestar_queues.consumerexit with distinct codes so Cloud Run Job execution logs reflect the exact outcome:
| Exit Code | TaskExitCode Constant |
Meaning |
|---|---|---|
0 |
TaskExitCode.SUCCESS |
Task claimed, executed, and persisted as completed (or rescheduled for retry) |
1 |
TaskExitCode.FAILURE |
Task raised an exception that exhausted retries and settled as failed |
2 |
TaskExitCode.MISSING_TASK_ID |
Neither --task-id nor QUEUES_TASK_ID / LITESTAR_QUEUES_TASK_ID was provided |
3 |
TaskExitCode.INVALID_TASK_ID |
Task ID value is not a valid UUID |
4 |
TaskExitCode.MISSING_RECORD |
Record was not found, already terminal, or claimed by another worker |
5 |
TaskExitCode.UNKNOWN_TASK |
Record references a task_name that is not registered in the current process |
6 |
TaskExitCode.CLAIM_LOST |
Worker heartbeat fence detected claim loss during execution |
7 |
TaskExitCode.CANCELLED |
Task execution was cancelled (TaskCancelledError or asyncio.CancelledError) |
8 |
TaskExitCode.MISSING_CONFIG_FACTORY |
Standalone consumer invoked without QUEUES_CONFIG_FACTORY configured |