]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Introduce size *estimate* for record cache allocated size
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Wed, 18 Dec 2024 11:33:58 +0000 (12:33 +0100)
committerOtto Moerbeek <otto.moerbeek@open-xchange.com>
Tue, 14 Jan 2025 10:52:40 +0000 (11:52 +0100)
pdns/dnsname.hh
pdns/dnsparser.hh
pdns/dnsrecords.hh
pdns/recursordist/metrics_table.py
pdns/recursordist/recursor_cache.cc
pdns/recursordist/recursor_cache.hh
pdns/test-dnsrecords_cc.cc

index 9ba8ba7fbb7401dd1155cac39a0e42427538ab03..ccf8eb004de6f2d3f0d2ed8596314a91b06983c5 100644 (file)
@@ -179,6 +179,13 @@ public:
     return d_storage;
   }
 
+  [[nodiscard]] size_t sizeEstimate() const
+  {
+    return d_storage.size(); // knowingly overestimating small strings as most string
+                             // implementations have internal capacity and we always include
+                             // sizeof(*this)
+  }
+
   bool has8bitBytes() const; /* returns true if at least one byte of the labels forming the name is not included in [A-Za-z0-9_*./@ \\:-] */
 
   class RawLabelsVisitor
index 762358fddadbec71fd16037b0b28a93d5866b4d7..412dafb6c8f094c8cd04bcb32999876cf7df001f 100644 (file)
@@ -293,6 +293,8 @@ public:
     d_locked.store(true);
   }
 
+  [[nodiscard]] virtual size_t sizeEstimate() const = 0;
+
 protected:
   typedef std::map<std::pair<uint16_t, uint16_t>, makerfunc_t* > typemap_t;
   typedef std::map<std::pair<uint16_t, uint16_t>, zmakerfunc_t* > zmakermap_t;
@@ -433,6 +435,11 @@ public:
 
     return *d_content == *rhs.d_content;
   }
+
+  [[nodiscard]] size_t sizeEstimate() const
+  {
+    return sizeof(*this) + d_name.sizeEstimate() + (d_content ? d_content->sizeEstimate() : 0U);
+  }
 };
 
 struct DNSZoneRecord
@@ -469,6 +476,11 @@ public:
     return d_record;
   }
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_dr.sizeEstimate() + d_record.size();
+  }
+
 private:
   DNSRecord d_dr;
   vector<uint8_t> d_record;
index 8e3c46aacd07674f53af9c5940f92087268ebb63..b5239b68fadfa4bc7ec581c50a61589ad28e1325 100644 (file)
 
 struct ReportIsOnlyCallableByReportAllTypes;
 
-#define includeboilerplate(RNAME)   RNAME##RecordContent(const DNSRecord& dr, PacketReader& pr); \
-  RNAME##RecordContent(const string& zoneData);                                                  \
-  static void report(const ReportIsOnlyCallableByReportAllTypes& guard);                         \
-  static std::shared_ptr<DNSRecordContent> make(const DNSRecord &dr, PacketReader& pr);          \
-  static std::shared_ptr<DNSRecordContent> make(const string& zonedata);                         \
-  string getZoneRepresentation(bool noDot=false) const override;                                 \
-  void toPacket(DNSPacketWriter& pw) const override;                                             \
-  uint16_t getType() const override { return QType::RNAME; }                                     \
-  template<class Convertor> void xfrPacket(Convertor& conv, bool noDot=false) const;             \
-  template<class Convertor> void xfrPacket(Convertor& conv, bool noDot=false);
+#define includeboilerplate(RNAME)                                                       \
+  RNAME##RecordContent(const DNSRecord& dr, PacketReader& pr);                          \
+  RNAME##RecordContent(const string& zoneData);                                         \
+  static void report(const ReportIsOnlyCallableByReportAllTypes& guard);                \
+  static std::shared_ptr<DNSRecordContent> make(const DNSRecord& dr, PacketReader& pr); \
+  static std::shared_ptr<DNSRecordContent> make(const string& zonedata);                \
+  string getZoneRepresentation(bool noDot = false) const override;                      \
+  void toPacket(DNSPacketWriter& pw) const override;                                    \
+  uint16_t getType() const override { return QType::RNAME; }                            \
+  template <class Convertor>                                                            \
+  void xfrPacket(Convertor& conv, bool noDot = false) const;                            \
+  template <class Convertor>                                                            \
+  void xfrPacket(Convertor& conv, bool noDot = false);
 
 class NAPTRRecordContent : public DNSRecordContent
 {
@@ -62,6 +65,10 @@ public:
   {
     return d_replacement;
   }
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_replacement.sizeEstimate() + d_flags.size() + d_services.size() + d_regexp.size();
+  }
 private:
   uint16_t d_order, d_preference;
   string d_flags, d_services, d_regexp;
@@ -83,6 +90,10 @@ public:
       return false;
     return d_ip == dynamic_cast<const ARecordContent&>(rhs).d_ip;
   }
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this);
+  }
 private:
   uint32_t d_ip;
 };
@@ -100,6 +111,10 @@ public:
       return false;
     return d_ip6 == dynamic_cast<const decltype(this)>(&rhs)->d_ip6;
   }
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_ip6.size();
+  }
 private:
   string d_ip6; // why??
 };
@@ -121,7 +136,10 @@ public:
     auto rrhs =dynamic_cast<const decltype(this)>(&rhs);
     return std::tie(d_preference, d_mxname) == std::tie(rrhs->d_preference, rrhs->d_mxname);
   }
-
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_mxname.sizeEstimate();
+  }
 };
 
 class KXRecordContent : public DNSRecordContent
@@ -131,6 +149,10 @@ public:
 
   includeboilerplate(KX)
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_exchanger.sizeEstimate();
+  }
 private:
   uint16_t d_preference;
   DNSName d_exchanger;
@@ -143,6 +165,10 @@ public:
 
   includeboilerplate(IPSECKEY)
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_gateway.sizeEstimate() + d_publickey.size() + d_ip6.size();
+  }
 private:
   uint32_t d_ip4;
   DNSName d_gateway;
@@ -155,6 +181,10 @@ class DHCIDRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(DHCID)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_content.size();
+  }
 
 private:
   string d_content;
@@ -167,6 +197,10 @@ public:
   SRVRecordContent(uint16_t preference, uint16_t weight, uint16_t port, DNSName  target);
 
   includeboilerplate(SRV)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_target.sizeEstimate();
+  }
 
   uint16_t d_weight, d_port;
   DNSName d_target;
@@ -178,6 +212,10 @@ class TSIGRecordContent : public DNSRecordContent
 public:
   includeboilerplate(TSIG)
   TSIGRecordContent() = default;
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_algoName.sizeEstimate() + d_mac.size() + d_otherData.size();
+  }
 
   uint16_t d_origID{0};
   uint16_t d_fudge{0};
@@ -197,6 +235,11 @@ class TXTRecordContent : public DNSRecordContent
 public:
   includeboilerplate(TXT)
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_text.size();
+  }
+
   string d_text;
 };
 
@@ -205,6 +248,10 @@ class LUARecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(LUA)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + getCode().size();
+  }
   string getCode() const;
   uint16_t d_type;
   string d_code;
@@ -215,6 +262,10 @@ class ENTRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(ENT)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this);
+  }
 };
 
 class SPFRecordContent : public DNSRecordContent
@@ -225,6 +276,10 @@ public:
   {
     return d_text;
   }
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_text.size();
+  }
 
 private:
   string d_text;
@@ -244,7 +299,10 @@ public:
     auto rrhs =dynamic_cast<const decltype(this)>(&rhs);
     return d_content == rrhs->d_content;
   }
-
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_content.sizeEstimate();
+  }
 private:
   DNSName d_content;
 };
@@ -255,6 +313,10 @@ public:
   includeboilerplate(PTR)
   explicit PTRRecordContent(const DNSName& content) : d_content(content){}
   const DNSName& getContent() const { return d_content; }
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_content.sizeEstimate();
+  }
 private:
   DNSName d_content;
 };
@@ -265,6 +327,10 @@ public:
   includeboilerplate(CNAME)
   CNAMERecordContent(const DNSName& content) : d_content(content){}
   DNSName getTarget() const { return d_content; }
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_content.sizeEstimate();
+  }
 private:
   DNSName d_content;
 };
@@ -279,6 +345,10 @@ public:
   {
     return d_content;
   }
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_content.sizeEstimate();
+  }
 private:
   DNSName d_content;
 };
@@ -290,6 +360,10 @@ public:
   includeboilerplate(DNAME)
   DNAMERecordContent(const DNSName& content) : d_content(content){}
   const DNSName& getTarget() const { return d_content; }
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_content.sizeEstimate();
+  }
 private:
   DNSName d_content;
 };
@@ -299,6 +373,10 @@ class MBRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(MB)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_madname.sizeEstimate();
+  }
 
 private:
   DNSName d_madname;
@@ -308,6 +386,10 @@ class MGRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(MG)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_mgmname.sizeEstimate();
+  }
 
 private:
   DNSName d_mgmname;
@@ -317,6 +399,10 @@ class MRRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(MR)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_alias.sizeEstimate();
+  }
 
 private:
   DNSName d_alias;
@@ -326,6 +412,10 @@ class MINFORecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(MINFO)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_rmailbx.sizeEstimate() + d_emailbx.sizeEstimate();
+  }
 
 private:
   DNSName d_rmailbx;
@@ -338,6 +428,10 @@ public:
   OPTRecordContent() = default;
   includeboilerplate(OPT)
   void getData(vector<pair<uint16_t, string> > &opts) const;
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_data.size();
+  }
 private:
   string d_data;
 };
@@ -347,6 +441,10 @@ class HINFORecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(HINFO)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_cpu.size() + d_host.size();
+  }
 
 private:
   string d_cpu, d_host;
@@ -356,7 +454,10 @@ class RPRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(RP)
-
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_mbox.sizeEstimate() + d_info.sizeEstimate();
+  }
 private:
   DNSName d_mbox, d_info;
 };
@@ -378,6 +479,10 @@ public:
     return std::tie(d_flags, d_protocol, d_algorithm, d_key) <
       std::tie(rhs.d_flags, rhs.d_protocol, rhs.d_algorithm, rhs.d_key);
   }
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_key.size();
+  }
 };
 
 class CDNSKEYRecordContent : public DNSRecordContent
@@ -386,6 +491,10 @@ public:
   CDNSKEYRecordContent();
   includeboilerplate(CDNSKEY)
   uint16_t getTag();
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_key.size();
+  }
 
   uint16_t d_flags{0};
   uint8_t d_protocol{0};
@@ -412,6 +521,10 @@ public:
   }
 
   includeboilerplate(DS)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_digest.size();
+  }
 
   uint16_t d_tag{0};
   uint8_t d_algorithm{0}, d_digesttype{0};
@@ -423,6 +536,10 @@ class CDSRecordContent : public DNSRecordContent
 public:
   CDSRecordContent();
   includeboilerplate(CDS)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_digest.size();
+  }
 
   uint16_t d_tag{0};
   uint8_t d_algorithm{0}, d_digesttype{0};
@@ -434,6 +551,10 @@ class DLVRecordContent : public DNSRecordContent
 public:
   DLVRecordContent();
   includeboilerplate(DLV)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_digest.size();
+  }
 
   uint16_t d_tag{0};
   uint8_t d_algorithm{0}, d_digesttype{0};
@@ -445,6 +566,10 @@ class SSHFPRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(SSHFP)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_fingerprint.size();
+  }
 
 private:
   uint8_t d_algorithm, d_fptype;
@@ -455,6 +580,10 @@ class KEYRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(KEY)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_certificate.size();
+  }
 
 private:
   uint16_t d_flags;
@@ -466,6 +595,10 @@ class AFSDBRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(AFSDB)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_hostname.sizeEstimate();
+  }
 
 private:
   uint16_t d_subtype;
@@ -481,6 +614,10 @@ class CERTRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(CERT)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_certificate.size();
+  }
 
 private:
   uint16_t d_type, d_tag;
@@ -492,6 +629,10 @@ class TLSARecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(TLSA)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_cert.size();
+  }
 
 private:
   uint8_t d_certusage, d_selector, d_matchtype;
@@ -502,6 +643,10 @@ class SMIMEARecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(SMIMEA)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_cert.size();
+  }
 
 private:
   uint8_t d_certusage, d_selector, d_matchtype;
@@ -512,6 +657,10 @@ class OPENPGPKEYRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(OPENPGPKEY)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_keyring.size();
+  }
 
 private:
   string d_keyring;
@@ -536,6 +685,11 @@ class SVCBBaseRecordContent : public DNSRecordContent
     SvcParam getParam(const SvcParam::SvcParamKey &key) const;
     virtual std::shared_ptr<SVCBBaseRecordContent> clone() const = 0;
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_params.size() * sizeof(SvcParam) + d_target.sizeEstimate();
+  }
+
   protected:
     std::set<SvcParam> d_params;
     DNSName d_target;
@@ -565,6 +719,10 @@ public:
   RRSIGRecordContent();
   includeboilerplate(RRSIG)
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_signer.sizeEstimate() + d_signature.size();
+  }
   uint16_t d_type{0};
   uint16_t d_tag{0};
   DNSName d_signer;
@@ -589,6 +747,10 @@ class RKEYRecordContent : public DNSRecordContent
 public:
   RKEYRecordContent();
   includeboilerplate(RKEY)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_key.size();
+  }
   uint16_t d_flags{0};
   uint8_t d_protocol{0}, d_algorithm{0};
   string d_key;
@@ -600,6 +762,10 @@ public:
   includeboilerplate(SOA)
   SOARecordContent(DNSName  mname, DNSName  rname, const struct soatimes& st);
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_mname.sizeEstimate() + d_rname.sizeEstimate();
+  }
   DNSName d_mname;
   DNSName d_rname;
   struct soatimes d_st;
@@ -611,6 +777,10 @@ public:
   includeboilerplate(ZONEMD)
   //ZONEMDRecordContent(uint32_t serial, uint8_t scheme, uint8_t hashalgo, string digest);
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_digest.size();
+  }
   uint32_t d_serial;
   uint8_t d_scheme;
   uint8_t d_hashalgo;
@@ -680,6 +850,11 @@ public:
 
   static constexpr size_t const nbTypes = 65536;
 
+  [[nodiscard]] size_t sizeEstimate() const
+  {
+    return  d_bitset ? nbTypes / 8 : d_set.size() * (2U + sizeof(std::set<uint16_t>)); // XXX
+  }
+
 private:
 
   void migrateToBitSet()
@@ -731,6 +906,10 @@ public:
     return d_bitmap.count();
   }
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_next.sizeEstimate() + d_bitmap.sizeEstimate();
+  }
   DNSName d_next;
 private:
   NSECBitmap d_bitmap;
@@ -778,6 +957,10 @@ public:
     return d_flags & 1;
   }
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_salt.size() + d_nexthash.size() + d_bitmap.sizeEstimate();
+  }
 private:
   NSECBitmap d_bitmap;
 };
@@ -804,6 +987,11 @@ public:
     d_bitmap.set(type);
   }
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_bitmap.sizeEstimate();
+  }
+
 private:
   uint32_t d_serial{0};
   uint16_t d_flags{0};
@@ -827,6 +1015,10 @@ public:
     return QType::NSEC3PARAM;
   }
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_salt.size();
+  }
 
   uint8_t d_algorithm{0}, d_flags{0};
   uint16_t d_iterations{0};
@@ -853,6 +1045,10 @@ public:
     return QType::LOC;
   }
 
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this);
+  }
 private:
 };
 
@@ -861,6 +1057,10 @@ class NIDRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(NID);
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this);
+  }
 
 private:
   uint16_t d_preference;
@@ -871,6 +1071,10 @@ class L32RecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(L32);
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this);
+  }
 
 private:
   uint16_t d_preference;
@@ -881,6 +1085,10 @@ class L64RecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(L64);
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this);
+  }
 
 private:
   uint16_t d_preference;
@@ -891,6 +1099,10 @@ class LPRecordContent : public DNSRecordContent
 {
 public:
   includeboilerplate(LP);
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_fqdn.sizeEstimate();
+  }
 
 private:
   uint16_t d_preference;
@@ -907,6 +1119,10 @@ public:
   string getZoneRepresentation(bool noDot=false) const override;
   void toPacket(DNSPacketWriter& pw) const override;
   uint16_t getType() const override { return QType::EUI48; }
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this);
+  }
 private:
  // storage for the bytes
  uint8_t d_eui48[6];
@@ -922,6 +1138,10 @@ public:
   string getZoneRepresentation(bool noDot=false) const override;
   void toPacket(DNSPacketWriter& pw) const override;
   uint16_t getType() const override { return QType::EUI64; }
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this);
+  }
 private:
  // storage for the bytes
  uint8_t d_eui64[8];
@@ -944,6 +1164,10 @@ class APLRecordContent : public DNSRecordContent
 public:
   APLRecordContent() = default;
   includeboilerplate(APL)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + aplrdata.size() * sizeof(APLRDataElement);
+  }
 private:
   std::vector<APLRDataElement> aplrdata;
   APLRDataElement parseAPLElement(const string &element);
@@ -955,6 +1179,10 @@ class TKEYRecordContent : public DNSRecordContent
 public:
   TKEYRecordContent();
   includeboilerplate(TKEY)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_algo.sizeEstimate() + d_key.size() + d_other.size();
+  }
 
   // storage for the bytes
   uint16_t d_othersize{0};
@@ -974,15 +1202,23 @@ private:
 class URIRecordContent : public DNSRecordContent {
   public:
     includeboilerplate(URI)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_target.size();
+  }
   private:
     uint16_t d_priority, d_weight;
     string d_target;
 };
 
 class CAARecordContent : public DNSRecordContent {
-  public:
-    includeboilerplate(CAA)
-  private:
+public:
+  includeboilerplate(CAA)
+  [[nodiscard]] size_t sizeEstimate() const override
+  {
+    return sizeof(*this) + d_tag.size() + d_value.size();
+  }
+private:
     uint8_t d_flags;
     string d_tag, d_value;
 };
index 8ed450dfa62442b1c96a935a0320d455d9f83945..93e6e3ba9ae633a6741e2880c450a2524369c00a 100644 (file)
@@ -85,7 +85,7 @@
         'lambda': 'doGetCacheBytes',
         'ptype': 'gauge',
         'desc': 'Size of the cache in bytes',
-        'longdesc': '''Disabled by default, see :ref:`setting-stats-rec-control-disabled-list`. This metric is currently broken, it always is 0.''',
+        'longdesc': '''Since version 5.3.0 this metric computes a rough estimate of the number of bytes allocated by the record cache. Older versions return a number that cannot be relied upon. Disabled by default, as computing this number is CPU intensive, see :ref:`setting-stats-rec-control-disabled-list`.''',
         'snmp': 7,
     },
     {
index fd9d6e12c417ecb1a2274457a3d354164625d93b..df29606e46b350dfaf58941e866a64ec5885e132 100644 (file)
@@ -126,6 +126,41 @@ size_t MemRecursorCache::ecsIndexSize()
   return count;
 }
 
+size_t MemRecursorCache::CacheEntry::authRecsSizeEstimate() const
+{
+  size_t ret = 0;
+  if (d_authorityRecs) {
+    for (const auto& record : *d_authorityRecs) {
+      ret += record.sizeEstimate();
+    }
+  }
+  return ret;
+}
+
+size_t MemRecursorCache::CacheEntry::sigRecsSizeEstimate() const
+{
+  size_t ret = 0;
+  if (d_signatures) {
+    for (const auto& record : *d_signatures) {
+      ret += record->sizeEstimate();
+    }
+  }
+  return ret;
+}
+
+size_t MemRecursorCache::CacheEntry::sizeEstimate() const
+{
+  auto ret = sizeof(struct CacheEntry);
+  ret += d_qname.sizeEstimate();
+  ret += d_authZone.sizeEstimate();
+  for (const auto& record : d_records) {
+    ret += record->sizeEstimate();
+  }
+  ret += authRecsSizeEstimate();
+  ret += sigRecsSizeEstimate();
+  return ret;
+}
+
 // this function is too slow to poll!
 size_t MemRecursorCache::bytes()
 {
@@ -133,11 +168,7 @@ size_t MemRecursorCache::bytes()
   for (auto& shard : d_maps) {
     auto lockedShard = shard.lock();
     for (const auto& entry : lockedShard->d_map) {
-      ret += sizeof(struct CacheEntry);
-      ret += entry.d_qname.toString().length();
-      for (const auto& record : entry.d_records) {
-        ret += sizeof(record); // XXX WRONG we don't know the stored size!
-      }
+      ret += entry.sizeEstimate();
     }
   }
   return ret;
@@ -881,7 +912,11 @@ uint64_t MemRecursorCache::doDump(int fileDesc, size_t maxCacheEntries)
   for (auto& shard : d_maps) {
     auto lockedShard = shard.lock();
     const auto shardSize = lockedShard->d_map.size();
-    fprintf(filePtr.get(), "; record cache shard %zu; size %zu\n", shardNumber, shardSize);
+    size_t bytes = 0;
+    for (const auto& entry : lockedShard->d_map) {
+      bytes += entry.sizeEstimate();
+    }
+    fprintf(filePtr.get(), "; record cache shard %zu; size %zu bytes %zu\n", shardNumber, shardSize, bytes);
     min = std::min(min, shardSize);
     max = std::max(max, shardSize);
     shardNumber++;
index beaf44a9799d71f5d8bf01d819bd240561b292d4..004b0c196530166b4e067a675cb64b7866ec4529 100644 (file)
@@ -156,6 +156,10 @@ private:
 
     bool shouldReplace(time_t now, bool auth, vState state, bool refresh);
 
+    [[nodiscard]] size_t sizeEstimate() const;
+    [[nodiscard]] size_t authRecsSizeEstimate() const;
+    [[nodiscard]] size_t sigRecsSizeEstimate() const;
+
     records_t d_records;
     SigRecs d_signatures;
     AuthRecs d_authorityRecs;
index ddcd04271a2444a87ccb34d1ada8a11c1ef013bb..fb83a86bbc3153c2e3c0623233a0c6117ec7885e 100644 (file)
@@ -716,4 +716,27 @@ BOOST_AUTO_TEST_CASE(test_nsec3_records_types) {
   }
 }
 
+BOOST_AUTO_TEST_CASE(test_size_estimate)
+{
+  auto anA = DNSRecordContent::make(QType::A, QClass::IN, "1.2.3.4");
+  auto anAAAA = DNSRecordContent::make(QType::AAAA, QClass::IN, "1234:4578:89ab::");
+  auto aTXT = DNSRecordContent::make(QType::TXT, QClass::IN, "\"a somewhat long text\"");
+  BOOST_CHECK(anAAAA->sizeEstimate() < aTXT->sizeEstimate());
+
+  auto rrsigA = DNSRecordContent::make(QType::RRSIG, QClass::IN, "SOA 8 3 300 20130523000000 20130509000000 54216 rec.test. ecWKD/OsdAiXpbM/sgPT82KVD/WiQnnqcxoJgiH3ixHa+LOAcYU7FG7V4BRRJxLriY1e0rB2gAs3kCel9D4bzfK6wAqG4Di/eHUgHptRlaR2ycELJ4t1pjzrnuGiIzA1wM2izRmeE+Xoy1367Qu0pOz5DLzTfQITWFsB2iUzN4Y=");
+  auto rrsigB = DNSRecordContent::make(QType::RRSIG, QClass::IN, "SOA 8 3 300 20130523000000 20130509000000 54216 rec.test. fcWKD/OsdAiXpbM/sgPT82KVD/WiQnnqcxoJgiH3ixHa+LOAcYU7FG7V4BRRJxLriY1e0rB2gAs3kCel9D4bzfK6wAqG4Di/eHUgHptRlaR2ycELJ4t1pjzrnuGiIzA1wM2izRmeE+Xoy1367Qu0pOz5DLzTfQITWFsB2iUzN4YAAAA=");
+  BOOST_CHECK(rrsigA->sizeEstimate() < rrsigB->sizeEstimate());
+  BOOST_CHECK(rrsigA->sizeEstimate() > aTXT->sizeEstimate());
+
+  auto nsecA = DNSRecordContent::make(QType::NSEC, QClass::IN, "host.example.com. A MX RRSIG NSEC TYPE1234");
+  auto nsecB = DNSRecordContent::make(QType::NSEC, QClass::IN, "host1.example.com. A MX RRSIG NSEC TYPE1234");
+  BOOST_CHECK(nsecA->sizeEstimate() < nsecB->sizeEstimate());
+  BOOST_CHECK(nsecA->sizeEstimate() > aTXT->sizeEstimate());
+
+  auto nsec3A = DNSRecordContent::make(QType::NSEC3, QClass::IN, "1 1 12 aabbccdd 2vptu5timamqttgl4luu9kg21e0aor3s a mx rrsig nsec3 type1234 type65535");
+  auto nsec3B = DNSRecordContent::make(QType::NSEC3, QClass::IN, "1 1 0 - 2vptu5timamqttgl4luu9kg21e0aor3s");
+  BOOST_CHECK(nsec3A->sizeEstimate() > nsec3B->sizeEstimate());
+  BOOST_CHECK(nsec3B->sizeEstimate() > aTXT->sizeEstimate());
+}
+
 BOOST_AUTO_TEST_SUITE_END()