Repository navigation
v0.53.0
Cancel a Dataflow job and drain a streaming one (#274). Each write has a twin that builds its exact request without sending it, so the request can be confirmed before it is sent. Experimental, as the module is. No breaking change: the calls are new, dataflow.PreparedRequest is a new export, and dataflow.Error is unchanged. The default user agents changed.
// Drain a running streaming job, confirmed first, then watch it stop.
var job = try client.getJob(location, id);
defer job.deinit();
if (job.value.drainable()) {
var request = try client.drainJobRequest(location, id);
defer request.deinit();
if (try confirm(request.value.method, request.value.url, request.value.body)) {
try client.drainJob(location, id);
}
}| Call | What it does |
|---|---|
Client.cancelJob(location, id) |
Cancels a batch or streaming job |
Client.drainJob(location, id) |
Drains a streaming job: it stops reading and finishes what it holds |
Client.cancelJobRequest, Client.drainJobRequest |
The writes' exact requests, after the same checks with the same errors |
Job.cancellable(), Job.drainable() |
Whether production takes the write for the job as it stands |
- Both writes return nothing. When production takes a write, its whole answer is the job's type, and it said batch for a streaming job too. Read the job to watch it stop: a get shows
.cancellingor.drainingat once. - Each write is a
PUTof the job's requested state alone, as gcloud sends it. A repeat answers 200 and changes nothing, so both are retried the same way the reads are. - They take the
dataflow.jobs.cancelpermission, which Dataflow Viewer lacks.
What production showed
Measured on 2026-10-20 in a test project:
- A pending job refuses a cancel ("Job is not yet ready for canceling") until it runs, about 20 s after launch.
- An ended job refuses both writes, and the cancel's refusal names the state it ended in. A batch job refuses a drain in any state.
- A made-up region still reaches the job, while a real region the job is not in gets a 404. Take the region from the job as listed.
- A list's summary never carries the requested state, so read the job itself.
- A cancelled job took 240 s to end, and one cancelled as it finished ended
.done. Drains took 102 to 126 s, and a cancel during a drain wins. - A streaming job reads
.runningbefore its worker is up.
Testing
dataflow.testing.FakeDataflow takes both writes as production did, with every refusal above in production's words. It answers 501, naming what it does not model, where production's answer was not measured. setJobState to a terminal state now clears requested_state, and a list's summary no longer carries it, as production's did not. A slow fuzz property holds the fake's writes to a model, in a new nightly job.
The gated production test (DATAFLOW_WRITES_TEST=1, about 3 cents and ten minutes) cancels a real batch job and drains a real streaming one through the client. It passed twice: all 12 previews matched what was sent byte for byte, and a fake kept in step with production agreed on every write.
The guides: Cancel and drain and Testing. examples/dataflow_stop.zig --fake shows the confirm-then-write flow offline.
The changelog has each module's entry.
zig fetch --save git+https://github.com/kmoneil/zig-gcp#v0.53.0