github.com/koko1123/flow-go-1@v0.29.6/module/mempool/herocache/dns_cache.go (about) 1 package herocache 2 3 import ( 4 "fmt" 5 "net" 6 7 "github.com/rs/zerolog" 8 9 "github.com/koko1123/flow-go-1/model/flow" 10 "github.com/koko1123/flow-go-1/module" 11 "github.com/koko1123/flow-go-1/module/mempool" 12 herocache "github.com/koko1123/flow-go-1/module/mempool/herocache/backdata" 13 "github.com/koko1123/flow-go-1/module/mempool/herocache/backdata/heropool" 14 "github.com/koko1123/flow-go-1/module/mempool/stdmap" 15 ) 16 17 type DNSCache struct { 18 ipCache *stdmap.Backend 19 txtCache *stdmap.Backend 20 } 21 22 func NewDNSCache(sizeLimit uint32, logger zerolog.Logger, ipCollector module.HeroCacheMetrics, txtCollector module.HeroCacheMetrics) *DNSCache { 23 return &DNSCache{ 24 txtCache: stdmap.NewBackend( 25 stdmap.WithBackData( 26 herocache.NewCache( 27 sizeLimit, 28 herocache.DefaultOversizeFactor, 29 heropool.LRUEjection, 30 logger.With().Str("mempool", "dns-txt-cache").Logger(), 31 txtCollector))), 32 ipCache: stdmap.NewBackend( 33 stdmap.WithBackData( 34 herocache.NewCache( 35 sizeLimit, 36 herocache.DefaultOversizeFactor, 37 heropool.LRUEjection, 38 logger.With().Str("mempool", "dns-ip-cache").Logger(), 39 ipCollector))), 40 } 41 } 42 43 // PutIpDomain adds the given ip domain into the cache. 44 func (d *DNSCache) PutIpDomain(domain string, addresses []net.IPAddr, timestamp int64) bool { 45 i := ipEntity{ 46 IpRecord: mempool.IpRecord{ 47 Domain: domain, 48 Addresses: addresses, 49 Timestamp: timestamp, 50 Locked: false, 51 }, 52 id: domainToIdentifier(domain), 53 } 54 55 return d.ipCache.Add(i) 56 } 57 58 // PutTxtRecord adds the given txt record into the cache. 59 func (d *DNSCache) PutTxtRecord(domain string, record []string, timestamp int64) bool { 60 t := txtEntity{ 61 TxtRecord: mempool.TxtRecord{ 62 Txt: domain, 63 Records: record, 64 Timestamp: timestamp, 65 Locked: false, 66 }, 67 id: domainToIdentifier(domain), 68 } 69 70 return d.txtCache.Add(t) 71 } 72 73 // GetDomainIp returns the ip domain if exists in the cache. 74 // The boolean return value determines if domain exists in the cache. 75 func (d *DNSCache) GetDomainIp(domain string) (*mempool.IpRecord, bool) { 76 entity, ok := d.ipCache.ByID(domainToIdentifier(domain)) 77 if !ok { 78 return nil, false 79 } 80 81 i, ok := entity.(ipEntity) 82 if !ok { 83 return nil, false 84 } 85 ipRecord := i.IpRecord 86 87 return &ipRecord, true 88 } 89 90 // GetTxtRecord returns the txt record if exists in the cache. 91 // The boolean return value determines if record exists in the cache. 92 func (d *DNSCache) GetTxtRecord(domain string) (*mempool.TxtRecord, bool) { 93 entity, ok := d.txtCache.ByID(domainToIdentifier(domain)) 94 if !ok { 95 return nil, false 96 } 97 98 t, ok := entity.(txtEntity) 99 if !ok { 100 return nil, false 101 } 102 txtRecord := t.TxtRecord 103 104 return &txtRecord, true 105 } 106 107 // RemoveIp removes an ip domain from cache. 108 func (d *DNSCache) RemoveIp(domain string) bool { 109 return d.ipCache.Remove(domainToIdentifier(domain)) 110 } 111 112 // RemoveTxt removes a txt record from cache. 113 func (d *DNSCache) RemoveTxt(domain string) bool { 114 return d.txtCache.Remove(domainToIdentifier(domain)) 115 } 116 117 // LockIPDomain locks an ip address dns record if exists in the cache. 118 // The boolean return value determines whether attempt on locking was successful. 119 // 120 // A locking attempt is successful when the domain record exists in the cache and has not 121 // been locked before. 122 // Once a domain record gets locked the only way to unlock it is through updating that record. 123 // 124 // The locking process is defined to record that a resolving attempt is ongoing for an expired domain. 125 // So the locking happens to avoid any other parallel resolving 126 func (d *DNSCache) LockIPDomain(domain string) (bool, error) { 127 locked := false 128 err := d.ipCache.Run(func(backdata mempool.BackData) error { 129 id := domainToIdentifier(domain) 130 entity, ok := backdata.ByID(id) 131 if !ok { 132 return fmt.Errorf("ip record does not exist in cache for locking: %s", domain) 133 } 134 135 record, ok := entity.(ipEntity) 136 if !ok { 137 return fmt.Errorf("unexpected type retrieved, expected: %T, obtained: %T", ipEntity{}, entity) 138 } 139 140 if record.Locked { 141 return nil // record has already been locked 142 } 143 144 record.Locked = true 145 146 if _, removed := backdata.Remove(id); !removed { 147 return fmt.Errorf("ip record could not be removed from backdata") 148 } 149 150 if added := backdata.Add(id, record); !added { 151 return fmt.Errorf("updated ip record could not be added to back data") 152 } 153 154 locked = record.Locked 155 return nil 156 }) 157 158 return locked, err 159 } 160 161 // UpdateIPDomain updates the dns record for the given ip domain with the new address and timestamp values. 162 func (d *DNSCache) UpdateIPDomain(domain string, addresses []net.IPAddr, timestamp int64) error { 163 return d.ipCache.Run(func(backdata mempool.BackData) error { 164 id := domainToIdentifier(domain) 165 166 // removes old entry if exists. 167 backdata.Remove(id) 168 169 ipRecord := ipEntity{ 170 IpRecord: mempool.IpRecord{ 171 Domain: domain, 172 Addresses: addresses, 173 Timestamp: timestamp, 174 Locked: false, // by default an ip record is unlocked. 175 }, 176 id: id, 177 } 178 179 if added := backdata.Add(id, ipRecord); !added { 180 return fmt.Errorf("updated ip record could not be added to backdata") 181 } 182 183 return nil 184 }) 185 } 186 187 // UpdateTxtRecord updates the dns record for the given txt domain with the new address and timestamp values. 188 func (d *DNSCache) UpdateTxtRecord(txt string, records []string, timestamp int64) error { 189 return d.txtCache.Run(func(backdata mempool.BackData) error { 190 id := domainToIdentifier(txt) 191 192 // removes old entry if exists. 193 backdata.Remove(id) 194 195 txtRecord := txtEntity{ 196 TxtRecord: mempool.TxtRecord{ 197 Txt: txt, 198 Records: records, 199 Timestamp: timestamp, 200 Locked: false, // by default a txt record is unlocked. 201 }, 202 id: id, 203 } 204 205 if added := backdata.Add(id, txtRecord); !added { 206 return fmt.Errorf("updated txt record could not be added to backdata") 207 } 208 209 return nil 210 }) 211 } 212 213 // LockTxtRecord locks a txt address dns record if exists in the cache. 214 // The boolean return value determines whether attempt on locking was successful. 215 // 216 // A locking attempt is successful when the domain record exists in the cache and has not 217 // been locked before. 218 // Once a domain record gets locked the only way to unlock it is through updating that record. 219 // 220 // The locking process is defined to record that a resolving attempt is ongoing for an expired domain. 221 // So the locking happens to avoid any other parallel resolving. 222 func (d *DNSCache) LockTxtRecord(txt string) (bool, error) { 223 locked := false 224 err := d.txtCache.Run(func(backdata mempool.BackData) error { 225 id := domainToIdentifier(txt) 226 entity, ok := backdata.ByID(id) 227 if !ok { 228 return fmt.Errorf("txt record does not exist in cache for locking: %s", txt) 229 } 230 231 record, ok := entity.(txtEntity) 232 if !ok { 233 return fmt.Errorf("unexpected type retrieved, expected: %T, obtained: %T", txtEntity{}, entity) 234 } 235 236 if record.Locked { 237 return nil // record has already been locked 238 } 239 240 record.Locked = true 241 242 if _, removed := backdata.Remove(id); !removed { 243 return fmt.Errorf("txt record could not be removed from backdata") 244 } 245 246 if added := backdata.Add(id, record); !added { 247 return fmt.Errorf("updated txt record could not be added to back data") 248 } 249 250 locked = record.Locked 251 return nil 252 }) 253 254 return locked, err 255 } 256 257 // Size returns total domains maintained into this cache. 258 // The first returned value determines number of ip domains. 259 // The second returned value determines number of txt records. 260 func (d DNSCache) Size() (uint, uint) { 261 return d.ipCache.Size(), d.txtCache.Size() 262 } 263 264 // ipEntity is a dns cache entry for ip records. 265 type ipEntity struct { 266 mempool.IpRecord 267 // caching identifier to avoid cpu overhead 268 // per query. 269 id flow.Identifier 270 } 271 272 func (i ipEntity) ID() flow.Identifier { 273 return i.id 274 } 275 276 func (i ipEntity) Checksum() flow.Identifier { 277 return domainToIdentifier(i.IpRecord.Domain) 278 } 279 280 // txtEntity is a dns cache entry for txt records. 281 type txtEntity struct { 282 mempool.TxtRecord 283 // caching identifier to avoid cpu overhead 284 // per query. 285 id flow.Identifier 286 } 287 288 func (t txtEntity) ID() flow.Identifier { 289 return t.id 290 } 291 292 func (t txtEntity) Checksum() flow.Identifier { 293 return domainToIdentifier(t.TxtRecord.Txt) 294 } 295 296 func domainToIdentifier(domain string) flow.Identifier { 297 return flow.MakeID(domain) 298 }