xref: /aosp_15_r20/external/grpc-grpc/test/core/client_channel/resolvers/dns_resolver_cooldown_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include <inttypes.h>
20 
21 #include <functional>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 #include <vector>
26 
27 #include "absl/status/status.h"
28 #include "absl/status/statusor.h"
29 #include "absl/strings/string_view.h"
30 #include "gtest/gtest.h"
31 
32 #include <grpc/event_engine/event_engine.h>
33 #include <grpc/grpc.h>
34 #include <grpc/impl/channel_arg_names.h>
35 #include <grpc/support/alloc.h>
36 #include <grpc/support/atm.h>
37 #include <grpc/support/log.h>
38 #include <grpc/support/sync.h>
39 #include <grpc/support/time.h>
40 
41 #include "src/core/lib/channel/channel_args.h"
42 #include "src/core/lib/config/core_configuration.h"
43 #include "src/core/lib/event_engine/default_event_engine.h"
44 #include "src/core/lib/experiments/experiments.h"
45 #include "src/core/lib/gprpp/debug_location.h"
46 #include "src/core/lib/gprpp/no_destruct.h"
47 #include "src/core/lib/gprpp/notification.h"
48 #include "src/core/lib/gprpp/orphanable.h"
49 #include "src/core/lib/gprpp/time.h"
50 #include "src/core/lib/gprpp/work_serializer.h"
51 #include "src/core/lib/iomgr/closure.h"
52 #include "src/core/lib/iomgr/error.h"
53 #include "src/core/lib/iomgr/exec_ctx.h"
54 #include "src/core/lib/iomgr/iomgr_fwd.h"
55 #include "src/core/lib/iomgr/pollset.h"
56 #include "src/core/lib/iomgr/pollset_set.h"
57 #include "src/core/lib/iomgr/resolve_address.h"
58 #include "src/core/lib/iomgr/resolved_address.h"
59 #include "src/core/lib/uri/uri_parser.h"
60 #include "src/core/resolver/dns/c_ares/grpc_ares_wrapper.h"
61 #include "src/core/resolver/endpoint_addresses.h"
62 #include "src/core/resolver/resolver.h"
63 #include "src/core/resolver/resolver_factory.h"
64 #include "src/core/resolver/resolver_registry.h"
65 #include "test/core/util/test_config.h"
66 
67 using ::grpc_event_engine::experimental::GetDefaultEventEngine;
68 
69 constexpr int kMinResolutionPeriodMs = 1000;
70 
71 static std::shared_ptr<grpc_core::WorkSerializer>* g_work_serializer;
72 
73 static grpc_ares_request* (*g_default_dns_lookup_ares)(
74     const char* dns_server, const char* name, const char* default_port,
75     grpc_pollset_set* interested_parties, grpc_closure* on_done,
76     std::unique_ptr<grpc_core::EndpointAddressesList>* addresses,
77     int query_timeout_ms);
78 
79 // Counter incremented by TestDNSResolver::LookupHostname indicating the
80 // number of times a system-level resolution has happened.
81 static int g_resolution_count;
82 
83 static struct iomgr_args {
84   gpr_event ev;
85   gpr_atm done_atm;
86   gpr_mu* mu;
87   grpc_pollset* pollset;
88   grpc_pollset_set* pollset_set;
89 } g_iomgr_args;
90 
91 namespace {
92 
93 class TestDNSResolver : public grpc_core::DNSResolver {
94  public:
TestDNSResolver(std::shared_ptr<grpc_core::DNSResolver> default_resolver)95   explicit TestDNSResolver(
96       std::shared_ptr<grpc_core::DNSResolver> default_resolver)
97       : default_resolver_(std::move(default_resolver)),
98         engine_(GetDefaultEventEngine()) {}
99   // Wrapper around default resolve_address in order to count the number of
100   // times we incur in a system-level name resolution.
LookupHostname(std::function<void (absl::StatusOr<std::vector<grpc_resolved_address>>)> on_resolved,absl::string_view name,absl::string_view default_port,grpc_core::Duration timeout,grpc_pollset_set * interested_parties,absl::string_view name_server)101   TaskHandle LookupHostname(
102       std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
103           on_resolved,
104       absl::string_view name, absl::string_view default_port,
105       grpc_core::Duration timeout, grpc_pollset_set* interested_parties,
106       absl::string_view name_server) override {
107     auto result = default_resolver_->LookupHostname(
108         std::move(on_resolved), name, default_port, timeout, interested_parties,
109         name_server);
110     ++g_resolution_count;
111     static grpc_core::Timestamp last_resolution_time =
112         grpc_core::Timestamp::ProcessEpoch();
113     if (last_resolution_time == grpc_core::Timestamp::ProcessEpoch()) {
114       last_resolution_time = grpc_core::Timestamp::FromTimespecRoundUp(
115           gpr_now(GPR_CLOCK_MONOTONIC));
116     } else {
117       auto now = grpc_core::Timestamp::FromTimespecRoundUp(
118           gpr_now(GPR_CLOCK_MONOTONIC));
119       EXPECT_GE(now - last_resolution_time,
120                 grpc_core::Duration::Milliseconds(kMinResolutionPeriodMs));
121       last_resolution_time = now;
122     }
123     // For correct time diff comparisons, make sure that any subsequent calls
124     // to grpc_core::Timestamp::Now() on this thread don't return a time
125     // which is earlier than that returned by the call(s) to
126     // gpr_now(GPR_CLOCK_MONOTONIC) within this function. This is important
127     // because the resolver's last_resolution_timestamp_ will be taken from
128     // grpc_core::Timestamp::Now() right after this returns.
129     grpc_core::ExecCtx::Get()->InvalidateNow();
130     return result;
131   }
132 
LookupHostnameBlocking(absl::string_view name,absl::string_view default_port)133   absl::StatusOr<std::vector<grpc_resolved_address>> LookupHostnameBlocking(
134       absl::string_view name, absl::string_view default_port) override {
135     return default_resolver_->LookupHostnameBlocking(name, default_port);
136   }
137 
LookupSRV(std::function<void (absl::StatusOr<std::vector<grpc_resolved_address>>)> on_resolved,absl::string_view,grpc_core::Duration,grpc_pollset_set *,absl::string_view)138   TaskHandle LookupSRV(
139       std::function<void(absl::StatusOr<std::vector<grpc_resolved_address>>)>
140           on_resolved,
141       absl::string_view /* name */, grpc_core::Duration /* timeout */,
142       grpc_pollset_set* /* interested_parties */,
143       absl::string_view /* name_server */) override {
144     engine_->Run([on_resolved] {
145       grpc_core::ApplicationCallbackExecCtx app_exec_ctx;
146       grpc_core::ExecCtx exec_ctx;
147       on_resolved(absl::UnimplementedError(
148           "The Testing DNS resolver does not support looking up SRV records"));
149     });
150     return {-1, -1};
151   };
152 
LookupTXT(std::function<void (absl::StatusOr<std::string>)> on_resolved,absl::string_view,grpc_core::Duration,grpc_pollset_set *,absl::string_view)153   TaskHandle LookupTXT(
154       std::function<void(absl::StatusOr<std::string>)> on_resolved,
155       absl::string_view /* name */, grpc_core::Duration /* timeout */,
156       grpc_pollset_set* /* interested_parties */,
157       absl::string_view /* name_server */) override {
158     // Not supported
159     engine_->Run([on_resolved] {
160       grpc_core::ApplicationCallbackExecCtx app_exec_ctx;
161       grpc_core::ExecCtx exec_ctx;
162       on_resolved(absl::UnimplementedError(
163           "The Testing DNS resolver does not support looking up TXT records"));
164     });
165     return {-1, -1};
166   };
167 
168   // Not cancellable
Cancel(TaskHandle)169   bool Cancel(TaskHandle /*handle*/) override { return false; }
170 
171  private:
172   std::shared_ptr<grpc_core::DNSResolver> default_resolver_;
173   std::shared_ptr<grpc_event_engine::experimental::EventEngine> engine_;
174 };
175 
176 }  // namespace
177 
test_dns_lookup_ares(const char * dns_server,const char * name,const char * default_port,grpc_pollset_set *,grpc_closure * on_done,std::unique_ptr<grpc_core::EndpointAddressesList> * addresses,int query_timeout_ms)178 static grpc_ares_request* test_dns_lookup_ares(
179     const char* dns_server, const char* name, const char* default_port,
180     grpc_pollset_set* /*interested_parties*/, grpc_closure* on_done,
181     std::unique_ptr<grpc_core::EndpointAddressesList>* addresses,
182     int query_timeout_ms) {
183   // A records should suffice
184   grpc_ares_request* result = g_default_dns_lookup_ares(
185       dns_server, name, default_port, g_iomgr_args.pollset_set, on_done,
186       addresses, query_timeout_ms);
187   ++g_resolution_count;
188   static auto last_resolution_time = grpc_core::Timestamp::ProcessEpoch();
189   auto now =
190       grpc_core::Timestamp::FromTimespecRoundUp(gpr_now(GPR_CLOCK_MONOTONIC));
191   gpr_log(GPR_DEBUG,
192           "last_resolution_time:%" PRId64 " now:%" PRId64
193           " min_time_between:%d",
194           last_resolution_time.milliseconds_after_process_epoch(),
195           now.milliseconds_after_process_epoch(), kMinResolutionPeriodMs);
196   if (last_resolution_time != grpc_core::Timestamp::ProcessEpoch()) {
197     EXPECT_GE(now - last_resolution_time,
198               grpc_core::Duration::Milliseconds(kMinResolutionPeriodMs));
199   }
200   last_resolution_time = now;
201   // For correct time diff comparisons, make sure that any subsequent calls
202   // to grpc_core::Timestamp::Now() on this thread don't return a time
203   // which is earlier than that returned by the call(s) to
204   // gpr_now(GPR_CLOCK_MONOTONIC) within this function. This is important
205   // because the resolver's last_resolution_timestamp_ will be taken from
206   // grpc_core::Timestamp::Now() right after this returns.
207   grpc_core::ExecCtx::Get()->InvalidateNow();
208   return result;
209 }
210 
test_deadline(void)211 static gpr_timespec test_deadline(void) {
212   return grpc_timeout_seconds_to_deadline(100);
213 }
214 
do_nothing(void *,grpc_error_handle)215 static void do_nothing(void* /*arg*/, grpc_error_handle /*error*/) {}
216 
iomgr_args_init(iomgr_args * args)217 static void iomgr_args_init(iomgr_args* args) {
218   gpr_event_init(&args->ev);
219   args->pollset = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
220   grpc_pollset_init(args->pollset, &args->mu);
221   args->pollset_set = grpc_pollset_set_create();
222   grpc_pollset_set_add_pollset(args->pollset_set, args->pollset);
223   gpr_atm_rel_store(&args->done_atm, 0);
224 }
225 
iomgr_args_finish(iomgr_args * args)226 static void iomgr_args_finish(iomgr_args* args) {
227   ASSERT_TRUE(gpr_event_wait(&args->ev, test_deadline()));
228   grpc_pollset_set_del_pollset(args->pollset_set, args->pollset);
229   grpc_pollset_set_destroy(args->pollset_set);
230   grpc_closure do_nothing_cb;
231   GRPC_CLOSURE_INIT(&do_nothing_cb, do_nothing, nullptr,
232                     grpc_schedule_on_exec_ctx);
233   gpr_mu_lock(args->mu);
234   grpc_pollset_shutdown(args->pollset, &do_nothing_cb);
235   gpr_mu_unlock(args->mu);
236   // exec_ctx needs to be flushed before calling grpc_pollset_destroy()
237   grpc_core::ExecCtx::Get()->Flush();
238   grpc_pollset_destroy(args->pollset);
239   gpr_free(args->pollset);
240 }
241 
n_sec_deadline(int seconds)242 static grpc_core::Timestamp n_sec_deadline(int seconds) {
243   return grpc_core::Timestamp::FromTimespecRoundUp(
244       grpc_timeout_seconds_to_deadline(seconds));
245 }
246 
poll_pollset_until_request_done(iomgr_args * args)247 static void poll_pollset_until_request_done(iomgr_args* args) {
248   grpc_core::ExecCtx exec_ctx;
249   grpc_core::Timestamp deadline = n_sec_deadline(10);
250   while (true) {
251     bool done = gpr_atm_acq_load(&args->done_atm) != 0;
252     if (done) {
253       break;
254     }
255     grpc_core::Duration time_left = deadline - grpc_core::Timestamp::Now();
256     gpr_log(GPR_DEBUG, "done=%d, time_left=%" PRId64, done, time_left.millis());
257     ASSERT_GE(time_left, grpc_core::Duration::Zero());
258     grpc_pollset_worker* worker = nullptr;
259     gpr_mu_lock(args->mu);
260     GRPC_LOG_IF_ERROR("pollset_work", grpc_pollset_work(args->pollset, &worker,
261                                                         n_sec_deadline(1)));
262     gpr_mu_unlock(args->mu);
263     grpc_core::ExecCtx::Get()->Flush();
264   }
265   gpr_event_set(&args->ev, reinterpret_cast<void*>(1));
266 }
267 
268 struct OnResolutionCallbackArg;
269 
270 class ResultHandler : public grpc_core::Resolver::ResultHandler {
271  public:
272   using ResultCallback = void (*)(OnResolutionCallbackArg* state);
273 
SetCallback(ResultCallback result_cb,OnResolutionCallbackArg * state)274   void SetCallback(ResultCallback result_cb, OnResolutionCallbackArg* state) {
275     ASSERT_EQ(result_cb_, nullptr);
276     result_cb_ = result_cb;
277     ASSERT_EQ(state_, nullptr);
278     state_ = state;
279   }
280 
ReportResult(grpc_core::Resolver::Result result)281   void ReportResult(grpc_core::Resolver::Result result) override {
282     if (result.result_health_callback != nullptr) {
283       result.result_health_callback(absl::OkStatus());
284     }
285     ASSERT_NE(result_cb_, nullptr);
286     ASSERT_NE(state_, nullptr);
287     ResultCallback cb = result_cb_;
288     OnResolutionCallbackArg* state = state_;
289     result_cb_ = nullptr;
290     state_ = nullptr;
291     cb(state);
292   }
293 
294  private:
295   ResultCallback result_cb_ = nullptr;
296   OnResolutionCallbackArg* state_ = nullptr;
297 };
298 
299 struct OnResolutionCallbackArg {
300   const char* uri_str = nullptr;
301   grpc_core::OrphanablePtr<grpc_core::Resolver> resolver;
302   ResultHandler* result_handler;
303 };
304 
305 // Set to true by the last callback in the resolution chain.
306 static grpc_core::NoDestruct<grpc_core::Notification> g_all_callbacks_invoked;
307 
308 // It's interesting to run a few rounds of this test because as
309 // we run more rounds, the base starting time
310 // (i.e. ExecCtx g_start_time) gets further and further away
311 // from "Now()". Thus the more rounds ran, the more highlighted the
312 // difference is between absolute and relative times values.
on_fourth_resolution(OnResolutionCallbackArg * cb_arg)313 static void on_fourth_resolution(OnResolutionCallbackArg* cb_arg) {
314   gpr_log(GPR_INFO, "4th: g_resolution_count: %d", g_resolution_count);
315   ASSERT_EQ(g_resolution_count, 4);
316   cb_arg->resolver.reset();
317   gpr_atm_rel_store(&g_iomgr_args.done_atm, 1);
318   gpr_mu_lock(g_iomgr_args.mu);
319   GRPC_LOG_IF_ERROR("pollset_kick",
320                     grpc_pollset_kick(g_iomgr_args.pollset, nullptr));
321   gpr_mu_unlock(g_iomgr_args.mu);
322   delete cb_arg;
323   g_all_callbacks_invoked->Notify();
324 }
325 
on_third_resolution(OnResolutionCallbackArg * cb_arg)326 static void on_third_resolution(OnResolutionCallbackArg* cb_arg) {
327   gpr_log(GPR_INFO, "3rd: g_resolution_count: %d", g_resolution_count);
328   ASSERT_EQ(g_resolution_count, 3);
329   cb_arg->result_handler->SetCallback(on_fourth_resolution, cb_arg);
330   cb_arg->resolver->RequestReresolutionLocked();
331   gpr_mu_lock(g_iomgr_args.mu);
332   GRPC_LOG_IF_ERROR("pollset_kick",
333                     grpc_pollset_kick(g_iomgr_args.pollset, nullptr));
334   gpr_mu_unlock(g_iomgr_args.mu);
335 }
336 
on_second_resolution(OnResolutionCallbackArg * cb_arg)337 static void on_second_resolution(OnResolutionCallbackArg* cb_arg) {
338   gpr_log(GPR_INFO, "2nd: g_resolution_count: %d", g_resolution_count);
339   // The resolution callback was not invoked until new data was
340   // available, which was delayed until after the cooldown period.
341   ASSERT_EQ(g_resolution_count, 2);
342   cb_arg->result_handler->SetCallback(on_third_resolution, cb_arg);
343   cb_arg->resolver->RequestReresolutionLocked();
344   gpr_mu_lock(g_iomgr_args.mu);
345   GRPC_LOG_IF_ERROR("pollset_kick",
346                     grpc_pollset_kick(g_iomgr_args.pollset, nullptr));
347   gpr_mu_unlock(g_iomgr_args.mu);
348 }
349 
on_first_resolution(OnResolutionCallbackArg * cb_arg)350 static void on_first_resolution(OnResolutionCallbackArg* cb_arg) {
351   gpr_log(GPR_INFO, "1st: g_resolution_count: %d", g_resolution_count);
352   // There's one initial system-level resolution and one invocation of a
353   // notification callback (the current function).
354   ASSERT_EQ(g_resolution_count, 1);
355   cb_arg->result_handler->SetCallback(on_second_resolution, cb_arg);
356   cb_arg->resolver->RequestReresolutionLocked();
357   gpr_mu_lock(g_iomgr_args.mu);
358   GRPC_LOG_IF_ERROR("pollset_kick",
359                     grpc_pollset_kick(g_iomgr_args.pollset, nullptr));
360   gpr_mu_unlock(g_iomgr_args.mu);
361 }
362 
start_test_under_work_serializer(void * arg)363 static void start_test_under_work_serializer(void* arg) {
364   OnResolutionCallbackArg* res_cb_arg =
365       static_cast<OnResolutionCallbackArg*>(arg);
366   res_cb_arg->result_handler = new ResultHandler();
367   grpc_core::ResolverFactory* factory = grpc_core::CoreConfiguration::Get()
368                                             .resolver_registry()
369                                             .LookupResolverFactory("dns");
370   absl::StatusOr<grpc_core::URI> uri =
371       grpc_core::URI::Parse(res_cb_arg->uri_str);
372   gpr_log(GPR_DEBUG, "test: '%s' should be valid for '%s'", res_cb_arg->uri_str,
373           std::string(factory->scheme()).c_str());
374   if (!uri.ok()) {
375     gpr_log(GPR_ERROR, "%s", uri.status().ToString().c_str());
376     ASSERT_TRUE(uri.ok());
377   }
378   grpc_core::ResolverArgs args;
379   args.uri = std::move(*uri);
380   args.work_serializer = *g_work_serializer;
381   args.result_handler = std::unique_ptr<grpc_core::Resolver::ResultHandler>(
382       res_cb_arg->result_handler);
383   g_resolution_count = 0;
384 
385   grpc_arg cooldown_arg = grpc_channel_arg_integer_create(
386       const_cast<char*>(GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS),
387       kMinResolutionPeriodMs);
388   grpc_channel_args cooldown_args = {1, &cooldown_arg};
389   args.args = grpc_core::ChannelArgs::FromC(&cooldown_args);
390   args.args = args.args.SetObject(GetDefaultEventEngine());
391   res_cb_arg->resolver = factory->CreateResolver(std::move(args));
392   ASSERT_NE(res_cb_arg->resolver, nullptr);
393   // First resolution, would incur in system-level resolution.
394   res_cb_arg->result_handler->SetCallback(on_first_resolution, res_cb_arg);
395   res_cb_arg->resolver->StartLocked();
396 }
397 
test_cooldown()398 static void test_cooldown() {
399   grpc_core::ExecCtx exec_ctx;
400   iomgr_args_init(&g_iomgr_args);
401   OnResolutionCallbackArg* res_cb_arg = new OnResolutionCallbackArg();
402   res_cb_arg->uri_str = "dns:127.0.0.1";
403 
404   (*g_work_serializer)
405       ->Run([res_cb_arg]() { start_test_under_work_serializer(res_cb_arg); },
406             DEBUG_LOCATION);
407   grpc_core::ExecCtx::Get()->Flush();
408   poll_pollset_until_request_done(&g_iomgr_args);
409   iomgr_args_finish(&g_iomgr_args);
410 }
411 
TEST(DnsResolverCooldownTest,MainTest)412 TEST(DnsResolverCooldownTest, MainTest) {
413   // TODO(yijiem): This test tests the cooldown behavior of the PollingResolver
414   // interface. To do that, it overrides the grpc_dns_lookup_hostname_ares
415   // function and overrides the iomgr's g_dns_resolver system. We would need to
416   // rewrite this test for EventEngine using a custom EE DNSResolver or adding
417   // to the resolver_fuzzer.
418   if (grpc_core::IsEventEngineDnsEnabled()) {
419     GTEST_SKIP() << "Not with event engine dns";
420   }
421   grpc_init();
422 
423   auto work_serializer = std::make_shared<grpc_core::WorkSerializer>(
424       grpc_event_engine::experimental::GetDefaultEventEngine());
425   g_work_serializer = &work_serializer;
426 
427   g_default_dns_lookup_ares = grpc_dns_lookup_hostname_ares;
428   grpc_dns_lookup_hostname_ares = test_dns_lookup_ares;
429   grpc_core::ResetDNSResolver(
430       std::make_unique<TestDNSResolver>(grpc_core::GetDNSResolver()));
431 
432   test_cooldown();
433 
434   grpc_shutdown();
435   g_all_callbacks_invoked->WaitForNotification();
436 }
437 
main(int argc,char ** argv)438 int main(int argc, char** argv) {
439   grpc::testing::TestEnvironment env(&argc, argv);
440   ::testing::InitGoogleTest(&argc, argv);
441   return RUN_ALL_TESTS();
442 }
443