Visitar URL original
[java-pubsub] PubSub: keepalive timeout (15s) is shorter than the client ping interval (30s) · Issue #14348 · googleapis/google-cloud-java · GitHub
Skip to content

[java-pubsub] PubSub: keepalive timeout (15s) is shorter than the client ping interval (30s) #14348

Description

@thermoweb

Issue Details

The Subscriber keepalive pings the server every 30s, but the monitor that watches
for the answer times out after 15s. The two values can't work together, and the
stream gets torn down on every ping the server doesn't answer.

This is a follow-up to googleapis/java-pubsub#2651,
which I can't comment on since that repo is archived. That issue was closed by
googleapis/java-pubsub#2652, which disabled the keepalive and lowered the log to
INFO. googleapis/java-pubsub#2672 re-enabled the keepalive. The log level stayed
at INFO, but the timing was never looked at, and I think that's the real problem.

In StreamingSubscriberConnection:

private static final Duration CLIENT_PING_INTERVAL = Duration.ofSeconds(30);
private static final Duration SERVER_MONITOR_INTERVAL = Duration.ofSeconds(10);
private static final Duration SERVER_PING_TIMEOUT_DURATION = Duration.ofSeconds(15);

The monitor measures the time since the last ping, and every ping resets that
clock:

Duration elapsedSincePing = Duration.ofNanos(now - lastPing);
if (elapsedSincePing.compareTo(SERVER_PING_TIMEOUT_DURATION) < 0) {
  return;
}

So with a ping every 30s and a 15s tolerance, a ping the server doesn't answer
tears the stream down ~20s later, before the next ping ever happens. Lowering
CLIENT_PING_INTERVAL below 15s doesn't fix it either, it just makes the check
unreachable. Either way the two constants don't line up.

Two ways to fix it as far as I can see:

  • make SERVER_PING_TIMEOUT_DURATION larger than CLIENT_PING_INTERVAL
  • or track the oldest unanswered ping instead of the last one, so a new ping
    doesn't reset the timeout

Happy to send a PR, just let me know which one you'd prefer.

Environment

Required: Provide details about your environment. If relevant, please also provide details about your GCP environment. The environment details MUST be filled out.

Provide the relevant details about your environment:

  • OS Type and Version: Ubuntu 24.04.5 LTS (kernel 6.8.0-139), and Docker
    eclipse-temurin:21-jre for the deployed services
  • Java Version and JDK Vendor: 21.0.5, Temurin 21.0.5+11 LTS
  • (If using GraalVM) GraalVM Version: not used

Dependencies

Relevant part of mvn dependency:tree:

com.google.cloud:spring-cloud-gcp-starter-pubsub:jar:7.4.10:compile
+- com.google.cloud:spring-cloud-gcp-starter:jar:7.4.10:compile
|  +- com.google.cloud:spring-cloud-gcp-core:jar:7.4.10:compile
|  |  \- com.google.cloud:google-cloud-core:jar:2.72.0:compile
|  \- com.google.cloud:spring-cloud-gcp-autoconfigure:jar:7.4.10:compile
\- com.google.cloud:spring-cloud-gcp-pubsub:jar:7.4.10:compile
   \- com.google.cloud:google-cloud-pubsub:jar:1.152.0:compile
      +- com.google.auth:google-auth-library-oauth2-http:jar:1.49.0:compile
      +- com.google.auth:google-auth-library-credentials:jar:1.49.0:compile
      +- com.google.http-client:google-http-client-gson:jar:2.1.1:compile
      +- com.google.api:gax:jar:2.82.0:compile
      +- com.google.api:gax-grpc:jar:2.82.0:compile
      +- io.grpc:grpc-core:jar:1.81.0:compile
      +- com.google.api:gax-httpjson:jar:2.82.0:compile
      \- com.google.http-client:google-http-client:jar:2.1.1:compile

Versions:

  • Libraries-Bom: 26.85.0, pulled in by spring-cloud-gcp-dependencies 7.4.10
  • google-cloud-pubsub: 1.152.0
  • Gax: gax 2.82.0 / gax-grpc 2.82.0 / gax-httpjson 2.82.0
  • Auth: google-auth-library-oauth2-http 1.49.0 / google-auth-library-credentials 1.49.0
  • Google-Http-Java-Client: 2.1.1
  • grpc-core: 1.81.0

Reproducer

I don't have a standalone reproducer to attach. The steps below are what I get
from reading the code, and they match what another user described on
googleapis/java-pubsub#2651 after the keepalive was re-enabled.

Steps to reproduce:

  1. Start the Pub/Sub emulator, which doesn't answer the keepalive ping
  2. Create a topic and a subscription
  3. Start a Subscriber on that subscription and leave it idle
  4. Watch the logs from com.google.cloud.pubsub.v1.StreamingSubscriberConnection

The log shows up every ~30s per subscriber thread, and the stream is closed with
Status.UNAVAILABLE / "Keepalive timeout with server" each time.

The same happens against real Pub/Sub, but only when a ping goes unanswered, so
it's occasional rather than constant.

Logs and Stack Trace

The line is logged by com.google.cloud.pubsub.v1.StreamingSubscriberConnection
at INFO and looks like this:

INFO: No response from server for 15 seconds since last ping. Closing stream.

There is no stack trace: the stream is closed on purpose by the monitor, with

clientStream.closeSendWithError(
    Status.UNAVAILABLE.withDescription("Keepalive timeout with server").asException());

Behavior

Optional: Any additional information about the behavior of the error is helpful to debug.

Behavioral Questions:

Activity

  1. HesandaLiyanage commented on Sep 11, 2026

    @HesandaLiyanage

    @lqiu96 Hi! I'd like to work on this fix. Could you please assign this issue to me?

  2. lqiu96 commented on Sep 11, 2026

    @lqiu96
    Member

    @michaelpri10 Would you have capacity to help with reviews for a PR from an external contributor(s)?

  3. michaelpri10 commented on Sep 14, 2026

    @michaelpri10
    Contributor

    This is expected behavior. The streaming pull keepalive is used to tear down streams where the connection may be broken, due server-side, client-side, or potentially networking issues (for example, we've seen issues with firewalls silently tearing down the connection, leading to delays in message delivery). The keepalive timeout is specifically for the duration between when a client ping is sent and when the server responds, so having it as a lower value than the client ping interval is okay. The server should always answer a ping within the 15 second timeout, so we consider an unanswered ping to be indicative of a broken stream and thus the client library restarts the streaming pull connection.

    The Pub/Sub emulator does not currently have streaming pull keepalive support, so this is something we would need to update the emulator to support instead of changing the Java client.

  4. michaelpri10 commented on Sep 14, 2026

    @michaelpri10
    Contributor

    The Pub/Sub emulator does not currently have streaming pull keepalive support, so this is something we would need to update the emulator to support instead of changing the Java client.

    I was mistaken here, streaming pull keepalive support was added to the Pub/Sub emulator in May 2026, the 568.0.0 release of the gcloud CLI (corresponding to the 0.8.32 release of the Pub/Sub emulator). Upgrading your emulator version to the most recent version should help resolve the issues testing this with the emulator.

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

Metadata

Metadata

Assignees

Labels

api: pubsubIssues related to the Pub/Sub API.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions