grpc 1.59.0 → 1.59.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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