github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/dns/cache.go (about) 1 package dns 2 3 import ( 4 "fmt" 5 "net" 6 "time" 7 8 "github.com/rs/zerolog" 9 10 "github.com/onflow/flow-go/module/mempool" 11 ) 12 13 // DefaultTimeToLive is the default duration a dns result is cached. 14 const ( 15 // DefaultTimeToLive is the default duration a dns result is cached. 16 DefaultTimeToLive = 5 * time.Minute 17 DefaultCacheSize = 10e3 18 cacheEntryExists = true 19 cacheEntryFresh = true // TTL yet has not reached 20 ) 21 22 // ipResolutionResult encapsulates result of resolving an ip address within dns cache. 23 type ipResolutionResult struct { 24 addresses []net.IPAddr 25 exists bool // determines whether the domain exists in the cache. 26 fresh bool // determines whether the domain cache is fresh, i.e., TTL has not yet reached. 27 locked bool // determines whether the domain record is currently being resolved by another thread. 28 } 29 30 // txtResolutionResult encapsulates result of resolving a txt record within dns cache. 31 type txtResolutionResult struct { 32 records []string 33 exists bool // determines whether the domain exists in the cache. 34 fresh bool // determines whether the domain cache is fresh, i.e., TTL has not yet reached. 35 locked bool // determines whether the domain record is currently being resolved by another thread. 36 } 37 38 // cache is a ttl-based cache for dns entries 39 type cache struct { 40 ttl time.Duration // time-to-live for cache entry 41 dCache mempool.DNSCache 42 logger zerolog.Logger 43 } 44 45 func newCache(logger zerolog.Logger, dnsCache mempool.DNSCache) *cache { 46 return &cache{ 47 ttl: DefaultTimeToLive, 48 dCache: dnsCache, 49 logger: logger.With().Str("component", "dns-cache").Logger(), 50 } 51 } 52 53 // resolveIPCache resolves the domain through the cache if it is available. 54 func (c *cache) resolveIPCache(domain string) *ipResolutionResult { 55 record, ok := c.dCache.GetDomainIp(domain) 56 currentTimeStamp := runtimeNano() 57 if !ok { 58 // does not exist 59 return &ipResolutionResult{ 60 addresses: nil, 61 exists: !cacheEntryExists, 62 fresh: !cacheEntryFresh, 63 locked: false, 64 } 65 } 66 c.logger.Trace(). 67 Str("domain", domain). 68 Str("address", fmt.Sprintf("%v", record.Addresses)). 69 Int64("record_timestamp", record.Timestamp). 70 Int64("current_timestamp", currentTimeStamp). 71 Bool("record_locked", record.Locked). 72 Msg("dns record retrieved") 73 74 if time.Duration(currentTimeStamp-record.Timestamp) > c.ttl { 75 // exists but fresh 76 return &ipResolutionResult{ 77 addresses: record.Addresses, 78 exists: cacheEntryExists, 79 fresh: !cacheEntryFresh, 80 locked: record.Locked, 81 } 82 } 83 84 // exists and fresh 85 return &ipResolutionResult{ 86 addresses: record.Addresses, 87 exists: cacheEntryExists, 88 fresh: cacheEntryFresh, 89 locked: record.Locked, 90 } 91 } 92 93 // resolveIPCache resolves the txt through the cache if it is available. 94 func (c *cache) resolveTXTCache(txt string) *txtResolutionResult { 95 record, ok := c.dCache.GetTxtRecord(txt) 96 currentTimeStamp := runtimeNano() 97 if !ok { 98 // does not exist 99 return &txtResolutionResult{ 100 records: nil, 101 exists: !cacheEntryExists, 102 fresh: !cacheEntryFresh, 103 locked: false, 104 } 105 } 106 c.logger.Trace(). 107 Str("txt", txt). 108 Str("address", fmt.Sprintf("%v", record.Records)). 109 Int64("record_timestamp", record.Timestamp). 110 Int64("current_timestamp", currentTimeStamp). 111 Bool("record_locked", record.Locked). 112 Msg("dns record retrieved") 113 114 if time.Duration(currentTimeStamp-record.Timestamp) > c.ttl { 115 // exists but fresh 116 return &txtResolutionResult{ 117 records: record.Records, 118 exists: cacheEntryExists, 119 fresh: !cacheEntryFresh, 120 locked: record.Locked, 121 } 122 } 123 124 // exists and fresh 125 return &txtResolutionResult{ 126 records: record.Records, 127 exists: cacheEntryExists, 128 fresh: cacheEntryFresh, 129 locked: record.Locked, 130 } 131 } 132 133 // updateIPCache updates the cache entry for the domain. 134 func (c *cache) updateIPCache(domain string, addr []net.IPAddr) { 135 lg := c.logger.With(). 136 Str("domain", domain). 137 Str("address", fmt.Sprintf("%v", addr)).Logger() 138 139 timestamp := runtimeNano() 140 141 err := c.dCache.UpdateIPDomain(domain, addr, runtimeNano()) 142 if err != nil { 143 lg.Error().Err(err).Msg("could not update ip record") 144 return 145 } 146 147 ipSize, txtSize := c.dCache.Size() 148 lg.Trace(). 149 Int64("timestamp", timestamp). 150 Uint("ip_size", ipSize). 151 Uint("txt_size", txtSize). 152 Msg("dns cache updated") 153 } 154 155 // updateTXTCache updates the cache entry for the txt record. 156 func (c *cache) updateTXTCache(txt string, record []string) { 157 lg := c.logger.With(). 158 Str("txt", txt). 159 Strs("record", record).Logger() 160 161 timestamp := runtimeNano() 162 err := c.dCache.UpdateTxtRecord(txt, record, runtimeNano()) 163 if err != nil { 164 lg.Error().Err(err).Msg("could not update txt record") 165 return 166 } 167 168 ipSize, txtSize := c.dCache.Size() 169 lg.Trace(). 170 Int64("timestamp", timestamp). 171 Uint("ip_size", ipSize). 172 Uint("txt_size", txtSize). 173 Msg("dns cache updated") 174 } 175 176 // shouldResolveIP returns true if there is no other concurrent attempt ongoing for resolving the domain. 177 func (c *cache) shouldResolveIP(domain string) bool { 178 lg := c.logger.With(). 179 Str("domain", domain).Logger() 180 181 locked, err := c.dCache.LockIPDomain(domain) 182 if err != nil { 183 lg.Error().Err(err).Msg("cannot lock ip domain") 184 return false 185 } 186 187 lg.Trace().Bool("locked", locked).Msg("attempt on locking ip domain") 188 return locked 189 } 190 191 // shouldResolveTxt returns true if there is no other concurrent attempt ongoing for resolving the txt. 192 func (c *cache) shouldResolveTXT(txt string) bool { 193 lg := c.logger.With(). 194 Str("txt", txt).Logger() 195 196 locked, err := c.dCache.LockTxtRecord(txt) 197 if err != nil { 198 lg.Error().Err(err).Msg("cannot lock txt domain") 199 return false 200 } 201 202 lg.Trace().Bool("locked", locked).Msg("attempt on locking txt domain") 203 return locked 204 } 205 206 // invalidateIPCacheEntry atomically invalidates ip cache entry. Boolean variable determines whether invalidation 207 // is successful. 208 func (c *cache) invalidateIPCacheEntry(domain string) bool { 209 return c.dCache.RemoveIp(domain) 210 } 211 212 // invalidateTXTCacheEntry atomically invalidates txt cache entry. Boolean variable determines whether invalidation 213 // is successful. 214 func (c *cache) invalidateTXTCacheEntry(txt string) bool { 215 return c.dCache.RemoveTxt(txt) 216 }