BOOST_CLASS_VERSION(LMDBBackend::KeyDataDB, 1)
BOOST_CLASS_VERSION(ZoneName, 1)
BOOST_CLASS_VERSION(DomainInfo, 2)
+BOOST_CLASS_VERSION(LMDBBackend::TransientDomainInfo, 0)
static bool s_first = true;
static uint32_t s_shards = 0;
}
d_write_notification_update = mustDo("write-notification-update");
+ d_split_domains_table = mustDo("split-domains-table");
if (mustDo("lightning-stream")) {
d_random_ids = true;
void LMDBBackend::openAllTheDatabases()
{
- d_tdomains = std::make_shared<tdomains_t>(getMDBEnv(getArg("filename").c_str(), MDB_NOSUBDIR | MDB_NORDAHEAD | d_asyncFlag, 0600, d_mapsize_main), "domains_v5");
+ auto filename = getArg("filename");
+ d_tdomains = std::make_shared<tdomains_t>(getMDBEnv(filename.c_str(), MDB_NOSUBDIR | MDB_NORDAHEAD | d_asyncFlag, 0600, d_mapsize_main), "domains_v5");
d_tmeta = std::make_shared<tmeta_t>(d_tdomains->getEnv(), "metadata_v5");
d_tkdb = std::make_shared<tkdb_t>(d_tdomains->getEnv(), "keydata_v5");
d_ttsig = std::make_shared<ttsig_t>(d_tdomains->getEnv(), "tsig_v5");
d_tnetworks = d_tdomains->getEnv()->openDB("networks_v6", MDB_CREATE);
d_tviews = d_tdomains->getEnv()->openDB("views_v6", MDB_CREATE);
+ if (d_split_domains_table) {
+ d_tdomains_extra = std::make_shared<tdomain_extra_t>(d_tdomains->getEnv(), "domains_extra_v6");
+ }
}
unsigned int LMDBBackend::getCapabilities()
}
}
+ template <class Archive>
+ void save(Archive& ar, const LMDBBackend::TransientDomainInfo& g, const unsigned int /* version */)
+ {
+ ar & g.last_check;
+ ar & g.notified_serial;
+ }
+
+ template <class Archive>
+ void load(Archive& ar, LMDBBackend::TransientDomainInfo& g, const unsigned int /* version */)
+ {
+ ar & g.last_check;
+ ar & g.notified_serial;
+ }
+
template <class Archive>
void serialize(Archive& ar, LMDBBackend::DomainMeta& g, const unsigned int /* version */)
{
BOOST_SERIALIZATION_SPLIT_FREE(ZoneName);
BOOST_SERIALIZATION_SPLIT_FREE(LMDBBackend::KeyDataDB);
BOOST_SERIALIZATION_SPLIT_FREE(DomainInfo);
+BOOST_SERIALIZATION_SPLIT_FREE(LMDBBackend::TransientDomainInfo);
BOOST_IS_BITWISE_SERIALIZABLE(ComboAddress);
// Resource records are serialized in the following format:
void LMDBBackend::consolidateDomainInfo(DomainInfo& info) const
{
- // Update the DomainInfo values if we have cached data in memory.
+ TransientDomainInfo tdi;
+ bool valid{false};
+
+ // Get data from the cache if we don't keep the database up to date.
if (!d_write_notification_update) {
auto container = s_transient_domain_info.read_lock();
- TransientDomainInfo tdi;
if (container->get(info.id, tdi)) {
- info.notified_serial = tdi.notified_serial;
- info.last_check = tdi.last_check;
+ valid = true;
}
}
+
+ // If the DomainInfo table is split, get the TransientDomainInfo part
+ // from the extra table.
+ if (!valid && d_split_domains_table) {
+ auto rotxn = d_tdomains_extra->getROTransaction();
+ if (rotxn.get(info.id, tdi)) {
+ valid = true;
+ }
+ }
+
+ if (valid) {
+ info.notified_serial = tdi.notified_serial;
+ info.last_check = tdi.last_check;
+ }
+}
+
+void LMDBBackend::writeTransientDomainInfo(const DomainInfo& info)
+{
+ // If the DomainInfo table is split, write the TransientDomainInfo part
+ // to the extra table.
+ if (d_split_domains_table) {
+ TransientDomainInfo tdi;
+ tdi.notified_serial = info.notified_serial;
+ tdi.last_check = info.last_check;
+ auto txn = d_tdomains_extra->getRWTransaction();
+ txn.put(tdi, info.id);
+ txn.commit();
+ }
}
void LMDBBackend::writeDomainInfo(const DomainInfo& info)
{
+ // Update the in-memory cache if we don't keep the database up to date.
if (!d_write_notification_update) {
- auto container = s_transient_domain_info.write_lock();
TransientDomainInfo tdi;
+ auto container = s_transient_domain_info.write_lock();
if (container->get(info.id, tdi)) {
// Only remove the in-memory value if it has not been modified since the
// DomainInfo data was set up.
container->remove(info.id);
}
}
+ return;
}
+
auto txn = d_tdomains->getRWTransaction();
txn.put(info, info.id);
txn.commit();
+ writeTransientDomainInfo(info);
}
/* Here's the complicated story. Other backends have just one transaction, which is either
return true;
}
+// Similar to the above, but callback will only change the TransientDomainInfo
+// fields.
+bool LMDBBackend::genChangeTransientDomain(domainid_t id, const std::function<void(DomainInfo&)>& func) // NOLINTNEXT(readability-identifier-length)
+{
+ DomainInfo info;
+ if (!findDomain(id, info)) {
+ return false;
+ }
+ consolidateDomainInfo(info);
+ func(info);
+ if (!d_write_notification_update) {
+ // This won't write anything but update the in-memory cache
+ writeDomainInfo(info);
+ }
+ else {
+ // No need to write the complete DomainInfo in this case
+ writeTransientDomainInfo(info);
+ }
+ return true;
+}
+
bool LMDBBackend::setKind(const ZoneName& domain, const DomainInfo::DomainKind kind)
{
return genChangeDomain(domain, [kind](DomainInfo& di) {
txn.put(info, 0, d_random_ids, domain.hash());
txn.commit();
+ writeTransientDomainInfo(info);
}
return true;
void LMDBBackend::setLastCheckTime(domainid_t domain_id, time_t last_check)
{
- if (d_write_notification_update) {
- genChangeDomain(domain_id, [last_check](DomainInfo& info) {
- info.last_check = last_check;
- });
+ if (!d_write_notification_update) {
+ DomainInfo info;
+ if (findDomain(domain_id, info)) {
+ auto container = s_transient_domain_info.write_lock();
+ TransientDomainInfo tdi;
+ if (!container->get(info.id, tdi)) {
+ // No data yet, initialize from DomainInfo
+ tdi.notified_serial = info.notified_serial;
+ }
+ tdi.last_check = last_check;
+ container->update(info.id, tdi);
+ }
return;
}
- DomainInfo info;
- if (findDomain(domain_id, info)) {
- auto container = s_transient_domain_info.write_lock();
- TransientDomainInfo tdi;
- if (!container->get(info.id, tdi)) {
- // No data yet, initialize from DomainInfo
- tdi.notified_serial = info.notified_serial;
- }
- tdi.last_check = last_check;
- container->update(info.id, tdi);
- }
+ genChangeTransientDomain(domain_id, [last_check](DomainInfo& info) {
+ info.last_check = last_check;
+ });
}
void LMDBBackend::getUpdatedPrimaries(vector<DomainInfo>& updatedDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
void LMDBBackend::setNotified(domainid_t domain_id, uint32_t serial)
{
- if (d_write_notification_update) {
- genChangeDomain(domain_id, [serial](DomainInfo& info) {
- info.notified_serial = serial;
- });
+ if (!d_write_notification_update) {
+ DomainInfo info;
+ if (findDomain(domain_id, info)) {
+ auto container = s_transient_domain_info.write_lock();
+ TransientDomainInfo tdi;
+ if (!container->get(info.id, tdi)) {
+ // No data yet, initialize from DomainInfo
+ tdi.last_check = info.last_check;
+ }
+ tdi.notified_serial = serial;
+ container->update(info.id, tdi);
+ }
return;
}
- DomainInfo info;
- if (findDomain(domain_id, info)) {
- auto container = s_transient_domain_info.write_lock();
- TransientDomainInfo tdi;
- if (!container->get(info.id, tdi)) {
- // No data yet, initialize from DomainInfo
- tdi.last_check = info.last_check;
- }
- tdi.notified_serial = serial;
- container->update(info.id, tdi);
- }
+ genChangeTransientDomain(domain_id, [serial](DomainInfo& info) {
+ info.notified_serial = serial;
+ });
}
class getCatalogMembersReturnFalseException : std::runtime_error
if (findDomain(domid, info)) {
info.notified_serial = tdi.notified_serial;
info.last_check = tdi.last_check;
- auto txn = d_tdomains->getRWTransaction();
- txn.put(info, info.id);
- txn.commit();
+ // If the DomainInfo table is split, only update the extra table.
+ if (d_split_domains_table) {
+ writeTransientDomainInfo(info);
+ }
+ else {
+ auto txn = d_tdomains->getRWTransaction();
+ txn.put(info, info.id);
+ txn.commit();
+ }
}
else {
// Domain has been removed. This should not happen because deletion
declare(suffix, "shards-map-size", "shard LMDB map size in megabytes, zero to use the same size as main", "0");
declare(suffix, "flag-deleted", "Flag entries on deletion instead of deleting them", "no");
declare(suffix, "write-notification-update", "Update domain table upon notification", "yes");
+ declare(suffix, "split-domains-table", "Use a split domain table to reduce I/O load after XFR notifications", "no");
declare(suffix, "lightning-stream", "Run in Lightning Stream compatible mode", "no");
}
DNSBackend* make(const string& suffix = "") override
bool active{true};
bool published{true};
};
+ // Transient DomainInfo data, not necessarily synchronized with the
+ // database.
+ // All the fields exist with the exact same types in DomainInfo.
+ struct TransientDomainInfo
+ {
+ time_t last_check{};
+ uint32_t notified_serial{};
+ };
class LMDBResourceRecord : public DNSResourceRecord
{
public:
index_on<TSIGKey, DNSName, &TSIGKey::name>>
ttsig_t;
+ using tdomain_extra_t = TypedDBI<TransientDomainInfo, nullindex_t>;
+
int d_asyncFlag;
struct RecordsDB
shared_ptr<ttsig_t> d_ttsig;
MDBDbi d_tnetworks;
MDBDbi d_tviews;
+ shared_ptr<tdomain_extra_t> d_tdomains_extra; // may be unset if no split domain data
shared_ptr<RecordsROTransaction> d_rotxn; // for lookup and list
shared_ptr<RecordsRWTransaction> d_rwtxn; // for feedrecord within begin/aborttransaction
std::shared_ptr<RecordsROTransaction> getRecordsROTransaction(domainid_t id, const std::shared_ptr<LMDBBackend::RecordsRWTransaction>& rwtxn = nullptr);
bool genChangeDomain(const ZoneName& domain, const std::function<void(DomainInfo&)>& func);
bool genChangeDomain(domainid_t id, const std::function<void(DomainInfo&)>& func);
+ bool genChangeTransientDomain(domainid_t id, const std::function<void(DomainInfo&)>& func);
static void deleteDomainRecords(RecordsRWTransaction& txn, const std::string& match, QType qtype = QType::ANY);
bool findDomain(const ZoneName& domain, DomainInfo& info) const;
bool findDomain(domainid_t domainid, DomainInfo& info) const;
void consolidateDomainInfo(DomainInfo& info) const;
void writeDomainInfo(const DomainInfo& info);
+ void writeTransientDomainInfo(const DomainInfo& info);
void setLastCheckTime(domainid_t domain_id, time_t last_check);
string directBackendCmd_list(std::vector<string>& argv);
- // Transient DomainInfo data, not necessarily synchronized with the
- // database.
- struct TransientDomainInfo
- {
- time_t last_check{};
- uint32_t notified_serial{};
- };
- // Cache of DomainInfo notified_serial values
+ // Cache of TransientDomainInfo
class TransientDomainInfoCache : public boost::noncopyable
{
public:
bool d_handle_dups;
bool d_views;
bool d_write_notification_update;
+ bool d_split_domains_table;
DTime d_dtime; // used only for logging
uint64_t d_mapsize_main;
uint64_t d_mapsize_shards;