grpc 1.59.0 → 1.59.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Makefile +7 -1
- data/src/core/ext/filters/http/server/http_server_filter.cc +21 -17
- data/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +504 -361
- data/src/core/ext/transport/chttp2/transport/frame_ping.cc +11 -1
- data/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc +9 -0
- data/src/core/ext/transport/chttp2/transport/internal.h +92 -28
- data/src/core/ext/transport/chttp2/transport/max_concurrent_streams_policy.cc +44 -0
- data/src/core/ext/transport/chttp2/transport/max_concurrent_streams_policy.h +67 -0
- data/src/core/ext/transport/chttp2/transport/parsing.cc +103 -14
- data/src/core/ext/transport/chttp2/transport/ping_callbacks.cc +108 -0
- data/src/core/ext/transport/chttp2/transport/ping_callbacks.h +115 -0
- data/src/core/ext/transport/chttp2/transport/ping_rate_policy.cc +26 -4
- data/src/core/ext/transport/chttp2/transport/ping_rate_policy.h +16 -1
- data/src/core/ext/transport/chttp2/transport/write_size_policy.cc +60 -0
- data/src/core/ext/transport/chttp2/transport/write_size_policy.h +66 -0
- data/src/core/ext/transport/chttp2/transport/writing.cc +149 -77
- data/src/core/lib/channel/promise_based_filter.cc +9 -4
- data/src/core/lib/channel/promise_based_filter.h +2 -1
- data/src/core/lib/experiments/experiments.cc +222 -0
- data/src/core/lib/experiments/experiments.h +135 -0
- data/src/core/lib/iomgr/combiner.cc +3 -0
- data/src/core/lib/transport/metadata_batch.h +11 -1
- data/src/core/lib/transport/transport.h +6 -0
- data/src/ruby/lib/grpc/version.rb +1 -1
- metadata +9 -3
@@ -21,8 +21,12 @@
|
|
21
21
|
#include <inttypes.h>
|
22
22
|
#include <string.h>
|
23
23
|
|
24
|
+
#include <atomic>
|
24
25
|
#include <initializer_list>
|
26
|
+
#include <limits>
|
27
|
+
#include <memory>
|
25
28
|
#include <string>
|
29
|
+
#include <utility>
|
26
30
|
|
27
31
|
#include "absl/base/attributes.h"
|
28
32
|
#include "absl/container/flat_hash_map.h"
|
@@ -31,8 +35,11 @@
|
|
31
35
|
#include "absl/strings/str_cat.h"
|
32
36
|
#include "absl/strings/str_format.h"
|
33
37
|
#include "absl/strings/string_view.h"
|
38
|
+
#include "absl/types/variant.h"
|
34
39
|
|
40
|
+
#include <grpc/event_engine/event_engine.h>
|
35
41
|
#include <grpc/slice.h>
|
42
|
+
#include <grpc/slice_buffer.h>
|
36
43
|
#include <grpc/support/log.h>
|
37
44
|
|
38
45
|
#include "src/core/ext/transport/chttp2/transport/flow_control.h"
|
@@ -48,11 +55,14 @@
|
|
48
55
|
#include "src/core/ext/transport/chttp2/transport/http_trace.h"
|
49
56
|
#include "src/core/ext/transport/chttp2/transport/internal.h"
|
50
57
|
#include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
|
58
|
+
#include "src/core/ext/transport/chttp2/transport/max_concurrent_streams_policy.h"
|
51
59
|
#include "src/core/ext/transport/chttp2/transport/ping_rate_policy.h"
|
60
|
+
#include "src/core/lib/backoff/random_early_detection.h"
|
52
61
|
#include "src/core/lib/channel/call_tracer.h"
|
53
62
|
#include "src/core/lib/channel/channelz.h"
|
54
63
|
#include "src/core/lib/channel/context.h"
|
55
64
|
#include "src/core/lib/debug/trace.h"
|
65
|
+
#include "src/core/lib/experiments/experiments.h"
|
56
66
|
#include "src/core/lib/gprpp/ref_counted_ptr.h"
|
57
67
|
#include "src/core/lib/gprpp/status_helper.h"
|
58
68
|
#include "src/core/lib/iomgr/closure.h"
|
@@ -70,9 +80,11 @@ using grpc_core::HPackParser;
|
|
70
80
|
|
71
81
|
grpc_core::TraceFlag grpc_trace_chttp2_new_stream(false, "chttp2_new_stream");
|
72
82
|
|
73
|
-
static grpc_error_handle init_frame_parser(grpc_chttp2_transport* t
|
83
|
+
static grpc_error_handle init_frame_parser(grpc_chttp2_transport* t,
|
84
|
+
size_t& requests_started);
|
74
85
|
static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
|
75
|
-
int is_continuation
|
86
|
+
int is_continuation,
|
87
|
+
size_t& requests_started);
|
76
88
|
static grpc_error_handle init_data_frame_parser(grpc_chttp2_transport* t);
|
77
89
|
static grpc_error_handle init_rst_stream_parser(grpc_chttp2_transport* t);
|
78
90
|
static grpc_error_handle init_settings_frame_parser(grpc_chttp2_transport* t);
|
@@ -194,8 +206,9 @@ std::string FrameTypeString(uint8_t frame_type, uint8_t flags) {
|
|
194
206
|
}
|
195
207
|
} // namespace
|
196
208
|
|
197
|
-
|
198
|
-
|
209
|
+
absl::variant<size_t, absl::Status> grpc_chttp2_perform_read(
|
210
|
+
grpc_chttp2_transport* t, const grpc_slice& slice,
|
211
|
+
size_t& requests_started) {
|
199
212
|
const uint8_t* beg = GRPC_SLICE_START_PTR(slice);
|
200
213
|
const uint8_t* end = GRPC_SLICE_END_PTR(slice);
|
201
214
|
const uint8_t* cur = beg;
|
@@ -249,6 +262,10 @@ grpc_error_handle grpc_chttp2_perform_read(grpc_chttp2_transport* t,
|
|
249
262
|
return absl::OkStatus();
|
250
263
|
}
|
251
264
|
dts_fh_0:
|
265
|
+
if (requests_started >= t->max_requests_per_read) {
|
266
|
+
t->deframe_state = GRPC_DTS_FH_0;
|
267
|
+
return static_cast<size_t>(cur - beg);
|
268
|
+
}
|
252
269
|
ABSL_FALLTHROUGH_INTENDED;
|
253
270
|
case GRPC_DTS_FH_0:
|
254
271
|
GPR_DEBUG_ASSERT(cur < end);
|
@@ -324,7 +341,7 @@ grpc_error_handle grpc_chttp2_perform_read(grpc_chttp2_transport* t,
|
|
324
341
|
t->incoming_frame_size, t->incoming_stream_id);
|
325
342
|
}
|
326
343
|
t->deframe_state = GRPC_DTS_FRAME;
|
327
|
-
err = init_frame_parser(t);
|
344
|
+
err = init_frame_parser(t, requests_started);
|
328
345
|
if (!err.ok()) {
|
329
346
|
return err;
|
330
347
|
}
|
@@ -378,6 +395,10 @@ grpc_error_handle grpc_chttp2_perform_read(grpc_chttp2_transport* t,
|
|
378
395
|
}
|
379
396
|
cur += t->incoming_frame_size;
|
380
397
|
t->incoming_stream = nullptr;
|
398
|
+
if (t->incoming_frame_type == GRPC_CHTTP2_FRAME_RST_STREAM &&
|
399
|
+
grpc_core::IsChttp2OffloadOnRstStreamEnabled()) {
|
400
|
+
requests_started = std::numeric_limits<size_t>::max();
|
401
|
+
}
|
381
402
|
goto dts_fh_0; // loop
|
382
403
|
} else {
|
383
404
|
err = parse_frame_slice(
|
@@ -397,7 +418,8 @@ grpc_error_handle grpc_chttp2_perform_read(grpc_chttp2_transport* t,
|
|
397
418
|
GPR_UNREACHABLE_CODE(return absl::OkStatus());
|
398
419
|
}
|
399
420
|
|
400
|
-
static grpc_error_handle init_frame_parser(grpc_chttp2_transport* t
|
421
|
+
static grpc_error_handle init_frame_parser(grpc_chttp2_transport* t,
|
422
|
+
size_t& requests_started) {
|
401
423
|
if (t->is_first_frame &&
|
402
424
|
t->incoming_frame_type != GRPC_CHTTP2_FRAME_SETTINGS) {
|
403
425
|
return GRPC_ERROR_CREATE(absl::StrCat(
|
@@ -417,13 +439,13 @@ static grpc_error_handle init_frame_parser(grpc_chttp2_transport* t) {
|
|
417
439
|
"grpc_chttp2_stream %08x",
|
418
440
|
t->expect_continuation_stream_id, t->incoming_stream_id));
|
419
441
|
}
|
420
|
-
return init_header_frame_parser(t, 1);
|
442
|
+
return init_header_frame_parser(t, 1, requests_started);
|
421
443
|
}
|
422
444
|
switch (t->incoming_frame_type) {
|
423
445
|
case GRPC_CHTTP2_FRAME_DATA:
|
424
446
|
return init_data_frame_parser(t);
|
425
447
|
case GRPC_CHTTP2_FRAME_HEADER:
|
426
|
-
return init_header_frame_parser(t, 0);
|
448
|
+
return init_header_frame_parser(t, 0, requests_started);
|
427
449
|
case GRPC_CHTTP2_FRAME_CONTINUATION:
|
428
450
|
return GRPC_ERROR_CREATE("Unexpected CONTINUATION frame");
|
429
451
|
case GRPC_CHTTP2_FRAME_RST_STREAM:
|
@@ -567,7 +589,8 @@ error_handler:
|
|
567
589
|
}
|
568
590
|
|
569
591
|
static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
|
570
|
-
int is_continuation
|
592
|
+
int is_continuation,
|
593
|
+
size_t& requests_started) {
|
571
594
|
const bool is_eoh =
|
572
595
|
(t->incoming_frame_flags & GRPC_CHTTP2_DATA_FLAG_END_HEADERS) != 0;
|
573
596
|
grpc_chttp2_stream* s;
|
@@ -625,10 +648,40 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
|
|
625
648
|
t->incoming_stream_id));
|
626
649
|
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
627
650
|
} else if (GPR_UNLIKELY(
|
628
|
-
t->stream_map.size() >=
|
651
|
+
t->stream_map.size() + t->extra_streams >=
|
629
652
|
t->settings[GRPC_ACKED_SETTINGS]
|
630
653
|
[GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS])) {
|
631
654
|
return GRPC_ERROR_CREATE("Max stream count exceeded");
|
655
|
+
} else if (GPR_UNLIKELY(
|
656
|
+
grpc_core::IsOverloadProtectionEnabled() &&
|
657
|
+
t->streams_allocated.load(std::memory_order_relaxed) >
|
658
|
+
t->max_concurrent_streams_policy.AdvertiseValue())) {
|
659
|
+
// We have more streams allocated than we'd like, so apply some pushback
|
660
|
+
// by refusing this stream.
|
661
|
+
++t->num_pending_induced_frames;
|
662
|
+
grpc_slice_buffer_add(&t->qbuf, grpc_chttp2_rst_stream_create(
|
663
|
+
t->incoming_stream_id,
|
664
|
+
GRPC_HTTP2_REFUSED_STREAM, nullptr));
|
665
|
+
grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM);
|
666
|
+
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
667
|
+
} else if (GPR_UNLIKELY(
|
668
|
+
grpc_core::IsRedMaxConcurrentStreamsEnabled() &&
|
669
|
+
t->stream_map.size() >=
|
670
|
+
t->max_concurrent_streams_policy.AdvertiseValue() &&
|
671
|
+
grpc_core::RandomEarlyDetection(
|
672
|
+
t->max_concurrent_streams_policy.AdvertiseValue(),
|
673
|
+
t->settings[GRPC_ACKED_SETTINGS]
|
674
|
+
[GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS])
|
675
|
+
.Reject(t->stream_map.size(), t->bitgen))) {
|
676
|
+
// We are under the limit of max concurrent streams for the current
|
677
|
+
// setting, but are over the next value that will be advertised.
|
678
|
+
// Apply some backpressure by randomly not accepting new streams.
|
679
|
+
++t->num_pending_induced_frames;
|
680
|
+
grpc_slice_buffer_add(&t->qbuf, grpc_chttp2_rst_stream_create(
|
681
|
+
t->incoming_stream_id,
|
682
|
+
GRPC_HTTP2_REFUSED_STREAM, nullptr));
|
683
|
+
grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM);
|
684
|
+
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
632
685
|
} else if (t->sent_goaway_state == GRPC_CHTTP2_FINAL_GOAWAY_SENT ||
|
633
686
|
t->sent_goaway_state ==
|
634
687
|
GRPC_CHTTP2_FINAL_GOAWAY_SEND_SCHEDULED) {
|
@@ -639,10 +692,29 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
|
|
639
692
|
t, std::string(t->peer_string.as_string_view()).c_str(),
|
640
693
|
t->incoming_stream_id, t->last_new_stream_id));
|
641
694
|
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
695
|
+
} else if (grpc_core::IsBlockExcessiveRequestsBeforeSettingsAckEnabled() &&
|
696
|
+
t->num_incoming_streams_before_settings_ack == 0) {
|
697
|
+
GRPC_CHTTP2_IF_TRACING(gpr_log(
|
698
|
+
GPR_ERROR,
|
699
|
+
"transport:%p SERVER peer:%s rejecting grpc_chttp2_stream id=%d, "
|
700
|
+
"last grpc_chttp2_stream id=%d before settings have been "
|
701
|
+
"acknowledged",
|
702
|
+
t, std::string(t->peer_string.as_string_view()).c_str(),
|
703
|
+
t->incoming_stream_id, t->last_new_stream_id));
|
704
|
+
++t->num_pending_induced_frames;
|
705
|
+
grpc_slice_buffer_add(
|
706
|
+
&t->qbuf,
|
707
|
+
grpc_chttp2_rst_stream_create(t->incoming_stream_id,
|
708
|
+
GRPC_HTTP2_ENHANCE_YOUR_CALM, nullptr));
|
709
|
+
grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM);
|
710
|
+
t->last_new_stream_id = t->incoming_stream_id;
|
711
|
+
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
642
712
|
}
|
713
|
+
--t->num_incoming_streams_before_settings_ack;
|
643
714
|
t->last_new_stream_id = t->incoming_stream_id;
|
644
715
|
s = t->incoming_stream =
|
645
716
|
grpc_chttp2_parsing_accept_stream(t, t->incoming_stream_id);
|
717
|
+
++requests_started;
|
646
718
|
if (GPR_UNLIKELY(s == nullptr)) {
|
647
719
|
GRPC_CHTTP2_IF_TRACING(
|
648
720
|
gpr_log(GPR_ERROR, "grpc_chttp2_stream not accepted"));
|
@@ -650,9 +722,12 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
|
|
650
722
|
}
|
651
723
|
if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
|
652
724
|
GRPC_TRACE_FLAG_ENABLED(grpc_trace_chttp2_new_stream)) {
|
653
|
-
gpr_log(GPR_INFO,
|
654
|
-
|
655
|
-
|
725
|
+
gpr_log(GPR_INFO,
|
726
|
+
"[t:%p fd:%d peer:%s] Accepting new stream; "
|
727
|
+
"num_incoming_streams_before_settings_ack=%u",
|
728
|
+
t, grpc_endpoint_get_fd(t->ep),
|
729
|
+
std::string(t->peer_string.as_string_view()).c_str(),
|
730
|
+
t->num_incoming_streams_before_settings_ack);
|
656
731
|
}
|
657
732
|
if (t->channelz_socket != nullptr) {
|
658
733
|
t->channelz_socket->RecordStreamStartedFromRemote();
|
@@ -763,6 +838,9 @@ static grpc_error_handle init_rst_stream_parser(grpc_chttp2_transport* t) {
|
|
763
838
|
s->stats.incoming.framing_bytes += 9;
|
764
839
|
t->parser = grpc_chttp2_transport::Parser{
|
765
840
|
"rst_stream", grpc_chttp2_rst_stream_parser_parse, &t->simple.rst_stream};
|
841
|
+
if (!t->is_client && grpc_core::IsRstpitEnabled()) {
|
842
|
+
t->max_concurrent_streams_policy.AddDemerit();
|
843
|
+
}
|
766
844
|
return absl::OkStatus();
|
767
845
|
}
|
768
846
|
|
@@ -787,6 +865,7 @@ static grpc_error_handle init_settings_frame_parser(grpc_chttp2_transport* t) {
|
|
787
865
|
return err;
|
788
866
|
}
|
789
867
|
if (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) {
|
868
|
+
t->max_concurrent_streams_policy.AckLastSend();
|
790
869
|
memcpy(t->settings[GRPC_ACKED_SETTINGS], t->settings[GRPC_SENT_SETTINGS],
|
791
870
|
GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
|
792
871
|
t->hpack_parser.hpack_table()->SetMaxBytes(
|
@@ -797,7 +876,17 @@ static grpc_error_handle init_settings_frame_parser(grpc_chttp2_transport* t) {
|
|
797
876
|
t->settings[GRPC_ACKED_SETTINGS]
|
798
877
|
[GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]),
|
799
878
|
t, nullptr);
|
879
|
+
if (t->settings_ack_watchdog !=
|
880
|
+
grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid) {
|
881
|
+
t->event_engine->Cancel(std::exchange(
|
882
|
+
t->settings_ack_watchdog,
|
883
|
+
grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid));
|
884
|
+
}
|
800
885
|
t->sent_local_settings = false;
|
886
|
+
// This is more streams than can be started in http2, so setting this
|
887
|
+
// effictively removes the limit for the rest of the connection.
|
888
|
+
t->num_incoming_streams_before_settings_ack =
|
889
|
+
std::numeric_limits<uint32_t>::max();
|
801
890
|
}
|
802
891
|
t->parser = grpc_chttp2_transport::Parser{
|
803
892
|
"settings", grpc_chttp2_settings_parser_parse, &t->simple.settings};
|
@@ -828,7 +917,7 @@ static grpc_error_handle parse_frame_slice(grpc_chttp2_transport* t,
|
|
828
917
|
&unused)) {
|
829
918
|
grpc_chttp2_parsing_become_skip_parser(t);
|
830
919
|
if (s) {
|
831
|
-
grpc_chttp2_cancel_stream(t, s, err);
|
920
|
+
grpc_chttp2_cancel_stream(t, s, err, true);
|
832
921
|
}
|
833
922
|
return absl::OkStatus();
|
834
923
|
}
|
@@ -0,0 +1,108 @@
|
|
1
|
+
// Copyright 2023 gRPC authors.
|
2
|
+
//
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
// you may not use this file except in compliance with the License.
|
5
|
+
// You may obtain a copy of the License at
|
6
|
+
//
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
//
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
// See the License for the specific language governing permissions and
|
13
|
+
// limitations under the License.
|
14
|
+
|
15
|
+
#include <grpc/support/port_platform.h>
|
16
|
+
|
17
|
+
#include "src/core/ext/transport/chttp2/transport/ping_callbacks.h"
|
18
|
+
|
19
|
+
#include <utility>
|
20
|
+
|
21
|
+
#include "absl/meta/type_traits.h"
|
22
|
+
#include "absl/random/distributions.h"
|
23
|
+
|
24
|
+
#include <grpc/support/log.h>
|
25
|
+
|
26
|
+
grpc_core::TraceFlag grpc_ping_trace(false, "http2_ping");
|
27
|
+
|
28
|
+
namespace grpc_core {
|
29
|
+
|
30
|
+
void Chttp2PingCallbacks::OnPing(Callback on_start, Callback on_ack) {
|
31
|
+
on_start_.emplace_back(std::move(on_start));
|
32
|
+
on_ack_.emplace_back(std::move(on_ack));
|
33
|
+
ping_requested_ = true;
|
34
|
+
}
|
35
|
+
|
36
|
+
void Chttp2PingCallbacks::OnPingAck(Callback on_ack) {
|
37
|
+
auto it = inflight_.find(most_recent_inflight_);
|
38
|
+
if (it != inflight_.end()) {
|
39
|
+
it->second.on_ack.emplace_back(std::move(on_ack));
|
40
|
+
return;
|
41
|
+
}
|
42
|
+
ping_requested_ = true;
|
43
|
+
on_ack_.emplace_back(std::move(on_ack));
|
44
|
+
}
|
45
|
+
|
46
|
+
uint64_t Chttp2PingCallbacks::StartPing(absl::BitGenRef bitgen) {
|
47
|
+
uint64_t id;
|
48
|
+
do {
|
49
|
+
id = absl::Uniform<uint64_t>(bitgen);
|
50
|
+
} while (inflight_.contains(id));
|
51
|
+
CallbackVec cbs = std::move(on_start_);
|
52
|
+
CallbackVec().swap(on_start_);
|
53
|
+
InflightPing inflight;
|
54
|
+
inflight.on_ack.swap(on_ack_);
|
55
|
+
started_new_ping_without_setting_timeout_ = true;
|
56
|
+
inflight_.emplace(id, std::move(inflight));
|
57
|
+
most_recent_inflight_ = id;
|
58
|
+
ping_requested_ = false;
|
59
|
+
for (auto& cb : cbs) {
|
60
|
+
cb();
|
61
|
+
}
|
62
|
+
return id;
|
63
|
+
}
|
64
|
+
|
65
|
+
bool Chttp2PingCallbacks::AckPing(
|
66
|
+
uint64_t id, grpc_event_engine::experimental::EventEngine* event_engine) {
|
67
|
+
auto ping = inflight_.extract(id);
|
68
|
+
if (ping.empty()) return false;
|
69
|
+
if (ping.mapped().on_timeout !=
|
70
|
+
grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid) {
|
71
|
+
event_engine->Cancel(ping.mapped().on_timeout);
|
72
|
+
}
|
73
|
+
for (auto& cb : ping.mapped().on_ack) {
|
74
|
+
cb();
|
75
|
+
}
|
76
|
+
return true;
|
77
|
+
}
|
78
|
+
|
79
|
+
void Chttp2PingCallbacks::CancelAll(
|
80
|
+
grpc_event_engine::experimental::EventEngine* event_engine) {
|
81
|
+
CallbackVec().swap(on_start_);
|
82
|
+
CallbackVec().swap(on_ack_);
|
83
|
+
for (auto& cbs : inflight_) {
|
84
|
+
CallbackVec().swap(cbs.second.on_ack);
|
85
|
+
if (cbs.second.on_timeout !=
|
86
|
+
grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid) {
|
87
|
+
event_engine->Cancel(std::exchange(
|
88
|
+
cbs.second.on_timeout,
|
89
|
+
grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid));
|
90
|
+
}
|
91
|
+
}
|
92
|
+
ping_requested_ = false;
|
93
|
+
}
|
94
|
+
|
95
|
+
absl::optional<uint64_t> Chttp2PingCallbacks::OnPingTimeout(
|
96
|
+
Duration ping_timeout,
|
97
|
+
grpc_event_engine::experimental::EventEngine* event_engine,
|
98
|
+
Callback callback) {
|
99
|
+
GPR_ASSERT(started_new_ping_without_setting_timeout_);
|
100
|
+
started_new_ping_without_setting_timeout_ = false;
|
101
|
+
auto it = inflight_.find(most_recent_inflight_);
|
102
|
+
if (it == inflight_.end()) return absl::nullopt;
|
103
|
+
it->second.on_timeout =
|
104
|
+
event_engine->RunAfter(ping_timeout, std::move(callback));
|
105
|
+
return most_recent_inflight_;
|
106
|
+
}
|
107
|
+
|
108
|
+
} // namespace grpc_core
|
@@ -0,0 +1,115 @@
|
|
1
|
+
// Copyright 2023 gRPC authors.
|
2
|
+
//
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
// you may not use this file except in compliance with the License.
|
5
|
+
// You may obtain a copy of the License at
|
6
|
+
//
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
//
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
// See the License for the specific language governing permissions and
|
13
|
+
// limitations under the License.
|
14
|
+
|
15
|
+
#ifndef GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_PING_CALLBACKS_H
|
16
|
+
#define GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_PING_CALLBACKS_H
|
17
|
+
|
18
|
+
#include <grpc/support/port_platform.h>
|
19
|
+
|
20
|
+
#include <stddef.h>
|
21
|
+
#include <stdint.h>
|
22
|
+
|
23
|
+
#include <algorithm>
|
24
|
+
#include <vector>
|
25
|
+
|
26
|
+
#include "absl/container/flat_hash_map.h"
|
27
|
+
#include "absl/functional/any_invocable.h"
|
28
|
+
#include "absl/hash/hash.h"
|
29
|
+
#include "absl/random/bit_gen_ref.h"
|
30
|
+
#include "absl/types/optional.h"
|
31
|
+
|
32
|
+
#include <grpc/event_engine/event_engine.h>
|
33
|
+
|
34
|
+
#include "src/core/lib/debug/trace.h"
|
35
|
+
#include "src/core/lib/gprpp/time.h"
|
36
|
+
|
37
|
+
extern grpc_core::TraceFlag grpc_ping_trace;
|
38
|
+
|
39
|
+
namespace grpc_core {
|
40
|
+
|
41
|
+
class Chttp2PingCallbacks {
|
42
|
+
public:
|
43
|
+
// One callback from OnPing/OnPingAck or the timeout.
|
44
|
+
using Callback = absl::AnyInvocable<void()>;
|
45
|
+
|
46
|
+
// Request a ping (but one we don't need any notification for when it begins
|
47
|
+
// or ends).
|
48
|
+
void RequestPing() { ping_requested_ = true; }
|
49
|
+
|
50
|
+
// Request a ping, and specify callbacks for when it begins and ends.
|
51
|
+
// on_start is invoked during the call to StartPing.
|
52
|
+
// on_ack is invoked during the call to AckPing.
|
53
|
+
void OnPing(Callback on_start, Callback on_ack);
|
54
|
+
|
55
|
+
// Request a notification when *some* ping is acked:
|
56
|
+
// If there is no ping in flight, one will be scheduled and the callback
|
57
|
+
// will be invoked when it is acked. (ie as per OnPing([]{}, on_ack)).
|
58
|
+
// If there is a ping in flight, the callback will be invoked when the most
|
59
|
+
// recently sent ping is acked.
|
60
|
+
// on_ack is invoked during the call to AckPing.
|
61
|
+
void OnPingAck(Callback on_ack);
|
62
|
+
|
63
|
+
// Write path: begin a ping.
|
64
|
+
// Uses bitgen to generate a randomized id for the ping.
|
65
|
+
// Sets started_new_ping_without_setting_timeout.
|
66
|
+
GRPC_MUST_USE_RESULT uint64_t StartPing(absl::BitGenRef bitgen);
|
67
|
+
bool AckPing(uint64_t id,
|
68
|
+
grpc_event_engine::experimental::EventEngine* event_engine);
|
69
|
+
|
70
|
+
// Cancel all the ping callbacks.
|
71
|
+
// Sufficient state is maintained such that AckPing will still return true
|
72
|
+
// if a ping is acked after this call.
|
73
|
+
// No timeouts or start or ack callbacks previously scheduled will be invoked.
|
74
|
+
void CancelAll(grpc_event_engine::experimental::EventEngine* event_engine);
|
75
|
+
|
76
|
+
// Return true if a ping needs to be started due to
|
77
|
+
// RequestPing/OnPing/OnPingAck.
|
78
|
+
bool ping_requested() const { return ping_requested_; }
|
79
|
+
|
80
|
+
// Returns the number of pings currently in flight.
|
81
|
+
size_t pings_inflight() const { return inflight_.size(); }
|
82
|
+
|
83
|
+
// Returns true if a ping was started without setting a timeout yet.
|
84
|
+
bool started_new_ping_without_setting_timeout() const {
|
85
|
+
return started_new_ping_without_setting_timeout_;
|
86
|
+
}
|
87
|
+
|
88
|
+
// Add a ping timeout for the most recently started ping.
|
89
|
+
// started_new_ping_without_setting_timeout must be set.
|
90
|
+
// Clears started_new_ping_without_setting_timeout.
|
91
|
+
// Returns the ping id of the ping the timeout was attached to if a timer was
|
92
|
+
// started, or nullopt otherwise.
|
93
|
+
absl::optional<uint64_t> OnPingTimeout(
|
94
|
+
Duration ping_timeout,
|
95
|
+
grpc_event_engine::experimental::EventEngine* event_engine,
|
96
|
+
Callback callback);
|
97
|
+
|
98
|
+
private:
|
99
|
+
using CallbackVec = std::vector<Callback>;
|
100
|
+
struct InflightPing {
|
101
|
+
grpc_event_engine::experimental::EventEngine::TaskHandle on_timeout =
|
102
|
+
grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid;
|
103
|
+
CallbackVec on_ack;
|
104
|
+
};
|
105
|
+
absl::flat_hash_map<uint64_t, InflightPing> inflight_;
|
106
|
+
uint64_t most_recent_inflight_ = 0;
|
107
|
+
bool ping_requested_ = false;
|
108
|
+
bool started_new_ping_without_setting_timeout_ = false;
|
109
|
+
CallbackVec on_start_;
|
110
|
+
CallbackVec on_ack_;
|
111
|
+
};
|
112
|
+
|
113
|
+
} // namespace grpc_core
|
114
|
+
|
115
|
+
#endif // GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_PING_CALLBACKS_H
|
@@ -25,12 +25,20 @@
|
|
25
25
|
|
26
26
|
#include <grpc/impl/channel_arg_names.h>
|
27
27
|
|
28
|
+
#include "src/core/lib/experiments/experiments.h"
|
28
29
|
#include "src/core/lib/gprpp/match.h"
|
29
30
|
|
31
|
+
// How many pings do we allow to be inflight at any given time?
|
32
|
+
// In older versions of gRPC this was implicitly 1.
|
33
|
+
// With the multiping experiment we allow this to rise to 100 by default.
|
34
|
+
// TODO(ctiller): consider making this public API
|
35
|
+
#define GRPC_ARG_HTTP2_MAX_INFLIGHT_PINGS "grpc.http2.max_inflight_pings"
|
36
|
+
|
30
37
|
namespace grpc_core {
|
31
38
|
|
32
39
|
namespace {
|
33
40
|
int g_default_max_pings_without_data = 2;
|
41
|
+
absl::optional<int> g_default_max_inflight_pings;
|
34
42
|
} // namespace
|
35
43
|
|
36
44
|
Chttp2PingRatePolicy::Chttp2PingRatePolicy(const ChannelArgs& args,
|
@@ -39,19 +47,30 @@ Chttp2PingRatePolicy::Chttp2PingRatePolicy(const ChannelArgs& args,
|
|
39
47
|
is_client
|
40
48
|
? std::max(0, args.GetInt(GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)
|
41
49
|
.value_or(g_default_max_pings_without_data))
|
42
|
-
: 0)
|
50
|
+
: 0),
|
51
|
+
// Configuration via channel arg dominates, otherwise if the multiping
|
52
|
+
// experiment is enabled we use 100, otherwise 1.
|
53
|
+
max_inflight_pings_(
|
54
|
+
std::max(0, args.GetInt(GRPC_ARG_HTTP2_MAX_INFLIGHT_PINGS)
|
55
|
+
.value_or(g_default_max_inflight_pings.value_or(
|
56
|
+
IsMultipingEnabled() ? 100 : 1)))) {}
|
43
57
|
|
44
58
|
void Chttp2PingRatePolicy::SetDefaults(const ChannelArgs& args) {
|
45
59
|
g_default_max_pings_without_data =
|
46
60
|
std::max(0, args.GetInt(GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)
|
47
61
|
.value_or(g_default_max_pings_without_data));
|
62
|
+
g_default_max_inflight_pings = args.GetInt(GRPC_ARG_HTTP2_MAX_INFLIGHT_PINGS);
|
48
63
|
}
|
49
64
|
|
50
65
|
Chttp2PingRatePolicy::RequestSendPingResult
|
51
|
-
Chttp2PingRatePolicy::RequestSendPing(Duration next_allowed_ping_interval
|
66
|
+
Chttp2PingRatePolicy::RequestSendPing(Duration next_allowed_ping_interval,
|
67
|
+
size_t inflight_pings) const {
|
52
68
|
if (max_pings_without_data_ != 0 && pings_before_data_required_ == 0) {
|
53
69
|
return TooManyRecentPings{};
|
54
70
|
}
|
71
|
+
if (max_inflight_pings_ != 0 && inflight_pings > max_inflight_pings_) {
|
72
|
+
return TooManyRecentPings{};
|
73
|
+
}
|
55
74
|
const Timestamp next_allowed_ping =
|
56
75
|
last_ping_sent_time_ + next_allowed_ping_interval;
|
57
76
|
const Timestamp now = Timestamp::Now();
|
@@ -59,11 +78,14 @@ Chttp2PingRatePolicy::RequestSendPing(Duration next_allowed_ping_interval) {
|
|
59
78
|
return TooSoon{next_allowed_ping_interval, last_ping_sent_time_,
|
60
79
|
next_allowed_ping - now};
|
61
80
|
}
|
62
|
-
last_ping_sent_time_ = now;
|
63
|
-
if (pings_before_data_required_) --pings_before_data_required_;
|
64
81
|
return SendGranted{};
|
65
82
|
}
|
66
83
|
|
84
|
+
void Chttp2PingRatePolicy::SentPing() {
|
85
|
+
last_ping_sent_time_ = Timestamp::Now();
|
86
|
+
if (pings_before_data_required_) --pings_before_data_required_;
|
87
|
+
}
|
88
|
+
|
67
89
|
void Chttp2PingRatePolicy::ReceivedDataFrame() {
|
68
90
|
last_ping_sent_time_ = Timestamp::InfPast();
|
69
91
|
}
|
@@ -17,6 +17,8 @@
|
|
17
17
|
|
18
18
|
#include <grpc/support/port_platform.h>
|
19
19
|
|
20
|
+
#include <stddef.h>
|
21
|
+
|
20
22
|
#include <iosfwd>
|
21
23
|
#include <string>
|
22
24
|
|
@@ -51,8 +53,20 @@ class Chttp2PingRatePolicy {
|
|
51
53
|
using RequestSendPingResult =
|
52
54
|
absl::variant<SendGranted, TooManyRecentPings, TooSoon>;
|
53
55
|
|
54
|
-
|
56
|
+
// Request that one ping be sent.
|
57
|
+
// Returns:
|
58
|
+
// - SendGranted if a ping can be sent.
|
59
|
+
// - TooManyRecentPings if too many pings have been sent recently and we
|
60
|
+
// should wait for some future write.
|
61
|
+
// - TooSoon if we should wait for some time before sending the ping.
|
62
|
+
RequestSendPingResult RequestSendPing(Duration next_allowed_ping_interval,
|
63
|
+
size_t inflight_pings) const;
|
64
|
+
// Notify the policy that one ping has been sent.
|
65
|
+
void SentPing();
|
66
|
+
// Notify the policy that some data has been sent and so we should no longer
|
67
|
+
// block pings on that basis.
|
55
68
|
void ResetPingsBeforeDataRequired();
|
69
|
+
// Notify the policy that we've received some data.
|
56
70
|
void ReceivedDataFrame();
|
57
71
|
std::string GetDebugString() const;
|
58
72
|
|
@@ -60,6 +74,7 @@ class Chttp2PingRatePolicy {
|
|
60
74
|
|
61
75
|
private:
|
62
76
|
const int max_pings_without_data_;
|
77
|
+
const int max_inflight_pings_;
|
63
78
|
// No pings allowed before receiving a header or data frame.
|
64
79
|
int pings_before_data_required_ = 0;
|
65
80
|
Timestamp last_ping_sent_time_ = Timestamp::InfPast();
|
@@ -0,0 +1,60 @@
|
|
1
|
+
// Copyright 2023 gRPC authors.
|
2
|
+
//
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
// you may not use this file except in compliance with the License.
|
5
|
+
// You may obtain a copy of the License at
|
6
|
+
//
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
//
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
// See the License for the specific language governing permissions and
|
13
|
+
// limitations under the License.
|
14
|
+
|
15
|
+
#include <grpc/support/port_platform.h>
|
16
|
+
|
17
|
+
#include "src/core/ext/transport/chttp2/transport/write_size_policy.h"
|
18
|
+
|
19
|
+
#include <algorithm>
|
20
|
+
|
21
|
+
#include <grpc/support/log.h>
|
22
|
+
|
23
|
+
namespace grpc_core {
|
24
|
+
|
25
|
+
size_t Chttp2WriteSizePolicy::WriteTargetSize() { return current_target_; }
|
26
|
+
|
27
|
+
void Chttp2WriteSizePolicy::BeginWrite(size_t size) {
|
28
|
+
GPR_ASSERT(experiment_start_time_ == Timestamp::InfFuture());
|
29
|
+
if (size < current_target_ * 7 / 10) {
|
30
|
+
// If we were trending fast but stopped getting enough data to verify, then
|
31
|
+
// reset back to the default state.
|
32
|
+
if (state_ < 0) state_ = 0;
|
33
|
+
return;
|
34
|
+
}
|
35
|
+
experiment_start_time_ = Timestamp::Now();
|
36
|
+
}
|
37
|
+
|
38
|
+
void Chttp2WriteSizePolicy::EndWrite(bool success) {
|
39
|
+
if (experiment_start_time_ == Timestamp::InfFuture()) return;
|
40
|
+
const auto elapsed = Timestamp::Now() - experiment_start_time_;
|
41
|
+
experiment_start_time_ = Timestamp::InfFuture();
|
42
|
+
if (!success) return;
|
43
|
+
if (elapsed < FastWrite()) {
|
44
|
+
--state_;
|
45
|
+
if (state_ == -2) {
|
46
|
+
state_ = 0;
|
47
|
+
current_target_ = std::min(current_target_ * 3 / 2, MaxTarget());
|
48
|
+
}
|
49
|
+
} else if (elapsed > SlowWrite()) {
|
50
|
+
++state_;
|
51
|
+
if (state_ == 2) {
|
52
|
+
state_ = 0;
|
53
|
+
current_target_ = std::max(current_target_ / 3, MinTarget());
|
54
|
+
}
|
55
|
+
} else {
|
56
|
+
state_ = 0;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
} // namespace grpc_core
|