Merge remote-tracking branch 'upstream/master' into xds_watchers_in_xds_resolver

pull/35011/head
Mark D. Roth 1 year ago
commit 93c85a636c
  1. 17
      BUILD
  2. 2
      CMakeLists.txt
  3. 4
      Package.swift
  4. 2
      bazel/grpc_build_system.bzl
  5. 27
      build_autogenerated.yaml
  6. 8
      gRPC-C++.podspec
  7. 8
      gRPC-Core.podspec
  8. 4
      grpc.gemspec
  9. 11
      include/grpcpp/ext/csm_observability.h
  10. 108
      include/grpcpp/ext/otel_plugin.h
  11. 4
      package.xml
  12. 7
      src/core/BUILD
  13. 76
      src/core/ext/filters/client_channel/resolver/xds/xds_dependency_manager.cc
  14. 4
      src/core/ext/transport/chaotic_good/client_transport.cc
  15. 2
      src/core/ext/transport/chaotic_good/client_transport.h
  16. 89
      src/core/ext/xds/xds_client.cc
  17. 19
      src/core/ext/xds/xds_client.h
  18. 10
      src/core/ext/xds/xds_resource_type_impl.h
  19. 46
      src/core/ext/xds/xds_server_config_fetcher.cc
  20. 3
      src/core/ext/xds/xds_transport.h
  21. 92
      src/core/ext/xds/xds_transport_grpc.cc
  22. 4
      src/core/ext/xds/xds_transport_grpc.h
  23. 10
      src/core/lib/channel/channel_stack.cc
  24. 81
      src/core/lib/channel/connected_channel.cc
  25. 1
      src/core/lib/promise/detail/promise_factory.h
  26. 2
      src/core/lib/promise/latch.h
  27. 49
      src/core/lib/promise/status_flag.h
  28. 45
      src/core/lib/promise/try_join.h
  29. 4
      src/core/lib/promise/try_seq.h
  30. 862
      src/core/lib/surface/call.cc
  31. 38
      src/core/lib/surface/call.h
  32. 305
      src/core/lib/surface/server.cc
  33. 1
      src/core/lib/surface/server.h
  34. 94
      src/core/lib/surface/wait_for_cq_end_op.h
  35. 12
      src/core/lib/transport/transport.h
  36. 20
      src/cpp/ext/csm/csm_observability.cc
  37. 1
      src/cpp/ext/otel/BUILD
  38. 10
      src/cpp/ext/otel/key_value_iterable.h
  39. 74
      src/cpp/ext/otel/otel_client_filter.cc
  40. 227
      src/cpp/ext/otel/otel_plugin.cc
  41. 54
      src/cpp/ext/otel/otel_plugin.h
  42. 60
      src/cpp/ext/otel/otel_server_call_tracer.cc
  43. 5
      test/core/promise/status_flag_test.cc
  44. 150
      test/core/promise/try_join_test.cc
  45. 138
      test/core/promise/try_seq_test.cc
  46. 15
      test/core/xds/xds_client_fuzzer.cc
  47. 148
      test/core/xds/xds_client_test.cc
  48. 48
      test/core/xds/xds_transport_fake.cc
  49. 23
      test/core/xds/xds_transport_fake.h
  50. 1
      test/cpp/ext/csm/BUILD
  51. 24
      test/cpp/ext/csm/metadata_exchange_test.cc
  52. 112
      test/cpp/ext/otel/otel_plugin_test.cc
  53. 15
      test/cpp/ext/otel/otel_test_library.cc
  54. 2
      test/cpp/ext/otel/otel_test_library.h
  55. 2
      test/cpp/interop/observability_client.cc
  56. 2
      test/cpp/interop/xds_interop_client.cc
  57. 2
      test/cpp/interop/xds_interop_server.cc
  58. 6
      tools/bazelify_tests/build_defs.bzl
  59. 3
      tools/bazelify_tests/test/bazel_distribtests.bzl
  60. 4
      tools/doxygen/Doxyfile.c++.internal
  61. 4
      tools/doxygen/Doxyfile.core.internal

17
BUILD

@ -1273,6 +1273,7 @@ grpc_cc_library(
tags = [
"nofixdeps",
],
visibility = ["@grpc:event_engine_base_hdrs"],
deps = [
"channel_arg_names",
"gpr",
@ -1462,6 +1463,7 @@ grpc_cc_library(
"//src/core:lib/surface/lame_client.h",
"//src/core:lib/surface/server.h",
"//src/core:lib/surface/validate_metadata.h",
"//src/core:lib/surface/wait_for_cq_end_op.h",
"//src/core:lib/transport/batch_builder.h",
"//src/core:lib/transport/connectivity_state.h",
"//src/core:lib/transport/custom_metadata.h",
@ -1538,6 +1540,7 @@ grpc_cc_library(
"work_serializer",
"//src/core:1999",
"//src/core:activity",
"//src/core:all_ok",
"//src/core:arena",
"//src/core:arena_promise",
"//src/core:atomic_utils",
@ -1610,6 +1613,7 @@ grpc_cc_library(
"//src/core:thread_quota",
"//src/core:time",
"//src/core:transport_fwd",
"//src/core:try_join",
"//src/core:try_seq",
"//src/core:type_list",
"//src/core:useful",
@ -2399,6 +2403,18 @@ grpc_cc_library(
],
)
# This is an EXPERIMENTAL target subject to change.
grpc_cc_library(
name = "grpcpp_otel_plugin",
hdrs = [
"include/grpcpp/ext/otel_plugin.h",
],
language = "c++",
deps = [
"//src/cpp/ext/otel:otel_plugin",
],
)
grpc_cc_library(
name = "work_serializer",
srcs = [
@ -3657,6 +3673,7 @@ grpc_cc_library(
],
external_deps = [
"absl/base:core_headers",
"absl/cleanup",
"absl/memory",
"absl/status",
"absl/status:statusor",

2
CMakeLists.txt generated

@ -23457,8 +23457,8 @@ target_include_directories(status_flag_test
target_link_libraries(status_flag_test
${_gRPC_ALLTARGETS_LIBRARIES}
gtest
absl::status
absl::statusor
gpr
)

4
Package.swift generated

@ -1637,10 +1637,12 @@ let package = Package(
"src/core/lib/matchers/matchers.h",
"src/core/lib/promise/activity.cc",
"src/core/lib/promise/activity.h",
"src/core/lib/promise/all_ok.h",
"src/core/lib/promise/arena_promise.h",
"src/core/lib/promise/cancel_callback.h",
"src/core/lib/promise/context.h",
"src/core/lib/promise/detail/basic_seq.h",
"src/core/lib/promise/detail/join_state.h",
"src/core/lib/promise/detail/promise_factory.h",
"src/core/lib/promise/detail/promise_like.h",
"src/core/lib/promise/detail/seq_state.h",
@ -1665,6 +1667,7 @@ let package = Package(
"src/core/lib/promise/status_flag.h",
"src/core/lib/promise/trace.cc",
"src/core/lib/promise/trace.h",
"src/core/lib/promise/try_join.h",
"src/core/lib/promise/try_seq.h",
"src/core/lib/resolver/endpoint_addresses.cc",
"src/core/lib/resolver/endpoint_addresses.h",
@ -1872,6 +1875,7 @@ let package = Package(
"src/core/lib/surface/validate_metadata.cc",
"src/core/lib/surface/validate_metadata.h",
"src/core/lib/surface/version.cc",
"src/core/lib/surface/wait_for_cq_end_op.h",
"src/core/lib/transport/batch_builder.cc",
"src/core/lib/transport/batch_builder.h",
"src/core/lib/transport/bdp_estimator.cc",

@ -120,6 +120,8 @@ def _update_visibility(visibility):
"xds": PRIVATE,
"xds_client_core": PRIVATE,
"grpc_python_observability": PRIVATE,
"event_engine_base_hdrs": PRIVATE,
"useful": PRIVATE,
}
final_visibility = []
for rule in visibility:

@ -1035,10 +1035,12 @@ libs:
- src/core/lib/load_balancing/subchannel_interface.h
- src/core/lib/matchers/matchers.h
- src/core/lib/promise/activity.h
- src/core/lib/promise/all_ok.h
- src/core/lib/promise/arena_promise.h
- src/core/lib/promise/cancel_callback.h
- src/core/lib/promise/context.h
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/join_state.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
@ -1060,6 +1062,7 @@ libs:
- src/core/lib/promise/sleep.h
- src/core/lib/promise/status_flag.h
- src/core/lib/promise/trace.h
- src/core/lib/promise/try_join.h
- src/core/lib/promise/try_seq.h
- src/core/lib/resolver/endpoint_addresses.h
- src/core/lib/resolver/resolver.h
@ -1158,6 +1161,7 @@ libs:
- src/core/lib/surface/lame_client.h
- src/core/lib/surface/server.h
- src/core/lib/surface/validate_metadata.h
- src/core/lib/surface/wait_for_cq_end_op.h
- src/core/lib/transport/batch_builder.h
- src/core/lib/transport/bdp_estimator.h
- src/core/lib/transport/connectivity_state.h
@ -2493,10 +2497,12 @@ libs:
- src/core/lib/load_balancing/lb_policy_registry.h
- src/core/lib/load_balancing/subchannel_interface.h
- src/core/lib/promise/activity.h
- src/core/lib/promise/all_ok.h
- src/core/lib/promise/arena_promise.h
- src/core/lib/promise/cancel_callback.h
- src/core/lib/promise/context.h
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/join_state.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
@ -2518,6 +2524,7 @@ libs:
- src/core/lib/promise/sleep.h
- src/core/lib/promise/status_flag.h
- src/core/lib/promise/trace.h
- src/core/lib/promise/try_join.h
- src/core/lib/promise/try_seq.h
- src/core/lib/resolver/endpoint_addresses.h
- src/core/lib/resolver/resolver.h
@ -2585,6 +2592,7 @@ libs:
- src/core/lib/surface/lame_client.h
- src/core/lib/surface/server.h
- src/core/lib/surface/validate_metadata.h
- src/core/lib/surface/wait_for_cq_end_op.h
- src/core/lib/transport/batch_builder.h
- src/core/lib/transport/bdp_estimator.h
- src/core/lib/transport/connectivity_state.h
@ -4627,10 +4635,12 @@ libs:
- src/core/lib/load_balancing/subchannel_interface.h
- src/core/lib/matchers/matchers.h
- src/core/lib/promise/activity.h
- src/core/lib/promise/all_ok.h
- src/core/lib/promise/arena_promise.h
- src/core/lib/promise/cancel_callback.h
- src/core/lib/promise/context.h
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/join_state.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
@ -4650,6 +4660,7 @@ libs:
- src/core/lib/promise/seq.h
- src/core/lib/promise/status_flag.h
- src/core/lib/promise/trace.h
- src/core/lib/promise/try_join.h
- src/core/lib/promise/try_seq.h
- src/core/lib/resolver/endpoint_addresses.h
- src/core/lib/resolver/resolver.h
@ -4719,6 +4730,7 @@ libs:
- src/core/lib/surface/lame_client.h
- src/core/lib/surface/server.h
- src/core/lib/surface/validate_metadata.h
- src/core/lib/surface/wait_for_cq_end_op.h
- src/core/lib/transport/batch_builder.h
- src/core/lib/transport/connectivity_state.h
- src/core/lib/transport/custom_metadata.h
@ -7641,12 +7653,10 @@ targets:
- src/core/ext/transport/chaotic_good/client_transport.h
- src/core/ext/transport/chaotic_good/frame.h
- src/core/ext/transport/chaotic_good/frame_header.h
- src/core/lib/promise/detail/join_state.h
- src/core/lib/promise/event_engine_wakeup_scheduler.h
- src/core/lib/promise/inter_activity_pipe.h
- src/core/lib/promise/join.h
- src/core/lib/promise/mpsc.h
- src/core/lib/promise/try_join.h
- src/core/lib/promise/wait_set.h
- src/core/lib/transport/promise_endpoint.h
- test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h
@ -7671,12 +7681,10 @@ targets:
- src/core/ext/transport/chaotic_good/client_transport.h
- src/core/ext/transport/chaotic_good/frame.h
- src/core/ext/transport/chaotic_good/frame_header.h
- src/core/lib/promise/detail/join_state.h
- src/core/lib/promise/event_engine_wakeup_scheduler.h
- src/core/lib/promise/inter_activity_pipe.h
- src/core/lib/promise/join.h
- src/core/lib/promise/mpsc.h
- src/core/lib/promise/try_join.h
- src/core/lib/promise/wait_set.h
- src/core/lib/transport/promise_endpoint.h
- test/core/event_engine/fuzzing_event_engine/fuzzing_event_engine.h
@ -12924,7 +12932,6 @@ targets:
build: test
language: c++
headers:
- src/core/lib/promise/detail/join_state.h
- src/core/lib/promise/join.h
- test/core/promise/test_wakeup_schedulers.h
src:
@ -13125,7 +13132,6 @@ targets:
build: test
language: c++
headers:
- src/core/lib/promise/detail/join_state.h
- src/core/lib/promise/join.h
- src/core/lib/transport/promise_endpoint.h
- test/core/promise/test_wakeup_schedulers.h
@ -16129,8 +16135,8 @@ targets:
- test/core/promise/status_flag_test.cc
deps:
- gtest
- absl/status:status
- absl/status:statusor
- gpr
uses_polling: false
- name: status_helper_test
gtest: true
@ -17007,10 +17013,12 @@ targets:
- src/core/lib/load_balancing/lb_policy_registry.h
- src/core/lib/load_balancing/subchannel_interface.h
- src/core/lib/promise/activity.h
- src/core/lib/promise/all_ok.h
- src/core/lib/promise/arena_promise.h
- src/core/lib/promise/cancel_callback.h
- src/core/lib/promise/context.h
- src/core/lib/promise/detail/basic_seq.h
- src/core/lib/promise/detail/join_state.h
- src/core/lib/promise/detail/promise_factory.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/seq_state.h
@ -17030,6 +17038,7 @@ targets:
- src/core/lib/promise/seq.h
- src/core/lib/promise/status_flag.h
- src/core/lib/promise/trace.h
- src/core/lib/promise/try_join.h
- src/core/lib/promise/try_seq.h
- src/core/lib/resolver/endpoint_addresses.h
- src/core/lib/resolver/resolver.h
@ -17074,6 +17083,7 @@ targets:
- src/core/lib/surface/lame_client.h
- src/core/lib/surface/server.h
- src/core/lib/surface/validate_metadata.h
- src/core/lib/surface/wait_for_cq_end_op.h
- src/core/lib/transport/batch_builder.h
- src/core/lib/transport/connectivity_state.h
- src/core/lib/transport/custom_metadata.h
@ -18006,8 +18016,10 @@ targets:
- src/core/lib/gprpp/bitset.h
- src/core/lib/promise/detail/join_state.h
- src/core/lib/promise/detail/promise_like.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/map.h
- src/core/lib/promise/poll.h
- src/core/lib/promise/status_flag.h
- src/core/lib/promise/trace.h
- src/core/lib/promise/try_join.h
src:
@ -18044,6 +18056,7 @@ targets:
- src/core/lib/promise/detail/seq_state.h
- src/core/lib/promise/detail/status.h
- src/core/lib/promise/poll.h
- src/core/lib/promise/status_flag.h
- src/core/lib/promise/trace.h
- src/core/lib/promise/try_seq.h
src:

8
gRPC-C++.podspec generated

@ -1130,10 +1130,12 @@ Pod::Spec.new do |s|
'src/core/lib/load_balancing/subchannel_interface.h',
'src/core/lib/matchers/matchers.h',
'src/core/lib/promise/activity.h',
'src/core/lib/promise/all_ok.h',
'src/core/lib/promise/arena_promise.h',
'src/core/lib/promise/cancel_callback.h',
'src/core/lib/promise/context.h',
'src/core/lib/promise/detail/basic_seq.h',
'src/core/lib/promise/detail/join_state.h',
'src/core/lib/promise/detail/promise_factory.h',
'src/core/lib/promise/detail/promise_like.h',
'src/core/lib/promise/detail/seq_state.h',
@ -1155,6 +1157,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/sleep.h',
'src/core/lib/promise/status_flag.h',
'src/core/lib/promise/trace.h',
'src/core/lib/promise/try_join.h',
'src/core/lib/promise/try_seq.h',
'src/core/lib/resolver/endpoint_addresses.h',
'src/core/lib/resolver/resolver.h',
@ -1253,6 +1256,7 @@ Pod::Spec.new do |s|
'src/core/lib/surface/lame_client.h',
'src/core/lib/surface/server.h',
'src/core/lib/surface/validate_metadata.h',
'src/core/lib/surface/wait_for_cq_end_op.h',
'src/core/lib/transport/batch_builder.h',
'src/core/lib/transport/bdp_estimator.h',
'src/core/lib/transport/connectivity_state.h',
@ -2368,10 +2372,12 @@ Pod::Spec.new do |s|
'src/core/lib/load_balancing/subchannel_interface.h',
'src/core/lib/matchers/matchers.h',
'src/core/lib/promise/activity.h',
'src/core/lib/promise/all_ok.h',
'src/core/lib/promise/arena_promise.h',
'src/core/lib/promise/cancel_callback.h',
'src/core/lib/promise/context.h',
'src/core/lib/promise/detail/basic_seq.h',
'src/core/lib/promise/detail/join_state.h',
'src/core/lib/promise/detail/promise_factory.h',
'src/core/lib/promise/detail/promise_like.h',
'src/core/lib/promise/detail/seq_state.h',
@ -2393,6 +2399,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/sleep.h',
'src/core/lib/promise/status_flag.h',
'src/core/lib/promise/trace.h',
'src/core/lib/promise/try_join.h',
'src/core/lib/promise/try_seq.h',
'src/core/lib/resolver/endpoint_addresses.h',
'src/core/lib/resolver/resolver.h',
@ -2491,6 +2498,7 @@ Pod::Spec.new do |s|
'src/core/lib/surface/lame_client.h',
'src/core/lib/surface/server.h',
'src/core/lib/surface/validate_metadata.h',
'src/core/lib/surface/wait_for_cq_end_op.h',
'src/core/lib/transport/batch_builder.h',
'src/core/lib/transport/bdp_estimator.h',
'src/core/lib/transport/connectivity_state.h',

8
gRPC-Core.podspec generated

@ -1740,10 +1740,12 @@ Pod::Spec.new do |s|
'src/core/lib/matchers/matchers.h',
'src/core/lib/promise/activity.cc',
'src/core/lib/promise/activity.h',
'src/core/lib/promise/all_ok.h',
'src/core/lib/promise/arena_promise.h',
'src/core/lib/promise/cancel_callback.h',
'src/core/lib/promise/context.h',
'src/core/lib/promise/detail/basic_seq.h',
'src/core/lib/promise/detail/join_state.h',
'src/core/lib/promise/detail/promise_factory.h',
'src/core/lib/promise/detail/promise_like.h',
'src/core/lib/promise/detail/seq_state.h',
@ -1768,6 +1770,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/status_flag.h',
'src/core/lib/promise/trace.cc',
'src/core/lib/promise/trace.h',
'src/core/lib/promise/try_join.h',
'src/core/lib/promise/try_seq.h',
'src/core/lib/resolver/endpoint_addresses.cc',
'src/core/lib/resolver/endpoint_addresses.h',
@ -1971,6 +1974,7 @@ Pod::Spec.new do |s|
'src/core/lib/surface/validate_metadata.cc',
'src/core/lib/surface/validate_metadata.h',
'src/core/lib/surface/version.cc',
'src/core/lib/surface/wait_for_cq_end_op.h',
'src/core/lib/transport/batch_builder.cc',
'src/core/lib/transport/batch_builder.h',
'src/core/lib/transport/bdp_estimator.cc',
@ -3135,10 +3139,12 @@ Pod::Spec.new do |s|
'src/core/lib/load_balancing/subchannel_interface.h',
'src/core/lib/matchers/matchers.h',
'src/core/lib/promise/activity.h',
'src/core/lib/promise/all_ok.h',
'src/core/lib/promise/arena_promise.h',
'src/core/lib/promise/cancel_callback.h',
'src/core/lib/promise/context.h',
'src/core/lib/promise/detail/basic_seq.h',
'src/core/lib/promise/detail/join_state.h',
'src/core/lib/promise/detail/promise_factory.h',
'src/core/lib/promise/detail/promise_like.h',
'src/core/lib/promise/detail/seq_state.h',
@ -3160,6 +3166,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/sleep.h',
'src/core/lib/promise/status_flag.h',
'src/core/lib/promise/trace.h',
'src/core/lib/promise/try_join.h',
'src/core/lib/promise/try_seq.h',
'src/core/lib/resolver/endpoint_addresses.h',
'src/core/lib/resolver/resolver.h',
@ -3258,6 +3265,7 @@ Pod::Spec.new do |s|
'src/core/lib/surface/lame_client.h',
'src/core/lib/surface/server.h',
'src/core/lib/surface/validate_metadata.h',
'src/core/lib/surface/wait_for_cq_end_op.h',
'src/core/lib/transport/batch_builder.h',
'src/core/lib/transport/bdp_estimator.h',
'src/core/lib/transport/connectivity_state.h',

4
grpc.gemspec generated

@ -1643,10 +1643,12 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/matchers/matchers.h )
s.files += %w( src/core/lib/promise/activity.cc )
s.files += %w( src/core/lib/promise/activity.h )
s.files += %w( src/core/lib/promise/all_ok.h )
s.files += %w( src/core/lib/promise/arena_promise.h )
s.files += %w( src/core/lib/promise/cancel_callback.h )
s.files += %w( src/core/lib/promise/context.h )
s.files += %w( src/core/lib/promise/detail/basic_seq.h )
s.files += %w( src/core/lib/promise/detail/join_state.h )
s.files += %w( src/core/lib/promise/detail/promise_factory.h )
s.files += %w( src/core/lib/promise/detail/promise_like.h )
s.files += %w( src/core/lib/promise/detail/seq_state.h )
@ -1671,6 +1673,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/promise/status_flag.h )
s.files += %w( src/core/lib/promise/trace.cc )
s.files += %w( src/core/lib/promise/trace.h )
s.files += %w( src/core/lib/promise/try_join.h )
s.files += %w( src/core/lib/promise/try_seq.h )
s.files += %w( src/core/lib/resolver/endpoint_addresses.cc )
s.files += %w( src/core/lib/resolver/endpoint_addresses.h )
@ -1874,6 +1877,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/surface/validate_metadata.cc )
s.files += %w( src/core/lib/surface/validate_metadata.h )
s.files += %w( src/core/lib/surface/version.cc )
s.files += %w( src/core/lib/surface/wait_for_cq_end_op.h )
s.files += %w( src/core/lib/transport/batch_builder.cc )
s.files += %w( src/core/lib/transport/batch_builder.h )
s.files += %w( src/core/lib/transport/bdp_estimator.cc )

@ -28,9 +28,12 @@
#include "absl/strings/string_view.h"
#include "opentelemetry/sdk/metrics/meter_provider.h"
#include "src/cpp/ext/otel/otel_plugin.h"
namespace grpc {
namespace internal {
class OpenTelemetryPluginBuilderImpl;
} // namespace internal
namespace experimental {
// This is a no-op at present, but in the future, this object would be useful
@ -41,6 +44,8 @@ class CsmObservability {};
// for a binary running on CSM.
class CsmObservabilityBuilder {
public:
CsmObservabilityBuilder();
~CsmObservabilityBuilder();
CsmObservabilityBuilder& SetMeterProvider(
std::shared_ptr<opentelemetry::sdk::metrics::MeterProvider>
meter_provider);
@ -80,7 +85,7 @@ class CsmObservabilityBuilder {
absl::StatusOr<CsmObservability> BuildAndRegister();
private:
internal::OpenTelemetryPluginBuilder builder_;
std::unique_ptr<grpc::internal::OpenTelemetryPluginBuilderImpl> builder_;
};
} // namespace experimental

@ -0,0 +1,108 @@
//
//
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPCPP_EXT_OTEL_PLUGIN_H
#define GRPCPP_EXT_OTEL_PLUGIN_H
#include <grpc/support/port_platform.h>
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "opentelemetry/metrics/meter_provider.h"
namespace grpc {
namespace internal {
class OpenTelemetryPluginBuilderImpl;
} // namespace internal
namespace experimental {
/// The most common way to use this API is -
///
/// OpenTelemetryPluginBuilder().SetMeterProvider(provider).BuildAndRegister();
///
/// The set of instruments available are -
/// grpc.client.attempt.started
/// grpc.client.attempt.duration
/// grpc.client.attempt.sent_total_compressed_message_size
/// grpc.client.attempt.rcvd_total_compressed_message_size
/// grpc.server.call.started
/// grpc.server.call.duration
/// grpc.server.call.sent_total_compressed_message_size
/// grpc.server.call.rcvd_total_compressed_message_size
class OpenTelemetryPluginBuilder {
public:
/// Metrics
static constexpr absl::string_view kClientAttemptStartedInstrumentName =
"grpc.client.attempt.started";
static constexpr absl::string_view kClientAttemptDurationInstrumentName =
"grpc.client.attempt.duration";
static constexpr absl::string_view
kClientAttemptSentTotalCompressedMessageSizeInstrumentName =
"grpc.client.attempt.sent_total_compressed_message_size";
static constexpr absl::string_view
kClientAttemptRcvdTotalCompressedMessageSizeInstrumentName =
"grpc.client.attempt.rcvd_total_compressed_message_size";
static constexpr absl::string_view kServerCallStartedInstrumentName =
"grpc.server.call.started";
static constexpr absl::string_view kServerCallDurationInstrumentName =
"grpc.server.call.duration";
static constexpr absl::string_view
kServerCallSentTotalCompressedMessageSizeInstrumentName =
"grpc.server.call.sent_total_compressed_message_size";
static constexpr absl::string_view
kServerCallRcvdTotalCompressedMessageSizeInstrumentName =
"grpc.server.call.rcvd_total_compressed_message_size";
OpenTelemetryPluginBuilder();
/// If `SetMeterProvider()` is not called, no metrics are collected.
OpenTelemetryPluginBuilder& SetMeterProvider(
std::shared_ptr<opentelemetry::metrics::MeterProvider> meter_provider);
/// If set, \a target_attribute_filter is called per channel to decide whether
/// to record the target attribute on client or to replace it with "other".
/// This helps reduce the cardinality on metrics in cases where many channels
/// are created with different targets in the same binary (which might happen
/// for example, if the channel target string uses IP addresses directly).
OpenTelemetryPluginBuilder& SetTargetAttributeFilter(
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_attribute_filter);
/// If set, \a generic_method_attribute_filter is called per call with a
/// generic method type to decide whether to record the method name or to
/// replace it with "other". Non-generic or pre-registered methods remain
/// unaffected. If not set, by default, generic method names are replaced with
/// "other" when recording metrics.
OpenTelemetryPluginBuilder& SetGenericMethodAttributeFilter(
absl::AnyInvocable<bool(absl::string_view /*generic_method*/) const>
generic_method_attribute_filter);
/// Registers a global plugin that acts on all channels and servers running on
/// the process.
void BuildAndRegisterGlobal();
private:
std::unique_ptr<internal::OpenTelemetryPluginBuilderImpl> impl_;
};
} // namespace experimental
} // namespace grpc
#endif // GRPCPP_EXT_OTEL_PLUGIN_H

4
package.xml generated

@ -1625,10 +1625,12 @@
<file baseinstalldir="/" name="src/core/lib/matchers/matchers.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/activity.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/activity.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/all_ok.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/arena_promise.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/cancel_callback.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/context.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/detail/basic_seq.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/detail/join_state.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/detail/promise_factory.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/detail/promise_like.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/detail/seq_state.h" role="src" />
@ -1653,6 +1655,7 @@
<file baseinstalldir="/" name="src/core/lib/promise/status_flag.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/trace.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/trace.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/try_join.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/promise/try_seq.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/resolver/endpoint_addresses.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/resolver/endpoint_addresses.h" role="src" />
@ -1856,6 +1859,7 @@
<file baseinstalldir="/" name="src/core/lib/surface/validate_metadata.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/validate_metadata.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/version.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/surface/wait_for_cq_end_op.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/batch_builder.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/batch_builder.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/transport/bdp_estimator.cc" role="src" />

@ -155,6 +155,7 @@ grpc_cc_library(
"absl/types:variant",
],
language = "c++",
visibility = ["@grpc:useful"],
deps = ["//:gpr_platform"],
)
@ -438,6 +439,7 @@ grpc_cc_library(
],
deps = [
"promise_status",
"//:gpr",
"//:gpr_platform",
],
)
@ -709,6 +711,7 @@ grpc_cc_library(
"join_state",
"map",
"poll",
"status_flag",
"//:gpr_platform",
],
)
@ -809,6 +812,7 @@ grpc_cc_library(
"promise_like",
"promise_status",
"seq_state",
"status_flag",
"//:debug_location",
"//:gpr_platform",
],
@ -4451,6 +4455,7 @@ grpc_cc_library(
],
external_deps = [
"absl/base:core_headers",
"absl/cleanup",
"absl/functional:bind_front",
"absl/memory",
"absl/random",
@ -4660,6 +4665,7 @@ grpc_cc_library(
"//:ref_counted_ptr",
"//:sockaddr_utils",
"//:uri_parser",
"//:xds_client",
],
)
@ -4729,6 +4735,7 @@ grpc_cc_library(
"//:orphanable",
"//:ref_counted_ptr",
"//:work_serializer",
"//:xds_client",
],
)

@ -117,28 +117,35 @@ class XdsDependencyManager::ListenerWatcher
: dependency_mgr_(std::move(dependency_mgr)) {}
void OnResourceChanged(
std::shared_ptr<const XdsListenerResource> listener) override {
std::shared_ptr<const XdsListenerResource> listener,
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
dependency_mgr_->work_serializer_->Run(
[dependency_mgr = dependency_mgr_,
listener = std::move(listener)]() mutable {
listener = std::move(listener),
read_delay_handle = std::move(read_delay_handle)]() mutable {
dependency_mgr->OnListenerUpdate(std::move(listener));
},
DEBUG_LOCATION);
}
void OnError(absl::Status status) override {
void OnError(
absl::Status status,
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
dependency_mgr_->work_serializer_->Run(
[dependency_mgr = dependency_mgr_,
status = std::move(status)]() mutable {
status = std::move(status),
read_delay_handle = std::move(read_delay_handle)]() mutable {
dependency_mgr->OnError(dependency_mgr->listener_resource_name_,
std::move(status));
},
DEBUG_LOCATION);
}
void OnResourceDoesNotExist() override {
void OnResourceDoesNotExist(
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
dependency_mgr_->work_serializer_->Run(
[dependency_mgr = dependency_mgr_]() {
[dependency_mgr = dependency_mgr_,
read_delay_handle = std::move(read_delay_handle)]() {
dependency_mgr->OnResourceDoesNotExist(
absl::StrCat(dependency_mgr->listener_resource_name_,
": xDS listener resource does not exist"));
@ -162,28 +169,35 @@ class XdsDependencyManager::RouteConfigWatcher
: dependency_mgr_(std::move(dependency_mgr)), name_(std::move(name)) {}
void OnResourceChanged(
std::shared_ptr<const XdsRouteConfigResource> route_config) override {
std::shared_ptr<const XdsRouteConfigResource> route_config,
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
dependency_mgr_->work_serializer_->Run(
[self = RefAsSubclass<RouteConfigWatcher>(),
route_config = std::move(route_config)]() mutable {
route_config = std::move(route_config),
read_delay_handle = std::move(read_delay_handle)]() mutable {
self->dependency_mgr_->OnRouteConfigUpdate(self->name_,
std::move(route_config));
},
DEBUG_LOCATION);
}
void OnError(absl::Status status) override {
void OnError(
absl::Status status,
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
dependency_mgr_->work_serializer_->Run(
[self = RefAsSubclass<RouteConfigWatcher>(),
status = std::move(status)]() mutable {
status = std::move(status),
read_delay_handle = std::move(read_delay_handle)]() mutable {
self->dependency_mgr_->OnError(self->name_, std::move(status));
},
DEBUG_LOCATION);
}
void OnResourceDoesNotExist() override {
void OnResourceDoesNotExist(
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
dependency_mgr_->work_serializer_->Run(
[self = RefAsSubclass<RouteConfigWatcher>()]() {
[self = RefAsSubclass<RouteConfigWatcher>(),
read_delay_handle = std::move(read_delay_handle)]() {
self->dependency_mgr_->OnResourceDoesNotExist(absl::StrCat(
self->name_,
": xDS route configuration resource does not exist"));
@ -208,28 +222,35 @@ class XdsDependencyManager::ClusterWatcher
: dependency_mgr_(std::move(dependency_mgr)), name_(name) {}
void OnResourceChanged(
std::shared_ptr<const XdsClusterResource> cluster) override {
std::shared_ptr<const XdsClusterResource> cluster,
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
dependency_mgr_->work_serializer_->Run(
[self = RefAsSubclass<ClusterWatcher>(),
cluster = std::move(cluster)]() mutable {
cluster = std::move(cluster),
read_delay_handle = std::move(read_delay_handle)]() mutable {
self->dependency_mgr_->OnClusterUpdate(self->name_,
std::move(cluster));
},
DEBUG_LOCATION);
}
void OnError(absl::Status status) override {
void OnError(
absl::Status status,
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
dependency_mgr_->work_serializer_->Run(
[self = RefAsSubclass<ClusterWatcher>(),
status = std::move(status)]() mutable {
status = std::move(status),
read_delay_handle = std::move(read_delay_handle)]() mutable {
self->dependency_mgr_->OnClusterError(self->name_, std::move(status));
},
DEBUG_LOCATION);
}
void OnResourceDoesNotExist() override {
void OnResourceDoesNotExist(
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
dependency_mgr_->work_serializer_->Run(
[self = RefAsSubclass<ClusterWatcher>()]() {
[self = RefAsSubclass<ClusterWatcher>(),
read_delay_handle = std::move(read_delay_handle)]() {
self->dependency_mgr_->OnClusterDoesNotExist(self->name_);
},
DEBUG_LOCATION);
@ -252,29 +273,36 @@ class XdsDependencyManager::EndpointWatcher
: dependency_mgr_(std::move(dependency_mgr)), name_(name) {}
void OnResourceChanged(
std::shared_ptr<const XdsEndpointResource> endpoint) override {
std::shared_ptr<const XdsEndpointResource> endpoint,
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
dependency_mgr_->work_serializer_->Run(
[self = RefAsSubclass<EndpointWatcher>(),
endpoint = std::move(endpoint)]() mutable {
endpoint = std::move(endpoint),
read_delay_handle = std::move(read_delay_handle)]() mutable {
self->dependency_mgr_->OnEndpointUpdate(self->name_,
std::move(endpoint));
},
DEBUG_LOCATION);
}
void OnError(absl::Status status) override {
void OnError(
absl::Status status,
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
dependency_mgr_->work_serializer_->Run(
[self = RefAsSubclass<EndpointWatcher>(),
status = std::move(status)]() mutable {
status = std::move(status),
read_delay_handle = std::move(read_delay_handle)]() mutable {
self->dependency_mgr_->OnEndpointError(self->name_,
std::move(status));
},
DEBUG_LOCATION);
}
void OnResourceDoesNotExist() override {
void OnResourceDoesNotExist(
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
dependency_mgr_->work_serializer_->Run(
[self = RefAsSubclass<EndpointWatcher>()]() {
[self = RefAsSubclass<EndpointWatcher>(),
read_delay_handle = std::move(read_delay_handle)]() {
self->dependency_mgr_->OnEndpointDoesNotExist(self->name_);
},
DEBUG_LOCATION);

@ -96,7 +96,7 @@ ClientTransport::ClientTransport(
},
// Write buffers to corresponding endpoints concurrently.
[this]() {
return TryJoin(
return TryJoin<absl::StatusOr>(
control_endpoint_->Write(
std::move(control_endpoint_write_buffer_)),
data_endpoint_->Write(std::move(data_endpoint_write_buffer_)));
@ -134,7 +134,7 @@ ClientTransport::ClientTransport(
.value());
// Read header and trailers from control endpoint.
// Read message padding and message from data endpoint.
return TryJoin(
return TryJoin<absl::StatusOr>(
control_endpoint_->Read(frame_header_->GetFrameLength()),
data_endpoint_->Read(frame_header_->message_padding +
frame_header_->message_length));

@ -105,7 +105,7 @@ class ClientTransport {
std::move(pipe_server_frames.sender))));
}
return TrySeq(
TryJoin(
TryJoin<absl::StatusOr>(
// Continuously send client frame with client to server messages.
ForEach(std::move(*call_args.client_to_server_messages),
[stream_id, initial_frame = true,

@ -23,8 +23,10 @@
#include <algorithm>
#include <functional>
#include <memory>
#include <type_traits>
#include "absl/cleanup/cleanup.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
@ -129,6 +131,16 @@ class XdsClient::ChannelState::AdsCallState
bool HasSubscribedResources() const;
private:
class AdsReadDelayHandle : public ReadDelayHandle {
public:
explicit AdsReadDelayHandle(RefCountedPtr<AdsCallState> ads_call_state)
: ads_call_state_(std::move(ads_call_state)) {}
~AdsReadDelayHandle() override;
private:
RefCountedPtr<AdsCallState> ads_call_state_;
};
class AdsResponseParser : public XdsApi::AdsResponseParserInterface {
public:
struct Result {
@ -140,6 +152,7 @@ class XdsClient::ChannelState::AdsCallState
std::map<std::string /*authority*/, std::set<XdsResourceKey>>
resources_seen;
bool have_valid_resources = false;
RefCountedPtr<ReadDelayHandle> read_delay_handle;
};
explicit AdsResponseParser(AdsCallState* ads_call_state)
@ -259,7 +272,7 @@ class XdsClient::ChannelState::AdsCallState
ResourceState& state = authority_state.resource_map[type_][name_.key];
state.meta.client_status = XdsApi::ResourceMetadata::DOES_NOT_EXIST;
ads_calld_->xds_client()->NotifyWatchersOnResourceDoesNotExist(
state.watchers);
state.watchers, ReadDelayHandle::NoWait());
}
ads_calld_->xds_client()->work_serializer_.DrainQueue();
ads_calld_.reset();
@ -588,7 +601,7 @@ void XdsClient::ChannelState::SetChannelStatusLocked(absl::Status status) {
[watchers = std::move(watchers), status = std::move(status)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(xds_client_->work_serializer_) {
for (const auto& watcher : watchers) {
watcher->OnError(status);
watcher->OnError(status, ReadDelayHandle::NoWait());
}
},
DEBUG_LOCATION);
@ -686,6 +699,20 @@ void XdsClient::ChannelState::RetryableCall<T>::OnRetryTimer() {
}
}
//
// XdsClient::ChannelState::AdsCallState::AdsReadDelayHandle
//
XdsClient::ChannelState::AdsCallState::AdsReadDelayHandle::
~AdsReadDelayHandle() {
XdsClient* client = ads_call_state_->xds_client();
MutexLock lock(&client->mu_);
auto call = ads_call_state_->call_.get();
if (call != nullptr) {
call->StartRecvMessage();
}
}
//
// XdsClient::ChannelState::AdsCallState::AdsResponseParser
//
@ -711,6 +738,8 @@ absl::Status XdsClient::ChannelState::AdsCallState::AdsResponseParser::
result_.type_url = std::move(fields.type_url);
result_.version = std::move(fields.version);
result_.nonce = std::move(fields.nonce);
result_.read_delay_handle =
MakeRefCounted<AdsReadDelayHandle>(ads_call_state_->Ref());
return absl::OkStatus();
}
@ -841,7 +870,8 @@ void XdsClient::ChannelState::AdsCallState::AdsResponseParser::ParseResource(
xds_client()->NotifyWatchersOnErrorLocked(
resource_state.watchers,
absl::UnavailableError(
absl::StrCat("invalid resource: ", decode_status.ToString())));
absl::StrCat("invalid resource: ", decode_status.ToString())),
result_.read_delay_handle);
UpdateResourceMetadataNacked(result_.version, decode_status.ToString(),
update_time_, &resource_state.meta);
return;
@ -867,10 +897,11 @@ void XdsClient::ChannelState::AdsCallState::AdsResponseParser::ParseResource(
// Notify watchers.
auto& watchers_list = resource_state.watchers;
xds_client()->work_serializer_.Schedule(
[watchers_list, value = resource_state.resource]()
[watchers_list, value = resource_state.resource,
read_delay_handle = result_.read_delay_handle]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&xds_client()->work_serializer_) {
for (const auto& p : watchers_list) {
p.first->OnGenericResourceChanged(value);
p.first->OnGenericResourceChanged(value, read_delay_handle);
}
},
DEBUG_LOCATION);
@ -930,6 +961,7 @@ XdsClient::ChannelState::AdsCallState::AdsCallState(
for (const auto& p : state_map_) {
SendMessageLocked(p.first);
}
call_->StartRecvMessage();
}
void XdsClient::ChannelState::AdsCallState::Orphan() {
@ -1034,12 +1066,17 @@ void XdsClient::ChannelState::AdsCallState::OnRequestSent(bool ok) {
void XdsClient::ChannelState::AdsCallState::OnRecvMessage(
absl::string_view payload) {
// Needs to be destroyed after the mutex is released.
RefCountedPtr<ReadDelayHandle> read_delay_handle;
{
MutexLock lock(&xds_client()->mu_);
if (!IsCurrentCallOnChannel()) return;
// Parse and validate the response.
AdsResponseParser parser(this);
absl::Status status = xds_client()->api_.ParseAdsResponse(payload, &parser);
// This includes a handle that will trigger an ADS read.
AdsResponseParser::Result result = parser.TakeResult();
read_delay_handle = std::move(result.read_delay_handle);
if (!status.ok()) {
// Ignore unparsable response.
gpr_log(GPR_ERROR,
@ -1050,7 +1087,6 @@ void XdsClient::ChannelState::AdsCallState::OnRecvMessage(
} else {
seen_response_ = true;
chand()->status_ = absl::OkStatus();
AdsResponseParser::Result result = parser.TakeResult();
// Update nonce.
auto& state = state_map_[result.type];
state.nonce = result.nonce;
@ -1110,7 +1146,7 @@ void XdsClient::ChannelState::AdsCallState::OnRecvMessage(
resource_state.meta.client_status =
XdsApi::ResourceMetadata::DOES_NOT_EXIST;
xds_client()->NotifyWatchersOnResourceDoesNotExist(
resource_state.watchers);
resource_state.watchers, read_delay_handle);
}
}
}
@ -1335,6 +1371,7 @@ XdsClient::ChannelState::LrsCallState::LrsCallState(
std::string serialized_payload = xds_client()->api_.CreateLrsInitialRequest();
call_->SendMessage(std::move(serialized_payload));
send_message_pending_ = true;
call_->StartRecvMessage();
}
void XdsClient::ChannelState::LrsCallState::Orphan() {
@ -1385,6 +1422,9 @@ void XdsClient::ChannelState::LrsCallState::OnRecvMessage(
MutexLock lock(&xds_client()->mu_);
// If we're no longer the current call, ignore the result.
if (!IsCurrentCallOnChannel()) return;
// Start recv after any code branch
auto cleanup =
absl::MakeCleanup([call = call_.get()]() { call->StartRecvMessage(); });
// Parse the response.
bool send_all_clusters = false;
std::set<std::string> new_cluster_names;
@ -1555,7 +1595,7 @@ void XdsClient::WatchResource(const XdsResourceType* type,
work_serializer_.Run(
[watcher = std::move(watcher), status = std::move(status)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) {
watcher->OnError(status);
watcher->OnError(status, ReadDelayHandle::NoWait());
},
DEBUG_LOCATION);
};
@ -1606,7 +1646,8 @@ void XdsClient::WatchResource(const XdsResourceType* type,
work_serializer_.Schedule(
[watcher, value = resource_state.resource]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) {
watcher->OnGenericResourceChanged(value);
watcher->OnGenericResourceChanged(value,
ReadDelayHandle::NoWait());
},
DEBUG_LOCATION);
} else if (resource_state.meta.client_status ==
@ -1618,7 +1659,7 @@ void XdsClient::WatchResource(const XdsResourceType* type,
}
work_serializer_.Schedule(
[watcher]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) {
watcher->OnResourceDoesNotExist();
watcher->OnResourceDoesNotExist(ReadDelayHandle::NoWait());
},
DEBUG_LOCATION);
} else if (resource_state.meta.client_status ==
@ -1638,8 +1679,9 @@ void XdsClient::WatchResource(const XdsResourceType* type,
work_serializer_.Schedule(
[watcher, details = std::move(details)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) {
watcher->OnError(absl::UnavailableError(
absl::StrCat("invalid resource: ", details)));
watcher->OnError(absl::UnavailableError(absl::StrCat(
"invalid resource: ", details)),
ReadDelayHandle::NoWait());
},
DEBUG_LOCATION);
}
@ -1660,7 +1702,7 @@ void XdsClient::WatchResource(const XdsResourceType* type,
work_serializer_.Schedule(
[watcher = std::move(watcher), status = std::move(channel_status)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) mutable {
watcher->OnError(std::move(status));
watcher->OnError(std::move(status), ReadDelayHandle::NoWait());
},
DEBUG_LOCATION);
}
@ -1927,7 +1969,7 @@ void XdsClient::ResetBackoff() {
void XdsClient::NotifyWatchersOnErrorLocked(
const std::map<ResourceWatcherInterface*,
RefCountedPtr<ResourceWatcherInterface>>& watchers,
absl::Status status) {
absl::Status status, RefCountedPtr<ReadDelayHandle> read_delay_handle) {
const auto* node = bootstrap_->node();
if (node != nullptr) {
status = absl::Status(
@ -1935,10 +1977,11 @@ void XdsClient::NotifyWatchersOnErrorLocked(
absl::StrCat(status.message(), " (node ID:", node->id(), ")"));
}
work_serializer_.Schedule(
[watchers, status = std::move(status)]()
[watchers, status = std::move(status),
read_delay_handle = std::move(read_delay_handle)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) {
for (const auto& p : watchers) {
p.first->OnError(status);
p.first->OnError(status, read_delay_handle);
}
},
DEBUG_LOCATION);
@ -1946,13 +1989,15 @@ void XdsClient::NotifyWatchersOnErrorLocked(
void XdsClient::NotifyWatchersOnResourceDoesNotExist(
const std::map<ResourceWatcherInterface*,
RefCountedPtr<ResourceWatcherInterface>>& watchers) {
RefCountedPtr<ResourceWatcherInterface>>& watchers,
RefCountedPtr<ReadDelayHandle> read_delay_handle) {
work_serializer_.Schedule(
[watchers]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) {
for (const auto& p : watchers) {
p.first->OnResourceDoesNotExist();
}
},
[watchers, read_delay_handle = std::move(read_delay_handle)]()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) {
for (const auto& p : watchers) {
p.first->OnResourceDoesNotExist(read_delay_handle);
}
},
DEBUG_LOCATION);
}

@ -56,6 +56,11 @@ extern TraceFlag grpc_xds_client_refcount_trace;
class XdsClient : public DualRefCounted<XdsClient> {
public:
class ReadDelayHandle : public RefCounted<ReadDelayHandle> {
public:
static RefCountedPtr<ReadDelayHandle> NoWait() { return nullptr; }
};
// Resource watcher interface. Implemented by callers.
// Note: Most callers will not use this API directly but rather via a
// resource-type-specific wrapper API provided by the relevant
@ -63,11 +68,14 @@ class XdsClient : public DualRefCounted<XdsClient> {
class ResourceWatcherInterface : public RefCounted<ResourceWatcherInterface> {
public:
virtual void OnGenericResourceChanged(
std::shared_ptr<const XdsResourceType::ResourceData> resource)
std::shared_ptr<const XdsResourceType::ResourceData> resource,
RefCountedPtr<ReadDelayHandle> read_delay_handle)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) = 0;
virtual void OnError(absl::Status status)
virtual void OnError(absl::Status status,
RefCountedPtr<ReadDelayHandle> read_delay_handle)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) = 0;
virtual void OnResourceDoesNotExist()
virtual void OnResourceDoesNotExist(
RefCountedPtr<ReadDelayHandle> read_delay_handle)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) = 0;
};
@ -277,11 +285,12 @@ class XdsClient : public DualRefCounted<XdsClient> {
void NotifyWatchersOnErrorLocked(
const std::map<ResourceWatcherInterface*,
RefCountedPtr<ResourceWatcherInterface>>& watchers,
absl::Status status);
absl::Status status, RefCountedPtr<ReadDelayHandle> read_delay_handle);
// Sends a resource-does-not-exist notification to a specific set of watchers.
void NotifyWatchersOnResourceDoesNotExist(
const std::map<ResourceWatcherInterface*,
RefCountedPtr<ResourceWatcherInterface>>& watchers);
RefCountedPtr<ResourceWatcherInterface>>& watchers,
RefCountedPtr<ReadDelayHandle> read_delay_handle);
void MaybeRegisterResourceTypeLocked(const XdsResourceType* resource_type)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_);

@ -42,16 +42,18 @@ class XdsResourceTypeImpl : public XdsResourceType {
class WatcherInterface : public XdsClient::ResourceWatcherInterface {
public:
virtual void OnResourceChanged(
std::shared_ptr<const ResourceType> resource) = 0;
std::shared_ptr<const ResourceType> resource,
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) = 0;
private:
// Get result from XdsClient generic watcher interface, perform
// down-casting, and invoke the caller's OnResourceChanged() method.
void OnGenericResourceChanged(
std::shared_ptr<const XdsResourceType::ResourceData> resource)
override {
std::shared_ptr<const XdsResourceType::ResourceData> resource,
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle) override {
OnResourceChanged(
std::static_pointer_cast<const ResourceType>(std::move(resource)));
std::static_pointer_cast<const ResourceType>(std::move(resource)),
std::move(read_delay_handle));
}
};

@ -52,6 +52,7 @@
#include "src/core/ext/xds/xds_bootstrap_grpc.h"
#include "src/core/ext/xds/xds_certificate_provider.h"
#include "src/core/ext/xds/xds_channel_stack_modifier.h"
#include "src/core/ext/xds/xds_client.h"
#include "src/core/ext/xds/xds_client_grpc.h"
#include "src/core/ext/xds/xds_common_types.h"
#include "src/core/ext/xds/xds_http_filters.h"
@ -91,6 +92,8 @@
namespace grpc_core {
namespace {
using ReadDelayHandle = XdsClient::ReadDelayHandle;
TraceFlag grpc_xds_server_config_fetcher_trace(false,
"xds_server_config_fetcher");
@ -151,11 +154,14 @@ class XdsServerConfigFetcher::ListenerWatcher
}
void OnResourceChanged(
std::shared_ptr<const XdsListenerResource> listener) override;
std::shared_ptr<const XdsListenerResource> listener,
RefCountedPtr<ReadDelayHandle> read_delay_handle) override;
void OnError(absl::Status status) override;
void OnError(absl::Status status,
RefCountedPtr<ReadDelayHandle> read_delay_handle) override;
void OnResourceDoesNotExist() override;
void OnResourceDoesNotExist(
RefCountedPtr<ReadDelayHandle> read_delay_handle) override;
const std::string& listening_address() const { return listening_address_; }
@ -284,16 +290,20 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager::
filter_chain_match_manager_(std::move(filter_chain_match_manager)) {}
void OnResourceChanged(
std::shared_ptr<const XdsRouteConfigResource> route_config) override {
std::shared_ptr<const XdsRouteConfigResource> route_config,
RefCountedPtr<ReadDelayHandle> /* read_delay_handle */) override {
filter_chain_match_manager_->OnRouteConfigChanged(resource_name_,
std::move(route_config));
}
void OnError(absl::Status status) override {
void OnError(
absl::Status status,
RefCountedPtr<ReadDelayHandle> /* read_delay_handle */) override {
filter_chain_match_manager_->OnError(resource_name_, status);
}
void OnResourceDoesNotExist() override {
void OnResourceDoesNotExist(
RefCountedPtr<ReadDelayHandle> /* read_delay_handle */) override {
filter_chain_match_manager_->OnResourceDoesNotExist(resource_name_);
}
@ -480,13 +490,21 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager::
: parent_(std::move(parent)) {}
void OnResourceChanged(
std::shared_ptr<const XdsRouteConfigResource> route_config) override {
std::shared_ptr<const XdsRouteConfigResource> route_config,
RefCountedPtr<ReadDelayHandle> /* read_delay_handle */) override {
parent_->OnRouteConfigChanged(std::move(route_config));
}
void OnError(absl::Status status) override { parent_->OnError(status); }
void OnError(
absl::Status status,
RefCountedPtr<ReadDelayHandle> /* read_delay_handle */) override {
parent_->OnError(status);
}
void OnResourceDoesNotExist() override { parent_->OnResourceDoesNotExist(); }
void OnResourceDoesNotExist(
RefCountedPtr<ReadDelayHandle> /* read_delay_handle */) override {
parent_->OnResourceDoesNotExist();
}
private:
WeakRefCountedPtr<DynamicXdsServerConfigSelectorProvider> parent_;
@ -566,7 +584,8 @@ XdsServerConfigFetcher::ListenerWatcher::ListenerWatcher(
listening_address_(std::move(listening_address)) {}
void XdsServerConfigFetcher::ListenerWatcher::OnResourceChanged(
std::shared_ptr<const XdsListenerResource> listener) {
std::shared_ptr<const XdsListenerResource> listener,
RefCountedPtr<ReadDelayHandle> /* read_delay_handle */) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_server_config_fetcher_trace)) {
gpr_log(GPR_INFO,
"[ListenerWatcher %p] Received LDS update from xds client %p: %s",
@ -602,7 +621,9 @@ void XdsServerConfigFetcher::ListenerWatcher::OnResourceChanged(
}
}
void XdsServerConfigFetcher::ListenerWatcher::OnError(absl::Status status) {
void XdsServerConfigFetcher::ListenerWatcher::OnError(
absl::Status status,
RefCountedPtr<ReadDelayHandle> /* read_delay_handle */) {
MutexLock lock(&mu_);
if (filter_chain_match_manager_ != nullptr ||
pending_filter_chain_match_manager_ != nullptr) {
@ -645,7 +666,8 @@ void XdsServerConfigFetcher::ListenerWatcher::OnFatalError(
}
}
void XdsServerConfigFetcher::ListenerWatcher::OnResourceDoesNotExist() {
void XdsServerConfigFetcher::ListenerWatcher::OnResourceDoesNotExist(
RefCountedPtr<ReadDelayHandle> /* read_delay_handle */) {
MutexLock lock(&mu_);
OnFatalError(absl::NotFoundError("Requested listener does not exist"));
}

@ -58,6 +58,9 @@ class XdsTransportFactory : public InternallyRefCounted<XdsTransportFactory> {
// Only one message will be in flight at a time; subsequent
// messages will not be sent until this one is done.
virtual void SendMessage(std::string payload) = 0;
// Starts a recv_message operation on the stream.
virtual void StartRecvMessage() = 0;
};
// Create a streaming call on this transport for the specified method.

@ -86,39 +86,31 @@ GrpcXdsTransportFactory::GrpcXdsTransport::GrpcStreamingCall::GrpcStreamingCall(
GRPC_CLOSURE_INIT(&on_request_sent_, OnRequestSent, this, nullptr);
// Start ops on the call.
grpc_call_error call_error;
grpc_op ops[3];
grpc_op ops[2];
memset(ops, 0, sizeof(ops));
// Send initial metadata. No callback for this, since we don't really
// care when it finishes.
// Send initial metadata.
grpc_op* op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY |
GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET;
op->reserved = nullptr;
op++;
call_error = grpc_call_start_batch_and_execute(
call_, ops, static_cast<size_t>(op - ops), nullptr);
GPR_ASSERT(GRPC_CALL_OK == call_error);
// Start a batch with recv_initial_metadata and recv_message.
op = ops;
++op;
op->op = GRPC_OP_RECV_INITIAL_METADATA;
op->data.recv_initial_metadata.recv_initial_metadata =
&initial_metadata_recv_;
op->flags = 0;
op->reserved = nullptr;
op++;
op->op = GRPC_OP_RECV_MESSAGE;
op->data.recv_message.recv_message = &recv_message_payload_;
op->flags = 0;
op->reserved = nullptr;
op++;
Ref(DEBUG_LOCATION, "OnResponseReceived").release();
GRPC_CLOSURE_INIT(&on_response_received_, OnResponseReceived, this, nullptr);
++op;
// Ref will be released in the callback
GRPC_CLOSURE_INIT(
&on_recv_initial_metadata_, OnRecvInitialMetadata,
this->Ref(DEBUG_LOCATION, "OnRecvInitialMetadata").release(), nullptr);
call_error = grpc_call_start_batch_and_execute(
call_, ops, static_cast<size_t>(op - ops), &on_response_received_);
call_, ops, static_cast<size_t>(op - ops), &on_recv_initial_metadata_);
GPR_ASSERT(GRPC_CALL_OK == call_error);
// Start a batch for recv_trailing_metadata.
memset(ops, 0, sizeof(ops));
op = ops;
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv_;
@ -126,7 +118,7 @@ GrpcXdsTransportFactory::GrpcXdsTransport::GrpcStreamingCall::GrpcStreamingCall(
op->data.recv_status_on_client.status_details = &status_details_;
op->flags = 0;
op->reserved = nullptr;
op++;
++op;
// This callback signals the end of the call, so it relies on the initial
// ref instead of a new ref. When it's invoked, it's the initial ref that is
// unreffed.
@ -134,11 +126,11 @@ GrpcXdsTransportFactory::GrpcXdsTransport::GrpcStreamingCall::GrpcStreamingCall(
call_error = grpc_call_start_batch_and_execute(
call_, ops, static_cast<size_t>(op - ops), &on_status_received_);
GPR_ASSERT(GRPC_CALL_OK == call_error);
GRPC_CLOSURE_INIT(&on_response_received_, OnResponseReceived, this, nullptr);
}
GrpcXdsTransportFactory::GrpcXdsTransport::GrpcStreamingCall::
~GrpcStreamingCall() {
grpc_metadata_array_destroy(&initial_metadata_recv_);
grpc_metadata_array_destroy(&trailing_metadata_recv_);
grpc_byte_buffer_destroy(send_message_payload_);
grpc_byte_buffer_destroy(recv_message_payload_);
@ -175,55 +167,59 @@ void GrpcXdsTransportFactory::GrpcXdsTransport::GrpcStreamingCall::SendMessage(
GPR_ASSERT(GRPC_CALL_OK == call_error);
}
void GrpcXdsTransportFactory::GrpcXdsTransport::GrpcStreamingCall::
StartRecvMessage() {
Ref(DEBUG_LOCATION, "StartRecvMessage").release();
grpc_op op;
memset(&op, 0, sizeof(op));
op.op = GRPC_OP_RECV_MESSAGE;
op.data.recv_message.recv_message = &recv_message_payload_;
GPR_ASSERT(call_ != nullptr);
const grpc_call_error call_error =
grpc_call_start_batch_and_execute(call_, &op, 1, &on_response_received_);
GPR_ASSERT(GRPC_CALL_OK == call_error);
}
void GrpcXdsTransportFactory::GrpcXdsTransport::GrpcStreamingCall::
OnRecvInitialMetadata(void* arg, grpc_error_handle /*error*/) {
RefCountedPtr<GrpcStreamingCall> self(static_cast<GrpcStreamingCall*>(arg));
grpc_metadata_array_destroy(&self->initial_metadata_recv_);
}
void GrpcXdsTransportFactory::GrpcXdsTransport::GrpcStreamingCall::
OnRequestSent(void* arg, grpc_error_handle error) {
auto* self = static_cast<GrpcStreamingCall*>(arg);
RefCountedPtr<GrpcStreamingCall> self(static_cast<GrpcStreamingCall*>(arg));
// Clean up the sent message.
grpc_byte_buffer_destroy(self->send_message_payload_);
self->send_message_payload_ = nullptr;
// Invoke request handler.
self->event_handler_->OnRequestSent(error.ok());
// Drop the ref.
self->Unref(DEBUG_LOCATION, "OnRequestSent");
}
void GrpcXdsTransportFactory::GrpcXdsTransport::GrpcStreamingCall::
OnResponseReceived(void* arg, grpc_error_handle /*error*/) {
auto* self = static_cast<GrpcStreamingCall*>(arg);
RefCountedPtr<GrpcStreamingCall> self(static_cast<GrpcStreamingCall*>(arg));
// If there was no payload, then we received status before we received
// another message, so we stop reading.
if (self->recv_message_payload_ == nullptr) {
self->Unref(DEBUG_LOCATION, "OnResponseReceived");
return;
if (self->recv_message_payload_ != nullptr) {
// Process the response.
grpc_byte_buffer_reader bbr;
grpc_byte_buffer_reader_init(&bbr, self->recv_message_payload_);
grpc_slice response_slice = grpc_byte_buffer_reader_readall(&bbr);
grpc_byte_buffer_reader_destroy(&bbr);
grpc_byte_buffer_destroy(self->recv_message_payload_);
self->recv_message_payload_ = nullptr;
self->event_handler_->OnRecvMessage(StringViewFromSlice(response_slice));
CSliceUnref(response_slice);
}
// Process the response.
grpc_byte_buffer_reader bbr;
grpc_byte_buffer_reader_init(&bbr, self->recv_message_payload_);
grpc_slice response_slice = grpc_byte_buffer_reader_readall(&bbr);
grpc_byte_buffer_reader_destroy(&bbr);
grpc_byte_buffer_destroy(self->recv_message_payload_);
self->recv_message_payload_ = nullptr;
self->event_handler_->OnRecvMessage(StringViewFromSlice(response_slice));
CSliceUnref(response_slice);
// Keep reading.
grpc_op op;
memset(&op, 0, sizeof(op));
op.op = GRPC_OP_RECV_MESSAGE;
op.data.recv_message.recv_message = &self->recv_message_payload_;
GPR_ASSERT(self->call_ != nullptr);
// Reuses the "OnResponseReceived" ref taken in ctor.
const grpc_call_error call_error = grpc_call_start_batch_and_execute(
self->call_, &op, 1, &self->on_response_received_);
GPR_ASSERT(GRPC_CALL_OK == call_error);
}
void GrpcXdsTransportFactory::GrpcXdsTransport::GrpcStreamingCall::
OnStatusReceived(void* arg, grpc_error_handle /*error*/) {
auto* self = static_cast<GrpcStreamingCall*>(arg);
RefCountedPtr<GrpcStreamingCall> self(static_cast<GrpcStreamingCall*>(arg));
self->event_handler_->OnStatusReceived(
absl::Status(static_cast<absl::StatusCode>(self->status_code_),
StringViewFromSlice(self->status_details_)));
self->Unref(DEBUG_LOCATION, "OnStatusReceived");
}
//

@ -100,7 +100,10 @@ class GrpcXdsTransportFactory::GrpcXdsTransport::GrpcStreamingCall
void SendMessage(std::string payload) override;
void StartRecvMessage() override;
private:
static void OnRecvInitialMetadata(void* arg, grpc_error_handle /*error*/);
static void OnRequestSent(void* arg, grpc_error_handle error);
static void OnResponseReceived(void* arg, grpc_error_handle /*error*/);
static void OnStatusReceived(void* arg, grpc_error_handle /*error*/);
@ -114,6 +117,7 @@ class GrpcXdsTransportFactory::GrpcXdsTransport::GrpcStreamingCall
// recv_initial_metadata
grpc_metadata_array initial_metadata_recv_;
grpc_closure on_recv_initial_metadata_;
// send_message
grpc_byte_buffer* send_message_payload_ = nullptr;

@ -326,6 +326,11 @@ void grpc_channel_stack::InitClientCallSpine(
grpc_core::CallSpineInterface* call) {
for (size_t i = 0; i < count; i++) {
auto* elem = grpc_channel_stack_element(this, i);
if (elem->filter->init_call == nullptr) {
grpc_core::Crash(
absl::StrCat("Filter '", elem->filter->name,
"' does not support the call-v3 interface"));
}
elem->filter->init_call(elem, call);
}
}
@ -334,6 +339,11 @@ void grpc_channel_stack::InitServerCallSpine(
grpc_core::CallSpineInterface* call) {
for (size_t i = 0; i < count; i++) {
auto* elem = grpc_channel_stack_element(this, count - 1 - i);
if (elem->filter->init_call == nullptr) {
grpc_core::Crash(
absl::StrCat("Filter '", elem->filter->name,
"' does not support the call-v3 interface"));
}
elem->filter->init_call(elem, call);
}
}

@ -873,10 +873,12 @@ grpc_channel_filter MakeConnectedFilter() {
// do this, and I'm not sure what that is yet. This is only "safe"
// because call stacks place no additional data after the last call
// element, and the last call element MUST be the connected channel.
channel_stack->call_stack_size +=
static_cast<channel_data*>(elem->channel_data)
->transport->filter_stack_transport()
->SizeOfStream();
auto* transport =
static_cast<channel_data*>(elem->channel_data)->transport;
if (transport->filter_stack_transport() != nullptr) {
channel_stack->call_stack_size +=
transport->filter_stack_transport()->SizeOfStream();
}
},
connected_channel_destroy_channel_elem,
connected_channel_get_channel_info,
@ -884,13 +886,27 @@ grpc_channel_filter MakeConnectedFilter() {
};
}
ArenaPromise<ServerMetadataHandle> MakeTransportCallPromise(
Transport*, CallArgs, NextPromiseFactory) {
Crash("unimplemented");
ArenaPromise<ServerMetadataHandle> MakeClientTransportCallPromise(
Transport* transport, CallArgs call_args, NextPromiseFactory) {
auto spine = GetContext<CallContext>()->MakeCallSpine(std::move(call_args));
transport->client_transport()->StartCall(CallHandler{spine});
return Map(spine->server_trailing_metadata().receiver.Next(),
[](NextResult<ServerMetadataHandle> r) {
if (r.has_value()) {
auto md = std::move(r.value());
md->Set(GrpcStatusFromWire(), true);
return md;
}
auto m = GetContext<Arena>()->MakePooled<ServerMetadata>(
GetContext<Arena>());
m->Set(GrpcStatusMetadata(), GRPC_STATUS_CANCELLED);
m->Set(GrpcCallWasCancelled(), true);
return m;
});
}
const grpc_channel_filter kPromiseBasedTransportFilter =
MakeConnectedFilter<MakeTransportCallPromise>();
const grpc_channel_filter kClientPromiseBasedTransportFilter =
MakeConnectedFilter<MakeClientTransportCallPromise>();
#ifdef GRPC_EXPERIMENT_IS_INCLUDED_PROMISE_BASED_CLIENT_CALL
const grpc_channel_filter kClientEmulatedFilter =
@ -908,11 +924,37 @@ const grpc_channel_filter kServerEmulatedFilter =
MakeConnectedFilter<nullptr>();
#endif
bool TransportSupportsPromiseBasedCalls(const ChannelArgs& args) {
// noop filter for the v3 stack: placeholder for now because other code requires
// we have a terminator.
// TODO(ctiller): delete when v3 transition is complete.
const grpc_channel_filter kServerPromiseBasedTransportFilter = {
nullptr,
[](grpc_channel_element*, CallArgs, NextPromiseFactory)
-> ArenaPromise<ServerMetadataHandle> { Crash("not implemented"); },
/* init_call: */ [](grpc_channel_element*, CallSpineInterface*) {},
connected_channel_start_transport_op,
0,
nullptr,
set_pollset_or_pollset_set,
nullptr,
sizeof(channel_data),
connected_channel_init_channel_elem,
+[](grpc_channel_stack*, grpc_channel_element*) {},
connected_channel_destroy_channel_elem,
connected_channel_get_channel_info,
"connected",
};
bool TransportSupportsClientPromiseBasedCalls(const ChannelArgs& args) {
auto* transport = args.GetObject<Transport>();
return transport->client_transport() != nullptr;
}
bool TransportSupportsServerPromiseBasedCalls(const ChannelArgs& args) {
auto* transport = args.GetObject<Transport>();
return transport->server_transport() != nullptr;
}
} // namespace
void RegisterConnectedChannel(CoreConfiguration::Builder* builder) {
@ -925,32 +967,33 @@ void RegisterConnectedChannel(CoreConfiguration::Builder* builder) {
// Option 1, and our ideal: the transport supports promise based calls,
// and so we simply use the transport directly.
builder->channel_init()
->RegisterFilter(GRPC_CLIENT_SUBCHANNEL, &kPromiseBasedTransportFilter)
->RegisterFilter(GRPC_CLIENT_SUBCHANNEL,
&kClientPromiseBasedTransportFilter)
.Terminal()
.If(TransportSupportsPromiseBasedCalls);
.If(TransportSupportsClientPromiseBasedCalls);
builder->channel_init()
->RegisterFilter(GRPC_CLIENT_DIRECT_CHANNEL,
&kPromiseBasedTransportFilter)
&kClientPromiseBasedTransportFilter)
.Terminal()
.If(TransportSupportsPromiseBasedCalls);
.If(TransportSupportsClientPromiseBasedCalls);
builder->channel_init()
->RegisterFilter(GRPC_SERVER_CHANNEL, &kPromiseBasedTransportFilter)
->RegisterFilter(GRPC_SERVER_CHANNEL, &kServerPromiseBasedTransportFilter)
.Terminal()
.If(TransportSupportsPromiseBasedCalls);
.If(TransportSupportsServerPromiseBasedCalls);
// Option 2: the transport does not support promise based calls.
builder->channel_init()
->RegisterFilter(GRPC_CLIENT_SUBCHANNEL, &kClientEmulatedFilter)
.Terminal()
.IfNot(TransportSupportsPromiseBasedCalls);
.IfNot(TransportSupportsClientPromiseBasedCalls);
builder->channel_init()
->RegisterFilter(GRPC_CLIENT_DIRECT_CHANNEL, &kClientEmulatedFilter)
.Terminal()
.IfNot(TransportSupportsPromiseBasedCalls);
.IfNot(TransportSupportsClientPromiseBasedCalls);
builder->channel_init()
->RegisterFilter(GRPC_SERVER_CHANNEL, &kServerEmulatedFilter)
.Terminal()
.IfNot(TransportSupportsPromiseBasedCalls);
.IfNot(TransportSupportsServerPromiseBasedCalls);
}
} // namespace grpc_core

@ -18,6 +18,7 @@
#include <grpc/support/port_platform.h>
#include <memory>
#include <type_traits>
#include <utility>
#include "absl/meta/type_traits.h"

@ -185,6 +185,8 @@ class Latch<void> {
waiter_.Wake();
}
bool is_set() const { return is_set_; }
private:
std::string DebugTag() {
return absl::StrCat(Activity::current()->DebugTag(), " LATCH(void)[0x",

@ -21,6 +21,8 @@
#include "absl/status/statusor.h"
#include "absl/types/optional.h"
#include <grpc/support/log.h>
#include "src/core/lib/promise/detail/status.h"
namespace grpc_core {
@ -41,6 +43,16 @@ struct StatusCastImpl<absl::Status, const Success&> {
static absl::Status Cast(Success) { return absl::OkStatus(); }
};
template <>
struct StatusCastImpl<absl::Status, Failure> {
static absl::Status Cast(Failure) { return absl::CancelledError(); }
};
template <typename T>
struct StatusCastImpl<absl::StatusOr<T>, Failure> {
static absl::StatusOr<T> Cast(Failure) { return absl::CancelledError(); }
};
// A boolean representing whether an operation succeeded (true) or failed
// (false).
class StatusFlag {
@ -91,18 +103,25 @@ class ValueOrFailure {
ValueOrFailure(T value) : value_(std::move(value)) {}
// NOLINTNEXTLINE(google-explicit-constructor)
ValueOrFailure(Failure) {}
// NOLINTNEXTLINE(google-explicit-constructor)
ValueOrFailure(StatusFlag status) { GPR_ASSERT(!status.ok()); }
static ValueOrFailure FromOptional(absl::optional<T> value) {
return ValueOrFailure{std::move(value)};
}
bool ok() const { return value_.has_value(); }
StatusFlag status() const { return StatusFlag(ok()); }
const T& value() const { return value_.value(); }
T& value() { return value_.value(); }
const T& operator*() const { return *value_; }
T& operator*() { return *value_; }
bool operator==(const ValueOrFailure& other) const {
return value_ == other.value_;
}
private:
absl::optional<T> value_;
};
@ -117,13 +136,6 @@ inline T TakeValue(ValueOrFailure<T>&& value) {
return std::move(value.value());
}
template <typename T>
struct StatusCastImpl<absl::Status, ValueOrFailure<T>> {
static absl::Status Cast(const ValueOrFailure<T> flag) {
return flag.ok() ? absl::OkStatus() : absl::CancelledError();
}
};
template <typename T>
struct StatusCastImpl<absl::StatusOr<T>, ValueOrFailure<T>> {
static absl::StatusOr<T> Cast(ValueOrFailure<T> value) {
@ -132,6 +144,29 @@ struct StatusCastImpl<absl::StatusOr<T>, ValueOrFailure<T>> {
}
};
template <typename T>
struct StatusCastImpl<ValueOrFailure<T>, Failure> {
static ValueOrFailure<T> Cast(Failure) {
return ValueOrFailure<T>(Failure{});
}
};
template <typename T>
struct StatusCastImpl<ValueOrFailure<T>, StatusFlag&> {
static ValueOrFailure<T> Cast(StatusFlag f) {
GPR_ASSERT(!f.ok());
return ValueOrFailure<T>(Failure{});
}
};
template <typename T>
struct StatusCastImpl<ValueOrFailure<T>, StatusFlag> {
static ValueOrFailure<T> Cast(StatusFlag f) {
GPR_ASSERT(!f.ok());
return ValueOrFailure<T>(Failure{});
}
};
} // namespace grpc_core
#endif // GRPC_SRC_CORE_LIB_PROMISE_STATUS_FLAG_H

@ -27,6 +27,7 @@
#include "src/core/lib/promise/detail/join_state.h"
#include "src/core/lib/promise/map.h"
#include "src/core/lib/promise/poll.h"
#include "src/core/lib/promise/status_flag.h"
namespace grpc_core {
@ -44,48 +45,68 @@ T IntoResult(absl::StatusOr<T>* status) {
inline Empty IntoResult(absl::Status*) { return Empty{}; }
// Traits object to pass to BasicJoin
template <template <typename> class Result>
struct TryJoinTraits {
template <typename T>
using ResultType = absl::StatusOr<absl::remove_reference_t<T>>;
using ResultType = Result<absl::remove_reference_t<T>>;
template <typename T>
static bool IsOk(const absl::StatusOr<T>& x) {
return x.ok();
}
static bool IsOk(const absl::Status& x) { return x.ok(); }
static bool IsOk(StatusFlag x) { return x.ok(); }
template <typename T>
static bool IsOk(const ValueOrFailure<T>& x) {
return x.ok();
}
template <typename T>
static T Unwrapped(absl::StatusOr<T> x) {
return std::move(*x);
}
template <typename T>
static T Unwrapped(ValueOrFailure<T> x) {
return std::move(*x);
}
static Empty Unwrapped(absl::Status) { return Empty{}; }
static Empty Unwrapped(StatusFlag) { return Empty{}; }
template <typename R, typename T>
static R EarlyReturn(absl::StatusOr<T> x) {
return x.status();
}
template <typename R>
static R EarlyReturn(absl::Status x) {
return x;
return StatusCast<R>(std::move(x));
}
template <typename R>
static R EarlyReturn(StatusFlag x) {
return StatusCast<R>(x);
}
template <typename R, typename T>
static R EarlyReturn(const ValueOrFailure<T>& x) {
GPR_ASSERT(!x.ok());
return StatusCast<R>(Failure{});
}
template <typename... A>
static auto FinalReturn(A&&... a) {
return absl::StatusOr<std::tuple<A...>>(
std::make_tuple(std::forward<A>(a)...));
return Result<std::tuple<A...>>(std::make_tuple(std::forward<A>(a)...));
}
};
// Implementation of TryJoin combinator.
template <typename... Promises>
template <template <typename> class R, typename... Promises>
class TryJoin {
public:
explicit TryJoin(Promises... promises) : state_(std::move(promises)...) {}
auto operator()() { return state_.PollOnce(); }
private:
JoinState<TryJoinTraits, Promises...> state_;
JoinState<TryJoinTraits<R>, Promises...> state_;
};
template <template <typename> class R>
struct WrapInStatusOrTuple {
template <typename T>
absl::StatusOr<std::tuple<T>> operator()(absl::StatusOr<T> x) {
R<std::tuple<T>> operator()(R<T> x) {
if (!x.ok()) return x.status();
return std::make_tuple(std::move(*x));
}
@ -96,14 +117,14 @@ struct WrapInStatusOrTuple {
// Run all promises.
// If any fail, cancel the rest and return the failure.
// If all succeed, return Ok(tuple-of-results).
template <typename... Promises>
promise_detail::TryJoin<Promises...> TryJoin(Promises... promises) {
return promise_detail::TryJoin<Promises...>(std::move(promises)...);
template <template <typename> class R, typename... Promises>
promise_detail::TryJoin<R, Promises...> TryJoin(Promises... promises) {
return promise_detail::TryJoin<R, Promises...>(std::move(promises)...);
}
template <typename F>
template <template <typename> class R, typename F>
auto TryJoin(F promise) {
return Map(promise, promise_detail::WrapInStatusOrTuple{});
return Map(promise, promise_detail::WrapInStatusOrTuple<R>{});
}
} // namespace grpc_core

@ -31,6 +31,7 @@
#include "src/core/lib/promise/detail/seq_state.h"
#include "src/core/lib/promise/detail/status.h"
#include "src/core/lib/promise/poll.h"
#include "src/core/lib/promise/status_flag.h"
namespace grpc_core {
@ -89,6 +90,7 @@ struct TrySeqTraitsWithSfinae<absl::StatusOr<T>> {
return run_next(std::move(prior));
}
};
template <typename T, typename AnyType = void>
struct TakeValueExists {
static constexpr bool value = false;
@ -146,7 +148,7 @@ struct TrySeqTraitsWithSfinae<
template <typename R>
static R ReturnValue(T&& status) {
GPR_DEBUG_ASSERT(!IsStatusOk(status));
return StatusCast<R>(std::move(status));
return StatusCast<R>(status.status());
}
template <typename Result, typename RunNext>
static Poll<Result> CheckResultAndRunNext(T prior, RunNext run_next) {

File diff suppressed because it is too large Load Diff

@ -77,34 +77,35 @@ typedef struct grpc_call_create_args {
} grpc_call_create_args;
namespace grpc_core {
class PromiseBasedCall;
class BasicPromiseBasedCall;
class ServerPromiseBasedCall;
class ServerCallContext {
public:
ServerCallContext(ServerPromiseBasedCall* call,
const void* server_stream_data)
: call_(call), server_stream_data_(server_stream_data) {}
ArenaPromise<ServerMetadataHandle> MakeTopOfServerCallPromise(
virtual void PublishInitialMetadata(
ClientMetadataHandle metadata,
grpc_metadata_array* publish_initial_metadata) = 0;
// Construct the top of the server call promise for the v2 filter stack.
// TODO(ctiller): delete when v3 is available.
virtual ArenaPromise<ServerMetadataHandle> MakeTopOfServerCallPromise(
CallArgs call_args, grpc_completion_queue* cq,
grpc_metadata_array* publish_initial_metadata,
absl::FunctionRef<void(grpc_call* call)> publish);
absl::FunctionRef<void(grpc_call* call)> publish) = 0;
// Server stream data as supplied by the transport (so we can link the
// transport stream up with the call again).
// TODO(ctiller): legacy API - once we move transports to promises we'll
// create the promise directly and not need to pass around this token.
const void* server_stream_data() { return server_stream_data_; }
virtual const void* server_stream_data() = 0;
private:
ServerPromiseBasedCall* const call_;
const void* const server_stream_data_;
protected:
~ServerCallContext() = default;
};
// TODO(ctiller): move more call things into this type
class CallContext {
public:
explicit CallContext(PromiseBasedCall* call) : call_(call) {}
explicit CallContext(BasicPromiseBasedCall* call) : call_(call) {}
// Update the deadline (if deadline < the current deadline).
void UpdateDeadline(Timestamp deadline);
@ -135,13 +136,21 @@ class CallContext {
void set_traced(bool traced) { traced_ = traced; }
bool traced() const { return traced_; }
// TEMPORARY HACK
// Create a call spine object for this call.
// Said object should only be created once.
// Allows interop between the v2 call stack and the v3 (which is required by
// transports).
RefCountedPtr<CallSpineInterface> MakeCallSpine(CallArgs call_args);
grpc_call* c_call();
private:
friend class PromiseBasedCall;
// Call final info.
grpc_call_stats call_stats_;
// TODO(ctiller): remove this once transport APIs are promise based and we
// don't need refcounting here.
PromiseBasedCall* const call_;
BasicPromiseBasedCall* const call_;
gpr_cycle_counter start_time_ = gpr_get_cycle_counter();
// Is this call traced?
bool traced_ = false;
@ -150,6 +159,9 @@ class CallContext {
template <>
struct ContextType<CallContext> {};
RefCountedPtr<CallSpineInterface> MakeServerCall(Server* server,
Channel* channel);
} // namespace grpc_core
// Create a new call based on \a args.

@ -67,6 +67,7 @@
#include "src/core/lib/promise/poll.h"
#include "src/core/lib/promise/promise.h"
#include "src/core/lib/promise/seq.h"
#include "src/core/lib/promise/try_join.h"
#include "src/core/lib/promise/try_seq.h"
#include "src/core/lib/slice/slice_buffer.h"
#include "src/core/lib/slice/slice_internal.h"
@ -75,6 +76,7 @@
#include "src/core/lib/surface/channel.h"
#include "src/core/lib/surface/channel_stack_type.h"
#include "src/core/lib/surface/completion_queue.h"
#include "src/core/lib/surface/wait_for_cq_end_op.h"
#include "src/core/lib/transport/connectivity_state.h"
#include "src/core/lib/transport/error_utils.h"
@ -82,57 +84,6 @@ namespace grpc_core {
TraceFlag grpc_server_channel_trace(false, "server_channel");
//
// Server::RequestedCall
//
struct Server::RequestedCall {
enum class Type { BATCH_CALL, REGISTERED_CALL };
RequestedCall(void* tag_arg, grpc_completion_queue* call_cq,
grpc_call** call_arg, grpc_metadata_array* initial_md,
grpc_call_details* details)
: type(Type::BATCH_CALL),
tag(tag_arg),
cq_bound_to_call(call_cq),
call(call_arg),
initial_metadata(initial_md) {
data.batch.details = details;
}
RequestedCall(void* tag_arg, grpc_completion_queue* call_cq,
grpc_call** call_arg, grpc_metadata_array* initial_md,
RegisteredMethod* rm, gpr_timespec* deadline,
grpc_byte_buffer** optional_payload)
: type(Type::REGISTERED_CALL),
tag(tag_arg),
cq_bound_to_call(call_cq),
call(call_arg),
initial_metadata(initial_md) {
data.registered.method = rm;
data.registered.deadline = deadline;
data.registered.optional_payload = optional_payload;
}
MultiProducerSingleConsumerQueue::Node mpscq_node;
const Type type;
void* const tag;
grpc_completion_queue* const cq_bound_to_call;
grpc_call** const call;
grpc_cq_completion completion;
grpc_metadata_array* const initial_metadata;
union {
struct {
grpc_call_details* details;
} batch;
struct {
RegisteredMethod* method;
gpr_timespec* deadline;
grpc_byte_buffer** optional_payload;
} registered;
} data;
};
//
// Server::RegisteredMethod
//
@ -248,6 +199,87 @@ class Server::RequestMatcherInterface {
virtual Server* server() const = 0;
};
//
// Server::RequestedCall
//
struct Server::RequestedCall {
enum class Type { BATCH_CALL, REGISTERED_CALL };
RequestedCall(void* tag_arg, grpc_completion_queue* call_cq,
grpc_call** call_arg, grpc_metadata_array* initial_md,
grpc_call_details* details)
: type(Type::BATCH_CALL),
tag(tag_arg),
cq_bound_to_call(call_cq),
call(call_arg),
initial_metadata(initial_md) {
data.batch.details = details;
}
RequestedCall(void* tag_arg, grpc_completion_queue* call_cq,
grpc_call** call_arg, grpc_metadata_array* initial_md,
RegisteredMethod* rm, gpr_timespec* deadline,
grpc_byte_buffer** optional_payload)
: type(Type::REGISTERED_CALL),
tag(tag_arg),
cq_bound_to_call(call_cq),
call(call_arg),
initial_metadata(initial_md) {
data.registered.method = rm;
data.registered.deadline = deadline;
data.registered.optional_payload = optional_payload;
}
void Complete(NextResult<MessageHandle> payload, ClientMetadata& md) {
Timestamp deadline = GetContext<CallContext>()->deadline();
switch (type) {
case RequestedCall::Type::BATCH_CALL:
GPR_ASSERT(!payload.has_value());
data.batch.details->host =
CSliceRef(md.get_pointer(HttpAuthorityMetadata())->c_slice());
data.batch.details->method =
CSliceRef(md.Take(HttpPathMetadata())->c_slice());
data.batch.details->deadline =
deadline.as_timespec(GPR_CLOCK_MONOTONIC);
break;
case RequestedCall::Type::REGISTERED_CALL:
md.Remove(HttpPathMetadata());
*data.registered.deadline = deadline.as_timespec(GPR_CLOCK_MONOTONIC);
if (data.registered.optional_payload != nullptr) {
if (payload.has_value()) {
auto* sb = payload.value()->payload()->c_slice_buffer();
*data.registered.optional_payload =
grpc_raw_byte_buffer_create(sb->slices, sb->count);
} else {
*data.registered.optional_payload = nullptr;
}
}
break;
default:
GPR_UNREACHABLE_CODE(abort());
}
}
MultiProducerSingleConsumerQueue::Node mpscq_node;
const Type type;
void* const tag;
grpc_completion_queue* const cq_bound_to_call;
grpc_call** const call;
grpc_cq_completion completion;
grpc_metadata_array* const initial_metadata;
union {
struct {
grpc_call_details* details;
} batch;
struct {
RegisteredMethod* method;
gpr_timespec* deadline;
grpc_byte_buffer** optional_payload;
} registered;
} data;
};
// The RealRequestMatcher is an implementation of RequestMatcherInterface that
// actually uses all the features of RequestMatcherInterface: expecting the
// application to explicitly request RPCs and then matching those to incoming
@ -757,7 +789,9 @@ class ChannelBroadcaster {
const grpc_channel_filter Server::kServerTopFilter = {
Server::CallData::StartTransportStreamOpBatch,
Server::ChannelData::MakeCallPromise,
/* init_call: */ nullptr,
[](grpc_channel_element*, CallSpineInterface*) {
// TODO(ctiller): remove the server filter when call-v3 is finalized
},
grpc_channel_next_op,
sizeof(Server::CallData),
Server::CallData::InitCallElement,
@ -1275,16 +1309,31 @@ void Server::ChannelData::InitTransport(RefCountedPtr<Server> server,
}
// Start accept_stream transport op.
grpc_transport_op* op = grpc_make_transport_op(nullptr);
op->set_accept_stream = true;
op->set_accept_stream_fn = AcceptStream;
if (IsRegisteredMethodLookupInTransportEnabled()) {
op->set_registered_method_matcher_fn = [](void* arg,
ClientMetadata* metadata) {
static_cast<ChannelData*>(arg)->SetRegisteredMethodOnMetadata(*metadata);
};
}
// op->set_registered_method_matcher_fn = Registered
op->set_accept_stream_user_data = this;
int accept_stream_types = 0;
if (transport->filter_stack_transport() != nullptr) {
++accept_stream_types;
op->set_accept_stream = true;
op->set_accept_stream_fn = AcceptStream;
if (IsRegisteredMethodLookupInTransportEnabled()) {
op->set_registered_method_matcher_fn = [](void* arg,
ClientMetadata* metadata) {
static_cast<ChannelData*>(arg)->SetRegisteredMethodOnMetadata(
*metadata);
};
}
op->set_accept_stream_user_data = this;
}
if (transport->server_transport() != nullptr) {
++accept_stream_types;
transport->server_transport()->SetAcceptFunction(
[this](ClientMetadata& metadata) {
SetRegisteredMethodOnMetadata(metadata);
auto call = MakeServerCall(server_.get(), channel_.get());
InitCall(call);
return CallInitiator(std::move(call));
});
}
GPR_ASSERT(accept_stream_types == 1);
op->start_connectivity_watch = MakeOrphanable<ConnectivityWatcher>(this);
if (server_->ShutdownCalled()) {
op->disconnect_with_error = GRPC_ERROR_CREATE("Server shutdown");
@ -1368,17 +1417,89 @@ auto CancelledDueToServerShutdown() {
}
} // namespace
void Server::ChannelData::InitCall(RefCountedPtr<CallSpineInterface> call) {
call->SpawnGuarded("request_matcher", [this, call]() {
return TrySeq(
// Wait for initial metadata to pass through all filters
Map(call->client_initial_metadata().receiver.Next(),
[](NextResult<ClientMetadataHandle> md)
-> absl::StatusOr<ClientMetadataHandle> {
if (!md.has_value()) {
return absl::InternalError("Missing metadata");
}
if (!md.value()->get_pointer(HttpPathMetadata())) {
return absl::InternalError("Missing :path header");
}
if (!md.value()->get_pointer(HttpAuthorityMetadata())) {
return absl::InternalError("Missing :authority header");
}
return std::move(*md);
}),
// Match request with requested call
[this, call](ClientMetadataHandle md) {
auto* registered_method = static_cast<RegisteredMethod*>(
md->get(GrpcRegisteredMethod()).value_or(nullptr));
RequestMatcherInterface* rm;
grpc_server_register_method_payload_handling payload_handling =
GRPC_SRM_PAYLOAD_NONE;
if (registered_method == nullptr) {
rm = server_->unregistered_request_matcher_.get();
} else {
rm = registered_method->matcher.get();
}
auto maybe_read_first_message = If(
payload_handling == GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER,
[call]() {
return call->client_to_server_messages().receiver.Next();
},
[]() -> NextResult<MessageHandle> {
return NextResult<MessageHandle>();
});
return TryJoin<absl::StatusOr>(
Map(std::move(maybe_read_first_message),
[](NextResult<MessageHandle> n) {
return ValueOrFailure<NextResult<MessageHandle>>{
std::move(n)};
}),
rm->MatchRequest(cq_idx()), [md = std::move(md)]() mutable {
return ValueOrFailure<ClientMetadataHandle>(std::move(md));
});
},
// Publish call to cq
[](std::tuple<NextResult<MessageHandle>,
RequestMatcherInterface::MatchResult,
ClientMetadataHandle>
r) {
RequestMatcherInterface::MatchResult& mr = std::get<1>(r);
auto md = std::move(std::get<2>(r));
auto* rc = mr.TakeCall();
rc->Complete(std::move(std::get<0>(r)), *md);
auto* call_context = GetContext<CallContext>();
*rc->call = call_context->c_call();
grpc_call_set_completion_queue(call_context->c_call(),
rc->cq_bound_to_call);
call_context->server_call_context()->PublishInitialMetadata(
std::move(md), rc->initial_metadata);
// TODO(ctiller): publish metadata
return Map(WaitForCqEndOp(false, rc->tag, absl::OkStatus(), mr.cq()),
[rc = std::unique_ptr<RequestedCall>(rc)](Empty) {
return absl::OkStatus();
});
});
});
}
ArenaPromise<ServerMetadataHandle> Server::ChannelData::MakeCallPromise(
grpc_channel_element* elem, CallArgs call_args, NextPromiseFactory) {
auto* chand = static_cast<Server::ChannelData*>(elem->channel_data);
auto* server = chand->server_.get();
absl::optional<Slice> path =
call_args.client_initial_metadata->Take(HttpPathMetadata());
if (server->ShutdownCalled()) return CancelledDueToServerShutdown();
auto cleanup_ref =
absl::MakeCleanup([server] { server->ShutdownUnrefOnRequest(); });
if (!server->ShutdownRefOnRequest()) return CancelledDueToServerShutdown();
if (!path.has_value()) {
auto path_ptr =
call_args.client_initial_metadata->get_pointer(HttpPathMetadata());
if (path_ptr == nullptr) {
return [] {
return ServerMetadataFromStatus(
absl::InternalError("Missing :path header"));
@ -1392,7 +1513,6 @@ ArenaPromise<ServerMetadataHandle> Server::ChannelData::MakeCallPromise(
absl::InternalError("Missing :authority header"));
};
}
Timestamp deadline = GetContext<CallContext>()->deadline();
// Find request matcher.
RequestMatcherInterface* matcher;
RegisteredMethod* rm = nullptr;
@ -1402,7 +1522,7 @@ ArenaPromise<ServerMetadataHandle> Server::ChannelData::MakeCallPromise(
.value_or(nullptr));
} else {
rm = chand->GetRegisteredMethod(host_ptr->as_string_view(),
path->as_string_view());
path_ptr->as_string_view());
}
ArenaPromise<absl::StatusOr<NextResult<MessageHandle>>>
maybe_read_first_message([] { return NextResult<MessageHandle>(); });
@ -1439,8 +1559,7 @@ ArenaPromise<ServerMetadataHandle> Server::ChannelData::MakeCallPromise(
return std::make_pair(std::move(*mr), std::move(payload));
});
},
[host_ptr, path = std::move(path), deadline,
call_args =
[call_args =
std::move(call_args)](std::pair<RequestMatcherInterface::MatchResult,
NextResult<MessageHandle>>
r) mutable {
@ -1448,41 +1567,19 @@ ArenaPromise<ServerMetadataHandle> Server::ChannelData::MakeCallPromise(
auto& payload = r.second;
auto* rc = mr.TakeCall();
auto* cq_for_new_request = mr.cq();
switch (rc->type) {
case RequestedCall::Type::BATCH_CALL:
GPR_ASSERT(!payload.has_value());
rc->data.batch.details->host = CSliceRef(host_ptr->c_slice());
rc->data.batch.details->method = CSliceRef(path->c_slice());
rc->data.batch.details->deadline =
deadline.as_timespec(GPR_CLOCK_MONOTONIC);
break;
case RequestedCall::Type::REGISTERED_CALL:
*rc->data.registered.deadline =
deadline.as_timespec(GPR_CLOCK_MONOTONIC);
if (rc->data.registered.optional_payload != nullptr) {
if (payload.has_value()) {
auto* sb = payload.value()->payload()->c_slice_buffer();
*rc->data.registered.optional_payload =
grpc_raw_byte_buffer_create(sb->slices, sb->count);
} else {
*rc->data.registered.optional_payload = nullptr;
}
}
break;
default:
GPR_UNREACHABLE_CODE(abort());
}
return GetContext<CallContext>()
->server_call_context()
->MakeTopOfServerCallPromise(
std::move(call_args), rc->cq_bound_to_call,
rc->initial_metadata,
[rc, cq_for_new_request](grpc_call* call) {
*rc->call = call;
grpc_cq_end_op(cq_for_new_request, rc->tag, absl::OkStatus(),
Server::DoneRequestEvent, rc, &rc->completion,
true);
});
auto* server_call_context =
GetContext<CallContext>()->server_call_context();
rc->Complete(std::move(payload), *call_args.client_initial_metadata);
server_call_context->PublishInitialMetadata(
std::move(call_args.client_initial_metadata), rc->initial_metadata);
return server_call_context->MakeTopOfServerCallPromise(
std::move(call_args), rc->cq_bound_to_call,
[rc, cq_for_new_request](grpc_call* call) {
*rc->call = call;
grpc_cq_end_op(cq_for_new_request, rc->tag, absl::OkStatus(),
Server::DoneRequestEvent, rc, &rc->completion,
true);
});
});
}

@ -239,6 +239,7 @@ class Server : public InternallyRefCounted<Server>,
static void DestroyChannelElement(grpc_channel_element* elem);
static ArenaPromise<ServerMetadataHandle> MakeCallPromise(
grpc_channel_element* elem, CallArgs call_args, NextPromiseFactory);
void InitCall(RefCountedPtr<CallSpineInterface> call);
private:
class ConnectivityWatcher;

@ -0,0 +1,94 @@
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GRPC_SRC_CORE_LIB_SURFACE_WAIT_FOR_CQ_END_OP_H
#define GRPC_SRC_CORE_LIB_SURFACE_WAIT_FOR_CQ_END_OP_H
#include <grpc/support/port_platform.h>
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/promise/activity.h"
#include "src/core/lib/surface/completion_queue.h"
namespace grpc_core {
// Defines a promise that calls grpc_cq_end_op() (on first poll) and then waits
// for the callback supplied to grpc_cq_end_op() to be called, before resolving
// to Empty{}
class WaitForCqEndOp {
public:
WaitForCqEndOp(bool is_closure, void* tag, grpc_error_handle error,
grpc_completion_queue* cq)
: state_{NotStarted{is_closure, tag, std::move(error), cq}} {}
Poll<Empty> operator()() {
if (auto* n = absl::get_if<NotStarted>(&state_)) {
if (n->is_closure) {
ExecCtx::Run(DEBUG_LOCATION, static_cast<grpc_closure*>(n->tag),
std::move(n->error));
return Empty{};
} else {
auto not_started = std::move(*n);
auto& started =
state_.emplace<Started>(Activity::current()->MakeOwningWaker());
grpc_cq_end_op(
not_started.cq, not_started.tag, std::move(not_started.error),
[](void* p, grpc_cq_completion*) {
auto started = static_cast<Started*>(p);
started->done.store(true, std::memory_order_release);
},
&started, &started.completion);
}
}
auto& started = absl::get<Started>(state_);
if (started.done.load(std::memory_order_acquire)) {
return Empty{};
} else {
return Pending{};
}
}
WaitForCqEndOp(const WaitForCqEndOp&) = delete;
WaitForCqEndOp& operator=(const WaitForCqEndOp&) = delete;
WaitForCqEndOp(WaitForCqEndOp&& other) noexcept
: state_(std::move(absl::get<NotStarted>(other.state_))) {
other.state_.emplace<Invalid>();
}
WaitForCqEndOp& operator=(WaitForCqEndOp&& other) noexcept {
state_ = std::move(absl::get<NotStarted>(other.state_));
other.state_.emplace<Invalid>();
return *this;
}
private:
struct NotStarted {
bool is_closure;
void* tag;
grpc_error_handle error;
grpc_completion_queue* cq;
};
struct Started {
explicit Started(Waker waker) : waker(std::move(waker)) {}
Waker waker;
grpc_cq_completion completion;
std::atomic<bool> done{false};
};
struct Invalid {};
using State = absl::variant<NotStarted, Started, Invalid>;
State state_{Invalid{}};
};
} // namespace grpc_core
#endif // GRPC_SRC_CORE_LIB_SURFACE_WAIT_FOR_CQ_END_OP_H

@ -329,6 +329,8 @@ class CallSpineInterface {
class CallSpine final : public CallSpineInterface {
public:
CallSpine() { Crash("unimplemented"); }
Pipe<ClientMetadataHandle>& client_initial_metadata() override {
return client_initial_metadata_;
}
@ -366,7 +368,7 @@ class CallSpine final : public CallSpineInterface {
class CallInitiator {
public:
explicit CallInitiator(RefCountedPtr<CallSpine> spine)
explicit CallInitiator(RefCountedPtr<CallSpineInterface> spine)
: spine_(std::move(spine)) {}
auto PushClientInitialMetadata(ClientMetadataHandle md) {
@ -427,12 +429,12 @@ class CallInitiator {
}
private:
const RefCountedPtr<CallSpine> spine_;
const RefCountedPtr<CallSpineInterface> spine_;
};
class CallHandler {
public:
explicit CallHandler(RefCountedPtr<CallSpine> spine)
explicit CallHandler(RefCountedPtr<CallSpineInterface> spine)
: spine_(std::move(spine)) {}
auto PullClientInitialMetadata() {
@ -453,7 +455,7 @@ class CallHandler {
auto PushServerTrailingMetadata(ClientMetadataHandle md) {
GPR_DEBUG_ASSERT(Activity::current() == &spine_->party());
return Map(spine_->server_initial_metadata().sender.Push(std::move(md)),
return Map(spine_->server_trailing_metadata().sender.Push(std::move(md)),
[](bool ok) { return StatusFlag(ok); });
}
@ -488,7 +490,7 @@ class CallHandler {
}
private:
const RefCountedPtr<CallSpine> spine_;
const RefCountedPtr<CallSpineInterface> spine_;
};
struct CallInitiatorAndHandler {

@ -48,17 +48,23 @@ namespace experimental {
// CsmObservabilityBuilder
//
CsmObservabilityBuilder::CsmObservabilityBuilder()
: builder_(
std::make_unique<grpc::internal::OpenTelemetryPluginBuilderImpl>()) {}
CsmObservabilityBuilder::~CsmObservabilityBuilder() = default;
CsmObservabilityBuilder& CsmObservabilityBuilder::SetMeterProvider(
std::shared_ptr<opentelemetry::sdk::metrics::MeterProvider>
meter_provider) {
builder_.SetMeterProvider(meter_provider);
builder_->SetMeterProvider(meter_provider);
return *this;
}
CsmObservabilityBuilder& CsmObservabilityBuilder::SetTargetAttributeFilter(
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_attribute_filter) {
builder_.SetTargetAttributeFilter(std::move(target_attribute_filter));
builder_->SetTargetAttributeFilter(std::move(target_attribute_filter));
return *this;
}
@ -66,22 +72,22 @@ CsmObservabilityBuilder&
CsmObservabilityBuilder::SetGenericMethodAttributeFilter(
absl::AnyInvocable<bool(absl::string_view /*generic_method*/) const>
generic_method_attribute_filter) {
builder_.SetGenericMethodAttributeFilter(
builder_->SetGenericMethodAttributeFilter(
std::move(generic_method_attribute_filter));
return *this;
}
absl::StatusOr<CsmObservability> CsmObservabilityBuilder::BuildAndRegister() {
builder_.SetServerSelector([](const grpc_core::ChannelArgs& args) {
builder_->SetServerSelector([](const grpc_core::ChannelArgs& args) {
return args.GetBool(GRPC_ARG_XDS_ENABLED_SERVER).value_or(false);
});
builder_.SetTargetSelector(internal::CsmChannelTargetSelector);
builder_.SetLabelsInjector(
builder_->SetTargetSelector(internal::CsmChannelTargetSelector);
builder_->SetLabelsInjector(
std::make_unique<internal::ServiceMeshLabelsInjector>(
google::cloud::otel::MakeResourceDetector()
->Detect()
.GetAttributes()));
builder_.BuildAndRegisterGlobal();
builder_->BuildAndRegisterGlobal();
return CsmObservability();
}

@ -41,6 +41,7 @@ grpc_cc_library(
"otel_client_filter.h",
"otel_plugin.h",
"otel_server_call_tracer.h",
"//:include/grpcpp/ext/otel_plugin.h",
],
external_deps = [
"absl/base:core_headers",

@ -38,7 +38,7 @@
namespace grpc {
namespace internal {
inline opentelemetry::nostd::string_view AbslStrViewToOTelStrView(
inline opentelemetry::nostd::string_view AbslStrViewToOpenTelemetryStrView(
absl::string_view str) {
return opentelemetry::nostd::string_view(str.data(), str.size());
}
@ -62,15 +62,15 @@ class KeyValueIterable : public opentelemetry::common::KeyValueIterable {
if (injected_labels_iterable_ != nullptr) {
injected_labels_iterable_->ResetIteratorPosition();
while (const auto& pair = injected_labels_iterable_->Next()) {
if (!callback(AbslStrViewToOTelStrView(pair->first),
AbslStrViewToOTelStrView(pair->second))) {
if (!callback(AbslStrViewToOpenTelemetryStrView(pair->first),
AbslStrViewToOpenTelemetryStrView(pair->second))) {
return false;
}
}
}
for (const auto& pair : additional_labels_) {
if (!callback(AbslStrViewToOTelStrView(pair.first),
AbslStrViewToOTelStrView(pair.second))) {
if (!callback(AbslStrViewToOpenTelemetryStrView(pair.first),
AbslStrViewToOpenTelemetryStrView(pair.second))) {
return false;
}
}

@ -76,8 +76,8 @@ absl::StatusOr<OpenTelemetryClientFilter> OpenTelemetryClientFilter::Create(
std::string target = args.GetOwnedString(GRPC_ARG_SERVER_URI).value_or("");
// Use the original target string only if a filter on the attribute is not
// registered or if the filter returns true, otherwise use "other".
if (OTelPluginState().target_attribute_filter == nullptr ||
OTelPluginState().target_attribute_filter(target)) {
if (OpenTelemetryPluginState().target_attribute_filter == nullptr ||
OpenTelemetryPluginState().target_attribute_filter(target)) {
return OpenTelemetryClientFilter(std::move(target));
}
return OpenTelemetryClientFilter("other");
@ -116,13 +116,14 @@ OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::
: parent_(parent),
arena_allocated_(arena_allocated),
start_time_(absl::Now()) {
if (OTelPluginState().client.attempt.started != nullptr) {
if (OpenTelemetryPluginState().client.attempt.started != nullptr) {
std::array<std::pair<absl::string_view, absl::string_view>, 2>
additional_labels = {{{OTelMethodKey(), parent_->MethodForStats()},
{OTelTargetKey(), parent_->parent_->target()}}};
additional_labels = {
{{OpenTelemetryMethodKey(), parent_->MethodForStats()},
{OpenTelemetryTargetKey(), parent_->parent_->target()}}};
// We might not have all the injected labels that we want at this point, so
// avoid recording a subset of injected labels here.
OTelPluginState().client.attempt.started->Add(
OpenTelemetryPluginState().client.attempt.started->Add(
1, KeyValueIterable(/*injected_labels_iterable=*/nullptr,
additional_labels));
}
@ -130,17 +131,17 @@ OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::
RecordReceivedInitialMetadata(grpc_metadata_batch* recv_initial_metadata) {
if (OTelPluginState().labels_injector != nullptr) {
injected_labels_ =
OTelPluginState().labels_injector->GetLabels(recv_initial_metadata);
if (OpenTelemetryPluginState().labels_injector != nullptr) {
injected_labels_ = OpenTelemetryPluginState().labels_injector->GetLabels(
recv_initial_metadata);
}
}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::
RecordSendInitialMetadata(grpc_metadata_batch* send_initial_metadata) {
if (OTelPluginState().labels_injector != nullptr) {
OTelPluginState().labels_injector->AddLabels(send_initial_metadata,
nullptr);
if (OpenTelemetryPluginState().labels_injector != nullptr) {
OpenTelemetryPluginState().labels_injector->AddLabels(send_initial_metadata,
nullptr);
}
}
@ -175,32 +176,35 @@ void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::
absl::Status status, grpc_metadata_batch* /*recv_trailing_metadata*/,
const grpc_transport_stream_stats* transport_stream_stats) {
std::array<std::pair<absl::string_view, absl::string_view>, 3>
additional_labels = {{{OTelMethodKey(), parent_->MethodForStats()},
{OTelTargetKey(), parent_->parent_->target()},
{OTelStatusKey(), grpc_status_code_to_string(
static_cast<grpc_status_code>(
status.code()))}}};
additional_labels = {
{{OpenTelemetryMethodKey(), parent_->MethodForStats()},
{OpenTelemetryTargetKey(), parent_->parent_->target()},
{OpenTelemetryStatusKey(),
grpc_status_code_to_string(
static_cast<grpc_status_code>(status.code()))}}};
KeyValueIterable labels(injected_labels_.get(), additional_labels);
if (OTelPluginState().client.attempt.duration != nullptr) {
OTelPluginState().client.attempt.duration->Record(
if (OpenTelemetryPluginState().client.attempt.duration != nullptr) {
OpenTelemetryPluginState().client.attempt.duration->Record(
absl::ToDoubleSeconds(absl::Now() - start_time_), labels,
opentelemetry::context::Context{});
}
if (OTelPluginState().client.attempt.sent_total_compressed_message_size !=
nullptr) {
OTelPluginState().client.attempt.sent_total_compressed_message_size->Record(
transport_stream_stats != nullptr
? transport_stream_stats->outgoing.data_bytes
: 0,
labels, opentelemetry::context::Context{});
if (OpenTelemetryPluginState()
.client.attempt.sent_total_compressed_message_size != nullptr) {
OpenTelemetryPluginState()
.client.attempt.sent_total_compressed_message_size->Record(
transport_stream_stats != nullptr
? transport_stream_stats->outgoing.data_bytes
: 0,
labels, opentelemetry::context::Context{});
}
if (OTelPluginState().client.attempt.rcvd_total_compressed_message_size !=
nullptr) {
OTelPluginState().client.attempt.rcvd_total_compressed_message_size->Record(
transport_stream_stats != nullptr
? transport_stream_stats->incoming.data_bytes
: 0,
labels, opentelemetry::context::Context{});
if (OpenTelemetryPluginState()
.client.attempt.rcvd_total_compressed_message_size != nullptr) {
OpenTelemetryPluginState()
.client.attempt.rcvd_total_compressed_message_size->Record(
transport_stream_stats != nullptr
? transport_stream_stats->incoming.data_bytes
: 0,
labels, opentelemetry::context::Context{});
}
}
@ -273,8 +277,8 @@ OpenTelemetryCallTracer::StartNewAttempt(bool is_transparent_retry) {
absl::string_view OpenTelemetryCallTracer::MethodForStats() const {
absl::string_view method = absl::StripPrefix(path_.as_string_view(), "/");
if (registered_method_ ||
(OTelPluginState().generic_method_attribute_filter != nullptr &&
OTelPluginState().generic_method_attribute_filter(method))) {
(OpenTelemetryPluginState().generic_method_attribute_filter != nullptr &&
OpenTelemetryPluginState().generic_method_attribute_filter(method))) {
return method;
}
return "other";

@ -29,6 +29,7 @@
#include "opentelemetry/nostd/unique_ptr.h"
#include <grpc/support/log.h>
#include <grpcpp/ext/otel_plugin.h>
#include <grpcpp/version_info.h>
#include "src/core/ext/filters/client_channel/client_channel.h"
@ -42,199 +43,194 @@
namespace grpc {
namespace internal {
// TODO(yashykt): Extend this to allow multiple OTel plugins to be registered in
// the same binary.
struct OTelPluginState* g_otel_plugin_state_;
// TODO(yashykt): Extend this to allow multiple OpenTelemetry plugins to be
// registered in the same binary.
struct OpenTelemetryPluginState* g_otel_plugin_state_;
const struct OTelPluginState& OTelPluginState() {
const struct OpenTelemetryPluginState& OpenTelemetryPluginState() {
GPR_DEBUG_ASSERT(g_otel_plugin_state_ != nullptr);
return *g_otel_plugin_state_;
}
absl::string_view OTelMethodKey() { return "grpc.method"; }
absl::string_view OpenTelemetryMethodKey() { return "grpc.method"; }
absl::string_view OTelStatusKey() { return "grpc.status"; }
absl::string_view OpenTelemetryStatusKey() { return "grpc.status"; }
absl::string_view OTelTargetKey() { return "grpc.target"; }
absl::string_view OTelClientAttemptStartedInstrumentName() {
return "grpc.client.attempt.started";
}
absl::string_view OTelClientAttemptDurationInstrumentName() {
return "grpc.client.attempt.duration";
}
absl::string_view
OTelClientAttemptSentTotalCompressedMessageSizeInstrumentName() {
return "grpc.client.attempt.sent_total_compressed_message_size";
}
absl::string_view
OTelClientAttemptRcvdTotalCompressedMessageSizeInstrumentName() {
return "grpc.client.attempt.rcvd_total_compressed_message_size";
}
absl::string_view OTelServerCallStartedInstrumentName() {
return "grpc.server.call.started";
}
absl::string_view OTelServerCallDurationInstrumentName() {
return "grpc.server.call.duration";
}
absl::string_view OTelServerCallSentTotalCompressedMessageSizeInstrumentName() {
return "grpc.server.call.sent_total_compressed_message_size";
}
absl::string_view OTelServerCallRcvdTotalCompressedMessageSizeInstrumentName() {
return "grpc.server.call.rcvd_total_compressed_message_size";
}
absl::string_view OpenTelemetryTargetKey() { return "grpc.target"; }
namespace {
absl::flat_hash_set<std::string> BaseMetrics() {
return absl::flat_hash_set<std::string>{
std::string(OTelClientAttemptStartedInstrumentName()),
std::string(OTelClientAttemptDurationInstrumentName()),
std::string(grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName),
std::string(grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptDurationInstrumentName),
std::string(
OTelClientAttemptSentTotalCompressedMessageSizeInstrumentName()),
grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptSentTotalCompressedMessageSizeInstrumentName),
std::string(
OTelClientAttemptRcvdTotalCompressedMessageSizeInstrumentName()),
std::string(OTelServerCallStartedInstrumentName()),
std::string(OTelServerCallDurationInstrumentName()),
std::string(OTelServerCallSentTotalCompressedMessageSizeInstrumentName()),
std::string(
OTelServerCallRcvdTotalCompressedMessageSizeInstrumentName())};
grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptRcvdTotalCompressedMessageSizeInstrumentName),
std::string(grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallStartedInstrumentName),
std::string(grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallDurationInstrumentName),
std::string(grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallSentTotalCompressedMessageSizeInstrumentName),
std::string(grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallRcvdTotalCompressedMessageSizeInstrumentName)};
}
} // namespace
//
// OpenTelemetryPluginBuilder
// OpenTelemetryPluginBuilderImpl
//
OpenTelemetryPluginBuilder::OpenTelemetryPluginBuilder()
OpenTelemetryPluginBuilderImpl::OpenTelemetryPluginBuilderImpl()
: metrics_(BaseMetrics()) {}
OpenTelemetryPluginBuilder& OpenTelemetryPluginBuilder::SetMeterProvider(
OpenTelemetryPluginBuilderImpl&
OpenTelemetryPluginBuilderImpl::SetMeterProvider(
std::shared_ptr<opentelemetry::metrics::MeterProvider> meter_provider) {
meter_provider_ = std::move(meter_provider);
return *this;
}
OpenTelemetryPluginBuilder& OpenTelemetryPluginBuilder::EnableMetric(
OpenTelemetryPluginBuilderImpl& OpenTelemetryPluginBuilderImpl::EnableMetric(
absl::string_view metric_name) {
metrics_.emplace(metric_name);
return *this;
}
OpenTelemetryPluginBuilder& OpenTelemetryPluginBuilder::DisableMetric(
OpenTelemetryPluginBuilderImpl& OpenTelemetryPluginBuilderImpl::DisableMetric(
absl::string_view metric_name) {
metrics_.erase(metric_name);
return *this;
}
OpenTelemetryPluginBuilder& OpenTelemetryPluginBuilder::DisableAllMetrics() {
OpenTelemetryPluginBuilderImpl&
OpenTelemetryPluginBuilderImpl::DisableAllMetrics() {
metrics_.clear();
return *this;
}
OpenTelemetryPluginBuilder& OpenTelemetryPluginBuilder::SetLabelsInjector(
OpenTelemetryPluginBuilderImpl&
OpenTelemetryPluginBuilderImpl::SetLabelsInjector(
std::unique_ptr<LabelsInjector> labels_injector) {
labels_injector_ = std::move(labels_injector);
return *this;
}
OpenTelemetryPluginBuilder& OpenTelemetryPluginBuilder::SetTargetSelector(
OpenTelemetryPluginBuilderImpl&
OpenTelemetryPluginBuilderImpl::SetTargetSelector(
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_selector) {
target_selector_ = std::move(target_selector);
return *this;
}
OpenTelemetryPluginBuilder&
OpenTelemetryPluginBuilder::SetTargetAttributeFilter(
OpenTelemetryPluginBuilderImpl&
OpenTelemetryPluginBuilderImpl::SetTargetAttributeFilter(
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_attribute_filter) {
target_attribute_filter_ = std::move(target_attribute_filter);
return *this;
}
OpenTelemetryPluginBuilder&
OpenTelemetryPluginBuilder::SetGenericMethodAttributeFilter(
OpenTelemetryPluginBuilderImpl&
OpenTelemetryPluginBuilderImpl::SetGenericMethodAttributeFilter(
absl::AnyInvocable<bool(absl::string_view /*generic_method*/) const>
generic_method_attribute_filter) {
generic_method_attribute_filter_ = std::move(generic_method_attribute_filter);
return *this;
}
OpenTelemetryPluginBuilder& OpenTelemetryPluginBuilder::SetServerSelector(
OpenTelemetryPluginBuilderImpl&
OpenTelemetryPluginBuilderImpl::SetServerSelector(
absl::AnyInvocable<bool(const grpc_core::ChannelArgs& /*args*/) const>
server_selector) {
server_selector_ = std::move(server_selector);
return *this;
}
void OpenTelemetryPluginBuilder::BuildAndRegisterGlobal() {
void OpenTelemetryPluginBuilderImpl::BuildAndRegisterGlobal() {
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::MeterProvider>
meter_provider = meter_provider_;
delete g_otel_plugin_state_;
g_otel_plugin_state_ = new struct OTelPluginState;
g_otel_plugin_state_ = new struct OpenTelemetryPluginState;
if (meter_provider == nullptr) {
return;
}
auto meter = meter_provider->GetMeter("grpc-c++", GRPC_CPP_VERSION_STRING);
if (metrics_.contains(OTelClientAttemptStartedInstrumentName())) {
if (metrics_.contains(grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName)) {
g_otel_plugin_state_->client.attempt.started = meter->CreateUInt64Counter(
std::string(OTelClientAttemptStartedInstrumentName()),
std::string(grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName),
"Number of client call attempts started", "{attempt}");
}
if (metrics_.contains(OTelClientAttemptDurationInstrumentName())) {
if (metrics_.contains(grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptDurationInstrumentName)) {
g_otel_plugin_state_->client.attempt.duration =
meter->CreateDoubleHistogram(
std::string(OTelClientAttemptDurationInstrumentName()),
std::string(grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptDurationInstrumentName),
"End-to-end time taken to complete a client call attempt", "s");
}
if (metrics_.contains(
OTelClientAttemptSentTotalCompressedMessageSizeInstrumentName())) {
g_otel_plugin_state_->client.attempt
.sent_total_compressed_message_size = meter->CreateUInt64Histogram(
std::string(
OTelClientAttemptSentTotalCompressedMessageSizeInstrumentName()),
"Compressed message bytes sent per client call attempt", "By");
grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptSentTotalCompressedMessageSizeInstrumentName)) {
g_otel_plugin_state_->client.attempt.sent_total_compressed_message_size =
meter->CreateUInt64Histogram(
std::string(
grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptSentTotalCompressedMessageSizeInstrumentName),
"Compressed message bytes sent per client call attempt", "By");
}
if (metrics_.contains(
OTelClientAttemptRcvdTotalCompressedMessageSizeInstrumentName())) {
g_otel_plugin_state_->client.attempt
.rcvd_total_compressed_message_size = meter->CreateUInt64Histogram(
std::string(
OTelClientAttemptRcvdTotalCompressedMessageSizeInstrumentName()),
"Compressed message bytes received per call attempt", "By");
grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptRcvdTotalCompressedMessageSizeInstrumentName)) {
g_otel_plugin_state_->client.attempt.rcvd_total_compressed_message_size =
meter->CreateUInt64Histogram(
std::string(
grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptRcvdTotalCompressedMessageSizeInstrumentName),
"Compressed message bytes received per call attempt", "By");
}
if (metrics_.contains(OTelServerCallStartedInstrumentName())) {
if (metrics_.contains(grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallStartedInstrumentName)) {
g_otel_plugin_state_->server.call.started = meter->CreateUInt64Counter(
std::string(OTelServerCallStartedInstrumentName()),
std::string(grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallStartedInstrumentName),
"Number of server calls started", "{call}");
}
if (metrics_.contains(OTelServerCallDurationInstrumentName())) {
if (metrics_.contains(grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallDurationInstrumentName)) {
g_otel_plugin_state_->server.call.duration = meter->CreateDoubleHistogram(
std::string(OTelServerCallDurationInstrumentName()),
std::string(grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallDurationInstrumentName),
"End-to-end time taken to complete a call from server transport's "
"perspective",
"s");
}
if (metrics_.contains(
OTelServerCallSentTotalCompressedMessageSizeInstrumentName())) {
grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallSentTotalCompressedMessageSizeInstrumentName)) {
g_otel_plugin_state_->server.call.sent_total_compressed_message_size =
meter->CreateUInt64Histogram(
std::string(
OTelServerCallSentTotalCompressedMessageSizeInstrumentName()),
grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallSentTotalCompressedMessageSizeInstrumentName),
"Compressed message bytes sent per server call", "By");
}
if (metrics_.contains(
OTelServerCallRcvdTotalCompressedMessageSizeInstrumentName())) {
grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallRcvdTotalCompressedMessageSizeInstrumentName)) {
g_otel_plugin_state_->server.call.rcvd_total_compressed_message_size =
meter->CreateUInt64Histogram(
std::string(
OTelServerCallRcvdTotalCompressedMessageSizeInstrumentName()),
grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallRcvdTotalCompressedMessageSizeInstrumentName),
"Compressed message bytes received per server call", "By");
}
g_otel_plugin_state_->labels_injector = std::move(labels_injector_);
@ -264,4 +260,59 @@ void OpenTelemetryPluginBuilder::BuildAndRegisterGlobal() {
}
} // namespace internal
namespace experimental {
constexpr absl::string_view
OpenTelemetryPluginBuilder::kClientAttemptStartedInstrumentName;
constexpr absl::string_view
OpenTelemetryPluginBuilder::kClientAttemptDurationInstrumentName;
constexpr absl::string_view OpenTelemetryPluginBuilder::
kClientAttemptSentTotalCompressedMessageSizeInstrumentName;
constexpr absl::string_view OpenTelemetryPluginBuilder::
kClientAttemptRcvdTotalCompressedMessageSizeInstrumentName;
constexpr absl::string_view
OpenTelemetryPluginBuilder::kServerCallStartedInstrumentName;
constexpr absl::string_view
OpenTelemetryPluginBuilder::kServerCallDurationInstrumentName;
constexpr absl::string_view OpenTelemetryPluginBuilder::
kServerCallSentTotalCompressedMessageSizeInstrumentName;
constexpr absl::string_view OpenTelemetryPluginBuilder::
kServerCallRcvdTotalCompressedMessageSizeInstrumentName;
//
// OpenTelemetryPluginBuilder
//
OpenTelemetryPluginBuilder::OpenTelemetryPluginBuilder()
: impl_(std::make_unique<internal::OpenTelemetryPluginBuilderImpl>()) {}
OpenTelemetryPluginBuilder& OpenTelemetryPluginBuilder::SetMeterProvider(
std::shared_ptr<opentelemetry::metrics::MeterProvider> meter_provider) {
impl_->SetMeterProvider(std::move(meter_provider));
return *this;
}
OpenTelemetryPluginBuilder&
OpenTelemetryPluginBuilder::SetTargetAttributeFilter(
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_attribute_filter) {
impl_->SetTargetAttributeFilter(std::move(target_attribute_filter));
return *this;
}
OpenTelemetryPluginBuilder&
OpenTelemetryPluginBuilder::SetGenericMethodAttributeFilter(
absl::AnyInvocable<bool(absl::string_view /*generic_method*/) const>
generic_method_attribute_filter) {
impl_->SetGenericMethodAttributeFilter(
std::move(generic_method_attribute_filter));
return *this;
}
void OpenTelemetryPluginBuilder::BuildAndRegisterGlobal() {
impl_->BuildAndRegisterGlobal();
}
} // namespace experimental
} // namespace grpc

@ -43,7 +43,7 @@ namespace grpc {
namespace internal {
// An iterable container interface that can be used as a return type for the
// OTel plugin's label injector.
// OpenTelemetry plugin's label injector.
class LabelsIterable {
public:
virtual ~LabelsIterable() = default;
@ -60,7 +60,7 @@ class LabelsIterable {
};
// An interface that allows you to add additional labels on the calls traced
// through the OTel plugin.
// through the OpenTelemetry plugin.
class LabelsInjector {
public:
virtual ~LabelsInjector() {}
@ -77,7 +77,7 @@ class LabelsInjector {
LabelsIterable* labels_from_incoming_metadata) = 0;
};
struct OTelPluginState {
struct OpenTelemetryPluginState {
struct Client {
struct Attempt {
std::unique_ptr<opentelemetry::metrics::Counter<uint64_t>> started;
@ -109,33 +109,21 @@ struct OTelPluginState {
server_selector;
};
const struct OTelPluginState& OTelPluginState();
const struct OpenTelemetryPluginState& OpenTelemetryPluginState();
// Tags
absl::string_view OTelMethodKey();
absl::string_view OTelStatusKey();
absl::string_view OTelTargetKey();
// Metrics
absl::string_view OTelClientAttemptStartedInstrumentName();
absl::string_view OTelClientAttemptDurationInstrumentName();
absl::string_view
OTelClientAttemptSentTotalCompressedMessageSizeInstrumentName();
absl::string_view
OTelClientAttemptRcvdTotalCompressedMessageSizeInstrumentName();
absl::string_view OTelServerCallStartedInstrumentName();
absl::string_view OTelServerCallDurationInstrumentName();
absl::string_view OTelServerCallSentTotalCompressedMessageSizeInstrumentName();
absl::string_view OTelServerCallRcvdTotalCompressedMessageSizeInstrumentName();
class OpenTelemetryPluginBuilder {
absl::string_view OpenTelemetryMethodKey();
absl::string_view OpenTelemetryStatusKey();
absl::string_view OpenTelemetryTargetKey();
class OpenTelemetryPluginBuilderImpl {
public:
OpenTelemetryPluginBuilder();
OpenTelemetryPluginBuilderImpl();
// If `SetMeterProvider()` is not called, no metrics are collected.
OpenTelemetryPluginBuilder& SetMeterProvider(
OpenTelemetryPluginBuilderImpl& SetMeterProvider(
std::shared_ptr<opentelemetry::metrics::MeterProvider> meter_provider);
// Methods to manipulate which instruments are enabled in the OTel Stats
// Plugin. The default set of instruments are -
// Methods to manipulate which instruments are enabled in the OpenTelemetry
// Stats Plugin. The default set of instruments are -
// grpc.client.attempt.started
// grpc.client.attempt.duration
// grpc.client.attempt.sent_total_compressed_message_size
@ -144,22 +132,22 @@ class OpenTelemetryPluginBuilder {
// grpc.server.call.duration
// grpc.server.call.sent_total_compressed_message_size
// grpc.server.call.rcvd_total_compressed_message_size
OpenTelemetryPluginBuilder& EnableMetric(absl::string_view metric_name);
OpenTelemetryPluginBuilder& DisableMetric(absl::string_view metric_name);
OpenTelemetryPluginBuilder& DisableAllMetrics();
OpenTelemetryPluginBuilderImpl& EnableMetric(absl::string_view metric_name);
OpenTelemetryPluginBuilderImpl& DisableMetric(absl::string_view metric_name);
OpenTelemetryPluginBuilderImpl& DisableAllMetrics();
// Allows setting a labels injector on calls traced through this plugin.
OpenTelemetryPluginBuilder& SetLabelsInjector(
OpenTelemetryPluginBuilderImpl& SetLabelsInjector(
std::unique_ptr<LabelsInjector> labels_injector);
// If set, \a target_selector is called per channel to decide whether to
// collect metrics on that target or not.
OpenTelemetryPluginBuilder& SetTargetSelector(
OpenTelemetryPluginBuilderImpl& SetTargetSelector(
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_selector);
// If set, \a server_selector is called per incoming call on the server
// to decide whether to collect metrics on that call or not.
// TODO(yashkt): We should only need to do this per server connection or even
// per server. Change this when we have a ServerTracer.
OpenTelemetryPluginBuilder& SetServerSelector(
OpenTelemetryPluginBuilderImpl& SetServerSelector(
absl::AnyInvocable<bool(const grpc_core::ChannelArgs& /*args*/) const>
server_selector);
// If set, \a target_attribute_filter is called per channel to decide whether
@ -167,7 +155,7 @@ class OpenTelemetryPluginBuilder {
// This helps reduce the cardinality on metrics in cases where many channels
// are created with different targets in the same binary (which might happen
// for example, if the channel target string uses IP addresses directly).
OpenTelemetryPluginBuilder& SetTargetAttributeFilter(
OpenTelemetryPluginBuilderImpl& SetTargetAttributeFilter(
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_attribute_filter);
// If set, \a generic_method_attribute_filter is called per call with a
@ -175,7 +163,7 @@ class OpenTelemetryPluginBuilder {
// replace it with "other". Non-generic or pre-registered methods remain
// unaffected. If not set, by default, generic method names are replaced with
// "other" when recording metrics.
OpenTelemetryPluginBuilder& SetGenericMethodAttributeFilter(
OpenTelemetryPluginBuilderImpl& SetGenericMethodAttributeFilter(
absl::AnyInvocable<bool(absl::string_view /*generic_method*/) const>
generic_method_attribute_filter);
void BuildAndRegisterGlobal();

@ -77,9 +77,9 @@ class OpenTelemetryServerCallTracer : public grpc_core::ServerCallTracer {
// arguments.
void RecordSendInitialMetadata(
grpc_metadata_batch* send_initial_metadata) override {
if (OTelPluginState().labels_injector != nullptr) {
OTelPluginState().labels_injector->AddLabels(send_initial_metadata,
injected_labels_.get());
if (OpenTelemetryPluginState().labels_injector != nullptr) {
OpenTelemetryPluginState().labels_injector->AddLabels(
send_initial_metadata, injected_labels_.get());
}
}
@ -135,8 +135,9 @@ class OpenTelemetryServerCallTracer : public grpc_core::ServerCallTracer {
absl::string_view MethodForStats() const {
absl::string_view method = absl::StripPrefix(path_.as_string_view(), "/");
if (registered_method_ ||
(OTelPluginState().generic_method_attribute_filter != nullptr &&
OTelPluginState().generic_method_attribute_filter(method))) {
(OpenTelemetryPluginState().generic_method_attribute_filter !=
nullptr &&
OpenTelemetryPluginState().generic_method_attribute_filter(method))) {
return method;
}
return "other";
@ -153,19 +154,19 @@ void OpenTelemetryServerCallTracer::RecordReceivedInitialMetadata(
grpc_metadata_batch* recv_initial_metadata) {
path_ =
recv_initial_metadata->get_pointer(grpc_core::HttpPathMetadata())->Ref();
if (OTelPluginState().labels_injector != nullptr) {
injected_labels_ =
OTelPluginState().labels_injector->GetLabels(recv_initial_metadata);
if (OpenTelemetryPluginState().labels_injector != nullptr) {
injected_labels_ = OpenTelemetryPluginState().labels_injector->GetLabels(
recv_initial_metadata);
}
registered_method_ =
recv_initial_metadata->get(grpc_core::GrpcRegisteredMethod())
.value_or(nullptr) != nullptr;
std::array<std::pair<absl::string_view, absl::string_view>, 1>
additional_labels = {{{OTelMethodKey(), MethodForStats()}}};
if (OTelPluginState().server.call.started != nullptr) {
additional_labels = {{{OpenTelemetryMethodKey(), MethodForStats()}}};
if (OpenTelemetryPluginState().server.call.started != nullptr) {
// We might not have all the injected labels that we want at this point, so
// avoid recording a subset of injected labels here.
OTelPluginState().server.call.started->Add(
OpenTelemetryPluginState().server.call.started->Add(
1, KeyValueIterable(/*injected_labels_iterable=*/nullptr,
additional_labels));
}
@ -181,26 +182,29 @@ void OpenTelemetryServerCallTracer::RecordSendTrailingMetadata(
void OpenTelemetryServerCallTracer::RecordEnd(
const grpc_call_final_info* final_info) {
std::array<std::pair<absl::string_view, absl::string_view>, 2>
additional_labels = {{{OTelMethodKey(), MethodForStats()},
{OTelStatusKey(), grpc_status_code_to_string(
final_info->final_status)}}};
additional_labels = {
{{OpenTelemetryMethodKey(), MethodForStats()},
{OpenTelemetryStatusKey(),
grpc_status_code_to_string(final_info->final_status)}}};
KeyValueIterable labels(injected_labels_.get(), additional_labels);
if (OTelPluginState().server.call.duration != nullptr) {
OTelPluginState().server.call.duration->Record(
if (OpenTelemetryPluginState().server.call.duration != nullptr) {
OpenTelemetryPluginState().server.call.duration->Record(
absl::ToDoubleSeconds(elapsed_time_), labels,
opentelemetry::context::Context{});
}
if (OTelPluginState().server.call.sent_total_compressed_message_size !=
nullptr) {
OTelPluginState().server.call.sent_total_compressed_message_size->Record(
final_info->stats.transport_stream_stats.outgoing.data_bytes, labels,
opentelemetry::context::Context{});
if (OpenTelemetryPluginState()
.server.call.sent_total_compressed_message_size != nullptr) {
OpenTelemetryPluginState()
.server.call.sent_total_compressed_message_size->Record(
final_info->stats.transport_stream_stats.outgoing.data_bytes,
labels, opentelemetry::context::Context{});
}
if (OTelPluginState().server.call.rcvd_total_compressed_message_size !=
nullptr) {
OTelPluginState().server.call.rcvd_total_compressed_message_size->Record(
final_info->stats.transport_stream_stats.incoming.data_bytes, labels,
opentelemetry::context::Context{});
if (OpenTelemetryPluginState()
.server.call.rcvd_total_compressed_message_size != nullptr) {
OpenTelemetryPluginState()
.server.call.rcvd_total_compressed_message_size->Record(
final_info->stats.transport_stream_stats.incoming.data_bytes,
labels, opentelemetry::context::Context{});
}
}
@ -220,8 +224,8 @@ bool OpenTelemetryServerCallTracerFactory::IsServerTraced(
const grpc_core::ChannelArgs& args) {
// Return true only if there is no server selector registered or if the server
// selector returns true.
return OTelPluginState().server_selector == nullptr ||
OTelPluginState().server_selector(args);
return OpenTelemetryPluginState().server_selector == nullptr ||
OpenTelemetryPluginState().server_selector(args);
}
} // namespace internal

@ -28,8 +28,9 @@ TEST(StatusFlagTest, Basics) {
EXPECT_FALSE(StatusCast<absl::Status>(StatusFlag(false)).ok());
EXPECT_TRUE(ValueOrFailure<int>(42).ok());
EXPECT_FALSE(ValueOrFailure<int>(Failure{}).ok());
EXPECT_TRUE(StatusCast<absl::Status>(ValueOrFailure<int>(42)).ok());
EXPECT_FALSE(StatusCast<absl::Status>(ValueOrFailure<int>(Failure{})).ok());
EXPECT_TRUE(StatusCast<absl::Status>(ValueOrFailure<int>(42).status()).ok());
EXPECT_FALSE(
StatusCast<absl::Status>(ValueOrFailure<int>(Failure{}).status()).ok());
EXPECT_EQ(ValueOrFailure<int>(42).value(), 42);
EXPECT_EQ(StatusCast<absl::StatusOr<int>>(ValueOrFailure<int>(42)).value(),
42);

@ -23,64 +23,134 @@
namespace grpc_core {
template <typename T>
using P = std::function<Poll<absl::StatusOr<T>>()>;
struct AbslStatusTraits {
template <typename... Promises>
static auto TryJoinImpl(Promises... promises) {
return TryJoin<absl::StatusOr>(std::move(promises)...);
}
template <typename T>
using Promise = std::function<Poll<absl::StatusOr<T>>()>;
template <typename T>
static Promise<T> instant_ok(T x) {
return [x] { return absl::StatusOr<T>(x); };
}
static auto instant_ok_status() {
return [] { return absl::OkStatus(); };
}
template <typename T>
static Promise<T> instant_fail() {
return [] { return absl::StatusOr<T>(); };
}
template <typename... T>
static Poll<absl::StatusOr<std::tuple<T...>>> ok(T... x) {
return absl::StatusOr<std::tuple<T...>>(absl::in_place, x...);
}
template <typename... T>
static Poll<absl::StatusOr<std::tuple<T...>>> fail() {
return absl::StatusOr<std::tuple<T...>>();
}
template <typename T>
static Promise<T> pending() {
return []() -> Poll<absl::StatusOr<T>> { return Pending(); };
}
};
struct ValueOrFailureTraits {
template <typename... Promises>
static auto TryJoinImpl(Promises... promises) {
return TryJoin<ValueOrFailure>(std::move(promises)...);
}
template <typename T>
using Promise = std::function<Poll<ValueOrFailure<T>>()>;
template <typename T>
static Promise<T> instant_ok(T x) {
return [x] { return ValueOrFailure<T>(x); };
}
static auto instant_ok_status() {
return [] { return StatusFlag(true); };
}
template <typename T>
static Promise<T> instant_fail() {
return [] { return Failure{}; };
}
template <typename... T>
static Poll<ValueOrFailure<std::tuple<T...>>> ok(T... x) {
return ValueOrFailure<std::tuple<T...>>(std::tuple<T...>(x...));
}
template <typename... T>
static Poll<ValueOrFailure<std::tuple<T...>>> fail() {
return Failure{};
}
template <typename T>
static Promise<T> pending() {
return []() -> Poll<ValueOrFailure<T>> { return Pending(); };
}
};
template <typename T>
P<T> instant_ok(T x) {
return [x] { return absl::StatusOr<T>(x); };
}
template <typename T>
P<T> instant_fail() {
return [] { return absl::StatusOr<T>(); };
}
class TryJoinTest : public ::testing::Test {};
template <typename... T>
Poll<absl::StatusOr<std::tuple<T...>>> ok(T... x) {
return absl::StatusOr<std::tuple<T...>>(absl::in_place, x...);
}
using Traits = ::testing::Types<AbslStatusTraits, ValueOrFailureTraits>;
TYPED_TEST_SUITE(TryJoinTest, Traits);
template <typename... T>
Poll<absl::StatusOr<std::tuple<T...>>> fail() {
return absl::StatusOr<std::tuple<T...>>();
TYPED_TEST(TryJoinTest, Join1) {
EXPECT_EQ(TypeParam::TryJoinImpl(TypeParam::instant_ok(1))(),
TypeParam::ok(1));
}
template <typename T>
P<T> pending() {
return []() -> Poll<absl::StatusOr<T>> { return Pending(); };
}
TEST(TryJoinTest, Join1) { EXPECT_EQ(TryJoin(instant_ok(1))(), ok(1)); }
TEST(TryJoinTest, Join1Fail) {
EXPECT_EQ(TryJoin(instant_fail<int>())(), fail<int>());
TYPED_TEST(TryJoinTest, Join1Fail) {
EXPECT_EQ(TypeParam::TryJoinImpl(TypeParam::template instant_fail<int>())(),
TypeParam::template fail<int>());
}
TEST(TryJoinTest, Join2Success) {
EXPECT_EQ(TryJoin(instant_ok(1), instant_ok(2))(), ok(1, 2));
TYPED_TEST(TryJoinTest, Join2Success) {
EXPECT_EQ(TypeParam::TryJoinImpl(TypeParam::instant_ok(1),
TypeParam::instant_ok(2))(),
TypeParam::ok(1, 2));
}
TEST(TryJoinTest, Join2Fail1) {
EXPECT_EQ(TryJoin(instant_ok(1), instant_fail<int>())(), (fail<int, int>()));
TYPED_TEST(TryJoinTest, Join2Fail1) {
EXPECT_EQ(TypeParam::TryJoinImpl(TypeParam::instant_ok(1),
TypeParam::template instant_fail<int>())(),
(TypeParam::template fail<int, int>()));
}
TEST(TryJoinTest, Join2Fail2) {
EXPECT_EQ(TryJoin(instant_fail<int>(), instant_ok(2))(), (fail<int, int>()));
TYPED_TEST(TryJoinTest, Join2Fail2) {
EXPECT_EQ(TypeParam::TryJoinImpl(TypeParam::template instant_fail<int>(),
TypeParam::instant_ok(2))(),
(TypeParam::template fail<int, int>()));
}
TEST(TryJoinTest, Join2Fail1P) {
EXPECT_EQ(TryJoin(pending<int>(), instant_fail<int>())(), (fail<int, int>()));
TYPED_TEST(TryJoinTest, Join2Fail1P) {
EXPECT_EQ(TypeParam::TryJoinImpl(TypeParam::template pending<int>(),
TypeParam::template instant_fail<int>())(),
(TypeParam::template fail<int, int>()));
}
TEST(TryJoinTest, Join2Fail2P) {
EXPECT_EQ(TryJoin(instant_fail<int>(), pending<int>())(), (fail<int, int>()));
TYPED_TEST(TryJoinTest, Join2Fail2P) {
EXPECT_EQ(TypeParam::TryJoinImpl(TypeParam::template instant_fail<int>(),
TypeParam::template pending<int>())(),
(TypeParam::template fail<int, int>()));
}
TEST(TryJoinTest, JoinStatus) {
EXPECT_EQ(TryJoin([]() { return absl::OkStatus(); },
[]() { return absl::OkStatus(); })(),
ok(Empty{}, Empty{}));
TYPED_TEST(TryJoinTest, JoinStatus) {
EXPECT_EQ(TypeParam::TryJoinImpl(TypeParam::instant_ok_status(),
TypeParam::instant_ok_status())(),
TypeParam::ok(Empty{}, Empty{}));
}
} // namespace grpc_core

@ -23,53 +23,133 @@
namespace grpc_core {
TEST(TrySeqTest, SucceedAndThen) {
EXPECT_EQ(TrySeq([] { return absl::StatusOr<int>(1); },
[](int i) {
return [i]() { return absl::StatusOr<int>(i + 1); };
})(),
Poll<absl::StatusOr<int>>(absl::StatusOr<int>(2)));
struct AbslStatusTraits {
template <typename T>
using Promise = std::function<Poll<absl::StatusOr<T>>()>;
template <typename T>
static Promise<T> instant_ok(T x) {
return [x] { return absl::StatusOr<T>(x); };
}
static auto instant_ok_status() {
return [] { return absl::OkStatus(); };
}
template <typename T>
static Promise<T> instant_fail() {
return [] { return absl::StatusOr<T>(); };
}
template <typename T>
static Poll<absl::StatusOr<T>> instant_crash() {
abort();
}
template <typename T>
static Poll<absl::StatusOr<T>> ok(T x) {
return absl::StatusOr<T>(x);
}
static Poll<absl::Status> ok_status() { return absl::OkStatus(); }
template <typename T>
static Poll<absl::StatusOr<T>> fail() {
return absl::StatusOr<T>();
}
template <typename T>
static Promise<T> pending() {
return []() -> Poll<absl::StatusOr<T>> { return Pending(); };
}
};
struct ValueOrFailureTraits {
template <typename T>
using Promise = std::function<Poll<ValueOrFailure<T>>()>;
template <typename T>
static Promise<T> instant_ok(T x) {
return [x] { return ValueOrFailure<T>(x); };
}
static auto instant_ok_status() {
return [] { return StatusFlag(true); };
}
template <typename T>
static Promise<T> instant_fail() {
return [] { return Failure{}; };
}
template <typename T>
static Poll<ValueOrFailure<T>> instant_crash() {
abort();
}
template <typename T>
static Poll<ValueOrFailure<T>> ok(T x) {
return ValueOrFailure<T>(x);
}
static Poll<StatusFlag> ok_status() { return Success{}; }
template <typename T>
static Poll<ValueOrFailure<T>> fail() {
return Failure{};
}
template <typename T>
static Promise<T> pending() {
return []() -> Poll<ValueOrFailure<T>> { return Pending(); };
}
};
template <typename T>
class TrySeqTest : public ::testing::Test {};
using Traits = ::testing::Types<AbslStatusTraits, ValueOrFailureTraits>;
TYPED_TEST_SUITE(TrySeqTest, Traits);
TYPED_TEST(TrySeqTest, SucceedAndThen) {
EXPECT_EQ(TrySeq(TypeParam::instant_ok(1),
[](int i) { return TypeParam::instant_ok(i + 1); })(),
TypeParam::ok(2));
}
TEST(TrySeqTest, SucceedDirectlyAndThenDirectly) {
TYPED_TEST(TrySeqTest, SucceedDirectlyAndThenDirectly) {
EXPECT_EQ(
TrySeq([] { return 1; }, [](int i) { return [i]() { return i + 1; }; })(),
Poll<absl::StatusOr<int>>(absl::StatusOr<int>(2)));
Poll<absl::StatusOr<int>>(2));
}
TEST(TrySeqTest, SucceedAndThenChangeType) {
TYPED_TEST(TrySeqTest, SucceedAndThenChangeType) {
EXPECT_EQ(
TrySeq([] { return absl::StatusOr<int>(42); },
[](int i) {
return [i]() {
return absl::StatusOr<std::string>(std::to_string(i));
};
})(),
Poll<absl::StatusOr<std::string>>(absl::StatusOr<std::string>("42")));
TrySeq(TypeParam::instant_ok(42),
[](int i) { return TypeParam::instant_ok(std::to_string(i)); })(),
TypeParam::ok(std::string("42")));
}
TEST(TrySeqTest, FailAndThen) {
EXPECT_EQ(TrySeq([]() { return absl::StatusOr<int>(absl::CancelledError()); },
[](int) {
return []() -> Poll<absl::StatusOr<double>> { abort(); };
})(),
Poll<absl::StatusOr<double>>(
absl::StatusOr<double>(absl::CancelledError())));
TYPED_TEST(TrySeqTest, FailAndThen) {
EXPECT_EQ(
TrySeq(TypeParam::template instant_fail<int>(),
[](int) { return TypeParam::template instant_crash<double>(); })(),
TypeParam::template fail<double>());
}
TEST(TrySeqTest, RawSucceedAndThen) {
EXPECT_EQ(TrySeq([] { return absl::OkStatus(); },
[] { return []() { return absl::OkStatus(); }; })(),
Poll<absl::Status>(absl::OkStatus()));
TYPED_TEST(TrySeqTest, RawSucceedAndThen) {
EXPECT_EQ(TrySeq(TypeParam::instant_ok_status(),
[] { return TypeParam::instant_ok_status(); })(),
TypeParam::ok_status());
}
TEST(TrySeqTest, RawFailAndThen) {
TYPED_TEST(TrySeqTest, RawFailAndThen) {
EXPECT_EQ(TrySeq([] { return absl::CancelledError(); },
[]() { return []() -> Poll<absl::Status> { abort(); }; })(),
Poll<absl::Status>(absl::CancelledError()));
}
TEST(TrySeqTest, RawSucceedAndThenValue) {
TYPED_TEST(TrySeqTest, RawSucceedAndThenValue) {
EXPECT_EQ(TrySeq([] { return absl::OkStatus(); },
[] { return []() { return absl::StatusOr<int>(42); }; })(),
Poll<absl::StatusOr<int>>(absl::StatusOr<int>(42)));

@ -57,7 +57,8 @@ class Fuzzer {
// Leave xds_client_ unset, so Act() will be a no-op.
return;
}
auto transport_factory = MakeOrphanable<FakeXdsTransportFactory>();
auto transport_factory = MakeOrphanable<FakeXdsTransportFactory>(
[]() { Crash("Multiple concurrent reads"); });
transport_factory->SetAutoCompleteMessagesFromClient(false);
transport_factory->SetAbortOnUndrainedMessages(false);
transport_factory_ = transport_factory.get();
@ -147,20 +148,26 @@ class Fuzzer {
: resource_name_(std::move(resource_name)) {}
void OnResourceChanged(
std::shared_ptr<const typename ResourceType::ResourceType> resource)
std::shared_ptr<const typename ResourceType::ResourceType> resource,
RefCountedPtr<XdsClient::ReadDelayHandle> /* read_delay_handle */)
override {
gpr_log(GPR_INFO, "==> OnResourceChanged(%s %s): %s",
std::string(ResourceType::Get()->type_url()).c_str(),
resource_name_.c_str(), resource->ToString().c_str());
}
void OnError(absl::Status status) override {
void OnError(
absl::Status status,
RefCountedPtr<XdsClient::ReadDelayHandle> /* read_delay_handle */)
override {
gpr_log(GPR_INFO, "==> OnError(%s %s): %s",
std::string(ResourceType::Get()->type_url()).c_str(),
resource_name_.c_str(), status.ToString().c_str());
}
void OnResourceDoesNotExist() override {
void OnResourceDoesNotExist(
RefCountedPtr<XdsClient::ReadDelayHandle> /* read_delay_handle */)
override {
gpr_log(GPR_INFO, "==> OnResourceDoesNotExist(%s %s)",
std::string(ResourceType::Get()->type_url()).c_str(),
resource_name_.c_str());

@ -235,6 +235,11 @@ class XdsClientTest : public ::testing::Test {
XdsTestResourceType<ResourceStruct, all_resources_required_in_sotw>,
ResourceStruct> {
public:
struct ResourceAndReadDelayHandle {
std::shared_ptr<const ResourceStruct> resource;
RefCountedPtr<XdsClient::ReadDelayHandle> read_delay_handle;
};
// A watcher implementation that queues delivered watches.
class Watcher : public XdsResourceTypeImpl<
XdsTestResourceType<ResourceStruct,
@ -252,28 +257,37 @@ class XdsClientTest : public ::testing::Test {
return !queue_.empty();
}
std::shared_ptr<const ResourceStruct> WaitForNextResource(
absl::optional<ResourceAndReadDelayHandle> WaitForNextResourceAndHandle(
absl::Duration timeout = absl::Seconds(1),
SourceLocation location = SourceLocation()) {
MutexLock lock(&mu_);
if (!WaitForEventLocked(timeout)) return nullptr;
if (!WaitForEventLocked(timeout)) return absl::nullopt;
Event& event = queue_.front();
if (!absl::holds_alternative<std::shared_ptr<const ResourceStruct>>(
event)) {
if (!absl::holds_alternative<ResourceAndReadDelayHandle>(event)) {
EXPECT_TRUE(false)
<< "got unexpected event "
<< (absl::holds_alternative<absl::Status>(event)
? "error"
: "does-not-exist")
<< " at " << location.file() << ":" << location.line();
return nullptr;
return absl::nullopt;
}
auto foo =
std::move(absl::get<std::shared_ptr<const ResourceStruct>>(event));
auto foo = std::move(absl::get<ResourceAndReadDelayHandle>(event));
queue_.pop_front();
return foo;
}
std::shared_ptr<const ResourceStruct> WaitForNextResource(
absl::Duration timeout = absl::Seconds(1),
SourceLocation location = SourceLocation()) {
auto resource_and_handle =
WaitForNextResourceAndHandle(timeout, location);
if (!resource_and_handle.has_value()) {
return nullptr;
}
return std::move(resource_and_handle->resource);
}
absl::optional<absl::Status> WaitForNextError(
absl::Duration timeout = absl::Seconds(1),
SourceLocation location = SourceLocation()) {
@ -283,8 +297,7 @@ class XdsClientTest : public ::testing::Test {
if (!absl::holds_alternative<absl::Status>(event)) {
EXPECT_TRUE(false)
<< "got unexpected event "
<< (absl::holds_alternative<
std::shared_ptr<const ResourceStruct>>(event)
<< (absl::holds_alternative<ResourceAndReadDelayHandle>(event)
? "resource"
: "does-not-exist")
<< " at " << location.file() << ":" << location.line();
@ -314,21 +327,31 @@ class XdsClientTest : public ::testing::Test {
private:
struct DoesNotExist {};
using Event = absl::variant<std::shared_ptr<const ResourceStruct>,
absl::Status, DoesNotExist>;
using Event =
absl::variant<ResourceAndReadDelayHandle, absl::Status, DoesNotExist>;
void OnResourceChanged(
std::shared_ptr<const ResourceStruct> foo) override {
void OnResourceChanged(std::shared_ptr<const ResourceStruct> foo,
RefCountedPtr<XdsClient::ReadDelayHandle>
read_delay_handle) override {
MutexLock lock(&mu_);
queue_.push_back(std::move(foo));
ResourceAndReadDelayHandle event_details = {
std::move(foo), std::move(read_delay_handle)};
queue_.emplace_back(std::move(event_details));
cv_.Signal();
}
void OnError(absl::Status status) override {
void OnError(
absl::Status status,
RefCountedPtr<XdsClient::ReadDelayHandle> /* read_delay_handle */)
override {
MutexLock lock(&mu_);
queue_.push_back(std::move(status));
cv_.Signal();
}
void OnResourceDoesNotExist() override {
void OnResourceDoesNotExist(
RefCountedPtr<XdsClient::ReadDelayHandle> /* read_delay_handle */)
override {
MutexLock lock(&mu_);
queue_.push_back(DoesNotExist());
cv_.Signal();
@ -577,7 +600,8 @@ class XdsClientTest : public ::testing::Test {
void InitXdsClient(
FakeXdsBootstrap::Builder bootstrap_builder = FakeXdsBootstrap::Builder(),
Duration resource_request_timeout = Duration::Seconds(15)) {
auto transport_factory = MakeOrphanable<FakeXdsTransportFactory>();
auto transport_factory = MakeOrphanable<FakeXdsTransportFactory>(
[]() { FAIL() << "Multiple concurrent reads"; });
transport_factory_ =
transport_factory->Ref().TakeAsSubclass<FakeXdsTransportFactory>();
xds_client_ = MakeRefCounted<XdsClient>(
@ -671,8 +695,9 @@ class XdsClientTest : public ::testing::Test {
// Helper function to check the fields of a DiscoveryRequest.
void CheckRequest(const DiscoveryRequest& request, absl::string_view type_url,
absl::string_view version_info,
absl::string_view response_nonce, absl::Status error_detail,
std::set<absl::string_view> resource_names,
absl::string_view response_nonce,
const absl::Status& error_detail,
const std::set<absl::string_view>& resource_names,
SourceLocation location = SourceLocation()) {
EXPECT_EQ(request.type_url(),
absl::StrCat("type.googleapis.com/", type_url))
@ -2695,6 +2720,91 @@ TEST_F(XdsClientTest, FederationChannelFailureReportedToWatchers) {
EXPECT_TRUE(stream2->Orphaned());
}
TEST_F(XdsClientTest, AdsReadWaitsForHandleRelease) {
InitXdsClient();
// Start watches for "foo1" and "foo2".
auto watcher1 = StartFooWatch("foo1");
// XdsClient should have created an ADS stream.
auto stream = WaitForAdsStream();
ASSERT_TRUE(stream != nullptr);
// XdsClient should have sent a subscription request on the ADS stream.
auto request = WaitForRequest(stream.get());
ASSERT_TRUE(request.has_value());
CheckRequest(*request, XdsFooResourceType::Get()->type_url(),
/*version_info=*/"", /*response_nonce=*/"",
/*error_detail=*/absl::OkStatus(),
/*resource_names=*/{"foo1"});
auto watcher2 = StartFooWatch("foo2");
request = WaitForRequest(stream.get());
ASSERT_TRUE(request.has_value());
CheckRequest(*request, XdsFooResourceType::Get()->type_url(),
/*version_info=*/"", /*response_nonce=*/"",
/*error_detail=*/absl::OkStatus(),
/*resource_names=*/{"foo1", "foo2"});
// Send a response with 2 resources.
stream->SendMessageToClient(
ResponseBuilder(XdsFooResourceType::Get()->type_url())
.set_version_info("1")
.set_nonce("A")
.AddFooResource(XdsFooResource("foo1", 6))
.AddFooResource(XdsFooResource("foo2", 10))
.Serialize());
// Send a response with a single resource, will not be read until the handle
// is released
stream->SendMessageToClient(
ResponseBuilder(XdsFooResourceType::Get()->type_url())
.set_version_info("2")
.set_nonce("B")
.AddFooResource(XdsFooResource("foo1", 8))
.Serialize());
// XdsClient should have delivered the response to the watcher.
auto resource1 = watcher1->WaitForNextResourceAndHandle();
ASSERT_NE(resource1, absl::nullopt);
EXPECT_EQ(resource1->resource->name, "foo1");
EXPECT_EQ(resource1->resource->value, 6);
auto resource2 = watcher2->WaitForNextResourceAndHandle();
ASSERT_NE(resource2, absl::nullopt);
EXPECT_EQ(resource2->resource->name, "foo2");
EXPECT_EQ(resource2->resource->value, 10);
// XdsClient should have sent an ACK message to the xDS server.
request = WaitForRequest(stream.get());
ASSERT_TRUE(request.has_value());
CheckRequest(*request, XdsFooResourceType::Get()->type_url(),
/*version_info=*/"1", /*response_nonce=*/"A",
/*error_detail=*/absl::OkStatus(),
/*resource_names=*/{"foo1", "foo2"});
EXPECT_EQ(stream->reads_started(), 1);
resource1->read_delay_handle.reset();
EXPECT_EQ(stream->reads_started(), 1);
resource2->read_delay_handle.reset();
EXPECT_EQ(stream->reads_started(), 2);
resource1 = watcher1->WaitForNextResourceAndHandle();
ASSERT_NE(resource1, absl::nullopt);
EXPECT_EQ(resource1->resource->name, "foo1");
EXPECT_EQ(resource1->resource->value, 8);
EXPECT_EQ(watcher2->WaitForNextResourceAndHandle(), absl::nullopt);
// XdsClient should have sent an ACK message to the xDS server.
request = WaitForRequest(stream.get());
ASSERT_TRUE(request.has_value());
CheckRequest(*request, XdsFooResourceType::Get()->type_url(),
/*version_info=*/"2", /*response_nonce=*/"B",
/*error_detail=*/absl::OkStatus(),
/*resource_names=*/{"foo1", "foo2"});
EXPECT_EQ(stream->reads_started(), 2);
resource1->read_delay_handle.reset();
EXPECT_EQ(stream->reads_started(), 3);
// Cancel watch.
CancelFooWatch(watcher1.get(), "foo1");
request = WaitForRequest(stream.get());
ASSERT_TRUE(request.has_value());
CheckRequest(*request, XdsFooResourceType::Get()->type_url(),
/*version_info=*/"2", /*response_nonce=*/"B",
/*error_detail=*/absl::OkStatus(),
/*resource_names=*/{"foo2"});
CancelFooWatch(watcher2.get(), "foo2");
EXPECT_TRUE(stream->Orphaned());
}
} // namespace
} // namespace testing
} // namespace grpc_core

@ -20,6 +20,8 @@
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include <type_traits>
#include <utility>
@ -46,6 +48,10 @@ FakeXdsTransportFactory::FakeStreamingCall::~FakeStreamingCall() {
{
MutexLock lock(&mu_);
if (transport_->abort_on_undrained_messages()) {
for (const auto& message : from_client_messages_) {
gpr_log(GPR_ERROR, "From client message left in queue: %s",
message.c_str());
}
GPR_ASSERT(from_client_messages_.empty());
}
}
@ -120,15 +126,49 @@ void FakeXdsTransportFactory::FakeStreamingCall::CompleteSendMessageFromClient(
CompleteSendMessageFromClientLocked(ok);
}
void FakeXdsTransportFactory::FakeStreamingCall::StartRecvMessage() {
MutexLock lock(&mu_);
if (num_pending_reads_ > 0) {
transport_->factory()->too_many_pending_reads_callback_();
}
++reads_started_;
++num_pending_reads_;
if (!to_client_messages_.empty()) {
// Dispatch pending message (if there's one) on a separate thread to avoid
// recursion
GetDefaultEventEngine()->Run([call = RefAsSubclass<FakeStreamingCall>()]() {
call->MaybeDeliverMessageToClient();
});
}
}
void FakeXdsTransportFactory::FakeStreamingCall::SendMessageToClient(
absl::string_view payload) {
ExecCtx exec_ctx;
RefCountedPtr<RefCountedEventHandler> event_handler;
{
MutexLock lock(&mu_);
event_handler = event_handler_->Ref();
to_client_messages_.emplace_back(payload);
}
MaybeDeliverMessageToClient();
}
void FakeXdsTransportFactory::FakeStreamingCall::MaybeDeliverMessageToClient() {
RefCountedPtr<RefCountedEventHandler> event_handler;
std::string message;
// Loop terminates with a break inside
while (true) {
{
MutexLock lock(&mu_);
if (num_pending_reads_ == 0 || to_client_messages_.empty()) {
break;
}
--num_pending_reads_;
message = std::move(to_client_messages_.front());
to_client_messages_.pop_front();
event_handler = event_handler_;
}
ExecCtx exec_ctx;
event_handler->OnRecvMessage(message);
}
event_handler->OnRecvMessage(payload);
}
void FakeXdsTransportFactory::FakeStreamingCall::MaybeSendStatusToClient(

@ -19,6 +19,8 @@
#include <grpc/support/port_platform.h>
#include <stddef.h>
#include <deque>
#include <functional>
#include <map>
@ -66,6 +68,8 @@ class FakeXdsTransportFactory : public XdsTransportFactory {
void Orphan() override;
void StartRecvMessage() override;
using StreamingCall::Ref; // Make it public.
bool HaveMessageFromClient();
@ -84,6 +88,11 @@ class FakeXdsTransportFactory : public XdsTransportFactory {
bool Orphaned();
size_t reads_started() {
MutexLock lock(&mu_);
return reads_started_;
}
private:
class RefCountedEventHandler : public RefCounted<RefCountedEventHandler> {
public:
@ -107,6 +116,7 @@ class FakeXdsTransportFactory : public XdsTransportFactory {
void CompleteSendMessageFromClientLocked(bool ok)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&mu_);
void MaybeDeliverMessageToClient();
RefCountedPtr<FakeXdsTransport> transport_;
const char* method_;
@ -117,9 +127,15 @@ class FakeXdsTransportFactory : public XdsTransportFactory {
std::deque<std::string> from_client_messages_ ABSL_GUARDED_BY(&mu_);
bool status_sent_ ABSL_GUARDED_BY(&mu_) = false;
bool orphaned_ ABSL_GUARDED_BY(&mu_) = false;
size_t reads_started_ ABSL_GUARDED_BY(&mu_) = 0;
size_t num_pending_reads_ ABSL_GUARDED_BY(&mu_) = 0;
std::deque<std::string> to_client_messages_ ABSL_GUARDED_BY(&mu_);
};
FakeXdsTransportFactory() = default;
explicit FakeXdsTransportFactory(
std::function<void()> too_many_pending_reads_callback)
: too_many_pending_reads_callback_(
std::move(too_many_pending_reads_callback)) {}
using XdsTransportFactory::Ref; // Make it public.
@ -130,7 +146,7 @@ class FakeXdsTransportFactory : public XdsTransportFactory {
// EventHandler::OnRequestSent() upon reading a request from the client.
// If this is set to false, that behavior will be inhibited, and
// EventHandler::OnRequestSent() will not be called until the test
// expicitly calls FakeStreamingCall::CompleteSendMessageFromClient().
// explicitly calls FakeStreamingCall::CompleteSendMessageFromClient().
//
// This value affects all transports created after this call is
// complete. Any transport that already exists prior to this call
@ -189,6 +205,8 @@ class FakeXdsTransportFactory : public XdsTransportFactory {
void RemoveStream(const char* method, FakeStreamingCall* call);
FakeXdsTransportFactory* factory() const { return factory_.get(); }
private:
class RefCountedOnConnectivityFailure
: public RefCounted<RefCountedOnConnectivityFailure> {
@ -237,6 +255,7 @@ class FakeXdsTransportFactory : public XdsTransportFactory {
transport_map_ ABSL_GUARDED_BY(&mu_);
bool auto_complete_messages_from_client_ ABSL_GUARDED_BY(&mu_) = true;
bool abort_on_undrained_messages_ ABSL_GUARDED_BY(&mu_) = true;
std::function<void()> too_many_pending_reads_callback_;
};
} // namespace grpc_core

@ -37,6 +37,7 @@ grpc_cc_test(
deps = [
"//:grpc++",
"//src/cpp/ext/csm:csm_observability",
"//src/cpp/ext/otel:otel_plugin",
"//test/core/util:grpc_test_util",
],
)

@ -26,6 +26,7 @@
#include "opentelemetry/sdk/metrics/meter_provider.h"
#include "opentelemetry/sdk/metrics/metric_reader.h"
#include <grpcpp/ext/otel_plugin.h>
#include <grpcpp/grpcpp.h>
#include "src/core/lib/channel/call_tracer.h"
@ -108,7 +109,7 @@ class TestScenario {
};
class MetadataExchangeTest
: public OTelPluginEnd2EndTest,
: public OpenTelemetryPluginEnd2EndTest,
public ::testing::WithParamInterface<TestScenario> {
protected:
void Init(const absl::flat_hash_set<absl::string_view>& metric_names,
@ -131,7 +132,7 @@ class MetadataExchangeTest
grpc_core::SetEnv("GRPC_XDS_BOOTSTRAP_CONFIG", kBootstrap);
break;
}
OTelPluginEnd2EndTest::Init(
OpenTelemetryPluginEnd2EndTest::Init(
metric_names, /*resource=*/GetParam().GetTestResource(),
/*labels_injector=*/
std::make_unique<grpc::internal::ServiceMeshLabelsInjector>(
@ -205,8 +206,8 @@ class MetadataExchangeTest
// Verify that grpc.client.attempt.started does not get service mesh attributes
TEST_P(MetadataExchangeTest, ClientAttemptStarted) {
Init(/*metric_names=*/{
grpc::internal::OTelClientAttemptStartedInstrumentName()});
Init(/*metric_names=*/{grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName});
SendRPC();
const char* kMetricName = "grpc.client.attempt.started";
auto data = ReadCurrentMetricsData(
@ -229,8 +230,8 @@ TEST_P(MetadataExchangeTest, ClientAttemptStarted) {
}
TEST_P(MetadataExchangeTest, ClientAttemptDuration) {
Init(/*metric_names=*/{
grpc::internal::OTelClientAttemptDurationInstrumentName()});
Init(/*metric_names=*/{grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptDurationInstrumentName});
SendRPC();
const char* kMetricName = "grpc.client.attempt.duration";
auto data = ReadCurrentMetricsData(
@ -255,7 +256,8 @@ TEST_P(MetadataExchangeTest, ClientAttemptDuration) {
// Verify that grpc.server.call.started does not get service mesh attributes
TEST_P(MetadataExchangeTest, ServerCallStarted) {
Init(
/*metric_names=*/{grpc::internal::OTelServerCallStartedInstrumentName()});
/*metric_names=*/{grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallStartedInstrumentName});
SendRPC();
const char* kMetricName = "grpc.server.call.started";
auto data = ReadCurrentMetricsData(
@ -274,8 +276,9 @@ TEST_P(MetadataExchangeTest, ServerCallStarted) {
}
TEST_P(MetadataExchangeTest, ServerCallDuration) {
Init(/*metric_names=*/{
grpc::internal::OTelServerCallDurationInstrumentName()});
Init(
/*metric_names=*/{grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallDurationInstrumentName});
SendRPC();
const char* kMetricName = "grpc.server.call.duration";
auto data = ReadCurrentMetricsData(
@ -298,7 +301,8 @@ TEST_P(MetadataExchangeTest, ServerCallDuration) {
// Test that the server records unknown when the client does not send metadata
TEST_P(MetadataExchangeTest, ClientDoesNotSendMetadata) {
Init(
/*metric_names=*/{grpc::internal::OTelServerCallDurationInstrumentName()},
/*metric_names=*/{grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallDurationInstrumentName},
/*enable_client_side_injector=*/false);
SendRPC();
const char* kMetricName = "grpc.server.call.duration";

@ -25,6 +25,7 @@
#include "opentelemetry/sdk/metrics/meter_provider.h"
#include "opentelemetry/sdk/metrics/metric_reader.h"
#include <grpcpp/ext/otel_plugin.h>
#include <grpcpp/grpcpp.h>
#include "src/core/lib/channel/call_tracer.h"
@ -37,16 +38,21 @@ namespace grpc {
namespace testing {
namespace {
TEST(OTelPluginBuildTest, ApiDependency) {
TEST(OpenTelemetryPluginBuildTest, ApiDependency) {
opentelemetry::metrics::Provider::GetMeterProvider();
}
TEST(OTelPluginBuildTest, SdkDependency) {
TEST(OpenTelemetryPluginBuildTest, SdkDependency) {
opentelemetry::sdk::metrics::MeterProvider();
}
TEST_F(OTelPluginEnd2EndTest, ClientAttemptStarted) {
Init({grpc::internal::OTelClientAttemptStartedInstrumentName()});
TEST(OpenTelemetryPluginBuildTest, Basic) {
grpc::experimental::OpenTelemetryPluginBuilder builder;
}
TEST_F(OpenTelemetryPluginEnd2EndTest, ClientAttemptStarted) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName});
SendRPC();
const char* kMetricName = "grpc.client.attempt.started";
auto data = ReadCurrentMetricsData(
@ -73,8 +79,9 @@ TEST_F(OTelPluginEnd2EndTest, ClientAttemptStarted) {
EXPECT_EQ(*target_value, canonical_server_address_);
}
TEST_F(OTelPluginEnd2EndTest, ClientAttemptDuration) {
Init({grpc::internal::OTelClientAttemptDurationInstrumentName()});
TEST_F(OpenTelemetryPluginEnd2EndTest, ClientAttemptDuration) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptDurationInstrumentName});
SendRPC();
const char* kMetricName = "grpc.client.attempt.duration";
auto data = ReadCurrentMetricsData(
@ -104,9 +111,10 @@ TEST_F(OTelPluginEnd2EndTest, ClientAttemptDuration) {
EXPECT_EQ(*status_value, "OK");
}
TEST_F(OTelPluginEnd2EndTest, ClientAttemptSentTotalCompressedMessageSize) {
Init({grpc::internal::
OTelClientAttemptSentTotalCompressedMessageSizeInstrumentName()});
TEST_F(OpenTelemetryPluginEnd2EndTest,
ClientAttemptSentTotalCompressedMessageSize) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptSentTotalCompressedMessageSizeInstrumentName});
SendRPC();
const char* kMetricName =
"grpc.client.attempt.sent_total_compressed_message_size";
@ -138,9 +146,10 @@ TEST_F(OTelPluginEnd2EndTest, ClientAttemptSentTotalCompressedMessageSize) {
EXPECT_EQ(*status_value, "OK");
}
TEST_F(OTelPluginEnd2EndTest, ClientAttemptRcvdTotalCompressedMessageSize) {
Init({grpc::internal::
OTelClientAttemptRcvdTotalCompressedMessageSizeInstrumentName()});
TEST_F(OpenTelemetryPluginEnd2EndTest,
ClientAttemptRcvdTotalCompressedMessageSize) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptRcvdTotalCompressedMessageSizeInstrumentName});
SendRPC();
const char* kMetricName =
"grpc.client.attempt.rcvd_total_compressed_message_size";
@ -172,8 +181,9 @@ TEST_F(OTelPluginEnd2EndTest, ClientAttemptRcvdTotalCompressedMessageSize) {
EXPECT_EQ(*status_value, "OK");
}
TEST_F(OTelPluginEnd2EndTest, ServerCallStarted) {
Init({grpc::internal::OTelServerCallStartedInstrumentName()});
TEST_F(OpenTelemetryPluginEnd2EndTest, ServerCallStarted) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallStartedInstrumentName});
SendRPC();
const char* kMetricName = "grpc.server.call.started";
auto data = ReadCurrentMetricsData(
@ -196,8 +206,9 @@ TEST_F(OTelPluginEnd2EndTest, ServerCallStarted) {
EXPECT_EQ(*method_value, kMethodName);
}
TEST_F(OTelPluginEnd2EndTest, ServerCallDuration) {
Init({grpc::internal::OTelServerCallDurationInstrumentName()});
TEST_F(OpenTelemetryPluginEnd2EndTest, ServerCallDuration) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallDurationInstrumentName});
SendRPC();
const char* kMetricName = "grpc.server.call.duration";
auto data = ReadCurrentMetricsData(
@ -223,9 +234,10 @@ TEST_F(OTelPluginEnd2EndTest, ServerCallDuration) {
EXPECT_EQ(*status_value, "OK");
}
TEST_F(OTelPluginEnd2EndTest, ServerCallSentTotalCompressedMessageSize) {
Init({grpc::internal::
OTelServerCallSentTotalCompressedMessageSizeInstrumentName()});
TEST_F(OpenTelemetryPluginEnd2EndTest,
ServerCallSentTotalCompressedMessageSize) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallSentTotalCompressedMessageSizeInstrumentName});
SendRPC();
const char* kMetricName =
"grpc.server.call.sent_total_compressed_message_size";
@ -253,9 +265,10 @@ TEST_F(OTelPluginEnd2EndTest, ServerCallSentTotalCompressedMessageSize) {
EXPECT_EQ(*status_value, "OK");
}
TEST_F(OTelPluginEnd2EndTest, ServerCallRcvdTotalCompressedMessageSize) {
Init({grpc::internal::
OTelServerCallRcvdTotalCompressedMessageSizeInstrumentName()});
TEST_F(OpenTelemetryPluginEnd2EndTest,
ServerCallRcvdTotalCompressedMessageSize) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallRcvdTotalCompressedMessageSizeInstrumentName});
SendRPC();
const char* kMetricName =
"grpc.server.call.rcvd_total_compressed_message_size";
@ -284,8 +297,9 @@ TEST_F(OTelPluginEnd2EndTest, ServerCallRcvdTotalCompressedMessageSize) {
}
// Make sure that no meter provider results in normal operations.
TEST_F(OTelPluginEnd2EndTest, NoMeterProviderRegistered) {
Init({grpc::internal::OTelClientAttemptStartedInstrumentName()},
TEST_F(OpenTelemetryPluginEnd2EndTest, NoMeterProviderRegistered) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName},
/*resource=*/opentelemetry::sdk::resource::Resource::Create({}),
/*labels_injector=*/nullptr,
/*test_no_meter_provider=*/true);
@ -293,8 +307,9 @@ TEST_F(OTelPluginEnd2EndTest, NoMeterProviderRegistered) {
}
// Test that a channel selector returning true records metrics on the channel.
TEST_F(OTelPluginEnd2EndTest, TargetSelectorReturnsTrue) {
Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}, /*resource=*/
TEST_F(OpenTelemetryPluginEnd2EndTest, TargetSelectorReturnsTrue) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName}, /*resource=*/
opentelemetry::sdk::resource::Resource::Create({}),
/*labels_injector=*/nullptr,
/*test_no_meter_provider=*/false,
@ -328,8 +343,9 @@ TEST_F(OTelPluginEnd2EndTest, TargetSelectorReturnsTrue) {
// Test that a target selector returning false does not record metrics on the
// channel.
TEST_F(OTelPluginEnd2EndTest, TargetSelectorReturnsFalse) {
Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}, /*resource=*/
TEST_F(OpenTelemetryPluginEnd2EndTest, TargetSelectorReturnsFalse) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName}, /*resource=*/
opentelemetry::sdk::resource::Resource::Create({}),
/*labels_injector=*/nullptr,
/*test_no_meter_provider=*/false,
@ -346,8 +362,9 @@ TEST_F(OTelPluginEnd2EndTest, TargetSelectorReturnsFalse) {
// Test that a target attribute filter returning true records metrics with the
// target as is on the channel.
TEST_F(OTelPluginEnd2EndTest, TargetAttributeFilterReturnsTrue) {
Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}, /*resource=*/
TEST_F(OpenTelemetryPluginEnd2EndTest, TargetAttributeFilterReturnsTrue) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName}, /*resource=*/
opentelemetry::sdk::resource::Resource::Create({}),
/*labels_injector=*/nullptr,
/*test_no_meter_provider=*/false,
@ -383,8 +400,9 @@ TEST_F(OTelPluginEnd2EndTest, TargetAttributeFilterReturnsTrue) {
// Test that a target attribute filter returning false records metrics with the
// target as "other".
TEST_F(OTelPluginEnd2EndTest, TargetAttributeFilterReturnsFalse) {
Init({grpc::internal::OTelClientAttemptStartedInstrumentName()}, /*resource=*/
TEST_F(OpenTelemetryPluginEnd2EndTest, TargetAttributeFilterReturnsFalse) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName}, /*resource=*/
opentelemetry::sdk::resource::Resource::Create({}),
/*labels_injector=*/nullptr,
/*test_no_meter_provider=*/false,
@ -419,8 +437,9 @@ TEST_F(OTelPluginEnd2EndTest, TargetAttributeFilterReturnsFalse) {
}
// Test that generic method names are scrubbed properly on the client side.
TEST_F(OTelPluginEnd2EndTest, GenericClientRpc) {
Init({grpc::internal::OTelClientAttemptStartedInstrumentName()});
TEST_F(OpenTelemetryPluginEnd2EndTest, GenericClientRpc) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName});
SendGenericRPC();
const char* kMetricName = "grpc.client.attempt.started";
auto data = ReadCurrentMetricsData(
@ -449,9 +468,10 @@ TEST_F(OTelPluginEnd2EndTest, GenericClientRpc) {
// Test that generic method names are scrubbed properly on the client side if
// the method attribute filter is set and it returns false.
TEST_F(OTelPluginEnd2EndTest,
TEST_F(OpenTelemetryPluginEnd2EndTest,
GenericClientRpcWithMethodAttributeFilterReturningFalse) {
Init({grpc::internal::OTelClientAttemptStartedInstrumentName()},
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName},
/*resource=*/opentelemetry::sdk::resource::Resource::Create({}),
/*labels_injector=*/nullptr,
/*test_no_meter_provider=*/false,
@ -488,9 +508,10 @@ TEST_F(OTelPluginEnd2EndTest,
// Test that generic method names is not scrubbed on the client side if
// the method attribute filter is set and it returns true.
TEST_F(OTelPluginEnd2EndTest,
TEST_F(OpenTelemetryPluginEnd2EndTest,
GenericClientRpcWithMethodAttributeFilterReturningTrue) {
Init({grpc::internal::OTelClientAttemptStartedInstrumentName()},
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName},
/*resource=*/opentelemetry::sdk::resource::Resource::Create({}),
/*labels_injector=*/nullptr,
/*test_no_meter_provider=*/false,
@ -526,8 +547,9 @@ TEST_F(OTelPluginEnd2EndTest,
}
// Test that generic method names are scrubbed properly on the server side.
TEST_F(OTelPluginEnd2EndTest, GenericServerRpc) {
Init({grpc::internal::OTelServerCallDurationInstrumentName()});
TEST_F(OpenTelemetryPluginEnd2EndTest, GenericServerRpc) {
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallDurationInstrumentName});
SendGenericRPC();
const char* kMetricName = "grpc.server.call.duration";
auto data = ReadCurrentMetricsData(
@ -555,9 +577,10 @@ TEST_F(OTelPluginEnd2EndTest, GenericServerRpc) {
// Test that generic method names are scrubbed properly on the server side if
// the method attribute filter is set and it returns false.
TEST_F(OTelPluginEnd2EndTest,
TEST_F(OpenTelemetryPluginEnd2EndTest,
GenericServerRpcWithMethodAttributeFilterReturningFalse) {
Init({grpc::internal::OTelServerCallDurationInstrumentName()},
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallDurationInstrumentName},
/*resource=*/opentelemetry::sdk::resource::Resource::Create({}),
/*labels_injector=*/nullptr,
/*test_no_meter_provider=*/false,
@ -593,9 +616,10 @@ TEST_F(OTelPluginEnd2EndTest,
// Test that generic method names are not scrubbed on the server side if
// the method attribute filter is set and it returns true.
TEST_F(OTelPluginEnd2EndTest,
TEST_F(OpenTelemetryPluginEnd2EndTest,
GenericServerRpcWithMethodAttributeFilterReturningTrue) {
Init({grpc::internal::OTelServerCallDurationInstrumentName()},
Init({grpc::experimental::OpenTelemetryPluginBuilder::
kServerCallDurationInstrumentName},
/*resource=*/opentelemetry::sdk::resource::Resource::Create({}),
/*labels_injector=*/nullptr,
/*test_no_meter_provider=*/false,

@ -38,7 +38,7 @@
namespace grpc {
namespace testing {
void OTelPluginEnd2EndTest::Init(
void OpenTelemetryPluginEnd2EndTest::Init(
const absl::flat_hash_set<absl::string_view>& metric_names,
opentelemetry::sdk::resource::Resource resource,
std::unique_ptr<grpc::internal::LabelsInjector> labels_injector,
@ -59,7 +59,7 @@ void OTelPluginEnd2EndTest::Init(
reader_.reset(new grpc::testing::MockMetricReader);
meter_provider->AddMetricReader(reader_);
grpc_core::CoreConfiguration::Reset();
grpc::internal::OpenTelemetryPluginBuilder ot_builder;
grpc::internal::OpenTelemetryPluginBuilderImpl ot_builder;
ot_builder.DisableAllMetrics();
for (const auto& metric_name : metric_names) {
ot_builder.EnableMetric(metric_name);
@ -96,19 +96,20 @@ void OTelPluginEnd2EndTest::Init(
generic_stub_ = std::make_unique<GenericStub>(std::move(channel));
}
void OTelPluginEnd2EndTest::TearDown() {
void OpenTelemetryPluginEnd2EndTest::TearDown() {
server_->Shutdown();
grpc_shutdown_blocking();
delete grpc_core::ServerCallTracerFactory::Get(grpc_core::ChannelArgs());
grpc_core::ServerCallTracerFactory::RegisterGlobal(nullptr);
}
void OTelPluginEnd2EndTest::ResetStub(std::shared_ptr<Channel> channel) {
void OpenTelemetryPluginEnd2EndTest::ResetStub(
std::shared_ptr<Channel> channel) {
stub_ = EchoTestService::NewStub(channel);
generic_stub_ = std::make_unique<GenericStub>(std::move(channel));
}
void OTelPluginEnd2EndTest::SendRPC() {
void OpenTelemetryPluginEnd2EndTest::SendRPC() {
EchoRequest request;
request.set_message("foo");
EchoResponse response;
@ -116,7 +117,7 @@ void OTelPluginEnd2EndTest::SendRPC() {
grpc::Status status = stub_->Echo(&context, request, &response);
}
void OTelPluginEnd2EndTest::SendGenericRPC() {
void OpenTelemetryPluginEnd2EndTest::SendGenericRPC() {
grpc::ClientContext context;
EchoRequest request;
std::unique_ptr<ByteBuffer> send_buf = SerializeToByteBuffer(&request);
@ -130,7 +131,7 @@ void OTelPluginEnd2EndTest::SendGenericRPC() {
absl::flat_hash_map<
std::string, std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>
OTelPluginEnd2EndTest::ReadCurrentMetricsData(
OpenTelemetryPluginEnd2EndTest::ReadCurrentMetricsData(
absl::AnyInvocable<
bool(const absl::flat_hash_map<
std::string,

@ -54,7 +54,7 @@ class MockMetricReader : public opentelemetry::sdk::metrics::MetricReader {
void OnInitialized() noexcept override {}
};
class OTelPluginEnd2EndTest : public ::testing::Test {
class OpenTelemetryPluginEnd2EndTest : public ::testing::Test {
protected:
// Note that we can't use SetUp() here since we want to send in parameters.
void Init(

@ -236,7 +236,7 @@ int main(int argc, char** argv) {
auto meter_provider =
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
meter_provider->AddMetricReader(std::move(prometheus_exporter));
grpc::internal::OpenTelemetryPluginBuilder otel_builder;
grpc::internal::OpenTelemetryPluginBuilderImpl otel_builder;
otel_builder.SetMeterProvider(std::move(meter_provider));
otel_builder.BuildAndRegisterGlobal();
}

@ -403,7 +403,7 @@ void EnableCsmObservability() {
auto meter_provider =
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
meter_provider->AddMetricReader(std::move(prometheus_exporter));
auto observability = grpc::experimental::CsmObservabilityBuilder();
grpc::experimental::CsmObservabilityBuilder observability;
observability.SetMeterProvider(std::move(meter_provider));
auto status = observability.BuildAndRegister();
}

@ -54,7 +54,7 @@ void EnableCsmObservability() {
auto meter_provider =
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
meter_provider->AddMetricReader(std::move(prometheus_exporter));
auto observability = grpc::experimental::CsmObservabilityBuilder();
grpc::experimental::CsmObservabilityBuilder observability;
observability.SetMeterProvider(std::move(meter_provider));
auto status = observability.BuildAndRegister();
}

@ -63,7 +63,7 @@ def _dockerized_sh_test(name, srcs = [], args = [], data = [], size = "medium",
**test_args
)
def _dockerized_genrule(name, cmd, outs, srcs = [], timeout = None, tags = [], exec_compatible_with = [], flaky = None, docker_image_version = None, docker_run_as_root = False):
def _dockerized_genrule(name, cmd, outs, srcs = [], tags = [], exec_compatible_with = [], docker_image_version = None, docker_run_as_root = False):
"""Runs genrule under docker either via RBE or via docker sandbox."""
if docker_image_version:
image_spec = DOCKERIMAGE_CURRENT_VERSIONS.get(docker_image_version, None)
@ -94,8 +94,6 @@ def _dockerized_genrule(name, cmd, outs, srcs = [], timeout = None, tags = [], e
"cmd": cmd,
"srcs": srcs,
"tags": tags,
"flaky": flaky,
"timeout": timeout,
"exec_compatible_with": exec_compatible_with,
"exec_properties": exec_properties,
"outs": outs,
@ -292,7 +290,7 @@ def grpc_build_artifact_task(name, timeout = None, artifact_deps = [], tags = []
cmd = cmd + " $(location " + dep_archive_name + ")"
genrule_srcs.append(dep_archive_name)
_dockerized_genrule(name = name, cmd = cmd, outs = genrule_outs, srcs = genrule_srcs, timeout = timeout, tags = tags, exec_compatible_with = exec_compatible_with, flaky = flaky, docker_image_version = docker_image_version, docker_run_as_root = False)
_dockerized_genrule(name = name, cmd = cmd, outs = genrule_outs, srcs = genrule_srcs, tags = tags, exec_compatible_with = exec_compatible_with, docker_image_version = docker_image_version, docker_run_as_root = False)
# The genrule above always succeeds (even if the underlying build fails), so that we can create rules that depend
# on multiple artifact builds (of which some can fail). The actual build status (exitcode) and the log of the build

@ -35,6 +35,9 @@ def generate_bazel_distribtests(name):
for bazel_version in SUPPORTED_BAZEL_VERSIONS:
for shard_name in _TEST_SHARDS:
# TODO(https://github.com/grpc/grpc/issues/35391): Fix bazel 7 + distribtest_python test
if bazel_version.startswith("7") and shard_name == "distribtest_python":
continue
test_name = "bazel_distribtest_%s_%s" % (bazel_version, shard_name)
grpc_run_bazel_distribtest_test(
name = test_name,

@ -2642,10 +2642,12 @@ src/core/lib/matchers/matchers.cc \
src/core/lib/matchers/matchers.h \
src/core/lib/promise/activity.cc \
src/core/lib/promise/activity.h \
src/core/lib/promise/all_ok.h \
src/core/lib/promise/arena_promise.h \
src/core/lib/promise/cancel_callback.h \
src/core/lib/promise/context.h \
src/core/lib/promise/detail/basic_seq.h \
src/core/lib/promise/detail/join_state.h \
src/core/lib/promise/detail/promise_factory.h \
src/core/lib/promise/detail/promise_like.h \
src/core/lib/promise/detail/seq_state.h \
@ -2670,6 +2672,7 @@ src/core/lib/promise/sleep.h \
src/core/lib/promise/status_flag.h \
src/core/lib/promise/trace.cc \
src/core/lib/promise/trace.h \
src/core/lib/promise/try_join.h \
src/core/lib/promise/try_seq.h \
src/core/lib/resolver/endpoint_addresses.cc \
src/core/lib/resolver/endpoint_addresses.h \
@ -2873,6 +2876,7 @@ src/core/lib/surface/server.h \
src/core/lib/surface/validate_metadata.cc \
src/core/lib/surface/validate_metadata.h \
src/core/lib/surface/version.cc \
src/core/lib/surface/wait_for_cq_end_op.h \
src/core/lib/transport/batch_builder.cc \
src/core/lib/transport/batch_builder.h \
src/core/lib/transport/bdp_estimator.cc \

@ -2421,10 +2421,12 @@ src/core/lib/matchers/matchers.cc \
src/core/lib/matchers/matchers.h \
src/core/lib/promise/activity.cc \
src/core/lib/promise/activity.h \
src/core/lib/promise/all_ok.h \
src/core/lib/promise/arena_promise.h \
src/core/lib/promise/cancel_callback.h \
src/core/lib/promise/context.h \
src/core/lib/promise/detail/basic_seq.h \
src/core/lib/promise/detail/join_state.h \
src/core/lib/promise/detail/promise_factory.h \
src/core/lib/promise/detail/promise_like.h \
src/core/lib/promise/detail/seq_state.h \
@ -2449,6 +2451,7 @@ src/core/lib/promise/sleep.h \
src/core/lib/promise/status_flag.h \
src/core/lib/promise/trace.cc \
src/core/lib/promise/trace.h \
src/core/lib/promise/try_join.h \
src/core/lib/promise/try_seq.h \
src/core/lib/resolver/endpoint_addresses.cc \
src/core/lib/resolver/endpoint_addresses.h \
@ -2653,6 +2656,7 @@ src/core/lib/surface/server.h \
src/core/lib/surface/validate_metadata.cc \
src/core/lib/surface/validate_metadata.h \
src/core/lib/surface/version.cc \
src/core/lib/surface/wait_for_cq_end_op.h \
src/core/lib/transport/README.md \
src/core/lib/transport/batch_builder.cc \
src/core/lib/transport/batch_builder.h \

Loading…
Cancel
Save