}
{
+ // process zone updates done while data collection for replace() was already in progress.
auto pending = d_pending.lock();
- if (pending->d_replacePending) {
- // add/replace all zones created while data collection for replace() was already in progress.
- for (const tuple<DNSName, int>& tup : pending->d_pendingAdds) {
- const DNSName& zone = tup.get<0>();
- CacheValue val;
- val.zoneId = tup.get<1>();
- auto& mc = newMaps[getMapIndex(zone)];
- mc[zone] = val;
+ assert(pending->d_replacePending); // make sure we never forget to call setReplacePending()
+ for (const tuple<DNSName, int, bool>& tup : pending->d_pendingUpdates) {
+ const DNSName& zone = tup.get<0>();
+ CacheValue val;
+ val.zoneId = tup.get<1>();
+ bool insert = tup.get<2>();
+ auto& mc = newMaps[getMapIndex(zone)];
+ auto iter = mc.find(zone);
+ if (iter != mc.end()) {
+ if (insert) {
+ iter->second = std::move(val);
+ }
+ else {
+ mc.erase(iter);
+ count--;
+ }
+ }
+ else if (insert) {
+ mc.emplace(zone, val);
+ count++;
}
}
*map = std::move(newMaps[mapIndex]);
}
- pending->d_pendingAdds.clear();
+ pending->d_pendingUpdates.clear();
pending->d_replacePending = false;
- }
- d_statnumentries->store(count);
+ d_statnumentries->store(count);
+ }
}
void AuthZoneCache::add(const DNSName& zone, const int zoneId)
{
auto pending = d_pending.lock();
if (pending->d_replacePending) {
- pending->d_pendingAdds.emplace_back(zone, zoneId);
+ pending->d_pendingUpdates.emplace_back(zone, zoneId, true);
}
}
}
else {
map->emplace(zone, val);
+ (*d_statnumentries)++;
+ }
+ }
+}
+
+void AuthZoneCache::remove(const DNSName& zone)
+{
+ if (!d_refreshinterval)
+ return;
+
+ {
+ auto pending = d_pending.lock();
+ if (pending->d_replacePending) {
+ pending->d_pendingUpdates.emplace_back(zone, -1, false);
+ }
+ }
+
+ int mapIndex = getMapIndex(zone);
+ {
+ auto& mc = d_maps[mapIndex];
+ auto map = mc.d_map.write_lock();
+ if (map->erase(zone)) {
+ (*d_statnumentries)--;
}
}
}
{
auto pending = d_pending.lock();
pending->d_replacePending = true;
- pending->d_pendingAdds.clear();
+ pending->d_pendingUpdates.clear();
}
}
void replace(const vector<tuple<DNSName, int>>& zone);
void add(const DNSName& zone, const int zoneId);
+ void remove(const DNSName& zone);
void setReplacePending(); //!< call this when data collection for the subsequent replace() call starts.
bool getEntry(const DNSName& zone, int& zoneId);
struct PendingData
{
- std::vector<tuple<DNSName, int>> d_pendingAdds;
+ std::vector<tuple<DNSName, int, bool>> d_pendingUpdates;
bool d_replacePending{false};
};
LockGuarded<PendingData> d_pending;
vector<tuple<DNSName, int>> zone_indices{
{DNSName("example.org."), 1},
};
+ cache.setReplacePending();
cache.replace(zone_indices);
int zoneId = 0;
}
}
+BOOST_AUTO_TEST_CASE(test_remove_while_pending_replace)
+{
+ AuthZoneCache cache;
+ cache.setRefreshInterval(3600);
+
+ vector<tuple<DNSName, int>> zone_indices{
+ {DNSName("powerdns.org."), 1}};
+ cache.setReplacePending();
+ cache.remove(DNSName("powerdns.org."));
+ cache.replace(zone_indices);
+
+ int zoneId = 0;
+ bool found = cache.getEntry(DNSName("example.org."), zoneId);
+ if (found) {
+ BOOST_FAIL("zone removed while replace was pending is found");
+ }
+}
+
// Add zone using .add(), but also in the .replace() data
BOOST_AUTO_TEST_CASE(test_add_while_pending_replace_duplicate)
{
throw ApiException("Deleting domain '"+zonename.toString()+"' failed: backend delete failed/unsupported");
di.backend->commitTransaction();
+
+ g_zoneCache.remove(zonename);
} catch (...) {
di.backend->abortTransaction();
throw;
// Handle this first, to avoid concurrent queries re-populating the other caches.
g_zoneCache.add(di.zone, di.id);
}
+ else {
+ g_zoneCache.remove(di.zone);
+ }
}
// purge entire zone from cache, not just zone-level records.