Skip to content

feat(client): Add drain command for the worker preStop hook - #810

Open
oioki wants to merge 2 commits into
mainfrom
feat/client-drain-command
Open

oioki wants to merge 2 commits into
mainfrom
feat/client-drain-command

Conversation

@oioki

@oioki oioki commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Add python3 -m taskbroker_client.drain <seconds>. It creates the worker drain file (/tmp/taskworker-draining), which stops the worker from publishing occupancy, then waits for seconds.

getsentry is moving to a distroless image, and the taskworker preStop hook (/bin/sh -c "touch /tmp/taskworker-draining && sleep 15") needs a shell. The drain file has to stay: it is how brokers stop routing to the pod before SIGTERM. That's why Kubernetes' native preStop: sleep isn't enough on its own. The client already owns both sides of this contract: the worker reads the file and clears a stale one at startup. The command keeps the writer next to them, so ops no longer copies the path.

Running a command shipped with the application is the usual way to drain without a shell:

seconds is a plain number, like sleep and Kubernetes' preStop.sleep.seconds, so the existing worker_prestop_sleep value passes through unchanged.

Merge order: a getsentry image with this release must be live in a region before its hook switches to ["python3", "-m", "taskbroker_client.drain", "15"]. On an older image the hook fails and the pod gets SIGTERM without draining.

Refs SEC-1112

oioki and others added 2 commits October 7, 2026 13:16
Add `python3 -m taskbroker_client.drain <seconds>`, which creates the
worker drain file and then sleeps. Pods currently do this with
`/bin/sh -c "touch /tmp/taskworker-draining && sleep N"`, which does not
work on distroless images that have no shell, touch or sleep.

The client owns the drain file path, so the hook no longer has to
duplicate it. The module imports only the constants, so the hook starts
in milliseconds instead of loading the worker.

Co-Authored-By: Claude <noreply@anthropic.com>
Replace the module docstring with a short one on drain(), since no other
client module has one. Drop the test for touching an existing file, which
the worker never relies on, and the import-weight test, which guarded
about 0.5 s inside a 15 s hook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Oct 8, 2026

Copy link
Copy Markdown

SEC-1112

@oioki
oioki marked this pull request as ready for review October 8, 2026 12:07
@oioki
oioki requested a review from a team as a code owner October 8, 2026 12:07
which must finish before Kubernetes sends SIGTERM.
"""
Path(path).touch()
time.sleep(seconds)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need to sleep? Can't it just exit immediately after writing the file?

@oioki oioki Oct 8, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an equivalent of the old touch /tmp/taskworker-draining && sleep 15. There is no sleep (because of no shell) outside of the subcommand, so we have to sleep here.

Initially that 15 sec sleep was added in https://github.com/getsentry/ops/pull/23208

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right OK makes sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants