tako docs guide reference api
GitHub

job.md — tako.Job + tako.Runner

Long-running compute (§2.7): work that outlives request timeouts. In the cloud an execution is a Cloud Run Job — no request deadline exists; a six-minute settlement just finishes. A job is compute you walk away from: Run hands off and returns, and the result lands wherever the body writes it (shop's settle job writes a reports entity).

Declare

Two vars, from examples/shop/app.go:

// settle is long-running compute (§2.7): a Cloud Run JOB in the
// cloud — no request timeout to outlive — and a tracked in-process
// execution locally.
settle = tako.Job[SettleParams]("settle")

Settle = tako.Runner(settle, runSettle)

The job is the carrier; the runner var is a tako.Handler, and it is the one that becomes the deployed Cloud Run Job.

tako.Job[T](name) declares the job and its parameter type; the name must be a string constant (analyzer-enforced). tako.Runner binds the body — like every async handler it is func(Ctx, T) error, and a job has exactly one body. Options on JobHandler:

Use

Trigger it from any handler — api, worker, cron — with handle.Run(ctx, params):

// startSettle HANDS OFF and returns immediately — the job is compute
// you walk away from; the report entity is where its result lands.
func startSettle(ctx tako.Ctx, req SettleReq) (SettleStarted, error) {
	if err := settle.Run(ctx, SettleParams{Day: req.Day, ThrottleMs: req.ThrottleMs}); err != nil {
		return SettleStarted{}, err
	}
	return SettleStarted{Started: true}, nil
}

Run returns once the execution is handed off — queued locally, a Cloud Run Job execution created in the cloud. It never waits for completion.

Retries and timeout

var Settle = tako.Runner(settle, runSettle,
	tako.RunnerConfig{
		Parallel:  4,
		Attempts:  1,
		Timeout:   2 * time.Hour,
		Footprint: tako.Footprint{Memory: "4Gi"},
	})

RunnerConfig.Attempts sets how many times a failed task is retried before the execution fails (default 3, range 0–10). Jobs are compute, not messaging: there is no dead letter to catch a task that runs out of attempts, which is why the ceiling is low — a task that fails ten times will not pass on the eleventh.

Timeout on the footprint caps one TASK (default 900s from the batch profile, up to 24h) — not the execution, which is as long as its slowest task.

Local

An execution rides the local queue dispatcher as a tracked in-process run — the ⚙ line in the dev console narrates it, DrainQueues/WaitIdle cover it in tests, retries follow the local queue policy (5 attempts, fixed 200ms backoff), and a failed run dead-letters inspectably under .tako/data/deadletter/ — no invisible goroutines.

Cloud

tako deploy creates a run job <pkg>-<var> (shop: shop-settle) — a Cloud Run Job v2, not a service. Executions run on demand: Run calls the Jobs API's runWithOverrides, handing params over as the --tako-job-params container arg the generated job main decodes. The spec is fixed: maxRetries: 3, timeout: "3600s", and there is no dead-letter — this is compute, not messaging; Cloud Run retries a failed task, then the execution is simply Failed. Task compute comes from the batch profile unless a footprint pins CPU/Memory.

The wiring is derived, never hand-configured: every service that calls Run gets the job's full resource name injected as TAKO_JOB_<NAME>, and its service account gets roles/run.developer on that job only — runWithOverrides (the params channel) sits in run.developer, not run.invoker. Cost: an execution bills CPU and memory only while it runs; an idle job costs nothing.

Test it

The shop pattern (examples/shop/api_test.go, TestSettleJobRunsToCompletion): trigger through the api, wait for the tracked execution, read the result entity:

started, res := postJSON[SettleStarted](t, alice, srv.URL+"/api/settle", SettleReq{Day: "2026-07-27"})
if err := runtime.WaitIdle(30 * time.Second); err != nil {
	t.Fatal(err)
}

Errors you'll meet

Not yet