grpc 1.59.0 → 1.59.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +7 -1
  3. data/src/core/ext/filters/http/server/http_server_filter.cc +21 -17
  4. data/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +504 -361
  5. data/src/core/ext/transport/chttp2/transport/frame_ping.cc +11 -1
  6. data/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc +9 -0
  7. data/src/core/ext/transport/chttp2/transport/internal.h +92 -28
  8. data/src/core/ext/transport/chttp2/transport/max_concurrent_streams_policy.cc +44 -0
  9. data/src/core/ext/transport/chttp2/transport/max_concurrent_streams_policy.h +67 -0
  10. data/src/core/ext/transport/chttp2/transport/parsing.cc +103 -14
  11. data/src/core/ext/transport/chttp2/transport/ping_callbacks.cc +108 -0
  12. data/src/core/ext/transport/chttp2/transport/ping_callbacks.h +115 -0
  13. data/src/core/ext/transport/chttp2/transport/ping_rate_policy.cc +26 -4
  14. data/src/core/ext/transport/chttp2/transport/ping_rate_policy.h +16 -1
  15. data/src/core/ext/transport/chttp2/transport/write_size_policy.cc +60 -0
  16. data/src/core/ext/transport/chttp2/transport/write_size_policy.h +66 -0
  17. data/src/core/ext/transport/chttp2/transport/writing.cc +149 -77
  18. data/src/core/lib/channel/promise_based_filter.cc +9 -4
  19. data/src/core/lib/channel/promise_based_filter.h +2 -1
  20. data/src/core/lib/experiments/experiments.cc +222 -0
  21. data/src/core/lib/experiments/experiments.h +135 -0
  22. data/src/core/lib/iomgr/combiner.cc +3 -0
  23. data/src/core/lib/transport/metadata_batch.h +11 -1
  24. data/src/core/lib/transport/transport.h +6 -0
  25. data/src/ruby/lib/grpc/version.rb +1 -1
  26. 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
- grpc_error_handle grpc_chttp2_perform_read(grpc_chttp2_transport* t,
198
- const grpc_slice& slice) {
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, "[t:%p fd:%d peer:%s] Accepting new stream", t,
654
- grpc_endpoint_get_fd(t->ep),
655
- std::string(t->peer_string.as_string_view()).c_str());
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
- RequestSendPingResult RequestSendPing(Duration next_allowed_ping_interval);
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