PowerDNS manual | ||
---|---|---|
Prev | Appendix C. Backend writers' guide |
The backends above are 'master capable' in that they contain all data relevant for a domain and do not pull in data from other nameservers. To enable storage of information, a backend must be able to do more.
Before diving into the details of the implementation some theory is in order. Slave domains are pulled from the master. PDNS needs to know for which domains it is to be a slave, and for each slave domain, what the IP address of the master is.
A slave zone is pulled from a master, after which it is 'fresh', but this is only temporary. In the SOA record of a zone there is a field which specifies the 'refresh' interval. After that interval has elapsed, the slave nameserver needs to check at the master ff the serial number there is higher than what is stored in the backend locally.
If this is the case, PDNS dubs the domain 'stale', and schedules a transfer of data from the remote. This transfer remains scheduled until the serial numbers remote and locally are identical again.
This theory is implemented by the getUnfreshSlaveInfos method, which is called on all backends periodically. This method fills a vector of SlaveDomains with domains that are unfresh and possibly stale.
PDNS then retrieves the SOA of those domains remotely and locally and creates a list of stale domains. For each of these domains, PDNS starts a zonetransfer to resynchronise. Because zone transfers can fail, it is important that the interface to the backend allows for transaction semantics because a zone might otherwise be left in a halfway updated situation.
The following excerpt from the DNSBackend shows the relevant functions:
class DNSBackend { public: /* ... */ virtual bool isMaster(const string &name, const string &ip); virtual bool startTransaction(const string &qname, int id); virtual bool commitTransaction(); virtual bool abortTransaction(); virtual bool feedRecord(const DNSResourceRecord &rr); virtual void getUnfreshSlaveInfos(vector<SlaveDomain>* domains); virtual void setFresh(int id); /* ... */ }
These functions all have a default implementation that returns false - which explains that these methods can be omitted in simple backends. Furthermore, unlike with simple backends, a slave capable backend must make sure that the 'DNSBackend *db' field of the SOAData record is filled out correctly - it is used to determine which backend will house this zone.
If a backend considers itself a slave for the domain name and if the IP address in ip is indeed a master, it should return true. False otherwise. This is a first line of checks to guard against reloading a domain unnecessarily.
When called, the backend should examine its list of slave domains and add any unfresh ones to the domains vector.
When called, the backend should start a transaction that can be committed or rolled back atomically later on. In SQL terms, this function should BEGIN a transaction and DELETE all records.
Insert this record.
Make the changes effective. In SQL terms, execute COMMIT.
Abort changes. In SQL terms, execute ABORT.
Indicate that a domain has either been updated or refreshed without the need for a retransfer. This causes the domain to vanish from the vector modified by getUnfreshSlaveInfos().
PDNS will always call startTransaction() before making calls to feedRecord(). Although it is likely that abortTransaction() will be called in case of problems, backends should also be prepared to abort from their destructor.
The actual code in PDNS is currently (1.99.9):
Resolver resolver; resolver.axfr(remote,domain.c_str()); db->startTransaction(domain, domain_id); L<<Logger::Error<<"AXFR started for '"<<domain<<"'"<<endl; Resolver::res_t recs; while(resolver.axfrChunk(recs)) { for(Resolver::res_t::const_iterator i=recs.begin();i!=recs.end();++i) { db->feedRecord(*i); } } db->commitTransaction(); db->setFresh(domain_id); L<<Logger::Error<<"AXFR done for '"<<domain<<"'"<<endl;