• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 13012068652

28 Jan 2025 01:59PM UTC coverage: 64.71% (+0.01%) from 64.699%
13012068652

Pull #14724

github

web-flow
Merge b15562560 into db18c3a17
Pull Request #14724: dnsdist: Add meson support

38328 of 90334 branches covered (42.43%)

Branch coverage included in aggregate %.

361 of 513 new or added lines in 35 files covered. (70.37%)

42 existing lines in 13 files now uncovered.

128150 of 166934 relevant lines covered (76.77%)

4540890.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.86
/pdns/dnsdistdist/test-dnsdistlbpolicies_cc.cc
1

2
#ifndef BOOST_TEST_DYN_LINK
3
#define BOOST_TEST_DYN_LINK
4
#endif
5

6
#define BOOST_TEST_NO_MAIN
7

8
#include <boost/test/unit_test.hpp>
9

10
#include "dnsdist.hh"
11
#include "dnsdist-lua.hh"
12
#include "dnsdist-lua-ffi.hh"
13
#include "dnsdist-snmp.hh"
14
#include "dolog.hh"
15

16
#include "ext/luawrapper/include/LuaContext.hpp"
17
RecursiveLockGuarded<LuaContext> g_lua{LuaContext()};
18

19
std::unique_ptr<DNSDistSNMPAgent> g_snmpAgent{nullptr};
20

21
#if BENCH_POLICIES
22
#include "dnsdist-rings.hh"
23
Rings g_rings;
24
#endif /* BENCH_POLICIES */
25

26
/* add stub implementations, we don't want to include the corresponding object files
27
   and their dependencies */
28

29
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): this is a stub, the real one is not that simple..
30
bool TLSFrontend::setupTLS()
31
{
×
32
  return true;
×
33
}
×
34

35
// NOLINTNEXTLINE(readability-convert-member-functions-to-static): this is a stub, the real one is not that simple..
36
bool DNSDistSNMPAgent::sendDNSTrap(const DNSQuestion& dnsQuestion, const std::string& reason)
37
{
×
NEW
38
  (void)dnsQuestion;
×
NEW
39
  (void)reason;
×
40
  return false;
×
41
}
×
42

43
void setLuaNoSideEffect()
44
{
×
45
}
×
46

47
bool setupDoTProtocolNegotiation(std::shared_ptr<TLSCtx>& tlsCtx)
48
{
×
49
  (void)tlsCtx;
×
50
  return true;
×
51
}
×
52

53
// NOLINTNEXTLINE(performance-unnecessary-value-param): this is a stub, the real one is not that simple and the performance does not matter
54
void responderThread(std::shared_ptr<DownstreamState> dss)
55
{
×
NEW
56
  (void)dss;
×
UNCOV
57
}
×
58

59
string g_outputBuffer;
60

61
static DNSQuestion getDQ(const DNSName* providedName = nullptr)
62
{
2,000✔
63
  static const DNSName qname("powerdns.com.");
2,000✔
64
  static PacketBuffer packet(sizeof(dnsheader));
2,000✔
65
  static InternalQueryState ids;
2,000✔
66
  ids.origDest = ComboAddress("127.0.0.1:53");
2,000✔
67
  ids.origRemote = ComboAddress("192.0.2.1:42");
2,000✔
68
  ids.qname = providedName != nullptr ? *providedName : qname;
2,000!
69
  ids.qtype = QType::A;
2,000✔
70
  ids.qclass = QClass::IN;
2,000✔
71
  ids.protocol = dnsdist::Protocol::DoUDP;
2,000✔
72
  ids.queryRealTime.start();
2,000✔
73

74
  DNSQuestion dnsQuestion(ids, packet);
2,000✔
75
  return dnsQuestion;
2,000✔
76
}
2,000✔
77

78
static void benchPolicy(const ServerPolicy& pol)
79
{
2✔
80
#if BENCH_POLICIES
81
  std::vector<DNSName> names;
82
  names.reserve(1000);
83
  for (size_t idx = 0; idx < 1000; idx++) {
84
    names.emplace_back("powerdns-" + std::to_string(idx) + ".com.");
85
  }
86
  ServerPolicy::NumberedServerVector servers;
87
  for (size_t idx = 1; idx <= 10; idx++) {
88
    servers.emplace_back(idx, std::make_shared<DownstreamState>(ComboAddress("192.0.2." + std::to_string(idx) + ":53")));
89
    servers.at(idx - 1).second->setUp();
90
    /* we need to have a weight of at least 1000 to get an optimal repartition with the consistent hashing algo */
91
    servers.at(idx - 1).second->setWeight(1000);
92
    /* make sure that the hashes have been computed */
93
    servers.at(idx - 1).second->hash();
94
  }
95

96
  StopWatch sw;
97
  sw.start();
98
  for (size_t idx = 0; idx < 1000; idx++) {
99
    for (const auto& name : names) {
100
      auto dnsQuestion = getDQ(&name);
101
      auto server = pol.getSelectedBackend(servers, dnsQuestion);
102
    }
103
  }
104
  cerr << pol.name << " took " << std::to_string(sw.udiff()) << " us for " << names.size() << endl;
105
#else
106
  (void)pol;
2✔
107
#endif /* BENCH_POLICIES */
2✔
108
}
2✔
109

110
static void resetLuaContext()
111
{
4✔
112
  /* we need to reset this before cleaning the Lua state because the server policy might holds
113
     a reference to a Lua function (Lua policies) */
114
  dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
4✔
115
    config.d_lbPolicy = std::make_shared<ServerPolicy>("leastOutstanding", leastOutstanding, false);
4✔
116
  });
4✔
117
  /* we actually need this line to clear the cached state for this thread */
118
  BOOST_REQUIRE_EQUAL(dnsdist::configuration::getCurrentRuntimeConfiguration().d_lbPolicy->getName(), "leastOutstanding");
4✔
119
  *(g_lua.lock()) = LuaContext();
4✔
120
}
4✔
121

122
BOOST_AUTO_TEST_SUITE(dnsdistlbpolicies)
123

124
#if 0
125
BOOST_AUTO_TEST_CASE(test_firstAvailable)
126
{
127
  auto dnsQuestion = getDQ();
128

129
  ServerPolicy pol{"firstAvailable", firstAvailable, false};
130
  ServerPolicy::NumberedServerVector servers;
131
  servers.emplace_back(1, std::make_shared<DownstreamState>(ComboAddress("192.0.2.1:53")));
132

133
  /* servers start as 'down' */
134
  auto server = pol.getSelectedBackend(servers, dnsQuestion);
135
  BOOST_CHECK(server == nullptr);
136

137
  /* mark the server as 'up' */
138
  servers.at(0).second->setUp();
139
  server = pol.getSelectedBackend(servers, dnsQuestion);
140
  BOOST_CHECK(server != nullptr);
141

142
  /* add a second server, we should still get the first one */
143
  servers.emplace_back(2, std::make_shared<DownstreamState>(ComboAddress("192.0.2.2:53")));
144
  server = pol.getSelectedBackend(servers, dnsQuestion);
145
  BOOST_REQUIRE(server != nullptr);
146
  BOOST_CHECK(server == servers.at(0).second);
147

148
  /* mark the first server as 'down', second as 'up' */
149
  servers.at(0).second->setDown();
150
  servers.at(1).second->setUp();
151
  server = pol.getSelectedBackend(servers, dnsQuestion);
152
  BOOST_REQUIRE(server != nullptr);
153
  BOOST_CHECK(server == servers.at(1).second);
154

155
  benchPolicy(pol);
156
}
157

158
BOOST_AUTO_TEST_CASE(test_firstAvailableWithOrderAndQPS)
159
{
160
  auto dnsQuestion = getDQ();
161
  size_t qpsLimit = 10;
162

163
  ServerPolicy pol{"firstAvailable", firstAvailable, false};
164
  ServerPolicy::NumberedServerVector servers;
165
  servers.emplace_back(1, std::make_shared<DownstreamState>(ComboAddress("192.0.2.1:53")));
166
  servers.emplace_back(2, std::make_shared<DownstreamState>(ComboAddress("192.0.2.2:53")));
167
  /* Second server has a higher order, so most queries should be routed to the first (remember that
168
     we need to keep them ordered!).
169
     However the first server has a QPS limit at 10 qps, so any query above that should be routed
170
     to the second server. */
171
  servers.at(0).second->d_config.order = 1;
172
  servers.at(1).second->d_config.order = 2;
173
  servers.at(0).second->qps = QPSLimiter(qpsLimit, qpsLimit);
174
  /* mark the servers as 'up' */
175
  servers.at(0).second->setUp();
176
  servers.at(1).second->setUp();
177

178
  /* the first queries under the QPS limit should be
179
     sent to the first server */
180
  for (size_t idx = 0; idx < qpsLimit; idx++) {
181
    auto server = pol.getSelectedBackend(servers, dnsQuestion);
182
    BOOST_REQUIRE(server != nullptr);
183
    BOOST_CHECK(server == servers.at(0).second);
184
    server->incQueriesCount();
185
  }
186

187
  /* then to the second server */
188
  for (size_t idx = 0; idx < 100; idx++) {
189
    auto server = pol.getSelectedBackend(servers, dnsQuestion);
190
    BOOST_REQUIRE(server != nullptr);
191
    BOOST_CHECK(server == servers.at(1).second);
192
    server->incQueriesCount();
193
  }
194
}
195

196
BOOST_AUTO_TEST_CASE(test_roundRobin)
197
{
198
  auto dnsQuestion = getDQ();
199

200
  ServerPolicy pol{"roundrobin", roundrobin, false};
201
  ServerPolicy::NumberedServerVector servers;
202

203
  /* selecting a server on an empty server list */
204
  dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
205
    config.d_roundrobinFailOnNoServer = false;
206
  });
207
  auto server = pol.getSelectedBackend(servers, dnsQuestion);
208
  BOOST_CHECK(server == nullptr);
209

210
  servers.emplace_back(1, std::make_shared<DownstreamState>(ComboAddress("192.0.2.1:53")));
211

212
  /* servers start as 'down' but the RR policy returns a server unless d_roundrobinFailOnNoServer is set */
213
  dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
214
    config.d_roundrobinFailOnNoServer = true;
215
  });
216
  server = pol.getSelectedBackend(servers, dnsQuestion);
217
  BOOST_CHECK(server == nullptr);
218
  dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
219
    config.d_roundrobinFailOnNoServer = false;
220
  });
221
  server = pol.getSelectedBackend(servers, dnsQuestion);
222
  BOOST_CHECK(server != nullptr);
223

224
  /* mark the server as 'up' */
225
  servers.at(0).second->setUp();
226
  server = pol.getSelectedBackend(servers, dnsQuestion);
227
  BOOST_CHECK(server != nullptr);
228

229
  /* add a second server, we should get the first one then the second one */
230
  servers.emplace_back(2, std::make_shared<DownstreamState>(ComboAddress("192.0.2.2:53")));
231
  servers.at(1).second->setUp();
232
  server = pol.getSelectedBackend(servers, dnsQuestion);
233
  BOOST_REQUIRE(server != nullptr);
234
  BOOST_CHECK(server == servers.at(0).second);
235
  server = pol.getSelectedBackend(servers, dnsQuestion);
236
  BOOST_REQUIRE(server != nullptr);
237
  BOOST_CHECK(server == servers.at(1).second);
238

239
  /* mark the first server as 'down', second as 'up' */
240
  servers.at(0).second->setDown();
241
  servers.at(1).second->setUp();
242
  server = pol.getSelectedBackend(servers, dnsQuestion);
243
  BOOST_REQUIRE(server != nullptr);
244
  BOOST_CHECK(server == servers.at(1).second);
245

246
  std::map<std::shared_ptr<DownstreamState>, uint64_t> serversMap;
247
  /* mark all servers 'up' */
248
  for (auto& serv : servers) {
249
    serv.second->setUp();
250
    serversMap[serv.second] = 0;
251
  }
252

253
  for (size_t idx = 0; idx < 1000; idx++) {
254
    server = pol.getSelectedBackend(servers, dnsQuestion);
255
    BOOST_REQUIRE(serversMap.count(server) == 1);
256
    ++serversMap[server];
257
  }
258
  uint64_t total = 0;
259
  for (const auto& entry : serversMap) {
260
    BOOST_CHECK_EQUAL(entry.second, 1000 / servers.size());
261
    total += entry.second;
262
  }
263
  BOOST_CHECK_EQUAL(total, 1000U);
264

265
  benchPolicy(pol);
266
}
267

268
BOOST_AUTO_TEST_CASE(test_leastOutstanding)
269
{
270
  auto dnsQuestion = getDQ();
271

272
  ServerPolicy pol{"leastOutstanding", leastOutstanding, false};
273
  ServerPolicy::NumberedServerVector servers;
274
  servers.emplace_back(1, std::make_shared<DownstreamState>(ComboAddress("192.0.2.1:53")));
275

276
  /* servers start as 'down' */
277
  auto server = pol.getSelectedBackend(servers, dnsQuestion);
278
  BOOST_CHECK(server == nullptr);
279

280
  /* mark the server as 'up' */
281
  servers.at(0).second->setUp();
282
  server = pol.getSelectedBackend(servers, dnsQuestion);
283
  BOOST_CHECK(server != nullptr);
284

285
  /* add a second server, we should still get the first one */
286
  servers.emplace_back(2, std::make_shared<DownstreamState>(ComboAddress("192.0.2.2:53")));
287
  server = pol.getSelectedBackend(servers, dnsQuestion);
288
  BOOST_REQUIRE(server != nullptr);
289
  BOOST_CHECK(server == servers.at(0).second);
290

291
  /* mark the first server as 'down', second as 'up' */
292
  servers.at(0).second->setDown();
293
  servers.at(1).second->setUp();
294
  server = pol.getSelectedBackend(servers, dnsQuestion);
295
  BOOST_REQUIRE(server != nullptr);
296
  BOOST_CHECK(server == servers.at(1).second);
297

298
  /* mark both servers as 'up', increase the outstanding count of the first one */
299
  servers.at(0).second->setUp();
300
  servers.at(0).second->outstanding = 42;
301
  servers.at(1).second->setUp();
302
  server = pol.getSelectedBackend(servers, dnsQuestion);
303
  BOOST_REQUIRE(server != nullptr);
304
  BOOST_CHECK(server == servers.at(1).second);
305

306
  benchPolicy(pol);
307
}
308

309
BOOST_AUTO_TEST_CASE(test_wrandom)
310
{
311
  auto dnsQuestion = getDQ();
312

313
  ServerPolicy pol{"wrandom", wrandom, false};
314
  ServerPolicy::NumberedServerVector servers;
315
  std::map<std::shared_ptr<DownstreamState>, uint64_t> serversMap;
316
  for (size_t idx = 1; idx <= 10; idx++) {
317
    servers.emplace_back(idx, std::make_shared<DownstreamState>(ComboAddress("192.0.2." + std::to_string(idx) + ":53")));
318
    serversMap[servers.at(idx - 1).second] = 0;
319
    servers.at(idx - 1).second->setUp();
320
  }
321

322
  benchPolicy(pol);
323

324
  for (size_t idx = 0; idx < 1000; idx++) {
325
    auto server = pol.getSelectedBackend(servers, dnsQuestion);
326
    BOOST_REQUIRE(serversMap.count(server) == 1);
327
    ++serversMap[server];
328
  }
329
  uint64_t total = 0;
330
  for (const auto& entry : serversMap) {
331
    BOOST_CHECK_GT(entry.second, 0U);
332
    BOOST_CHECK_GT(entry.second, (1000 / servers.size() / 2));
333
    BOOST_CHECK_LT(entry.second, (1000 / servers.size() * 2));
334
    total += entry.second;
335
  }
336
  BOOST_CHECK_EQUAL(total, 1000U);
337

338
  /* reset */
339
  for (auto& entry : serversMap) {
340
    entry.second = 0;
341
    BOOST_CHECK_EQUAL(entry.first->d_config.d_weight, 1);
342
  }
343

344
  /* reset */
345
  for (auto& entry : serversMap) {
346
    entry.second = 0;
347
    BOOST_CHECK_EQUAL(entry.first->d_config.d_weight, 1);
348
  }
349
  /* change the weight of the last server to 100, default is 1 */
350
  servers.at(servers.size() - 1).second->d_config.d_weight = 100;
351

352
  for (size_t idx = 0; idx < 1000; idx++) {
353
    auto server = pol.getSelectedBackend(servers, dnsQuestion);
354
    BOOST_REQUIRE(serversMap.count(server) == 1);
355
    ++serversMap[server];
356
  }
357

358
  total = 0;
359
  uint64_t totalW = 0;
360
  for (const auto& entry : serversMap) {
361
    total += entry.second;
362
    totalW += entry.first->d_config.d_weight;
363
  }
364
  BOOST_CHECK_EQUAL(total, 1000U);
365
  auto last = servers.at(servers.size() - 1).second;
366
  const auto got = serversMap[last];
367
  float expected = static_cast<float>(1000 * 1.0 * last->d_config.d_weight) / static_cast<float>(totalW);
368
  BOOST_CHECK_GT(got, expected / 2);
369
  BOOST_CHECK_LT(got, expected * 2);
370
}
371

372
BOOST_AUTO_TEST_CASE(test_whashed)
373
{
374
  std::vector<DNSName> names;
375
  names.reserve(1000);
376
  for (size_t idx = 0; idx < 1000; idx++) {
377
    names.emplace_back("powerdns-" + std::to_string(idx) + ".com.");
378
  }
379

380
  ServerPolicy pol{"whashed", whashed, false};
381
  ServerPolicy::NumberedServerVector servers;
382
  std::map<std::shared_ptr<DownstreamState>, uint64_t> serversMap;
383
  for (size_t idx = 1; idx <= 10; idx++) {
384
    servers.emplace_back(idx, std::make_shared<DownstreamState>(ComboAddress("192.0.2." + std::to_string(idx) + ":53")));
385
    serversMap[servers.at(idx - 1).second] = 0;
386
    servers.at(idx - 1).second->setUp();
387
  }
388

389
  benchPolicy(pol);
390

391
  for (const auto& name : names) {
392
    auto dnsQuestion = getDQ(&name);
393
    auto server = pol.getSelectedBackend(servers, dnsQuestion);
394
    BOOST_REQUIRE(serversMap.count(server) == 1);
395
    ++serversMap[server];
396
  }
397

398
  uint64_t total = 0;
399
  for (const auto& entry : serversMap) {
400
    BOOST_CHECK_GT(entry.second, 0U);
401
    BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
402
    BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
403
    total += entry.second;
404
  }
405
  BOOST_CHECK_EQUAL(total, names.size());
406

407
  /* reset */
408
  for (auto& entry : serversMap) {
409
    entry.second = 0;
410
    BOOST_CHECK_EQUAL(entry.first->d_config.d_weight, 1);
411
  }
412

413
  /* request 1000 times the same name, we should go to the same server every time */
414
  {
415
    auto dnsQuestion = getDQ(&names.at(0));
416
    auto server = pol.getSelectedBackend(servers, dnsQuestion);
417
    for (size_t idx = 0; idx < 1000; idx++) {
418
      BOOST_CHECK(pol.getSelectedBackend(servers, dnsQuestion) == server);
419
    }
420
  }
421

422
  /* reset */
423
  for (auto& entry : serversMap) {
424
    entry.second = 0;
425
    BOOST_CHECK_EQUAL(entry.first->d_config.d_weight, 1);
426
  }
427
  /* change the weight of the last server to 100, default is 1 */
428
  servers.at(servers.size() - 1).second->setWeight(100);
429

430
  for (const auto& name : names) {
431
    auto dnsQuestion = getDQ(&name);
432
    auto server = pol.getSelectedBackend(servers, dnsQuestion);
433
    BOOST_REQUIRE(serversMap.count(server) == 1);
434
    ++serversMap[server];
435
  }
436

437
  total = 0;
438
  uint64_t totalW = 0;
439
  for (const auto& entry : serversMap) {
440
    total += entry.second;
441
    totalW += entry.first->d_config.d_weight;
442
  }
443
  BOOST_CHECK_EQUAL(total, names.size());
444
  auto last = servers.at(servers.size() - 1).second;
445
  const auto got = serversMap[last];
446
  float expected = static_cast<float>(static_cast<double>(names.size()) * 1.0 * last->d_config.d_weight) / static_cast<float>(totalW);
447
  BOOST_CHECK_GT(got, expected / 2);
448
  BOOST_CHECK_LT(got, expected * 2);
449
}
450

451
BOOST_AUTO_TEST_CASE(test_chashed)
452
{
453
  bool existingVerboseValue = dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose;
454
  dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
455
    config.d_verbose = false;
456
  });
457

458
  std::vector<DNSName> names;
459
  names.reserve(1000);
460
  for (size_t idx = 0; idx < 1000; idx++) {
461
    names.emplace_back("powerdns-" + std::to_string(idx) + ".com.");
462
  }
463

464
  ServerPolicy pol{"chashed", chashed, false};
465
  ServerPolicy::NumberedServerVector servers;
466
  std::map<std::shared_ptr<DownstreamState>, uint64_t> serversMap;
467
  for (size_t idx = 1; idx <= 10; idx++) {
468
    servers.emplace_back(idx, std::make_shared<DownstreamState>(ComboAddress("192.0.2." + std::to_string(idx) + ":53")));
469
    serversMap[servers.at(idx - 1).second] = 0;
470
    servers.at(idx - 1).second->setUp();
471
    /* we need to have a weight of at least 1000 to get an optimal repartition with the consistent hashing algo */
472
    servers.at(idx - 1).second->setWeight(1000);
473
    /* make sure that the hashes have been computed */
474
    servers.at(idx - 1).second->hash();
475
  }
476

477
  benchPolicy(pol);
478

479
  for (const auto& name : names) {
480
    auto dnsQuestion = getDQ(&name);
481
    auto server = pol.getSelectedBackend(servers, dnsQuestion);
482
    BOOST_REQUIRE(serversMap.count(server) == 1);
483
    ++serversMap[server];
484
  }
485

486
  uint64_t total = 0;
487
  for (const auto& entry : serversMap) {
488
    BOOST_CHECK_GT(entry.second, 0U);
489
    BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
490
    BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
491
    total += entry.second;
492
  }
493
  BOOST_CHECK_EQUAL(total, names.size());
494

495
  /* reset */
496
  for (auto& entry : serversMap) {
497
    entry.second = 0;
498
    BOOST_CHECK_EQUAL(entry.first->d_config.d_weight, 1000);
499
  }
500

501
  /* request 1000 times the same name, we should go to the same server every time */
502
  {
503
    auto dnsQuestion = getDQ(&names.at(0));
504
    auto server = pol.getSelectedBackend(servers, dnsQuestion);
505
    for (size_t idx = 0; idx < 1000; idx++) {
506
      BOOST_CHECK(pol.getSelectedBackend(servers, dnsQuestion) == server);
507
    }
508
  }
509

510
  /* reset */
511
  for (auto& entry : serversMap) {
512
    entry.second = 0;
513
    BOOST_CHECK_EQUAL(entry.first->d_config.d_weight, 1000);
514
  }
515
  /* change the weight of the last server to 100000, others stay at 1000 */
516
  servers.at(servers.size() - 1).second->setWeight(100000);
517

518
  for (const auto& name : names) {
519
    auto dnsQuestion = getDQ(&name);
520
    auto server = pol.getSelectedBackend(servers, dnsQuestion);
521
    BOOST_REQUIRE(serversMap.count(server) == 1);
522
    ++serversMap[server];
523
  }
524

525
  total = 0;
526
  uint64_t totalW = 0;
527
  for (const auto& entry : serversMap) {
528
    total += entry.second;
529
    totalW += entry.first->d_config.d_weight;
530
  }
531
  BOOST_CHECK_EQUAL(total, names.size());
532
  auto last = servers.at(servers.size() - 1).second;
533
  const auto got = serversMap[last];
534
  float expected = static_cast<float>(static_cast<double>(names.size()) * 1.0 * last->d_config.d_weight) / static_cast<float>(totalW);
535
  BOOST_CHECK_GT(got, expected / 2);
536
  BOOST_CHECK_LT(got, expected * 2);
537

538
  dnsdist::configuration::updateRuntimeConfiguration([existingVerboseValue](dnsdist::configuration::RuntimeConfiguration& config) {
539
    config.d_verbose = existingVerboseValue;
540
  });
541
}
542
#endif
543

544
BOOST_AUTO_TEST_CASE(test_lua)
545
{
2✔
546
  std::vector<DNSName> names;
2✔
547
  names.reserve(1000);
2✔
548
  for (size_t idx = 0; idx < 1000; idx++) {
2,002✔
549
    names.emplace_back("powerdns-" + std::to_string(idx) + ".com.");
2,000✔
550
  }
2,000✔
551

552
  static const std::string policySetupStr = R"foo(
2✔
553
    local counter = 0
2✔
554
    function luaroundrobin(servers, dq)
2✔
555
      counter = counter + 1
2✔
556
      return servers[1 + (counter % #servers)]
2✔
557
    end
2✔
558

2✔
559
    setServerPolicyLua("luaroundrobin", luaroundrobin)
2✔
560
  )foo";
2✔
561
  resetLuaContext();
2✔
562
  g_lua.lock()->writeFunction("setServerPolicyLua", [](const string& name, const ServerPolicy::policyfunc_t& policy) {
2✔
563
    auto pol = std::make_shared<ServerPolicy>(name, policy, true);
2✔
564
    dnsdist::configuration::updateRuntimeConfiguration([&pol](dnsdist::configuration::RuntimeConfiguration& config) {
2✔
565
      config.d_lbPolicy = std::move(pol);
2✔
566
    });
2✔
567
  });
2✔
568
  g_lua.lock()->executeCode(policySetupStr);
2✔
569

570
  {
2✔
571
    const auto& pol = dnsdist::configuration::getCurrentRuntimeConfiguration().d_lbPolicy;
2✔
572
    BOOST_REQUIRE(pol != nullptr);
2✔
573
    BOOST_REQUIRE(pol != nullptr);
2✔
574
    ServerPolicy::NumberedServerVector servers;
2✔
575
    std::map<std::shared_ptr<DownstreamState>, uint64_t> serversMap;
2✔
576
    for (size_t idx = 1; idx <= 10; idx++) {
22✔
577
      servers.emplace_back(idx, std::make_shared<DownstreamState>(ComboAddress("192.0.2." + std::to_string(idx) + ":53")));
20✔
578
      serversMap[servers.at(idx - 1).second] = 0;
20✔
579
      servers.at(idx - 1).second->setUp();
20✔
580
    }
20✔
581
    BOOST_REQUIRE_EQUAL(servers.size(), 10U);
2✔
582

583
    for (const auto& name : names) {
2,000✔
584
      auto dnsQuestion = getDQ(&name);
2,000✔
585
      auto server = pol->getSelectedBackend(servers, dnsQuestion);
2,000✔
586
      BOOST_REQUIRE(serversMap.count(server) == 1);
2,000✔
587
      ++serversMap[server];
2,000✔
588
    }
2,000✔
589

590
    uint64_t total = 0;
2✔
591
    for (const auto& entry : serversMap) {
20✔
592
      BOOST_CHECK_GT(entry.second, 0U);
20✔
593
      BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
20✔
594
      BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
20✔
595
      total += entry.second;
20✔
596
    }
20✔
597
    BOOST_CHECK_EQUAL(total, names.size());
2✔
598

599
    benchPolicy(*pol);
2✔
600
  }
2✔
601
  resetLuaContext();
2✔
602
}
2✔
603
#if 0
604
#ifdef LUAJIT_VERSION
605

606
BOOST_AUTO_TEST_CASE(test_lua_ffi_rr)
607
{
608
  std::vector<DNSName> names;
609
  names.reserve(1000);
610
  for (size_t idx = 0; idx < 1000; idx++) {
611
    names.emplace_back("powerdns-" + std::to_string(idx) + ".com.");
612
  }
613

614
  static const std::string policySetupStr = R"foo(
615
    local ffi = require("ffi")
616
    local C = ffi.C
617
    local counter = 0
618
    function ffilb(servers_list, dq)
619
      local serversCount = tonumber(C.dnsdist_ffi_servers_list_get_count(servers_list))
620
      counter = counter + 1
621
      return counter % serversCount
622
    end
623

624
    setServerPolicyLuaFFI("FFI round-robin", ffilb)
625
  )foo";
626
  resetLuaContext();
627
  g_lua.lock()->executeCode(getLuaFFIWrappers());
628
  g_lua.lock()->writeFunction("setServerPolicyLuaFFI", [](const string& name, const ServerPolicy::ffipolicyfunc_t& policy) {
629
    auto pol = std::make_shared<ServerPolicy>(name, std::move(policy));
630
    dnsdist::configuration::updateRuntimeConfiguration([&pol](dnsdist::configuration::RuntimeConfiguration& config) {
631
      config.d_lbPolicy = std::move(pol);
632
    });
633
  });
634
  g_lua.lock()->executeCode(policySetupStr);
635

636
  {
637
    const auto& pol = dnsdist::configuration::getCurrentRuntimeConfiguration().d_lbPolicy;
638
    BOOST_REQUIRE(pol != nullptr);
639
    ServerPolicy::NumberedServerVector servers;
640
    std::map<std::shared_ptr<DownstreamState>, uint64_t> serversMap;
641
    for (size_t idx = 1; idx <= 10; idx++) {
642
      servers.emplace_back(idx, std::make_shared<DownstreamState>(ComboAddress("192.0.2." + std::to_string(idx) + ":53")));
643
      serversMap[servers.at(idx - 1).second] = 0;
644
      servers.at(idx - 1).second->setUp();
645
    }
646
    BOOST_REQUIRE_EQUAL(servers.size(), 10U);
647

648
    for (const auto& name : names) {
649
      auto dnsQuestion = getDQ(&name);
650
      auto server = pol->getSelectedBackend(servers, dnsQuestion);
651
      BOOST_REQUIRE(serversMap.count(server) == 1);
652
      ++serversMap[server];
653
    }
654

655
    uint64_t total = 0;
656
    for (const auto& entry : serversMap) {
657
      BOOST_CHECK_GT(entry.second, 0U);
658
      BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
659
      BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
660
      total += entry.second;
661
    }
662
    BOOST_CHECK_EQUAL(total, names.size());
663

664
    benchPolicy(*pol);
665
  }
666
  resetLuaContext();
667
}
668

669
BOOST_AUTO_TEST_CASE(test_lua_ffi_no_server_available)
670
{
671
  DNSName dnsName("powerdns.com.");
672
  static const std::string policySetupStr = R"foo(
673
    local ffi = require("ffi")
674
    local C = ffi.C
675
    local counter = 0
676
    function ffipolicy(servers_list, dq)
677
      local serversCount = tonumber(C.dnsdist_ffi_servers_list_get_count(servers_list))
678
      -- return clearly out of bounds value to indicate that no server can be used
679
      return serversCount + 100
680
    end
681

682
    setServerPolicyLuaFFI("FFI policy", ffipolicy)
683
  )foo";
684
  resetLuaContext();
685
  g_lua.lock()->executeCode(getLuaFFIWrappers());
686
  g_lua.lock()->writeFunction("setServerPolicyLuaFFI", [](const string& policyName, ServerPolicy::ffipolicyfunc_t policy) {
687
    auto pol = std::make_shared<ServerPolicy>(policyName, std::move(policy));
688
    dnsdist::configuration::updateRuntimeConfiguration([&pol](dnsdist::configuration::RuntimeConfiguration& config) {
689
      config.d_lbPolicy = std::move(pol);
690
    });
691
  });
692
  g_lua.lock()->executeCode(policySetupStr);
693

694
  {
695
    const auto& pol = dnsdist::configuration::getCurrentRuntimeConfiguration().d_lbPolicy;
696
    BOOST_REQUIRE(pol != nullptr);
697
    ServerPolicy::NumberedServerVector servers;
698
    for (size_t idx = 1; idx <= 10; idx++) {
699
      servers.emplace_back(idx, std::make_shared<DownstreamState>(ComboAddress("192.0.2." + std::to_string(idx) + ":53")));
700
      servers.at(idx - 1).second->setUp();
701
    }
702
    BOOST_REQUIRE_EQUAL(servers.size(), 10U);
703

704
    auto dnsQuestion = getDQ(&dnsName);
705
    auto server = pol->getSelectedBackend(servers, dnsQuestion);
706
    BOOST_REQUIRE(server == nullptr);
707
  }
708
  resetLuaContext();
709
}
710

711
BOOST_AUTO_TEST_CASE(test_lua_ffi_hashed)
712
{
713
  std::vector<DNSName> names;
714
  names.reserve(1000);
715
  for (size_t idx = 0; idx < 1000; idx++) {
716
    names.emplace_back("powerdns-" + std::to_string(idx) + ".com.");
717
  }
718

719
  static const std::string policySetupStr = R"foo(
720
    local ffi = require("ffi")
721
    local C = ffi.C
722
    function ffilb(servers_list, dq)
723
      local serversCount = tonumber(C.dnsdist_ffi_servers_list_get_count(servers_list))
724
      local hash = tonumber(C.dnsdist_ffi_dnsquestion_get_qname_hash(dq, 0))
725
      return hash % serversCount
726
    end
727

728
    setServerPolicyLuaFFI("FFI hashed", ffilb)
729
  )foo";
730
  resetLuaContext();
731
  g_lua.lock()->executeCode(getLuaFFIWrappers());
732
  g_lua.lock()->writeFunction("setServerPolicyLuaFFI", [](const string& name, const ServerPolicy::ffipolicyfunc_t& policy) {
733
    auto pol = std::make_shared<ServerPolicy>(name, std::move(policy));
734
    dnsdist::configuration::updateRuntimeConfiguration([&pol](dnsdist::configuration::RuntimeConfiguration& config) {
735
      config.d_lbPolicy = std::move(pol);
736
    });
737
  });
738
  g_lua.lock()->executeCode(policySetupStr);
739

740
  {
741
    const auto& pol = dnsdist::configuration::getCurrentRuntimeConfiguration().d_lbPolicy;
742
    BOOST_REQUIRE(pol != nullptr);
743
    ServerPolicy::NumberedServerVector servers;
744
    std::map<std::shared_ptr<DownstreamState>, uint64_t> serversMap;
745
    for (size_t idx = 1; idx <= 10; idx++) {
746
      servers.emplace_back(idx, std::make_shared<DownstreamState>(ComboAddress("192.0.2." + std::to_string(idx) + ":53")));
747
      serversMap[servers.at(idx - 1).second] = 0;
748
      servers.at(idx - 1).second->setUp();
749
    }
750
    BOOST_REQUIRE_EQUAL(servers.size(), 10U);
751

752
    for (const auto& name : names) {
753
      auto dnsQuestion = getDQ(&name);
754
      auto server = pol->getSelectedBackend(servers, dnsQuestion);
755
      BOOST_REQUIRE(serversMap.count(server) == 1);
756
      ++serversMap[server];
757
    }
758

759
    uint64_t total = 0;
760
    for (const auto& entry : serversMap) {
761
      BOOST_CHECK_GT(entry.second, 0U);
762
      BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
763
      BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
764
      total += entry.second;
765
    }
766
    BOOST_CHECK_EQUAL(total, names.size());
767

768
    benchPolicy(*pol);
769
  }
770
  resetLuaContext();
771
}
772

773
BOOST_AUTO_TEST_CASE(test_lua_ffi_whashed)
774
{
775
  std::vector<DNSName> names;
776
  names.reserve(1000);
777
  for (size_t idx = 0; idx < 1000; idx++) {
778
    names.emplace_back("powerdns-" + std::to_string(idx) + ".com.");
779
  }
780

781
  static const std::string policySetupStr = R"foo(
782
    local ffi = require("ffi")
783
    local C = ffi.C
784
    function ffilb(servers_list, dq)
785
      return tonumber(C.dnsdist_ffi_servers_list_whashed(servers_list, dq, C.dnsdist_ffi_dnsquestion_get_qname_hash(dq, 0)))
786
    end
787

788
    setServerPolicyLuaFFI("FFI whashed", ffilb)
789
  )foo";
790
  resetLuaContext();
791
  g_lua.lock()->executeCode(getLuaFFIWrappers());
792
  g_lua.lock()->writeFunction("setServerPolicyLuaFFI", [](const string& name, const ServerPolicy::ffipolicyfunc_t& policy) {
793
    auto pol = std::make_shared<ServerPolicy>(name, std::move(policy));
794
    dnsdist::configuration::updateRuntimeConfiguration([&pol](dnsdist::configuration::RuntimeConfiguration& config) {
795
      config.d_lbPolicy = std::move(pol);
796
    });
797
  });
798
  g_lua.lock()->executeCode(policySetupStr);
799

800
  {
801
    const auto& pol = dnsdist::configuration::getCurrentRuntimeConfiguration().d_lbPolicy;
802
    BOOST_REQUIRE(pol != nullptr);
803
    ServerPolicy::NumberedServerVector servers;
804
    std::map<std::shared_ptr<DownstreamState>, uint64_t> serversMap;
805
    for (size_t idx = 1; idx <= 10; idx++) {
806
      servers.emplace_back(idx, std::make_shared<DownstreamState>(ComboAddress("192.0.2." + std::to_string(idx) + ":53")));
807
      serversMap[servers.at(idx - 1).second] = 0;
808
      servers.at(idx - 1).second->setUp();
809
    }
810
    BOOST_REQUIRE_EQUAL(servers.size(), 10U);
811

812
    for (const auto& name : names) {
813
      auto dnsQuestion = getDQ(&name);
814
      auto server = pol->getSelectedBackend(servers, dnsQuestion);
815
      BOOST_REQUIRE(serversMap.count(server) == 1);
816
      ++serversMap[server];
817
    }
818

819
    uint64_t total = 0;
820
    for (const auto& entry : serversMap) {
821
      BOOST_CHECK_GT(entry.second, 0U);
822
      BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
823
      BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
824
      total += entry.second;
825
    }
826
    BOOST_CHECK_EQUAL(total, names.size());
827

828
    benchPolicy(*pol);
829
  }
830
  resetLuaContext();
831
}
832

833
BOOST_AUTO_TEST_CASE(test_lua_ffi_chashed)
834
{
835
  std::vector<DNSName> names;
836
  names.reserve(1000);
837
  for (size_t idx = 0; idx < 1000; idx++) {
838
    names.emplace_back("powerdns-" + std::to_string(idx) + ".com.");
839
  }
840

841
  static const std::string policySetupStr = R"foo(
842
    local ffi = require("ffi")
843
    local C = ffi.C
844
    function ffilb(servers_list, dq)
845
      return tonumber(C.dnsdist_ffi_servers_list_chashed(servers_list, dq, C.dnsdist_ffi_dnsquestion_get_qname_hash(dq, 0)))
846
    end
847

848
    setServerPolicyLuaFFI("FFI chashed", ffilb)
849
  )foo";
850
  resetLuaContext();
851
  g_lua.lock()->executeCode(getLuaFFIWrappers());
852
  g_lua.lock()->writeFunction("setServerPolicyLuaFFI", [](const string& name, const ServerPolicy::ffipolicyfunc_t& policy) {
853
    auto pol = std::make_shared<ServerPolicy>(name, std::move(policy));
854
    dnsdist::configuration::updateRuntimeConfiguration([&pol](dnsdist::configuration::RuntimeConfiguration& config) {
855
      config.d_lbPolicy = std::move(pol);
856
    });
857
  });
858
  g_lua.lock()->executeCode(policySetupStr);
859

860
  {
861
    const auto& pol = dnsdist::configuration::getCurrentRuntimeConfiguration().d_lbPolicy;
862
    BOOST_REQUIRE(pol != nullptr);
863
    ServerPolicy::NumberedServerVector servers;
864
    std::map<std::shared_ptr<DownstreamState>, uint64_t> serversMap;
865
    for (size_t idx = 1; idx <= 10; idx++) {
866
      servers.emplace_back(idx, std::make_shared<DownstreamState>(ComboAddress("192.0.2." + std::to_string(idx) + ":53")));
867
      serversMap[servers.at(idx - 1).second] = 0;
868
      servers.at(idx - 1).second->setUp();
869
      /* we need to have a weight of at least 1000 to get an optimal repartition with the consistent hashing algo */
870
      servers.at(idx - 1).second->setWeight(1000);
871
      /* make sure that the hashes have been computed */
872
      servers.at(idx - 1).second->hash();
873
    }
874
    BOOST_REQUIRE_EQUAL(servers.size(), 10U);
875

876
    for (const auto& name : names) {
877
      auto dnsQuestion = getDQ(&name);
878
      auto server = pol->getSelectedBackend(servers, dnsQuestion);
879
      BOOST_REQUIRE(serversMap.count(server) == 1);
880
      ++serversMap[server];
881
    }
882

883
    uint64_t total = 0;
884
    for (const auto& entry : serversMap) {
885
      BOOST_CHECK_GT(entry.second, 0U);
886
      BOOST_CHECK_GT(entry.second, (names.size() / servers.size() / 2));
887
      BOOST_CHECK_LT(entry.second, (names.size() / servers.size() * 2));
888
      total += entry.second;
889
    }
890
    BOOST_CHECK_EQUAL(total, names.size());
891

892
    benchPolicy(*pol);
893
  }
894
  resetLuaContext();
895
}
896

897
#endif /* LUAJIT_VERSION */
898
#endif
899
BOOST_AUTO_TEST_SUITE_END()
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc