Skip to content

Commit ceb559a

Browse files
committed
feat core: uint64_t Unix epoch us for X-Request-Deadline
commit_hash:b5353f7a5ed69d8f6dab87f490b4ca6bda024ee3
1 parent de70eb2 commit ceb559a

6 files changed

Lines changed: 43 additions & 36 deletions

File tree

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
VERSION = {'version': '2'}
1010

1111

12-
def _make_iso_deadline(offset_seconds: float) -> str:
13-
tp = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
12+
def _make_deadline_epoch_us(offset_seconds: float) -> str:
13+
deadline_utc = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
1414
seconds=offset_seconds,
1515
)
16-
return tp.strftime('%Y-%m-%dT%H:%M:%S.') + f'{tp.microsecond:06d}Z'
16+
unix_epoch_utc = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
17+
one_microsecond = datetime.timedelta(microseconds=1)
18+
microseconds_since_epoch = (deadline_utc - unix_epoch_utc) // one_microsecond
19+
return str(microseconds_since_epoch)
1720

1821

1922
@pytest.fixture(name='call')
@@ -397,7 +400,7 @@ async def test_dp_timeout_not_retried(
397400

398401

399402
async def test_absolute_deadline_propagated_as_is(call, mockserver):
400-
iso_deadline = _make_iso_deadline(10.0)
403+
epoch_us_deadline = _make_deadline_epoch_us(10.0)
401404
captured = {}
402405

403406
@mockserver.handler('/test')
@@ -408,8 +411,8 @@ async def mock(request):
408411
response = await call(
409412
headers={
410413
DP_TIMEOUT_MS: '500',
411-
DP_ABSOLUTE_DEADLINE: iso_deadline,
414+
DP_ABSOLUTE_DEADLINE: epoch_us_deadline,
412415
},
413416
)
414417
assert response.status == 200
415-
assert captured['headers'].get(DP_ABSOLUTE_DEADLINE) == iso_deadline
418+
assert captured['headers'].get(DP_ABSOLUTE_DEADLINE) == epoch_us_deadline

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

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515
DP_ABSOLUTE_DEADLINE = 'X-Request-Deadline'
1616

1717

18-
def _make_iso_deadline(offset_seconds: float) -> str:
19-
tp = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
18+
def _make_deadline_epoch_us(offset_seconds: float) -> str:
19+
deadline_utc = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
2020
seconds=offset_seconds,
2121
)
22-
return tp.strftime('%Y-%m-%dT%H:%M:%S.') + f'{tp.microsecond:06d}Z'
22+
unix_epoch_utc = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
23+
one_microsecond = datetime.timedelta(microseconds=1)
24+
microseconds_since_epoch = (deadline_utc - unix_epoch_utc) // one_microsecond
25+
return str(microseconds_since_epoch)
2326

2427

2528
@pytest.fixture(name='call')
@@ -106,7 +109,7 @@ async def test_absolute_deadline_used(call):
106109
headers={
107110
**HEADERS,
108111
DP_TIMEOUT_MS: '1',
109-
DP_ABSOLUTE_DEADLINE: _make_iso_deadline(5.0),
112+
DP_ABSOLUTE_DEADLINE: _make_deadline_epoch_us(5.0),
110113
},
111114
)
112115
assert response.status == 200
@@ -116,7 +119,7 @@ async def test_absolute_deadline_expired(call):
116119
response = await call(
117120
headers={
118121
**HEADERS,
119-
DP_ABSOLUTE_DEADLINE: _make_iso_deadline(-120.0),
122+
DP_ABSOLUTE_DEADLINE: _make_deadline_epoch_us(-120.0),
120123
},
121124
)
122125
_check_deadline_propagation_response(response)
@@ -127,7 +130,7 @@ async def test_absolute_deadline_expired_with_sleep(call):
127130
htype='sleep',
128131
headers={
129132
**HEADERS,
130-
DP_ABSOLUTE_DEADLINE: _make_iso_deadline(-120.0),
133+
DP_ABSOLUTE_DEADLINE: _make_deadline_epoch_us(-120.0),
131134
},
132135
)
133136
_check_deadline_propagation_response(response)
@@ -139,7 +142,7 @@ async def test_absolute_deadline_disabled_dynamically(call):
139142
headers={
140143
**HEADERS,
141144
DP_TIMEOUT_MS: '5000',
142-
DP_ABSOLUTE_DEADLINE: _make_iso_deadline(-1.0),
145+
DP_ABSOLUTE_DEADLINE: _make_deadline_epoch_us(-1.0),
143146
},
144147
)
145148
assert response.status == 200
@@ -150,7 +153,7 @@ async def test_absolute_deadline_clock_skew_fallback(call):
150153
headers={
151154
**HEADERS,
152155
DP_TIMEOUT_MS: '5000',
153-
DP_ABSOLUTE_DEADLINE: _make_iso_deadline(5.0 + 120.0),
156+
DP_ABSOLUTE_DEADLINE: _make_deadline_epoch_us(5.0 + 120.0),
154157
},
155158
)
156159
assert response.status == 200
@@ -161,7 +164,7 @@ async def test_absolute_deadline_clock_skew_fallback_when_negative_skew(call):
161164
headers={
162165
**HEADERS,
163166
DP_TIMEOUT_MS: '5000',
164-
DP_ABSOLUTE_DEADLINE: _make_iso_deadline(-120.0),
167+
DP_ABSOLUTE_DEADLINE: _make_deadline_epoch_us(-120.0),
165168
},
166169
)
167170
assert response.status == 200
@@ -173,7 +176,7 @@ async def test_absolute_deadline_threshold_zero_disables_skew_check(call):
173176
headers={
174177
**HEADERS,
175178
DP_TIMEOUT_MS: '5000',
176-
DP_ABSOLUTE_DEADLINE: _make_iso_deadline(-120.0),
179+
DP_ABSOLUTE_DEADLINE: _make_deadline_epoch_us(-120.0),
177180
},
178181
)
179182
_check_deadline_propagation_response(response)

core/include/userver/server/request/task_inherited_data.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ struct TaskInheritedData final {
4848
/// Signals when an operation has detected deadline expiration
4949
mutable DeadlineSignal deadline_signal{};
5050

51-
/// Original absolute deadline parsed from `X-Request-Deadline` header.
51+
/// Original absolute deadline from `X-Request-Deadline` (Unix epoch microseconds).
5252
/// Intended solely for propagation to downstream services; use @ref deadline for the task deadline.
5353
std::optional<TaskInheritedOriginalDeadline> original_deadline = std::nullopt;
5454
};

core/src/clients/http/request_state.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
#include <userver/utils/algo.hpp>
2525
#include <userver/utils/assert.hpp>
2626
#include <userver/utils/async.hpp>
27-
#include <userver/utils/datetime_light.hpp>
2827
#include <userver/utils/encoding/hex.hpp>
2928
#include <userver/utils/from_string.hpp>
3029
#include <userver/utils/overloaded.hpp>
@@ -973,11 +972,10 @@ void RequestState::UpdateTimeoutHeader() {
973972
);
974973

975974
if (inherited_original_deadline_.has_value()) {
976-
const auto iso_str =
977-
utils::datetime::UtcTimestring(*inherited_original_deadline_, utils::datetime::kAbsoluteDeadlineFormat);
975+
const auto deadline_header = std::to_string(inherited_original_deadline_->time_since_epoch().count());
978976
easy().add_header(
979977
USERVER_NAMESPACE::http::headers::kXRequestDeadline,
980-
iso_str,
978+
deadline_header,
981979
curl::easy::DuplicateHeaderAction::kReplace
982980
);
983981
}

core/src/server/request/impl/absolute_deadline.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include <userver/dynamic_config/snapshot.hpp>
66
#include <userver/logging/log.hpp>
77
#include <userver/tracing/span.hpp>
8-
#include <userver/utils/datetime.hpp>
8+
#include <userver/utils/from_string.hpp>
99

1010
#include <dynamic_config/variables/USERVER_DEADLINE_PROPAGATION_ABSOLUTE_TIMESTAMP_ENABLED.hpp>
1111
#include <dynamic_config/variables/USERVER_DEADLINE_PROPAGATION_CLOCK_SKEW_THRESHOLD_MS.hpp>
@@ -53,13 +53,13 @@ std::optional<TaskInheritedOriginalDeadline> ParseXRequestDeadlineString(const s
5353
if (timestring.empty()) {
5454
return std::nullopt;
5555
}
56-
try {
57-
const auto timestamp = utils::datetime::UtcStringtime(timestring, utils::datetime::kAbsoluteDeadlineFormat);
58-
return std::chrono::time_point_cast<std::chrono::microseconds>(timestamp);
59-
} catch (const std::exception& exception) {
60-
LOG_LIMITED_INFO() << "Can't parse X-Request-Deadline: " << exception.what();
56+
57+
const auto parsed = utils::FromStringNoThrow<std::uint64_t>(timestring);
58+
if (!parsed.has_value()) {
59+
LOG_LIMITED_INFO() << "Can't parse X-Request-Deadline: " << utils::ToString(parsed.error());
6160
return std::nullopt;
6261
}
62+
return TaskInheritedOriginalDeadline{std::chrono::microseconds{parsed.value()}};
6363
}
6464

6565
std::optional<engine::Deadline> TryMakeDeadlinePreferringAbsoluteTimestamp(

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,20 @@
1010
REQUEST = greeter_pb2.GreetingRequest(name='Python')
1111

1212

13-
def _make_iso_deadline(offset_seconds: float) -> str:
14-
tp = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
13+
def _make_deadline_epoch_us(offset_seconds: float) -> str:
14+
deadline_utc = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(
1515
seconds=offset_seconds,
1616
)
17-
return tp.strftime('%Y-%m-%dT%H:%M:%S.') + f'{tp.microsecond:06d}Z'
17+
unix_epoch_utc = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
18+
one_microsecond = datetime.timedelta(microseconds=1)
19+
microseconds_since_epoch = (deadline_utc - unix_epoch_utc) // one_microsecond
20+
return str(microseconds_since_epoch)
1821

1922

2023
async def test_absolute_deadline_used(grpc_client):
2124
response = await grpc_client.SayHello(
2225
REQUEST,
23-
metadata=[(DP_ABSOLUTE_DEADLINE, _make_iso_deadline(120.0))],
26+
metadata=[(DP_ABSOLUTE_DEADLINE, _make_deadline_epoch_us(120.0))],
2427
)
2528
assert response.greeting == 'Hello, Python!'
2629

@@ -29,7 +32,7 @@ async def test_absolute_deadline_expired(grpc_client):
2932
with pytest.raises(grpc.RpcError) as error:
3033
await grpc_client.SayHello(
3134
REQUEST,
32-
metadata=[(DP_ABSOLUTE_DEADLINE, _make_iso_deadline(-120.0))],
35+
metadata=[(DP_ABSOLUTE_DEADLINE, _make_deadline_epoch_us(-120.0))],
3336
)
3437
assert error.value.code() == grpc.StatusCode.DEADLINE_EXCEEDED
3538

@@ -38,7 +41,7 @@ async def test_absolute_deadline_expired(grpc_client):
3841
async def test_absolute_deadline_disabled_dynamically(grpc_client):
3942
response = await grpc_client.SayHello(
4043
REQUEST,
41-
metadata=[(DP_ABSOLUTE_DEADLINE, _make_iso_deadline(-1.0))],
44+
metadata=[(DP_ABSOLUTE_DEADLINE, _make_deadline_epoch_us(-1.0))],
4245
timeout=5.0,
4346
)
4447
assert response.greeting == 'Hello, Python!'
@@ -47,7 +50,7 @@ async def test_absolute_deadline_disabled_dynamically(grpc_client):
4750
async def test_absolute_deadline_clock_skew_fallback(grpc_client):
4851
response = await grpc_client.SayHello(
4952
REQUEST,
50-
metadata=[(DP_ABSOLUTE_DEADLINE, _make_iso_deadline(5.0 + 120.0))],
53+
metadata=[(DP_ABSOLUTE_DEADLINE, _make_deadline_epoch_us(5.0 + 120.0))],
5154
timeout=5.0,
5255
)
5356
assert response.greeting == 'Hello, Python!'
@@ -56,7 +59,7 @@ async def test_absolute_deadline_clock_skew_fallback(grpc_client):
5659
async def test_absolute_deadline_clock_skew_fallback_when_negative_skew(grpc_client):
5760
response = await grpc_client.SayHello(
5861
REQUEST,
59-
metadata=[(DP_ABSOLUTE_DEADLINE, _make_iso_deadline(-120.0))],
62+
metadata=[(DP_ABSOLUTE_DEADLINE, _make_deadline_epoch_us(-120.0))],
6063
timeout=5.0,
6164
)
6265
assert response.greeting == 'Hello, Python!'
@@ -69,7 +72,7 @@ async def test_absolute_deadline_threshold_zero_disables_skew_check(
6972
with pytest.raises(grpc.RpcError) as error:
7073
await grpc_client.SayHello(
7174
REQUEST,
72-
metadata=[(DP_ABSOLUTE_DEADLINE, _make_iso_deadline(-120.0))],
75+
metadata=[(DP_ABSOLUTE_DEADLINE, _make_deadline_epoch_us(-120.0))],
7376
)
7477
assert error.value.code() == grpc.StatusCode.DEADLINE_EXCEEDED
7578

0 commit comments

Comments
 (0)