Skip to content

feat(pubsub): implement streaming keep-alive logic#34653

Open
torreypayne wants to merge 5 commits into
mainfrom
pubsub-streaming-keepalive
Open

feat(pubsub): implement streaming keep-alive logic#34653
torreypayne wants to merge 5 commits into
mainfrom
pubsub-streaming-keepalive

Conversation

@torreypayne

@torreypayne torreypayne commented Jun 22, 2026

Copy link
Copy Markdown
Member

Overview

Implements proactive streaming keep-alive logic and connection health monitoring in Google::Cloud::PubSub::MessageListener::Stream, mirroring the design implemented in the .NET Pub/Sub client (dotnet#15649).

Long-running bi-directional gRPC streaming pull connections (StreamingPull) can experience silent TCP drops, intermediary network timeouts, or read deadlocks during periods of low message volume. This change introduces background timer tasks to push regular keep-alive requests and actively monitor server Pong timestamps.

Key Changes

  • Protocol Version Initialization: Explicitly initializes protocol_version = 1 on the initial StreamingPullRequest protobuf to enable bi-directional stream keep-alive support.
  • Unconditional Keep-Alive Pings: Configures a background timer task (@stream_keepalive_task) to dispatch empty StreamingPullRequest pings at regular intervals (default 30 seconds), regardless of current lease inventory volume.
  • Pong Monitoring & Automatic Reconnection: Introduces @pong_monitor_task to inspect timestamps (@last_ping_at, @last_pong_at). If a keep-alive response is overdue by more than pong_deadline seconds (default 15 seconds), the monitor raises RestartStream to safely recycle the connection and back off.
  • Concurrency Timestamp Guard: Guards ping timestamp updates (@last_ping_at = now if @last_pong_at >= @last_ping_at) to ensure consecutive un-ponged pings cannot overwrite the timestamp of an overdue request.

Testing & Validation

  • Unit Test Suite (keepalive_test.rb): Added targeted unit test coverage asserting protocol version flags, timer intervals, deadline timeouts, and non-disruptive Pong handling.
  • Resiliency & Robustness Suite: Validated against live GCP test instances (helical-zone-771) across simulated TCP socket hangs, sub-millisecond deadline starvation, and post-recovery downstream message delivery.

Fixes b/427319802

@torreypayne
torreypayne force-pushed the pubsub-streaming-keepalive branch 2 times, most recently from cf5df9b to b1acc8a Compare June 23, 2026 04:52
@torreypayne
torreypayne marked this pull request as ready for review June 23, 2026 15:54
@torreypayne
torreypayne requested review from a team and yoshi-approver as code owners June 23, 2026 15:54
@torreypayne
torreypayne force-pushed the pubsub-streaming-keepalive branch 2 times, most recently from 94e9f14 to ad59cd2 Compare June 23, 2026 17:29

@robertvoinescu-work robertvoinescu-work 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.

LGTM from a functional perspective. Just a few minor comments.

Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
@torreypayne
torreypayne force-pushed the pubsub-streaming-keepalive branch 2 times, most recently from ca925d3 to e53a9bd Compare June 26, 2026 20:41
@quartzmo

This comment was marked as resolved.

@torreypayne
torreypayne force-pushed the pubsub-streaming-keepalive branch from 26bcbb1 to 24055fd Compare June 30, 2026 15:53
@torreypayne torreypayne added kokoro:run Add this label to force Kokoro to re-run the tests. kokoro:force-run Add this label to force Kokoro to re-run the tests. labels Jun 30, 2026
@yoshi-kokoro yoshi-kokoro removed kokoro:run Add this label to force Kokoro to re-run the tests. kokoro:force-run Add this label to force Kokoro to re-run the tests. labels Jun 30, 2026
@torreypayne

This comment was marked as resolved.

Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
@torreypayne
torreypayne force-pushed the pubsub-streaming-keepalive branch 2 times, most recently from 2fa07a4 to cca4878 Compare July 7, 2026 15:36
Comment thread google-cloud-pubsub/test/google/cloud/pubsub/message_listener/keepalive_test.rb Outdated
quartzmo

This comment was marked as resolved.

@aandreassa aandreassa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Implementation looks great, just a couple of Ruby things & docs to think about!

Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/keepalive_monitor.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
@torreypayne
torreypayne force-pushed the pubsub-streaming-keepalive branch 2 times, most recently from 43562dd to f4927d5 Compare July 16, 2026 18:43
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/keepalive_monitor.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/keepalive_monitor.rb Outdated
@torreypayne
torreypayne force-pushed the pubsub-streaming-keepalive branch 3 times, most recently from 5bdc0a7 to d589b24 Compare July 22, 2026 21:53
@torreypayne
torreypayne requested a review from aandreassa July 23, 2026 01:48
@torreypayne
torreypayne force-pushed the pubsub-streaming-keepalive branch 2 times, most recently from 4e59244 to d998c4f Compare July 23, 2026 20:57
Implement bi-directional keep-alive pings and pong liveness monitoring for Cloud Pub/Sub StreamingPull streams in Ruby.

- Set protocol_version = 1 in initial StreamingPullRequest to negotiate keep-alive support.
- Extract KeepaliveMonitor collaborator class to manage keep-alive ping/pong timestamps, liveness checks, and timeout restarts cleanly.
- Disable liveness monitor evaluation during reconnect backoff and handshake phases to prevent false-positive restarts.
- Add unit, integration, and live acceptance test suites.
@torreypayne
torreypayne force-pushed the pubsub-streaming-keepalive branch from 346a70c to 3d9d3d1 Compare July 23, 2026 21:19
Comment thread google-cloud-pubsub/verify_keepalive.rb Outdated
@@ -0,0 +1,85 @@
# verify_keepalive.rb

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: Remove?

Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/stream.rb Outdated
Comment thread google-cloud-pubsub/lib/google/cloud/pubsub/message_listener/keepalive_monitor.rb Outdated
@stopped = nil
@paused = nil
@pause_cond = new_cond
@backoff_cond = new_cond

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A lightweight comment for each would be helpful since we have two versions now.

Could you please share what the bug was with only having one? Or point me to a test where you verify the behavior in case I missed it.

Comment thread google-cloud-pubsub/test/google/cloud/pubsub/message_listener/keepalive_test.rb Outdated
assert wait_called
end

it "does not bleed self sentinel into the new request queue after restart_stream_for_timeout!" do

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This test doesn't actually call restart_stream_for_timeout! or raise RestartStream. It manually pushes stream into the queue and directly calls private background_run.

Could we rewrite it to invoke it directly or simulate the monitor timing out? This way, we are testing the actual production path rather than manually stubbing internal variables.

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.

5 participants