new::base currently has some gaps regarding RRset handling.
Ideally, new::base::Record would implement [Partial]Ord to match the DNSSEC canonical order. However, DNSSEC canonical order does not account for records which are identical in every way except TTL; such records cannot coexist. But new::base::Record does implement [Partial]Eq, within which it does check TTLs. This is the most intuitive behavior we can provide. If we provided a [Partial]Ord impl, it would have to be consistent with [Partial]Eq, and so could not match DNSSEC canonical order perfectly. This needs to be documented in the API so that users understand why Record does not implement [Partial]Ord. Hash is not implemented for the same reason.
Because of this, new::base::Record cannot be stored in the standard {BTree,Hash}{Map,Set} types. A common use case here would be to deduplicate records or organize them into RRsets. This came up in the context of Cascade's zone storage, but it is not a particularly strong motivator, since we expect to implement some bespoke data structures and do everything manually. We expect there are many more use cases around deduplicating records and handling RRsets, which could benefit from ordering and hashing semantics, but we need to understand them better before we can provide convenient functionality for them.
This issue exists to remind us to add documentation on new::base or new::base::Record to explain this situation, and in the future, to point users towards good solutions for them. It also exists to collect use cases we want to design for.
Use Cases
- Writing a simple DNSSEC signer: organizing records (from an unsorted stream, e.g. zonefile or XFR) into RRsets to sign them. (Cascade does things manually for more efficiency.)
- Writing a DNS zone validator (possibly including DNSSEC verification): ensuring that the zone does not contain duplicate records.
- ...
Potential Solutions
One option is to provide wrapper types around Record that implement Ord (and perhaps Hash) in specific ways. For example, a CanonicalRecord wrapper would implement Ord as per DNSSEC canonical ordering, and ignore TTLs in Ord, Eq, and Hash.
type RRsets = BTreeSet<CanonicalRecord>;
struct CanonicalRecord(Record<Box<Name>, BoxedRecordData>);
While conceptually simple, this isn't so easy to work with; many useful BTreeSet methods have not been stabilized. It also duplicates a lot of information per RRset.
Another option would be to provide an "RRset key" type, which only stores the name and type (and perhaps class). We could also provide an "RRset data" type, which only stores the TTL and the data for each record. This is a little bit more complicated for the user, but quite flexible and powerful.
type RRsets = BTreeMap<RRsetKey, RRsetData>;
type RRsets = HashMap<RRsetKey, RRsetData>;
struct RRsetKey(Box<Name>, RType);
struct RRsetData(TTL, Vec<BoxedRecordData>);
new::basecurrently has some gaps regarding RRset handling.Ideally,
new::base::Recordwould implement[Partial]Ordto match the DNSSEC canonical order. However, DNSSEC canonical order does not account for records which are identical in every way except TTL; such records cannot coexist. Butnew::base::Recorddoes implement[Partial]Eq, within which it does check TTLs. This is the most intuitive behavior we can provide. If we provided a[Partial]Ordimpl, it would have to be consistent with[Partial]Eq, and so could not match DNSSEC canonical order perfectly. This needs to be documented in the API so that users understand whyRecorddoes not implement[Partial]Ord.Hashis not implemented for the same reason.Because of this,
new::base::Recordcannot be stored in the standard{BTree,Hash}{Map,Set}types. A common use case here would be to deduplicate records or organize them into RRsets. This came up in the context of Cascade's zone storage, but it is not a particularly strong motivator, since we expect to implement some bespoke data structures and do everything manually. We expect there are many more use cases around deduplicating records and handling RRsets, which could benefit from ordering and hashing semantics, but we need to understand them better before we can provide convenient functionality for them.This issue exists to remind us to add documentation on
new::baseornew::base::Recordto explain this situation, and in the future, to point users towards good solutions for them. It also exists to collect use cases we want to design for.Use Cases
Potential Solutions
One option is to provide wrapper types around
Recordthat implementOrd(and perhapsHash) in specific ways. For example, aCanonicalRecordwrapper would implementOrdas per DNSSEC canonical ordering, and ignore TTLs inOrd,Eq, andHash.While conceptually simple, this isn't so easy to work with; many useful
BTreeSetmethods have not been stabilized. It also duplicates a lot of information per RRset.Another option would be to provide an "RRset key" type, which only stores the name and type (and perhaps class). We could also provide an "RRset data" type, which only stores the TTL and the data for each record. This is a little bit more complicated for the user, but quite flexible and powerful.