Skip to content

fix(topology): don't hang a reload on a sink that stops polling its input - #26688

Open
hathawsh wants to merge 3 commits into
vectordotdev:masterfrom
hathawsh:fix/reload-stuck-sink-26687
Open

hathawsh wants to merge 3 commits into
vectordotdev:masterfrom
hathawsh:fix/reload-stuck-sink-26687

Conversation

@hathawsh

@hathawsh hathawsh commented Oct 14, 2026 •

Copy link
Copy Markdown

Summary

A config reload hangs forever if it changes a sink that has stopped polling its input. While it hangs, Vector ignores SIGTERM. A socket sink does this while it retries a connection that keeps failing (#26687).

Why it hangs:

  • RunningTopology::shutdown_diff cancels the sink's detach trigger, which ends its input stream via take_until_if(tripwire). Then it awaits the old sink task with no timeout. The task's output (TaskOutput::Sink(rx)) is how the buffer gets handed to the new sink.
  • TcpSink::run peeks one event and then calls connect_backoff(). That retries forever and never looks at the input again, so the task never finishes.
  • SIGTERM isn't actually ignored. It queues behind the reload, because StartedApplication::main awaits the reload inside a select! arm.

The fix is in the topology, in build_instrumented_sink. The sink task already owns rx and only lends it to sink.run(rx.by_ref()...). So the task now races sink.run(...) against "inputs detached, then the graceful shutdown limit elapsed". If the timer wins:

  • the run future is dropped and a warning is logged;
  • TaskOutput::Sink(rx) is returned as usual, so the reload completes and the new sink takes over the same buffer.

Notes:

  • Timeout. It reuses --graceful-shutdown-limit-secs (Config::graceful_shutdown_duration). With --no-graceful-shutdown-limit, the old wait-forever behavior is kept. A config loaded for a reload doesn't carry that value, so reload_config_and_respawn now copies the running topology's limit into the new config. Without that, sinks built by a reload would have no limit.
  • Normal reloads and shutdowns are unchanged. The timer starts only when the tripwire resolves true, which happens when shutdown_diff cancels the trigger. Removed inputs and normal shutdown disable the trigger instead.
  • Scope. Because the fix lives in the topology, not the socket sink, it should also help with other sinks whose retry loop doesn't poll their input. Vector fails to reload sink with failed events #24125 (http sink) looks like the same pattern, but I haven't verified that case.
  • Trade-off. Events the stuck sink had already taken from the buffer are dropped. For the TCP sink, that is the peeked event and the rest of its EventArray. Previously those events were stuck forever and the process needed SIGKILL.

I didn't fix the inside of the TCP sink. With an event already peeked, the sink can't tell that its input has ended without consuming more events.

Not addressed here:

  • Signals that arrive during a reload still wait for it to finish, but the wait is now bounded.
  • A changed sink that the reload doesn't wait for (for example, one whose memory buffer size changed) is detached and forgotten, so a stuck one leaks its task.

References

Vector configuration

From #26687; nothing listens on 127.0.0.1:7000:

sources:
  gen:
    type: demo_logs
    format: json
    interval: 0.1

sinks:
  out:
    type: socket
    inputs: [gen]
    mode: tcp
    address: 127.0.0.1:7000
    encoding:
      codec: json

The reload adds keepalive: {time_secs: 30} to the sink, then sends SIGHUP.

How did you test this PR?

New test: topology::test::topology_reload_replaces_sink_that_stops_polling_input.

  • It uses the test_backpressure mock sink, which takes one event and then never polls again.
  • The startup config carries the limit and the reload configs don't, as in a SIGHUP reload. The test replaces a stuck sink with another stuck sink, then replaces that one with basic_sink. It asserts that both reloads complete and the final sink receives later events.
  • On master it fails (the first reload times out after 10 s). Without the running.rs change, the second reload times out.

Existing topology:: tests: all pass, except reload::topology_reload_preserves_enrichment_table_state. That test also fails on unmodified master when the whole topology:: suite runs in parallel on my machine, and it passes when run alone, with or without this change.

Manual testing with a debug build on Linux x86_64, --graceful-shutdown-limit-secs 10, and the config above:

  • Nothing listening on 7000:
    • On master, the reload never completes, and Vector is still running 20 s after SIGTERM.
    • With this change, the reload completes after 10 s with the new warning, and the following SIGTERM shuts Vector down within the limit.
  • Listener running from the start: the reload completes immediately, with no warning.
  • Listener started after a stuck reload: the new sink delivers the buffered events and new ones.
  • Two SIGHUP reloads in a row, nothing listening: each completes after the limit.

Also ran cargo fmt --check and cargo vdev check changelog-fragments. Clippy (-D warnings, with the features I built) reports nothing in the changed files.

Does this PR include user facing changes?

  • Yes. Please add a changelog fragment based on our guidelines.
  • No. A maintainer will apply the no-changelog label to this PR.

Contributor Guidelines

  • Please read our Vector contributor resources.
  • Do not hesitate to use @vectordotdev/vector to reach out to us regarding this PR.
  • Before pushing, follow our pre-push guidance.
  • After a review is requested, please avoid force pushes to help us review incrementally.
    • Feel free to push as many commits as you want. They will be squashed into one before merging.
    • For example, you can run git merge origin master and git push.

🤖 Generated with Claude Code

…nput

When a reload changes a sink whose buffer is reused (or that has a disk
buffer), the topology cancels the sink's detach trigger and then waits for
the sink task to finish so that it can hand the buffer to the new sink. A
sink that never polls its input again never finishes, so the reload hangs
forever and later signals, including SIGTERM, are never handled. The TCP
socket sink does this while it retries a connection that keeps failing.

Once a sink's inputs are detached, give it up to the graceful shutdown
limit to finish, then drop it and return its buffer anyway. Events the sink
had already taken from the buffer are dropped. With no graceful shutdown
limit, the old behavior of waiting forever is kept.

Closes vectordotdev#26687

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@hathawsh
hathawsh requested a review from a team as a code owner October 14, 2026 20:52
@github-actions github-actions Bot added the domain: topology Anything related to Vector's topology code label Oct 14, 2026
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@hathawsh

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 423ee0f380

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/topology/builder.rs
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-17T20:27:27.649825Z 142f472 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

hathawsh and others added 2 commits October 14, 2026 15:02
The graceful shutdown limit comes from the command line, so a config loaded
for a reload has no limit. Sinks built by a reload therefore had no limit
on how long they get to finish once a later reload detached them, and a
second reload of a stuck sink still hung. Carry the running topology's
limit over to the new config, and extend the test to two reloads.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nk-26687

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

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

Labels

domain: topology Anything related to Vector's topology code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Config reload hangs forever while a socket sink is reconnecting, and SIGTERM is then ignored

1 participant