Skip to content

Commit

Permalink
Feat(deadline) add deadline property for client
Browse files Browse the repository at this point in the history
  • Loading branch information
Бацура Сергей Александрович authored and Бацура Сергей Александрович committed Aug 3, 2024
1 parent a18aeeb commit 72842c9
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2016-2023 The gRPC-Spring Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.devh.boot.grpc.client.autoconfigure;

import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import io.grpc.stub.AbstractStub;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
import net.devh.boot.grpc.client.deadline.DeadlineHelper;
import net.devh.boot.grpc.client.inject.StubTransformer;

/**
* The deadline auto configuration for the client.
*
* <p>
* You can disable this config by using:
* </p>
*
* <pre>
* <code>@ImportAutoConfiguration(exclude = GrpcClientDeadlineAutoConfiguration.class)</code>
* </pre>
*
* @author Sergei Batsura ([email protected])
*/
@Slf4j
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(GrpcClientAutoConfiguration.class)
public class GrpcClientDeadlineAutoConfiguration {

/**
* Creates a {@link StubTransformer} bean that will add the call credentials to the created stubs.
*
* @param props The properties for deadline configuration.
* @return The StubTransformer bean that will add the deadline from properties.
* @see DeadlineHelper#deadlineStubTransformer(GrpcChannelsProperties)
* @see AbstractStub#withDeadline(io.grpc.Deadline)
*/
@Bean
StubTransformer deadlineStubTransformer(final GrpcChannelsProperties props) {
return DeadlineHelper.deadlineStubTransformer(props);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,33 @@
@EqualsAndHashCode
public class GrpcChannelProperties {

// --------------------------------------------------
// Target Deadline
// --------------------------------------------------

private Duration deadline = null;

/**
* Gets the connection deadline.
*
* @return The connection deadline or null
* @see #setDeadline(Duration)
*/
public Duration getDeadline() {
return this.deadline;
}

/**
* Set the deadline for the stub. If nothing is configured then the deadline will not be used by default
*
* @param deadline The connection deadline or null.
*
* @see #setDeadline(Duration)
*/
public void setDeadline(Duration deadline) {
this.deadline = deadline;
}

// --------------------------------------------------
// Target Address
// --------------------------------------------------
Expand Down Expand Up @@ -477,6 +504,9 @@ public void copyDefaultsFrom(final GrpcChannelProperties config) {
if (this == config) {
return;
}
if (this.deadline == null) {
this.deadline = config.deadline;
}
if (this.address == null) {
this.address = config.address;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2016-2023 The gRPC-Spring Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.devh.boot.grpc.client.deadline;

import static java.util.Objects.requireNonNull;

import java.util.concurrent.TimeUnit;

import io.grpc.Deadline;
import io.grpc.stub.AbstractStub;
import net.devh.boot.grpc.client.config.GrpcChannelProperties;
import net.devh.boot.grpc.client.config.GrpcChannelsProperties;
import net.devh.boot.grpc.client.inject.StubTransformer;

public class DeadlineHelper {

/**
* Creates a new {@link StubTransformer} that will assign the given deadline to the given {@link AbstractStub}.
*
* @param props The properties to assign the deadline.
* @return The transformed stub.
* @see AbstractStub#withDeadline(Deadline)
*/
public static StubTransformer deadlineStubTransformer(final GrpcChannelsProperties props) {
requireNonNull(props, "properties");

return (name, stub) -> {
GrpcChannelProperties channelProps = props.getChannel(name);
if (channelProps != null && channelProps.getDeadline() != null) {
return stub.withDeadline(Deadline.after(channelProps.getDeadline().toMillis(), TimeUnit.MILLISECONDS));
} else {
return stub;
}
};
}

private DeadlineHelper() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Contains classes and utilities that help with the deadline.
*/

package net.devh.boot.grpc.client.deadline;
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@
"description": "Connection timeout at application startup. If set to a positive duration instructs a client to connect to GRPC-endpoint when GRPC stub is created.",
"defaultValue": 0
},
{
"name": "grpc.client.GLOBAL.deadline",
"type": "java.time.Duration",
"sourceType": "net.devh.boot.grpc.client.config.GrpcChannelProperties",
"description": "A deadline is used to specify a point in time past which a client is unwilling to wait for a response from a server"
},
{
"name": "grpc.client.GLOBAL.security.authority-override",
"type": "java.lang.String",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ net.devh.boot.grpc.client.autoconfigure.GrpcClientHealthAutoConfiguration
net.devh.boot.grpc.client.autoconfigure.GrpcClientMicrometerTraceAutoConfiguration
net.devh.boot.grpc.client.autoconfigure.GrpcClientSecurityAutoConfiguration
net.devh.boot.grpc.client.autoconfigure.GrpcDiscoveryClientAutoConfiguration
net.devh.boot.grpc.client.autoconfigure.GrpcClientDeadlineAutoConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.springframework.context.annotation.Configuration;

import net.devh.boot.grpc.client.autoconfigure.GrpcClientAutoConfiguration;
import net.devh.boot.grpc.client.autoconfigure.GrpcClientDeadlineAutoConfiguration;
import net.devh.boot.grpc.common.autoconfigure.GrpcCommonCodecAutoConfiguration;
import net.devh.boot.grpc.server.autoconfigure.GrpcServerAutoConfiguration;
import net.devh.boot.grpc.server.autoconfigure.GrpcServerFactoryAutoConfiguration;
Expand All @@ -28,7 +29,7 @@
@Configuration
@ImportAutoConfiguration({GrpcCommonCodecAutoConfiguration.class, GrpcServerAutoConfiguration.class,
GrpcServerFactoryAutoConfiguration.class, GrpcServerSecurityAutoConfiguration.class,
GrpcClientAutoConfiguration.class})
GrpcClientAutoConfiguration.class, GrpcClientDeadlineAutoConfiguration.class})
public class BaseAutoConfiguration {

}
103 changes: 103 additions & 0 deletions tests/src/test/java/net/devh/boot/grpc/test/setup/DeadlineTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2016-2023 The gRPC-Spring Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.devh.boot.grpc.test.setup;

import static io.grpc.Status.DEADLINE_EXCEEDED;
import static net.devh.boot.grpc.test.util.GrpcAssertions.assertStatus;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.concurrent.ExecutionException;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import com.google.protobuf.Empty;

import io.grpc.Channel;
import io.grpc.StatusRuntimeException;
import io.grpc.internal.testing.StreamRecorder;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.config.GrpcChannelProperties;
import net.devh.boot.grpc.client.inject.GrpcClient;
import net.devh.boot.grpc.test.config.BaseAutoConfiguration;
import net.devh.boot.grpc.test.config.ServiceConfiguration;
import net.devh.boot.grpc.test.proto.SomeType;
import net.devh.boot.grpc.test.proto.TestServiceGrpc;

/**
* These tests check the property {@link GrpcChannelProperties#getDeadline()}.
*/
public class DeadlineTests {

@Slf4j
@SpringBootTest(properties = {
"grpc.client.GLOBAL.address=localhost:9090",
"grpc.client.GLOBAL.deadline=10s",
"grpc.client.GLOBAL.negotiationType=PLAINTEXT",
})
@SpringJUnitConfig(classes = {ServiceConfiguration.class, BaseAutoConfiguration.class})
@DirtiesContext
static class SuccessfullyDeadlineTests extends AbstractSimpleServerClientTest {
}

@Slf4j
@SpringBootTest(properties = {
"grpc.client.GLOBAL.address=localhost:9090",
"grpc.client.GLOBAL.deadline=0s",
"grpc.client.GLOBAL.negotiationType=PLAINTEXT",
})
@SpringJUnitConfig(classes = {ServiceConfiguration.class, BaseAutoConfiguration.class})
@DirtiesContext
static class UnsuccessfullyDeadlineTests {

@GrpcClient("test")
protected Channel channel;
@GrpcClient("test")
protected TestServiceGrpc.TestServiceStub testServiceStub;
@GrpcClient("test")
protected TestServiceGrpc.TestServiceBlockingStub testServiceBlockingStub;
@GrpcClient("test")
protected TestServiceGrpc.TestServiceFutureStub testServiceFutureStub;

UnsuccessfullyDeadlineTests() {
log.info("--- UnsuccessfullyDeadlineTests ---");
}

@Test
@DirtiesContext
void testServiceStubDeadlineEnabledAndUnsuccessful() {
log.info("--- Starting tests with successful call ---");
final StreamRecorder<SomeType> streamRecorder = StreamRecorder.create();
this.testServiceStub.normal(Empty.getDefaultInstance(), streamRecorder);

assertThrows(ExecutionException.class, () -> streamRecorder.firstValue().get().getVersion());
assertNotNull(streamRecorder.getError());
assertEquals(StatusRuntimeException.class, streamRecorder.getError().getClass());
assertStatus(DEADLINE_EXCEEDED.getCode(), (StatusRuntimeException) streamRecorder.getError());
assertStatus(DEADLINE_EXCEEDED.getCode(),
assertThrows(StatusRuntimeException.class,
() -> testServiceBlockingStub.normal(Empty.getDefaultInstance())));
assertThrows(ExecutionException.class,
() -> testServiceFutureStub.normal(Empty.getDefaultInstance()).get());
log.info("--- Test completed ---");
}
}
}

0 comments on commit 72842c9

Please sign in to comment.