int g_luaRecordExecLimit;
time_t g_luaHealthChecksInterval{5};
time_t g_luaHealthChecksExpireDelay{3600};
+time_t g_luaConsistentHashesExpireDelay{86400};
+time_t g_luaConsistentHashesCleanupInterval{3600};
#endif
#ifdef ENABLE_GSS_TSIG
bool g_doGssTSIG;
::arg().set("lua-records-exec-limit", "LUA records scripts execution limit (instructions count). Values <= 0 mean no limit") = "1000";
::arg().set("lua-health-checks-expire-delay", "Stops doing health checks after the record hasn't been used for that delay (in seconds)") = "3600";
::arg().set("lua-health-checks-interval", "LUA records health checks monitoring interval in seconds") = "5";
+ ::arg().set("lua-consistent-hashes-cleanup-interval", "Pre-computed hashes cleanup interval (in seconds)") = "3600";
+ ::arg().set("lua-consistent-hashes-expire-delay", "Cleanup pre-computed hashes that haven't been used for the given delay (in seconds). See pickchashed() LUA function") = "86400";
#endif
::arg().setSwitch("axfr-lower-serial", "Also AXFR a zone from a primary with a lower serial") = "no";
g_LuaRecordSharedState = (::arg()["enable-lua-records"] == "shared");
g_luaRecordExecLimit = ::arg().asNum("lua-records-exec-limit");
g_luaHealthChecksInterval = ::arg().asNum("lua-health-checks-interval");
+ g_luaConsistentHashesExpireDelay = ::arg().asNum("lua-consistent-hashes-expire-delay");
+ g_luaConsistentHashesCleanupInterval = ::arg().asNum("lua-consistent-hashes-cleanup-interval");
g_luaHealthChecksExpireDelay = ::arg().asNum("lua-health-checks-expire-delay");
#endif
#ifdef ENABLE_GSS_TSIG
#include "sstuff.hh"
#include "minicurl.hh"
#include "ueberbackend.hh"
-#include "dnsrecords.hh"
#include "dns_random.hh"
#include "auth-main.hh"
#include "../modules/geoipbackend/geoipinterface.hh" // only for the enum
/*
* Holds computed hashes for a given entry
*/
-struct EntryHashesHolder {
+struct EntryHashesHolder
+{
std::atomic<size_t> weight;
std::string entry;
SharedLockGuarded<std::vector<unsigned int>> hashes;
+ std::atomic<time_t> lastUsed;
- EntryHashesHolder(size_t weight_, std::string entry_): weight(weight_), entry(std::move(entry_)) {
+ EntryHashesHolder(size_t weight_, std::string entry_, time_t lastUsed_ = time(nullptr)): weight(weight_), entry(std::move(entry_)), lastUsed(lastUsed_) {
}
bool hashesComputed() {
}
};
-static std::map<
- std::tuple<int, std::string, std::string>, // zoneid qname entry
+using zone_hashes_key_t = std::tuple<int, std::string, std::string>;
+
+static SharedLockGuarded<std::map<
+ zone_hashes_key_t, // zoneid qname entry
std::shared_ptr<EntryHashesHolder> // entry w/ corresponding hashes
- >
+ >>
s_zone_hashes;
+static std::atomic<time_t> s_lastConsistentHashesCleanup = 0;
+
+/**
+ * every ~g_luaConsistentHashesCleanupInterval, do a cleanup to delete entries that haven't been used in the last g_luaConsistentHashesExpireDelay
+ */
+static void cleanZoneHashes()
+{
+ auto now = time(nullptr);
+ if (s_lastConsistentHashesCleanup > (now - g_luaConsistentHashesCleanupInterval)) {
+ return ;
+ }
+ s_lastConsistentHashesCleanup = now;
+ std::vector<zone_hashes_key_t> toDelete{};
+ {
+ auto locked = s_zone_hashes.read_lock();
+ auto someTimeAgo = now - g_luaConsistentHashesExpireDelay;
+
+ for (const auto& [key, entry]: *locked) {
+ if (entry->lastUsed > someTimeAgo) {
+ toDelete.push_back(key);
+ }
+ }
+ }
+ if (!toDelete.empty()) {
+ auto wlocked = s_zone_hashes.write_lock();
+ for (const auto& key : toDelete) {
+ wlocked->erase(key);
+ }
+ }
+}
+
static std::vector<std::shared_ptr<EntryHashesHolder>> getCHashedEntries(const int zoneId, const std::string& queryName, const std::vector<std::pair<int, std::string>>& items)
{
std::vector<std::shared_ptr<EntryHashesHolder>> result{};
+ std::map<zone_hashes_key_t, std::shared_ptr<EntryHashesHolder>> newEntries{};
+
+ {
+ time_t now = time(nullptr);
+ auto locked = s_zone_hashes.read_lock();
- for (const auto& [weight, entry]: items) {
- auto key = std::make_tuple(zoneId, queryName, entry);
- if (s_zone_hashes.count(key) == 0) {
- s_zone_hashes[key] = std::make_shared<EntryHashesHolder>(weight, entry);
- } else {
- s_zone_hashes.at(key)->weight = weight;
+ for (const auto& [weight, entry]: items) {
+ auto key = std::make_tuple(zoneId, queryName, entry);
+ if (locked->count(key) == 0) {
+ newEntries[key] = std::make_shared<EntryHashesHolder>(weight, entry, now);
+ } else {
+ locked->at(key)->weight = weight;
+ locked->at(key)->lastUsed = now;
+ result.push_back(locked->at(key));
+ }
+ }
+ }
+ if (!newEntries.empty()) {
+ auto wlocked = s_zone_hashes.write_lock();
+
+ for (auto& [key, entry]: newEntries) {
+ result.push_back(entry);
+ (*wlocked)[key] = std::move(entry);
}
- result.push_back(s_zone_hashes.at(key));
}
return result;
boost::optional<std::string> ret;
boost::optional<std::string> first;
+ cleanZoneHashes();
+
auto entries = getCHashedEntries(zoneId, queryName, items);
ComboAddress::addressOnlyHash addrOnlyHash;
* supplied, as weighted by the various `weight` parameters and distributed consistently
* @example pickchashed({ {15, '1.2.3.4'}, {50, '5.4.3.2'} })
*/
- lua.writeFunction("pickchashed", [](std::unordered_map<int, wiplist_t > ips) {
- vector< pair<int, string> > items;
+ lua.writeFunction("pickchashed", [](const std::unordered_map<int, wiplist_t>& ips) {
+ std::vector<std::pair<int, std::string>> items;
items.reserve(ips.size());
- for (auto& entry : ips) {
- items.emplace_back(atoi(entry.second[1].c_str()), entry.second[2]);
+ for (const auto& entry : ips) {
+ items.emplace_back(atoi(entry.second.at(1).c_str()), entry.second.at(2));
}
return pickConsistentWeightedHashed(s_lua_record_ctx->bestwho, items);