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  }