Skip to content

fix(gcp_pubsub source): recover from stalled streams and reduce error noise#25901

Open
SamyDjemai wants to merge 13 commits into
vectordotdev:masterfrom
SamyDjemai:fix/gcp-pubsub-stalled-stream
Open

fix(gcp_pubsub source): recover from stalled streams and reduce error noise#25901
SamyDjemai wants to merge 13 commits into
vectordotdev:masterfrom
SamyDjemai:fix/gcp-pubsub-stalled-stream

Conversation

@SamyDjemai

@SamyDjemai SamyDjemai commented Jul 20, 2026

Copy link
Copy Markdown

Summary

The gcp_pubsub source could stop processing messages until Vector was restarted, and floods the logs with misleading failed_fetching_events errors on idle/low-volume/bursty subscriptions. This addresses both:

  • Send HTTP/2 keepalive pings so a silently broken connection is detected instead of hanging (interval/timeout configurable via the new keepalive option, mirroring the vector sink).
  • Use a non-blocking keepalive send so a backed-up request channel can't deadlock the task.
  • Add idle_timeout_secs (default 900) to restart a stream that stops delivering responses without erroring.
  • Only escalate stream failures to a component error after max_retry_errors (default 5) consecutive failures with no successful fetch in between; transient failures are logged at debug. A successful fetch resets the counter.

How did you test this PR?

cargo fmt --check, cargo clippy -- -D warnings, unit tests, and vdev check events all pass (run in a Rust 1.95 container).

Testing caveat, in the interest of transparency: the runtime behavior here is hard to cover with automated tests. The code=8a75 "no messages" response is a real-GCP behavior that the Pub/Sub emulator does not reproduce, and exercising the stalled-connection recovery (HTTP/2 keepalive + idle timeout) would require fault-injection infrastructure that doesn't currently exist in the repo. The existing gcp_pubsub emulator tests are also disabled (#24133). Unit tests here therefore only cover the config validation; the retry-escalation and connection-resilience changes are validated by reasoning and would benefit from manual verification against a real GCP subscription.

Change Type

  • Bug fix
  • New feature
  • Dependencies
  • Non-functional (chore, refactoring, docs)
  • Performance

Is this a breaking change?

  • Yes
  • No

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.

References


Per Vector's AI policy: this PR was substantially generated with an AI agent (Claude Opus 4.8). I have reviewed, understood, and tested the changes, and take ownership of them.

@github-actions github-actions Bot added docs review on hold The documentation team reviews PRs only after a PR is approved by the COSE team. domain: sources Anything related to the Vector's sources domain: external docs Anything related to Vector's external, public documentation and removed docs review on hold The documentation team reviews PRs only after a PR is approved by the COSE team. labels Jul 20, 2026
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

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

@datadog-vectordotdev

This comment has been minimized.

@SamyDjemai

Copy link
Copy Markdown
Author

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

@github-actions github-actions Bot added the docs review on hold The documentation team reviews PRs only after a PR is approved by the COSE team. label Jul 20, 2026
@SamyDjemai
SamyDjemai marked this pull request as ready for review July 20, 2026 18:42
@SamyDjemai
SamyDjemai requested review from a team as code owners July 20, 2026 18:42

@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: 7e6e1a0236

ℹ️ 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/sources/gcp_pubsub.rs
Comment thread src/sources/gcp_pubsub.rs Outdated
Comment thread src/sources/gcp_pubsub.rs Outdated
- Defer idle-timeout restart while acknowledgements are pending so
  finalized-but-unsent ack IDs are not dropped and redelivered.
- Skip the keepalive when the request channel is full instead of
  reconnecting, preserving queued acks.
- Make idle_timeout_secs optional and derive a default larger than
  keepalive_secs so existing configs with a long keepalive keep starting.

Adds an Option<DurationSecondsWithFrac> Configurable impl in vector-config
to support the now-optional field.
@SamyDjemai

Copy link
Copy Markdown
Author

@codex review

@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: 47479d311e

ℹ️ 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/sources/gcp_pubsub.rs Outdated
Comment thread changelog.d/19418_gcp_pubsub_idle_reconnect.fix.md
Comment thread src/sources/gcp_pubsub.rs Outdated
Comment thread src/sources/gcp_pubsub.rs Outdated
- Default the HTTP/2 keepalive interval to 300s: Pub/Sub follows gRPC's
  default server enforcement and closes streams pinged more often than
  every 5 minutes with too_many_pings.
- Share the consecutive-failure counter across concurrent streams so a
  successful fetch on any stream resets the source-level count.
- Also gate the idle restart on the request channel being drained, not
  just pending_acks, to avoid dropping queued ack IDs.
- Add the required authors trailer to the changelog fragment.
@SamyDjemai

Copy link
Copy Markdown
Author

@codex review

@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: 1f0b4d2881

ℹ️ 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/sources/gcp_pubsub.rs Outdated
Comment thread src/sources/gcp_pubsub.rs
Comment thread src/sources/gcp_pubsub.rs
- Always restart on idle timeout: the previous ack-drain guard disabled
  the recovery path exactly when the request stream stalled, hanging the
  source. Unsent ack IDs are dropped and redelivered, acceptable under
  at-least-once delivery.
- Emit the fetch component error once per failure streak (== threshold)
  instead of on every retry past it, so an idle subscription no longer
  resumes the error flood.
- Give max_retry_errors a derivative default so programmatic/generated
  configs keep the threshold instead of collapsing to 0.

@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: d45eddce26

ℹ️ 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/sources/gcp_pubsub.rs
Awaiting a full ack channel inside the select arm stalled the whole loop,
so a backed-up request stream could never be recovered by the idle
timeout. Use a non-blocking send and reconnect when the channel is full;
dropped ack IDs are redelivered under at-least-once delivery.
@SamyDjemai

Copy link
Copy Markdown
Author

@codex review

@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: 5526793559

ℹ️ 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/sources/gcp_pubsub.rs
Comment thread src/sources/gcp_pubsub.rs Outdated
Comment thread src/sources/gcp_pubsub.rs
Emitting only at the exact threshold silenced a persistently broken
stream after one report and made max_retry_errors: 1 report just the
first failure. Use >= so failures keep surfacing until a success resets
the counter, matching the documented behavior.
@SamyDjemai

Copy link
Copy Markdown
Author

@codex review

@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: bc31918cc9

ℹ️ 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/sources/gcp_pubsub.rs
Comment on lines +707 to +710
// The recovery path for a stalled stream: always fires, never
// gated on the request channel that may have stopped draining.
// Unsent ack IDs are dropped and redelivered (at-least-once).
_ = &mut idle_deadline => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep the idle timeout polling in auto-ack mode

When end-to-end source acknowledgements are disabled, which is the default, handle_response still auto-acks by awaiting the bounded ack_ids.send(ids) path. If the request stream is the stalled side and that 8-slot channel fills, the task is parked inside handle_response, so this newly added idle-timeout branch is never polled and the source can still hang until restart. Please make the auto-ack send non-blocking/reconnect as well, or otherwise keep the idle timer active while waiting on that channel.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch — this was the one remaining ack path that still blocked. Fixed in 3f992d5: the auto-ack branch in handle_response now uses a non-blocking try_send and reconnects on a full channel, matching the finalizer and keepalive paths, so the idle timeout can always recover a stalled request stream. Dropped ack IDs are redelivered under at-least-once.

The default (acknowledgements disabled) path in handle_response still
awaited the bounded ack channel, which parks the task inside the
stream.next() arm and starves the idle-timeout recovery when the request
stream stalls. Send non-blocking and reconnect on a full channel, like
the finalizer and keepalive paths; dropped ack IDs are redelivered.
@SamyDjemai

Copy link
Copy Markdown
Author

@codex review

@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: 3f992d59c8

ℹ️ 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/sources/gcp_pubsub.rs Outdated
Comment thread src/sources/gcp_pubsub.rs
…meout

If Pub/Sub accepts the connection but streaming_pull never resolves (the
no-data startup case), the source could hang with no recovery. Arm the
idle deadline before the startup call so a stuck startup also reconnects.
@SamyDjemai

Copy link
Copy Markdown
Author

@codex review

@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

emit!(GcpPubsubStreamingPullError { error });

P2 Badge Route startup stream failures through retry threshold

When Pub/Sub returns a transient Unavailable before response headers are received, this startup branch still emits a component error on every retry and never updates consecutive_failures; the new max_retry_errors threshold only applies later in stream.next(). This matters for idle or low-volume subscriptions because this code already notes that streaming_pull may sit in startup until data is available, so the same “no messages” failures can continue to flood failed_streaming_pull logs/metrics instead of being suppressed until the configured threshold.

ℹ️ 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/sources/gcp_pubsub.rs
Comment on lines +740 to +744
match ack_ids_sender.try_send(Vec::new()) {
Ok(()) => {}
Err(mpsc::error::TrySendError::Full(_)) => {
debug!("Keepalive skipped: request stream has acks queued.");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Avoid spinning when zero keepalive meets a full request queue

If a user configures keepalive_secs: 0 and the request stream stops draining, this new non-blocking full-queue path returns immediately; the surrounding tokio::time::sleep(self.keepalive) is then ready again on the next loop, so the task can hot-loop and repeatedly log this debug message instead of yielding until space is available. The previous awaited send backpressured on a full channel, so this regression only shows up with zero keepalive and a stalled/full ack queue.

Useful? React with 👍 / 👎.

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

Labels

docs review on hold The documentation team reviews PRs only after a PR is approved by the COSE team. domain: external docs Anything related to Vector's external, public documentation domain: sources Anything related to the Vector's sources

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GCP PubSub Source: messages stop being picked up and do not resume until restart

1 participant