From c68ae9fac05127814b86c22ca3b0869bb8ad9f05 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 27 Jun 2017 13:08:43 +0200 Subject: [PATCH] rec: Add unit tests for the cache removal queue (back/front) (cherry picked from commit 7e6f71937f0ac7678b81013da7538ca1e65d779a) --- pdns/recursor_cache.cc | 9 +- pdns/recursor_cache.hh | 1 + pdns/recursordist/test-recursorcache_cc.cc | 212 +++++++++++++++++++++ 3 files changed, 220 insertions(+), 2 deletions(-) diff --git a/pdns/recursor_cache.cc b/pdns/recursor_cache.cc index d64067d42..486d03ec9 100644 --- a/pdns/recursor_cache.cc +++ b/pdns/recursor_cache.cc @@ -461,10 +461,15 @@ uint64_t MemRecursorCache::doDump(int fd) return count; } -void MemRecursorCache::doPrune(void) +void MemRecursorCache::doPrune(unsigned int keep) { d_cachecachevalid=false; + pruneCollection(*this, d_cache, keep); +} + +void MemRecursorCache::doPrune(void) +{ unsigned int maxCached=::arg().asNum("max-cache-entries") / g_numThreads; - pruneCollection(*this, d_cache, maxCached); + doPrune(maxCached); } diff --git a/pdns/recursor_cache.hh b/pdns/recursor_cache.hh index 1fb437454..cd79a75a0 100644 --- a/pdns/recursor_cache.hh +++ b/pdns/recursor_cache.hh @@ -60,6 +60,7 @@ public: void replace(time_t, const DNSName &qname, const QType& qt, const vector& content, const vector>& signatures, const std::vector>& authorityRecs, bool auth, boost::optional ednsmask=boost::none, vState state=Indeterminate); void doPrune(void); + void doPrune(unsigned int keep); uint64_t doDump(int fd); int doWipeCache(const DNSName& name, bool sub, uint16_t qtype=0xffff); diff --git a/pdns/recursordist/test-recursorcache_cc.cc b/pdns/recursordist/test-recursorcache_cc.cc index ec95ee015..f3095d2d0 100644 --- a/pdns/recursordist/test-recursorcache_cc.cc +++ b/pdns/recursordist/test-recursorcache_cc.cc @@ -264,4 +264,216 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheSimple) { } } +BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingExpiredEntries) { + MemRecursorCache MRC; + + std::vector records; + std::vector> signatures; + std::vector> authRecs; + BOOST_CHECK_EQUAL(MRC.size(), 0); + time_t now = time(nullptr); + DNSName power1("powerdns.com."); + DNSName power2("powerdns-1.com."); + time_t ttd = now - 30; + std::vector retrieved; + ComboAddress who("192.0.2.1"); + + /* entry for power, which expired 30s ago */ + DNSRecord dr1; + ComboAddress dr1Content("2001:DB8::1"); + dr1.d_name = power1; + dr1.d_type = QType::AAAA; + dr1.d_class = QClass::IN; + dr1.d_content = std::make_shared(dr1Content); + dr1.d_ttl = static_cast(ttd); + dr1.d_place = DNSResourceRecord::ANSWER; + + /* entry for power1, which expired 30 ago too */ + DNSRecord dr2; + ComboAddress dr2Content("2001:DB8::2"); + dr2.d_name = power2; + dr2.d_type = QType::AAAA; + dr2.d_class = QClass::IN; + dr2.d_content = std::make_shared(dr2Content); + dr2.d_ttl = static_cast(ttd); + dr2.d_place = DNSResourceRecord::ANSWER; + + /* insert both entries */ + records.push_back(dr1); + MRC.replace(now, power1, QType(dr1.d_type), records, signatures, authRecs, true, boost::none); + records.clear(); + records.push_back(dr2); + MRC.replace(now, power2, QType(dr2.d_type), records, signatures, authRecs, true, boost::none); + records.clear(); + BOOST_CHECK_EQUAL(MRC.size(), 2); + + /* the one for power2 having been inserted + more recently should be removed last */ + /* we ask that only entry remains in the cache */ + MRC.doPrune(1); + BOOST_CHECK_EQUAL(MRC.size(), 1); + + /* the remaining entry should be power2, but to get it + we need to go back in the past a bit */ + BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), 1); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr2Content.toString()); + /* check that power1 is gone */ + BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), -1); + + /* clear everything up */ + MRC.doWipeCache(DNSName("."), true); + BOOST_CHECK_EQUAL(MRC.size(), 0); + records.clear(); + + /* insert both entries back */ + records.push_back(dr1); + MRC.replace(now, power1, QType(dr1.d_type), records, signatures, authRecs, true, boost::none); + records.clear(); + records.push_back(dr2); + MRC.replace(now, power2, QType(dr2.d_type), records, signatures, authRecs, true, boost::none); + records.clear(); + BOOST_CHECK_EQUAL(MRC.size(), 2); + + /* trigger a miss (expired) for power2 */ + BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), -now); + + /* power2 should have been moved to the front of the expunge + queue, and should this time be removed first */ + /* we ask that only entry remains in the cache */ + MRC.doPrune(1); + BOOST_CHECK_EQUAL(MRC.size(), 1); + + /* the remaining entry should be power1, but to get it + we need to go back in the past a bit */ + BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), 1); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); + /* check that power2 is gone */ + BOOST_CHECK_EQUAL(MRC.get(ttd - 1, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), -1); +} + +BOOST_AUTO_TEST_CASE(test_RecursorCache_ExpungingValidEntries) { + MemRecursorCache MRC; + + std::vector records; + std::vector> signatures; + std::vector> authRecs; + BOOST_CHECK_EQUAL(MRC.size(), 0); + time_t now = time(nullptr); + DNSName power1("powerdns.com."); + DNSName power2("powerdns-1.com."); + time_t ttd = now + 30; + std::vector retrieved; + ComboAddress who("192.0.2.1"); + + /* entry for power, which will expire in 30s */ + DNSRecord dr1; + ComboAddress dr1Content("2001:DB8::1"); + dr1.d_name = power1; + dr1.d_type = QType::AAAA; + dr1.d_class = QClass::IN; + dr1.d_content = std::make_shared(dr1Content); + dr1.d_ttl = static_cast(ttd); + dr1.d_place = DNSResourceRecord::ANSWER; + + /* entry for power1, which will expire in 30s too */ + DNSRecord dr2; + ComboAddress dr2Content("2001:DB8::2"); + dr2.d_name = power2; + dr2.d_type = QType::AAAA; + dr2.d_class = QClass::IN; + dr2.d_content = std::make_shared(dr2Content); + dr2.d_ttl = static_cast(ttd); + dr2.d_place = DNSResourceRecord::ANSWER; + + /* insert both entries */ + records.push_back(dr1); + MRC.replace(now, power1, QType(dr1.d_type), records, signatures, authRecs, true, boost::none); + records.clear(); + records.push_back(dr2); + MRC.replace(now, power2, QType(dr2.d_type), records, signatures, authRecs, true, boost::none); + records.clear(); + BOOST_CHECK_EQUAL(MRC.size(), 2); + + /* the one for power2 having been inserted + more recently should be removed last */ + /* we ask that only entry remains in the cache */ + MRC.doPrune(1); + BOOST_CHECK_EQUAL(MRC.size(), 1); + + /* the remaining entry should be power2 */ + BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), ttd-now); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr2Content.toString()); + /* check that power1 is gone */ + BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), -1); + + /* clear everything up */ + MRC.doWipeCache(DNSName("."), true); + BOOST_CHECK_EQUAL(MRC.size(), 0); + records.clear(); + + /* insert both entries back */ + records.push_back(dr1); + MRC.replace(now, power1, QType(dr1.d_type), records, signatures, authRecs, true, boost::none); + records.clear(); + records.push_back(dr2); + MRC.replace(now, power2, QType(dr2.d_type), records, signatures, authRecs, true, boost::none); + records.clear(); + BOOST_CHECK_EQUAL(MRC.size(), 2); + + /* replace the entry for power1 */ + records.push_back(dr1); + MRC.replace(now, power1, QType(dr1.d_type), records, signatures, authRecs, true, boost::none); + records.clear(); + BOOST_CHECK_EQUAL(MRC.size(), 2); + + /* the replaced entry for power1 should have been moved + to the back of the expunge queue, so power2 should be at the front + and should this time be removed first */ + /* we ask that only entry remains in the cache */ + MRC.doPrune(1); + BOOST_CHECK_EQUAL(MRC.size(), 1); + + /* the remaining entry should be power1 */ + BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), ttd-now); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); + /* check that power2 is gone */ + BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), -1); + + /* clear everything up */ + MRC.doWipeCache(DNSName("."), true); + BOOST_CHECK_EQUAL(MRC.size(), 0); + records.clear(); + + /* insert both entries back */ + records.push_back(dr1); + MRC.replace(now, power1, QType(dr1.d_type), records, signatures, authRecs, true, boost::none); + records.clear(); + records.push_back(dr2); + MRC.replace(now, power2, QType(dr2.d_type), records, signatures, authRecs, true, boost::none); + records.clear(); + BOOST_CHECK_EQUAL(MRC.size(), 2); + + /* get a hit for power1 */ + BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), ttd-now); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); + + /* the entry for power1 should have been moved to the back of the expunge queue + due to the hit, so power2 should be at the front and should this time be removed first */ + /* we ask that only entry remains in the cache */ + MRC.doPrune(1); + BOOST_CHECK_EQUAL(MRC.size(), 1); + + /* the remaining entry should be power1 */ + BOOST_CHECK_EQUAL(MRC.get(now, power1, QType(dr1.d_type), false, &retrieved, who, nullptr), ttd-now); + BOOST_REQUIRE_EQUAL(retrieved.size(), 1); + BOOST_CHECK_EQUAL(getRR(retrieved.at(0))->getCA().toString(), dr1Content.toString()); + /* check that power2 is gone */ + BOOST_CHECK_EQUAL(MRC.get(now, power2, QType(dr2.d_type), false, &retrieved, who, nullptr), -1); +} + BOOST_AUTO_TEST_SUITE_END() -- 2.40.0