Skip to content

Commit 5461432

Browse files
committed
feat grpc: add absolute timepoint propagation on the client side
commit_hash:313054dc2c8ba0b5d9e18757c8ced60f58ecd183
1 parent bc4aade commit 5461432

9 files changed

Lines changed: 97 additions & 7 deletions

File tree

core/functional_tests/basic_chaos/tests-deadline/test_client.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,11 +401,12 @@ async def test_dp_timeout_not_retried(
401401

402402
async def test_absolute_deadline_propagated_as_is(call, mockserver):
403403
epoch_us_deadline = _make_deadline_epoch_us(10.0)
404-
captured = {}
404+
captured_headers = None
405405

406406
@mockserver.handler('/test')
407407
async def mock(request):
408-
captured['headers'] = dict(request.headers)
408+
nonlocal captured_headers
409+
captured_headers = dict(request.headers)
409410
return mockserver.make_response('OK!')
410411

411412
response = await call(
@@ -415,4 +416,5 @@ async def mock(request):
415416
},
416417
)
417418
assert response.status == 200
418-
assert captured['headers'].get(DP_ABSOLUTE_DEADLINE) == epoch_us_deadline
419+
assert captured_headers is not None
420+
assert captured_headers.get(DP_ABSOLUTE_DEADLINE) == epoch_us_deadline

grpc/functional_tests/basic_chaos/service.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <samples/greeter_client.usrv.pb.hpp>
2121
#include <samples/greeter_service.usrv.pb.hpp>
2222

23+
#include "client.hpp"
24+
2325
namespace samples {
2426

2527
class GreeterServiceComponent final : public api::GreeterServiceBase::Component {
@@ -28,7 +30,8 @@ class GreeterServiceComponent final : public api::GreeterServiceBase::Component
2830

2931
GreeterServiceComponent(const components::ComponentConfig& config, const components::ComponentContext& context)
3032
: api::GreeterServiceBase::Component(config, context),
31-
prefix_(config["greeting-prefix"].As<std::string>())
33+
prefix_(config["greeting-prefix"].As<std::string>()),
34+
greeter_client_(context.FindComponent<GreeterClient>())
3235
{}
3336

3437
inline SayHelloResult SayHello(CallContext& context, api::GreetingRequest&& request) final;
@@ -53,6 +56,7 @@ class GreeterServiceComponent final : public api::GreeterServiceBase::Component
5356

5457
private:
5558
const std::string prefix_;
59+
GreeterClient& greeter_client_;
5660
};
5761

5862
GreeterServiceComponent::SayHelloResult GreeterServiceComponent::SayHello(
@@ -67,6 +71,10 @@ GreeterServiceComponent::SayHelloResult GreeterServiceComponent::SayHello(
6771
const engine::TaskCancellationBlocker block_cancel;
6872
TESTPOINT("testpoint_cancel", {});
6973
}
74+
} else if (request.name() == "test_deadline_propagation") {
75+
const auto greeting = greeter_client_.SayHello(request.name(), /*is_small_timeout=*/false);
76+
response.set_greeting(greeting);
77+
return response;
7078
}
7179

7280
response.set_greeting(fmt::format("{}, {}!", prefix_, request.name()));

grpc/functional_tests/basic_chaos/static_config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ components_manager:
8383
grpc-client-factory:
8484
middlewares:
8585
grpc-client-deadline-propagation:
86-
enabled: false
86+
enabled: true
8787
channel-args: {}
8888
greeter-client:
8989
endpoint: '[::]:8081'

grpc/functional_tests/basic_chaos/tests-nonchaos/conftest.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@
66

77
pytest_plugins = ['pytest_userver.plugins.grpc']
88

9+
USERVER_CONFIG_HOOKS = ['_prepare_service_config']
10+
11+
12+
@pytest.fixture(scope='session')
13+
def _prepare_service_config(grpc_mockserver_endpoint):
14+
def patch_config(config, config_vars):
15+
components = config['components_manager']['components']
16+
components['greeter-client']['endpoint'] = grpc_mockserver_endpoint
17+
18+
return patch_config
19+
920

1021
@pytest.fixture(scope='session')
1122
def service_env():

grpc/functional_tests/basic_chaos/tests-nonchaos/test_deadline_absolute.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pytest
55

66
import samples.greeter_pb2 as greeter_pb2
7+
import samples.greeter_pb2_grpc as greeter_pb2_grpc
78

89
DP_ABSOLUTE_DEADLINE = 'x-request-deadline'
910

@@ -84,3 +85,48 @@ async def test_absolute_deadline_invalid_format(grpc_client):
8485
timeout=5.0,
8586
)
8687
assert response.greeting == 'Hello, Python!'
88+
89+
90+
async def test_absolute_deadline_propagated_as_is(grpc_client, grpc_mockserver):
91+
epoch_us_deadline = _make_deadline_epoch_us(120.0)
92+
captured_metadata = None
93+
94+
@grpc_mockserver(greeter_pb2_grpc.GreeterServiceServicer.SayHello)
95+
async def mock_say_hello(request, context: grpc.aio.ServicerContext):
96+
nonlocal captured_metadata
97+
captured_metadata = dict(context.invocation_metadata())
98+
return greeter_pb2.GreetingResponse(
99+
greeting='Hello, Python!',
100+
)
101+
102+
response = await grpc_client.SayHello(
103+
greeter_pb2.GreetingRequest(name='test_deadline_propagation'),
104+
metadata=[(DP_ABSOLUTE_DEADLINE, epoch_us_deadline)],
105+
timeout=5.0,
106+
)
107+
assert response.greeting == 'Hello, Python!'
108+
assert captured_metadata is not None
109+
assert captured_metadata.get(DP_ABSOLUTE_DEADLINE) == epoch_us_deadline
110+
111+
112+
async def test_absolute_deadline_synthesized_when_only_duration_propagated(
113+
grpc_client,
114+
grpc_mockserver,
115+
):
116+
captured_metadata = None
117+
118+
@grpc_mockserver(greeter_pb2_grpc.GreeterServiceServicer.SayHello)
119+
async def mock_say_hello(request, context: grpc.aio.ServicerContext):
120+
nonlocal captured_metadata
121+
captured_metadata = dict(context.invocation_metadata())
122+
return greeter_pb2.GreetingResponse(
123+
greeting='Hello, Python!',
124+
)
125+
126+
response = await grpc_client.SayHello(
127+
greeter_pb2.GreetingRequest(name='test_deadline_propagation'),
128+
timeout=5.0,
129+
)
130+
assert response.greeting == 'Hello, Python!'
131+
assert captured_metadata is not None
132+
assert DP_ABSOLUTE_DEADLINE in captured_metadata

grpc/src/ugrpc/client/middlewares/deadline_propagation/middleware.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@
22

33
#include <userver/server/request/task_inherited_data.hpp>
44
#include <userver/tracing/tags.hpp>
5+
#include <userver/utils/datetime.hpp>
56
#include <userver/utils/impl/internal_tag.hpp>
67

8+
#include <ugrpc/impl/rpc_metadata.hpp>
79
#include <userver/ugrpc/client/impl/call_state.hpp>
10+
#include <userver/ugrpc/impl/to_string.hpp>
811
#include <userver/ugrpc/time_utils.hpp>
912

13+
#include <chrono>
14+
1015
USERVER_NAMESPACE_BEGIN
1116

1217
namespace ugrpc::client::middlewares::deadline_propagation {
@@ -29,8 +34,21 @@ void UpdateDeadline(MiddlewareCallContext& context) {
2934

3035
auto& client_context = context.GetClientContext();
3136

32-
const auto context_time_left = ugrpc::TimespecToDuration(client_context.raw_deadline());
3337
const engine::Deadline task_deadline = USERVER_NAMESPACE::server::request::GetTaskInheritedDeadline();
38+
const auto original_absolute_deadline = USERVER_NAMESPACE::server::request::GetTaskInheritedOriginalDeadline();
39+
40+
if (original_absolute_deadline.has_value()) {
41+
const auto deadline_header = std::to_string(original_absolute_deadline->time_since_epoch().count());
42+
client_context.AddMetadata(ugrpc::impl::kXRequestDeadline, ugrpc::impl::ToGrpcString(deadline_header));
43+
} else if (task_deadline.IsReachable()) {
44+
const auto absolute_deadline =
45+
std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now()) +
46+
std::chrono::duration_cast<std::chrono::microseconds>(task_deadline.TimeLeft());
47+
const auto deadline_header = std::to_string(absolute_deadline.time_since_epoch().count());
48+
client_context.AddMetadata(ugrpc::impl::kXRequestDeadline, ugrpc::impl::ToGrpcString(deadline_header));
49+
}
50+
51+
const auto context_time_left = ugrpc::TimespecToDuration(client_context.raw_deadline());
3452

3553
const auto client_deadline_reachable = (context_time_left != engine::Deadline::Duration::max());
3654
if (!task_deadline.IsReachable() && !client_deadline_reachable) {

grpc/src/ugrpc/impl/rpc_metadata.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const grpc::string kXTestsuiteClientName = "x-testsuite-client-name";
3131
const grpc::string kTestsuiteNetworkCode = "network";
3232
const grpc::string kTestsuiteTimeoutCode = "timeout";
3333

34+
const grpc::string kXRequestDeadline = "x-request-deadline";
35+
3436
} // namespace ugrpc::impl
3537

3638
USERVER_NAMESPACE_END

grpc/src/ugrpc/impl/rpc_metadata.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ extern const grpc::string kXTestsuiteClientName;
3131
extern const grpc::string kTestsuiteNetworkCode;
3232
extern const grpc::string kTestsuiteTimeoutCode;
3333

34+
extern const grpc::string kXRequestDeadline;
35+
3436
} // namespace ugrpc::impl
3537

3638
USERVER_NAMESPACE_END

grpc/src/ugrpc/server/middlewares/deadline_propagation/middleware.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <userver/server/request/task_inherited_data.hpp>
1111
#include <userver/utils/impl/internal_tag.hpp>
1212

13+
#include <ugrpc/impl/rpc_metadata.hpp>
1314
#include <userver/ugrpc/impl/statistics_scope.hpp>
1415
#include <userver/ugrpc/impl/to_string.hpp>
1516
#include <userver/ugrpc/status_codes.hpp>
@@ -42,7 +43,7 @@ bool CheckAndSetupDeadline(
4243

4344
std::optional<USERVER_NAMESPACE::server::request::TaskInheritedOriginalDeadline> absolute_original_deadline;
4445
const auto& client_metadata = server_context.client_metadata();
45-
const auto absolute_deadline_it = client_metadata.find("x-request-deadline");
46+
const auto absolute_deadline_it = client_metadata.find(ugrpc::impl::kXRequestDeadline);
4647
if (absolute_deadline_it != client_metadata.end()) {
4748
absolute_original_deadline = USERVER_NAMESPACE::server::request::impl::ParseXRequestDeadlineString(
4849
ugrpc::impl::ToString(absolute_deadline_it->second)

0 commit comments

Comments
 (0)