diff --git a/CHANGELOG.md b/CHANGELOG.md index e4611dca..b0528640 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ 1. [#306](https://github.com/InfluxCommunity/influxdb3-java/pull/306): Improve closing of Arrow `FlightStream`. +### Bug Fixes + +1. [#301](https://github.com/InfluxCommunity/influxdb3-java/issues/301): Fix gRPC deadline reuse issue causing "deadline exceeded" errors when `QueryOptions` instances are reused across multiple queries. + ## 1.5.0 [2025-10-22] ### Features diff --git a/src/main/java/com/influxdb/v3/client/internal/InfluxDBClientImpl.java b/src/main/java/com/influxdb/v3/client/internal/InfluxDBClientImpl.java index 5eb05f23..4d398c74 100644 --- a/src/main/java/com/influxdb/v3/client/internal/InfluxDBClientImpl.java +++ b/src/main/java/com/influxdb/v3/client/internal/InfluxDBClientImpl.java @@ -398,13 +398,6 @@ private Stream queryData(@Nonnull final String query, Arguments.checkNotNull(parameters, "parameters"); Arguments.checkNotNull(options, "options"); - if (options.grpcCallOptions().getDeadline() == null && config.getQueryTimeout() != null) { - options.setGrpcCallOptions(new GrpcCallOptions.Builder() - .fromGrpcCallOptions(options.grpcCallOptions()) - .withDeadline(Deadline.after(config.getQueryTimeout().toMillis(), TimeUnit.MILLISECONDS)) - .build()); - } - if (closed) { throw new IllegalStateException("InfluxDBClient has been closed."); } @@ -421,7 +414,16 @@ private Stream queryData(@Nonnull final String query, } }); - CallOption[] callOptions = options.grpcCallOptions().getCallOptions(); + // Create a fresh deadline for this query if queryTimeout is configured and no explicit deadline is set + GrpcCallOptions grpcCallOptions = options.grpcCallOptions(); + if (grpcCallOptions.getDeadline() == null && config.getQueryTimeout() != null) { + grpcCallOptions = new GrpcCallOptions.Builder() + .fromGrpcCallOptions(grpcCallOptions) + .withDeadline(Deadline.after(config.getQueryTimeout().toMillis(), TimeUnit.MILLISECONDS)) + .build(); + } + + CallOption[] callOptions = grpcCallOptions.getCallOptions(); return flightSqlClient.execute( query, diff --git a/src/test/java/com/influxdb/v3/client/internal/GrpcCallOptionsTest.java b/src/test/java/com/influxdb/v3/client/internal/GrpcCallOptionsTest.java index 6bb2cd22..8c820f73 100644 --- a/src/test/java/com/influxdb/v3/client/internal/GrpcCallOptionsTest.java +++ b/src/test/java/com/influxdb/v3/client/internal/GrpcCallOptionsTest.java @@ -252,4 +252,25 @@ void testToString() { + "maxOutboundMessageSize=5000}"; assertEquals(expected, options.toString()); } + + @Test + void testDeadlineIsNotZeroOrPast() throws InterruptedException { + // Create a deadline 5 seconds from now + Deadline deadline = Deadline.after(5000, TimeUnit.MILLISECONDS); + GrpcCallOptions options = new GrpcCallOptions.Builder() + .withDeadline(deadline) + .build(); + + // Verify the deadline is in the future + assertNotNull(options.getDeadline()); + long timeRemaining = options.getDeadline().timeRemaining(TimeUnit.MILLISECONDS); + Assertions.assertTrue(timeRemaining > 0, "Deadline should be in the future"); + Assertions.assertTrue(timeRemaining <= 5000, "Deadline should not exceed 5 seconds"); + + // Wait a bit and verify deadline is still valid but closer + TimeUnit.MILLISECONDS.sleep(100); + long timeRemainingAfter = options.getDeadline().timeRemaining(TimeUnit.MILLISECONDS); + Assertions.assertTrue(timeRemainingAfter > 0, "Deadline should still be in the future"); + Assertions.assertTrue(timeRemainingAfter < timeRemaining, "Deadline should be closer to expiration"); + } }