-
Notifications
You must be signed in to change notification settings - Fork 368
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cache TTLs for individual IP addresses in DNS responses. #6732
base: main
Are you sure you want to change the base?
Conversation
if _, exist := oldDNSMeta.responseIPs[ipStr]; !exist { | ||
ipMetaDataHolder[ipStr] = ipWithTTL{ | ||
ip: ip, | ||
ttl: time.Now().Add(time.Duration(lowestTTL) * time.Second), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's better if you call time.Now()
a single time, earlier in the function
// This old IP also exists in current response, so update it with new received TTl. | ||
ipMetaDataHolder[ipStr] = ipWithTTL{ | ||
ip: ipMetaData.ip, | ||
ttl: time.Now().Add(time.Duration(lowestTTL) * time.Second), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, the new TTL (or rather expiration time) should be the max of the old value and the new value
This way we always honor the TTL that was received previously by other queries potentially from other apps.
|
||
type ipWithTTL struct { | ||
ip net.IP | ||
ttl time.Time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a ttl is usually a duration, so I would suggest retaining the expirationTime
name for this field
test/e2e/fqdn_dns_cache_test.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remove the changes from your other PR from this PR
if _, exist := oldDNSMeta.responseIPs[ipStr]; !exist { | ||
ipMetaDataHolder[ipStr] = ipWithTTL{ | ||
ip: ip, | ||
ttl: time.Now().Add(time.Duration(lowestTTL) * time.Second), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, parseDNSResponse
should probably return map[string]uint32
(ip->ttl).
d491609
to
5fbf0f1
Compare
ipMetaDataHolder[ipStr] = ipWithTTL{ | ||
ip: ip, | ||
ttl: time.Now().Add(time.Duration(lowestTTL) * time.Second), | ||
ip: ipMeta.ip, | ||
expirationTime: ipMeta.expirationTime, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this equivalent to ipMetaDataHolder[ipStr] = ipMeta
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not addressed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -489,22 +500,22 @@ func (f *fqdnController) onDNSResponse( | |||
} | |||
// The FQDN will be added to the queue only after `lowestTTL` value which | |||
// would already have been derived using the minTTL logic. | |||
f.dnsQueryQueue.AddAfter(fqdn, time.Duration(lowestTTL)*time.Second) | |||
f.dnsQueryQueue.AddAfter(fqdn, maxTimeToReQuery.Sub(currentTime)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That doesn't seem right: the time after which we should automatically re-query should be a "min", not a "max"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right , i mean we need Min because when any of these IP expires its is then that we have to send DNS queries . Got your point.
} | ||
fqdn := strings.ToLower(msg.Question[0].Name) | ||
lowestTTL := uint32(math.MaxUint32) // a TTL must exist in the RRs | ||
responseIPs := map[string]net.IP{} | ||
// Case 1: In the upcoming patch an admin would set this to a maximum value, which will be read from the configuration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that in with the minTTL feature, expirationTime will be maxTime(minTTL, r.Header().Ttl
instead. However, without a minTTL set, it should still be minTime(maxConfiguredTTL, r.Header().Ttl)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Dyanngg Get it now, and indeed i was actually getting a little confused as i was trying to put the upcoming patch related modifications here, which i understand now, will happen in upcoming days when i start working on the patch itself.
ipMetaDataHolder := make(map[string]ipWithTTL) | ||
minTimeToReQuery := time.Unix(1<<63-62135596801, 999999999) | ||
|
||
minTime := func(t1, t2 time.Time) time.Time { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: func earlierOf(t1, t2) / laterOf(t1, t2)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Dyanngg Yes, the names make more meaning.
f.fqdnSelectorMutex.Lock() | ||
defer f.fqdnSelectorMutex.Unlock() | ||
oldDNSMeta, exist := f.dnsEntryCache[fqdn] | ||
ipMetaDataHolder := make(map[string]ipWithTTL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: move the variable definitions before the lock, and keep if exist
right beneath the existence check makes it more readable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Dyanngg Thank you , that was helpful in sense that i did realise that the code looked more readable with ease after this.
ipMetaDataHolder[ipStr] = ipWithTTL{ | ||
ip: ipMeta.ip, | ||
expirationTime: ipMeta.expirationTime, | ||
} | ||
minTimeToReQuery = minTime(ipMeta.expirationTime, minTimeToReQuery) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since thees lines appear four times at least, might make sense to make it a helper.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Dyanngg Yes, that was a nice improvement.
@@ -76,11 +76,16 @@ func (fs *fqdnSelectorItem) matches(fqdn string) bool { | |||
// expirationTime of the records, which is the DNS response | |||
// receiving time plus lowest applicable TTL. | |||
type dnsMeta struct { | |||
expirationTime time.Time | |||
//expirationTime time.Time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove if no longer needed
@@ -253,8 +258,8 @@ func (f *fqdnController) getIPsForFQDNSelectors(fqdns []string) []net.IP { | |||
} | |||
for fqdn := range fqdnsMatched { | |||
if dnsMeta, ok := f.dnsEntryCache[fqdn]; ok { | |||
for _, ip := range dnsMeta.responseIPs { | |||
matchedIPs = append(matchedIPs, ip) | |||
for _, ipWithMetaData := range dnsMeta.responseIPs { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: s/ipWithMetaData/ipData
|
||
currentTime := time.Now() | ||
ipWithTTLMap := make(map[string]ipWithTTL) | ||
minTimeToReQuery := time.Unix(1<<63-62135596801, 999999999) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what time is this supposed to be? At the very least there should be a comment to explain. But based on usage, I am not sure why we are not just using the zero value for time.Time
:
var minTimeToReQuery time.Time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@antoninbas Actually ,
minTimeToReQuery := time.Unix(1<<63-62135596801, 999999999)
i had chosen above expression to establish a reference point for tracking the minimum time to re-query DNS requests as IPs expire. Since our goal in this PR for this bug/issue is to send requests as and when an IP expires, initializing minTimeToReQuery
to a maximum time value allows us to compare and update it as we process various time values , ultimately allowing us to determine the earliest re-query time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you could do though is to make earlierOf
take time.Time
pointers as parameters, and return the non-nil value if one of t1/t2 is nil. That way you would not need to initialize minTimeToReQuery
. Note that in that case the return value should also be a pointer IMO (since earlierOf(nil, nil) is nil), and when it's used eventually you should check that minTimeToReQuery
is not nil
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @Dyanngg , yes i think using that approach we can avoid initialising minTimeToReQuery
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am fine with the pointer solution, but it seems to me that we could just pick a reasonable time such as now + 24*time.Hour
and use that (with an appropriate comment of course). The time is only used if len(ipWithTTLMap) > 0
anyway. And it seems reasonable to resend a query after 24h even if we don't really need to? At least it won't impact correctness.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, i think this was the point i was missing . Maybe i had set it to too large a value under assumption that i needed a very large value. The 24 hour time frame seems more reasonable as a replacement to existing large value that i had put.
minTimeToReQuery := time.Unix(1<<63-62135596801, 999999999) | ||
addressUpdate := false | ||
|
||
ipMapFiller := func(ip string, ipMeta ipWithTTL, minTime *time.Time) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the name of this function (ipMapFiller
) is really hurting the readability of the code. I would suggest something like addIPtoMap
/ addIPToCache
/ addIPToNewCache
/ setIPInMap
/ ...
Additionally, minTime
does not need to be a parameter to this function: you are always using &minTimeToReQuery
as the argument value, and minTimeToReQuery
is captured by the closure (by reference).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes , my bad. The closure captures the variable minTimeToReQuery
and should not be passed as an argument.
t.Errorf("Expected TTL for %s to be %v, got %v", ipStr, expectedTTL, ipMeta.expirationTime) | ||
} | ||
} | ||
println("\n\n") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't use a print statement in a test like this
// sample IP with time simulating expired time, i thought that using negative time will | ||
// simulate expired time as it will always equate to before when compared to any time. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
be more concise in your comments:
// expired IP that should be removed from the cache when onDNSResponse is called
// we get new IP | ||
"192.0.2.3": {ip: net.ParseIP("192.0.2.3"), expirationTime: currentTime.Add(10 * time.Second)}, | ||
// and an exisiting IP | ||
"192.0.2.1": {ip: net.ParseIP("192.0.2.1"), expirationTime: currentTime.Add(10 * time.Second)}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't the test more useful if the new TTL for this IP is different from the current one? If the new TTL in the response is greater than the existing one, the new one should be sued. If it is less than the existing one, the current one should remain in use.
}, | ||
}, | ||
{ | ||
name: "old IP expired", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a strict subset of the previous test, and doesn't need to be included
there are 2 possible approaches here:
- have a single unit test case with multiple different IPs, ensuring that all the branches / cases of the
onDNSResponse
are tested - have multiple test cases, each testing a different branch / scenario
Usually, we prefer the second approach because it makes test failures easier to identify / troubleshoot.
test/e2e/framework.go
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these changes are unrelated to the rest of the PR, and should be removed
f.setFQDNMatchSelector(fqdn, selectorItem) | ||
for ipStr, ipMeta := range newDNSresponseIPs { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not make sense to be in part of the for loop... We don't want to call addIPToCache
for as many times as the number of selectorItems matching the fqdn
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Dyanngg yes updated it.
if mustCacheResponse { | ||
|
||
if len(ipWithTTLMap) > 0 { | ||
addressUpdate = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is correct. Isn't addressUpdate
supposed to be determined by whether FQDN <-> IP mappings have changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Dyanngg updated .
@@ -405,80 +409,102 @@ func (f *fqdnController) deleteRuleSelectedPods(ruleID string) error { | |||
|
|||
func (f *fqdnController) onDNSResponse( | |||
fqdn string, | |||
responseIPs map[string]net.IP, | |||
lowestTTL uint32, | |||
newDNSresponseIPs map[string]ipWithTTL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the camel format, it should be newDNSResponseIPs
, however, I feel DNS and response is kind of duplicate with the function name, and given you name the struct ipWithTTL
, I would just name the parameter newIPWithTTLs
, or newIPWithTTLMap
to correspond to the variable at L424.
//minTimeToReQuery Establishes a maximum reference time for tracking the minimum re-query time to DNS, as IPs expire. | ||
minTimeToReQuery := time.Now().Add(24 * time.Hour) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
//minTimeToReQuery Establishes a maximum reference time for tracking the minimum re-query time to DNS, as IPs expire. | |
minTimeToReQuery := time.Now().Add(24 * time.Hour) | |
// minTimeToRequery establishes a maximum reference time for tracking the minimum requery time to DNS, as IPs expire. | |
minTimeToRequery := time.Now().Add(24 * time.Hour) |
// This IP entry has already expired and not seen in the latest DNS response. | ||
// It should be removed from the cache. | ||
|
||
for cachedIpStr, cachedIpMeta := range cachedDNSMeta.responseIPs { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for cachedIpStr, cachedIpMeta := range cachedDNSMeta.responseIPs { | |
for cachedIPStr, cachedIPMeta := range cachedDNSMeta.responseIPs { |
// It should be removed from the cache. | ||
|
||
for cachedIpStr, cachedIpMeta := range cachedDNSMeta.responseIPs { | ||
if _, exist := newDNSresponseIPs[cachedIpStr]; !exist { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if _, exist := newDNSresponseIPs[cachedIpStr]; !exist { | |
if newIPMeta, exist := newDNSresponseIPs[cachedIpStr]; !exist { |
then use it at L461, instead of querying the map another time.
if mustCacheResponse { | ||
|
||
if len(ipWithTTLMap) > 0 { | ||
addressUpdate = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when no IP is changed, I think the map is not empty, why this is set to true?
responseIPs map[string]ipWithTTL | ||
} | ||
|
||
type ipWithTTL struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be ipWithExpiration
(we store the expiration time, not the TTL)
@@ -405,80 +408,115 @@ func (f *fqdnController) deleteRuleSelectedPods(ruleID string) error { | |||
|
|||
func (f *fqdnController) onDNSResponse( | |||
fqdn string, | |||
responseIPs map[string]net.IP, | |||
lowestTTL uint32, | |||
newIPWithTTLMap map[string]ipWithTTL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment: it should be newIPWithExpirationMap
.
currentTime := time.Now() | ||
ipWithTTLMap := make(map[string]ipWithTTL) | ||
|
||
//minTimeToRequery establishes a maximum reference time for tracking the minimum re-query time to DNS, as IPs expire. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: leave whitespace after //
if !addressUpdate { | ||
addressUpdate = true | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typically, you just unconditionally write addressUpdate = true
in this case. I'm pretty sure there is no advantage in checking the value first. If anything, the extra branching probably makes it worse.
if !addToCache { | ||
addToCache = true | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
if !exists { | ||
t.Errorf("Expected %s to be found in dns cache.", ipStr) | ||
continue | ||
} | ||
|
||
if cachedIPMeta.expirationTime.Before(time.Now()) { | ||
t.Errorf("Expected %s to be found with a valid TTL,but it has expired.", ipStr) | ||
} | ||
|
||
if !cachedIPMeta.expirationTime.Equal(expectedTTL) { | ||
t.Errorf("Expected TTL for %s to be %v, got %v", ipStr, expectedTTL, cachedIPMeta.expirationTime) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use testify assertions for consistency, not t.Error
test/e2e/framework.go
Outdated
randomPatchAnnotationKey = "test.antrea.io/random-value" | ||
annotationValueLen = 8 | ||
iperfPort = 5201 | ||
iperfSvcPort = 9999 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I pointed this out before: please remove these unrelated changes from the PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reminder
//minTimeToRequery establishes a maximum reference time for tracking the minimum re-query time to DNS, as IPs expire. | ||
minTimeToRequery := currentTime.Add(24 * time.Hour) | ||
|
||
addIPtoIPWithTTLMap := func(ip string, ipMeta ipWithTTL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some nits: addIPtoIPWithTTLMap -> updateMapWithIP, also minTimeToRequery -> timeToRequery as the former sounds like a duration rather than a timestamp
} else { | ||
// First time seeing this domain. | ||
// check if this needs to be tracked, by checking its presence in the datapath rules. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// check if this needs to be tracked, by checking its presence in the datapath rules. | |
// check if this needs to be tracked, by checking if it matches any Antrea FQDN policy selectors. |
// If a FQDN policy had been applied then there must be rule records but because it's | ||
// not in cache hence its FQDN:SelectorItem mapping may not be present. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand this comment, but I'm pretty sure that it can be covered by the previous one
// timeToRequery establishes a maximum reference time for tracking the minimum re-query time to DNS, as IPs expire. | ||
timeToRequery := currentTime.Add(24 * time.Hour) | ||
|
||
updateMapWithIP := func(ip string, ipMeta ipWithExpiration) { | ||
ipWithExpirationMap[ip] = ipMeta | ||
if ipMeta.expirationTime.Before(timeToRequery) { | ||
timeToRequery = ipMeta.expirationTime | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// timeToRequery establishes a maximum reference time for tracking the minimum re-query time to DNS, as IPs expire. | |
timeToRequery := currentTime.Add(24 * time.Hour) | |
updateMapWithIP := func(ip string, ipMeta ipWithExpiration) { | |
ipWithExpirationMap[ip] = ipMeta | |
if ipMeta.expirationTime.Before(timeToRequery) { | |
timeToRequery = ipMeta.expirationTime | |
} | |
} | |
// timeToRequery establishes a maximum reference time for tracking the minimum re-query time to DNS, as IPs expire. | |
var timeToRequery *time.Time | |
updateMapWithIP := func(ip string, ipMeta ipWithExpiration) { | |
ipWithExpirationMap[ip] = ipMeta | |
if timeToRequery == nil || ipMeta.expirationTime.Before(*timeToRequery) { | |
timeToRequery = &ipMeta.expirationTime | |
} | |
} |
To avoid introducing the vague duration 24 * time.Hour
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another way is to move least expiration calculation to a function of dnsMeta
, then there is no need to have this function, and it can save a copy in "First time seeing this domain" case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome. I also had earlier thought on similar lines but i thought of it using as :
var timeToRequery time.Time
but as you know it meant timeToRequery
gets set to its zero value and would not have worked because its zero value would have always been lesser, making me to set an arbitrary value to it.
// timeToRequery establishes a maximum reference time for tracking the minimum re-query time to DNS, as IPs expire. | ||
timeToRequery := currentTime.Add(24 * time.Hour) | ||
|
||
updateMapWithIP := func(ip string, ipMeta ipWithExpiration) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updateMapWithIP := func(ip string, ipMeta ipWithExpiration) { | |
updateIPWithExpiration := func(ip string, ipMeta ipWithExpiration) { |
// check for presence of already cached IPs in the new response. | ||
for cachedIPStr, cachedIPMeta := range cachedDNSMeta.responseIPs { | ||
if newIPMeta, exist := newIPWithExpiration[cachedIPStr]; !exist { | ||
// An already cached IP has been found in new response. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment says the opposite of the code.
@@ -38,6 +39,12 @@ func newMockFQDNController(t *testing.T, controller *gomock.Controller, dnsServe | |||
if dnsServer != nil { | |||
dnsServerAddr = *dnsServer | |||
} | |||
mockDnsQueryQueue := workqueue.NewTypedRateLimitingQueueWithConfig( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mockDnsQueryQueue := workqueue.NewTypedRateLimitingQueueWithConfig( | |
mockDNSQueryQueue := workqueue.NewTypedRateLimitingQueueWithConfig( |
test/e2e/framework.go
Outdated
randomPatchAnnotationKey = "test.antrea.io/random-value" | ||
annotationValueLen = 8 | ||
iperfPort = 5201 | ||
iperfSvcPort = 9999 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reminder
Could you remove Also, it's perhaps time to squash the commits and write a good commit message. |
{ | ||
name: "new IP added", | ||
existingDNSCache: map[string]dnsMeta{ | ||
"fqdn-test-pod.lfx.test": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can use testFQDN
.
"192.1.1.3": currentTime.Add(10 * time.Second), | ||
}, | ||
expectedItem: testFQDN, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing two cases:
- existingDNSCache is empty, the new response matches a selector.
- existingDNSCache is empty, the new response doesn't meach any selector.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing this out.
assert.Equal(t, len(cachedDnsMetaData.responseIPs), len(tc.expectedIPs), "Expected %d IPs in cache, got %d", len(tc.expectedIPs), len(cachedDnsMetaData.responseIPs)) | ||
|
||
for ipStr, expectedTTL := range tc.expectedIPs { | ||
cachedIPMeta, exists := cachedDnsMetaData.responseIPs[ipStr] | ||
|
||
assert.True(t, exists, "Expected %s to be found in dns cache.", ipStr) | ||
assert.False(t, cachedIPMeta.expirationTime.Before(currentTime), "Expected %s to have a valid TTL, but it has expired.", ipStr) | ||
assert.Equal(t, expectedTTL, cachedIPMeta.expirationTime, "Expected TTL for %s to be %v, got %v", ipStr, expectedTTL, cachedIPMeta.expirationTime) | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could just change tc.dnsResponseIPs
to map[string]ipWithExpiration
, then one line code can validate the result and cover all cases.
assert.Equal(t, tc.expectedIPs, cachedDNSMetaData.responseIPs)
workqueue.NewTypedItemExponentialFailureRateLimiter[string](1*time.Millisecond, 100*time.Millisecond), | ||
workqueue.TypedRateLimitingQueueConfig[string]{ | ||
Name: "fqdn", | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should use a fake Clock
to speed up enqueue, see
antrea/pkg/agent/controller/ipseccertificate/ipsec_certificate_controller_test.go
Line 280 in 47ce51e
fakeController := newFakeController(t, fakeClock) |
|
||
} | ||
|
||
if tc.expectedItem == testFQDN { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if tc.expectedItem == testFQDN { | |
if tc.expectedItem != "" { |
…onses. - Implement tracking of individual IP TTLs for FQDNs in the DNS cache. - Upon expiration of an IP's TTL, trigger a new DNS query. - Evict only those IPs that are no longer present in the latest DNS response and have exceeded their original TTL. Signed-off-by: Hemant <[email protected]>
eb3f4ba
to
e98030f
Compare
} | ||
f.dnsQueryQueue.AddAfter(fqdn, recordTTL.Sub(time.Now())) | ||
f.dnsQueryQueue.AddAfter(fqdn, timeToReQuery.Sub(currentTime)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a comment explaining why timeToReQuery
cannot be nil here. Theoretically I think it cannot, but
it is not obvious at all
fakeClock := clocktesting.NewFakeClock(time.Now()) | ||
currentTime := fakeClock.Now() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
each test case should have its own clock, otherwise with your current code, one test advancing the clock will impact subsequent test cases
you can do something like this:
currentTime := time.Now()
// use currentTime to define your test cases, as you are doing now
// declare fakeClock := clocktesting.NewFakeClock(currentTime) at the beginning of each test case
currentTime := time.Now() | ||
ipWithExpirationMap := make(map[string]ipWithExpiration) | ||
|
||
// timeToReQuery establishes a maximum reference time for tracking the minimum re-query time to DNS, as IPs expire. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// timeToReQuery establishes a maximum reference time for tracking the minimum re-query time to DNS, as IPs expire. | |
// timeToRequery establishes the time after which a new DNS query should be sent for the FQDN. | |
// timeToRequery will be set to the minimum expiration time of all IPs added or updated in the cache. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggesting this as I think the current comment is not very helpful or accurate
ipWithExpirationMap := make(map[string]ipWithExpiration) | ||
|
||
// timeToReQuery establishes a maximum reference time for tracking the minimum re-query time to DNS, as IPs expire. | ||
var timeToReQuery *time.Time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can we rename timeToReQuery
to timeToRequery
?
f, _ := newMockFQDNController(t, controller, nil) | ||
mockDNSQueryQueue := workqueue.NewTypedRateLimitingQueueWithConfig( | ||
workqueue.NewTypedItemExponentialFailureRateLimiter[string](minRetryDelay, maxRetryDelay), | ||
workqueue.TypedRateLimitingQueueConfig[string]{ | ||
Name: "fqdn-test", | ||
Clock: fakeClock, | ||
}, | ||
) | ||
f.dnsQueryQueue = mockDNSQueryQueue | ||
f.dnsEntryCache = tc.existingDNSCache |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
while this may work, a better approach is to have a clock.Clock
as a field in fqdnController
, and make sure the entire implementation of fqdnController
uses that clock (e.g., calls clock.Now()
instead of time.Now()
). The default implementation will use clock.RealClock{}
, but newMockFQDNController
should support injecting a custom clock (fake clock).
with this approach, the test function won't need to build a custom workqueue
} | ||
|
||
if tc.expectedItem != "" { | ||
fakeClock.Step(5 * time.Second) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this 5 * time.Second
values comes out of nowhere. I know it is based on the inputs to the test case (existingDNSCache
, etc.) but it should itself be a part of the test case definition, otherwise it looks like some magical value
what I would do is remove tc.expectedItem
and add tc.expectedRequeryAfter *time.Duration
as a test case input. This way you only have one input for this. You already know that the expected item is testFQDN
, so this doesn't need to be an input.
I'm also fixing a couple of issues in the snippet below:
if tc.expectedRequeryAfter != nil {
fakeClock.Step(*tcexpectedRequeryAfter)
// needed to avoid blocking on Get() in case of failure
require.Eventually(t, func() bool { f.dnqQueryQueue.Len() > 0 }, 1*time.Second, 10*time.Millisecond)
item, _ := f.dnsQueryQueue.Get()
f.dnsQueryQueue.Done(item)
assert.Equal(t, testFqdn, item)
} else {
// make sure that there is no requery
assert.Never(t, func() bool { f.dnqQueryQueue.Len() > 0 }, 100*time.Millisecond, 10*time.Millisecond)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for feedback and proper outline on this . I got some logical insights from this, for instance, I was directly calling Get()
on the queue after advancing the time, without checking if the queue really contained any items or not because i "assumed" that it really must contain the item. This could have lead to blocking indefinitely until an item was actually available, and using require.Eventually
helps ensure that the queue actually has items before attempting to retrieve them.
- Replace time.Now() usages with the clock set in fqdnController. - Inject fakeClock for tests. Removed mockDnsQueryQueue implementation. Signed-off-by: Hemant <[email protected]>
@@ -17,7 +17,7 @@ package networkpolicy | |||
import ( | |||
"context" | |||
"fmt" | |||
"math" | |||
"k8s.io/utils/clock" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be in the second import group
@@ -152,6 +156,12 @@ type fqdnController struct { | |||
ipv4Enabled bool | |||
ipv6Enabled bool | |||
gwPort uint32 | |||
// clock allows for time-dependent operations within the fqdnController. | |||
// By using a clock.Clock as a field, we ensure that all time-related calls throughout the | |||
// implementation of the fqdnController utilize this clock, for example , calls to clock.Now() instead of time.Now() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// implementation of the fqdnController utilize this clock, for example , calls to clock.Now() instead of time.Now() | |
// implementation of the fqdnController utilize this clock |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"This patch enables the caching of TTLs for individual IPs in DNS responses." is not a good commit message title. Please follow the "git commit" link in https://github.com/antrea-io/antrea/blob/main/CONTRIBUTING.md#getting-reviewers to write a proper one.
{ | ||
name: "existing DNS cache not impacted", | ||
existingDNSCache: map[string]dnsMeta{ | ||
"fqdn-test-pod.lfx.test": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"fqdn-test-pod.lfx.test": { | |
testFQDN: { |
Same comment applies to other cases
expectedIPs: map[string]ipWithExpiration{ | ||
"192.1.1.3": {ip: net.ParseIP("192.1.1.3"), expirationTime: currentTime.Add(5 * time.Second)}, | ||
}, | ||
expectedRequeryAfter: getDuration(5 * time.Second), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expectedRequeryAfter: getDuration(5 * time.Second), | |
expectedRequeryAfter: ptr.To(5 * time.Second), |
and no need to create getDuration
}, | ||
}, | ||
{ | ||
name: "old IP resent in DNS response is retained with an updated TTL fetched from response", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could add another IP to existingDNSCache
and dnsResponseIPs
, the former has shorter expirationTime than the latter, to validate it's always the later expiration time is used.
expectedRequeryAfter: getDuration(5 * time.Second), | ||
}, | ||
{ | ||
name: "stale IP with unexpired TTL are retained", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one has no difference from "existing DNS cache not impacted", can be removed.
"stale IP with unexpired TTL are retained" has been covered by "new IP added".
dnsResponseIPs: map[string]ipWithExpiration{ | ||
"192.1.1.1": {ip: net.ParseIP("192.1.1.1"), expirationTime: currentTime.Add(5 * time.Second)}, | ||
}, | ||
expectedIPs: map[string]ipWithExpiration{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't the expectation wrong? If the response doesn't match any selector, the response shouldn't be cached.
if _, exists := f.selectorItemToRuleIDs[selectorItem2]; !exists { | ||
assert.Equal(t, tc.expectedIPs, cachedDnsMetaData.responseIPs, "Expected %+v in cache, got %+v", tc.expectedIPs, cachedDnsMetaData.responseIPs) | ||
} else { | ||
assert.NotEqual(t, tc.expectedIPs, cachedDnsMetaData.responseIPs, "Did not Expect %+v in cache, got %+v", tc.expectedIPs, cachedDnsMetaData.responseIPs) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should use test cases to set expectation, instead of checking test case's parameters to set test case specific expectation.
I think the expectation is as simple as assert.Equal(t, tc.expectedIPs, cachedDnsMetaData.responseIPs)
after you fix the expectedIPs
in "existingDNSCache is empty, the new response doesn't match any selector" to an empty map.
fakeClock := clocktesting.NewFakeClock(currentTime) | ||
controller := gomock.NewController(t) | ||
f, _ := newMockFQDNController(t, controller, nil) | ||
f.clock = fakeClock |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fake clock should also be passed to workqueue.NewTypedRateLimitingQueueWithConfig
. The unit test failure has proved it wouldn't work without it.
You can add a Clock parameter to newFQDNController
, pass a real Clock from non-testing code and a fake Clock from testing code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, my bad. My suggestion was inaccurate as I forgot about the workqueue initialization.
…upport - Implement tracking of TTLs for individual IPs linked to FQDNs in the DNS cache. - Automatically trigger a new DNS query when an IP's TTL expires. - Evict only those IPs that are no longer present in the latest DNS response and have surpassed their original TTL. - Added clock field of type clock.Clock in fqdnController. - Pass clock while initializing fqdn controller. Signed-off-by: Hemant <[email protected]>
…antrea into cache-individual-ip-ttl
Signed-off-by: Hemant <[email protected]>
Signed-off-by: Hemant <[email protected]>
currentTime := f.clock.Now() | ||
ipWithExpirationMap := make(map[string]ipWithExpiration) | ||
|
||
// timeToRequery establishes the time after which a new DNS query should be sent for the FQDN. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I had suggested a second line for this comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping
@@ -405,80 +417,111 @@ func (f *fqdnController) deleteRuleSelectedPods(ruleID string) error { | |||
|
|||
func (f *fqdnController) onDNSResponse( | |||
fqdn string, | |||
responseIPs map[string]net.IP, | |||
lowestTTL uint32, | |||
newIPWithExpiration map[string]ipWithExpiration, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/newIPWithExpiration/newIPsWithExpiration
there are potentially multiple IPs because it's a map
// timeToRequery passed below is guaranteed to be non-nil here because updateIPWithExpiration() is called on each IP in | ||
// newIPWithExpiration that either isn't cached in the existing DNS cache or it remains unexpired. This ensures that timeToReQuery is | ||
// always set to the earliest expiration time in the response, hence also enabling proper DNS re-query scheduling. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try to wrap lines to 100 chars at the max
// timeToRequery passed below is guaranteed to be non-nil here because updateIPWithExpiration() is called on each IP in | ||
// newIPWithExpiration that either isn't cached in the existing DNS cache or it remains unexpired. This ensures that timeToReQuery is | ||
// always set to the earliest expiration time in the response, hence also enabling proper DNS re-query scheduling. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is really a simpler explanation IMO:
timeToRequery is guaranteed not to be nil because ipWithExpirationMap is not empty, and adding an entry to ipWithExpirationMap requires updating timeToRequery
if tc.expectedRequeryAfter != nil { | ||
fakeClock.Step(*tc.expectedRequeryAfter) | ||
// needed to avoid blocking on Get() in case of failure | ||
require.Eventually(t, func() bool { return f.dnsQueryQueue.Len() > 0 }, 100*time.Second, 10*time.Millisecond) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why 100s? If the test is failing because of an incorrect change in the controller, we have to wait 100s for the test to run
For such timeouts, we usually stick to 1s, which should be enough to avoid flakes even on the slowest of machines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry i forgot to remove it and set it to 1 second. I had used that value for my local testing.
// clock allows for time-dependent operations within the fqdnController. | ||
// By using a clock.Clock as a field, we ensure that all time-related calls throughout the | ||
// implementation of the fqdnController utilize this clock | ||
// The default implementation uses clock.RealClock{}, this also allows to | ||
// inject a custom clock (like fake clock) when writing tests via the newMockFQDNController. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a bit too long and unnecessary. Something like his would suffice:
clock allows injecting a custom (fake) clock in unit tests.
…tuality test to 1 second Signed-off-by: Hemant <[email protected]>
currentTime := f.clock.Now() | ||
ipWithExpirationMap := make(map[string]ipWithExpiration) | ||
|
||
// timeToRequery establishes the time after which a new DNS query should be sent for the FQDN. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ping
} | ||
} else { | ||
// This already cached IP is part of the current response, so update it with max time between received time and its old cached time. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrap line at ~100 chars
} | ||
} else { | ||
// This already cached IP is part of the current response, so update it with max time between received time and its old cached time. | ||
expTime := laterOf(newIPMeta.expirationTime, cachedIPMeta.expirationTime) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: no need to define new variable
} | ||
f.dnsQueryQueue.AddAfter(fqdn, recordTTL.Sub(time.Now())) | ||
// timeToRequery is guaranteed not to be nil because ipWithExpirationMap is not empty, and adding an entry to ipWithExpirationMap requires updating timeToRequery |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrap line at ~100 chars, add trailing period to comment
} | ||
|
||
func newFQDNController(client openflow.Client, allocator *idAllocator, dnsServerOverride string, dirtyRuleHandler func(string), v4Enabled, v6Enabled bool, gwPort uint32) (*fqdnController, error) { | ||
func newFQDNController(client openflow.Client, allocator *idAllocator, dnsServerOverride string, dirtyRuleHandler func(string), v4Enabled, v6Enabled bool, gwPort uint32, clock clock.WithTicker) (*fqdnController, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may not be looking in the right place, but do we need a ticker anywhere? Asking because you are using clock.WithTicker
instead of clock.Clock
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my comment was wrong: the workqueue requires a ticker
|
||
"antrea.io/antrea/pkg/agent/config" | ||
openflowtest "antrea.io/antrea/pkg/agent/openflow/testing" | ||
) | ||
|
||
func newMockFQDNController(t *testing.T, controller *gomock.Controller, dnsServer *string) (*fqdnController, *openflowtest.MockClient) { | ||
func newMockFQDNController(t *testing.T, controller *gomock.Controller, dnsServer *string, fakeClock *clocktesting.FakeClock) (*fqdnController, *openflowtest.MockClient) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rather than fakeClock *clocktesting.FakeClock
, I would just use clock clock.Clock
as a parameter
there is no need to restrict the clock to be a fake clock here
|
||
cachedDnsMetaData, _ := f.dnsEntryCache[testFQDN] | ||
|
||
assert.Equal(t, tc.expectedIPs, cachedDnsMetaData.responseIPs, "Expected %+v in cache, got %+v", tc.expectedIPs, cachedDnsMetaData.responseIPs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The log message for this assertion just duplicates information that testify will show by default, and just makes test output unnecessary verbose in case of failure.
You could use assert.Equal(t, tc.expectedIPs, cachedDnsMetaData.responseIPs, "FQDN cache doesn't match expected entries")
expectedRequeryAfter: ptr.To(5 * time.Second), | ||
}, | ||
{ | ||
name: "existing DNS cache not impacted", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this test should just be called "empty DNS response"
This is what you provide as an input and the function under test (onDNSResponse
) as an early return for this case
}, | ||
}, | ||
{ | ||
name: "old IP present in DNS response is retained with an updated TTL fetched from response", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why we do not expect a requeue for this test case (expectedRequeryAfter
is nil
).
A requeue is pretty much needed every time (except if there is no cache entry and the DNS response doesn't match an FQDN selector, which is your last test case. This is different from needing to sync the rules (which unfortunately we don't check for in the test yet, we can do it as a follow-up PR potentially). cc @Dyanngg
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is right. Even if the IPs remain same in the DNS response, as long as we have rules in place and an entry in the DNS cache, we should keep sending DNS requests by queuing them.
} | ||
if mustCacheResponse { | ||
|
||
if len(ipWithExpirationMap) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a comment saying that the only way this condition can be false is if the FQDN doesn't match any selector. Based on my understanding of the code I don't see any other case where that could happen. Am I missing something @Dyanngg ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"or the IPs are not in response and have expired" can you give an example?
I cannot think of a case where the FQDN matches at last one selector, yet ipWithExpirationMap
is empty. The function returns early if responseIPs
is empty, and as long as responseIPs
is non empty and the FQDN matches a selector, can ipWithExpirationMap
be empty? We will always take all IPs from responseIPs
and add them to ipWithExpirationMap
(regardless of expiration time actually, even though presumably it will always be in the future).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to clarify, this is just the comment that I think is wrong here, not the implementation
…ueue. Updated onDNSResponse() to requeue DNS queries even when same IPs are receieved in a response. Added and updated comments as per review. Signed-off-by: Hemant <[email protected]>
Signed-off-by: Hemant <[email protected]>
// CustomWithTickerClock wraps clock.Clock and provides NewTicker to implement clock.WithTicker | ||
type CustomWithTickerClock struct { | ||
clock.Clock | ||
} | ||
|
||
// NewTicker is implemented to fulfill the clock.WithTicker interface. | ||
func (c *CustomWithTickerClock) NewTicker(d time.Duration) clock.Ticker { | ||
return &realTicker{time.NewTicker(d)} | ||
|
||
} | ||
|
||
// realTicker wraps time.Ticker to implement clock.Ticker interface | ||
type realTicker struct { | ||
*time.Ticker | ||
} | ||
|
||
func (t *realTicker) C() <-chan time.Time { | ||
return t.Ticker.C | ||
} | ||
|
||
func (t *realTicker) Stop() { | ||
t.Ticker.Stop() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not correct usage of the k8s.io/utils/clock
package
I must have misled you before, and your code was correct. TypedRateLimitingQueueConfig
requires a clock.WithTicker
(which I may not have realized), so newFQDNController
should take a clock.WithTicker
parameter, while the fqdnController
struct should store a clock.Clock
field.
} | ||
|
||
func newFQDNController(client openflow.Client, allocator *idAllocator, dnsServerOverride string, dirtyRuleHandler func(string), v4Enabled, v6Enabled bool, gwPort uint32) (*fqdnController, error) { | ||
func newFQDNController(client openflow.Client, allocator *idAllocator, dnsServerOverride string, dirtyRuleHandler func(string), v4Enabled, v6Enabled bool, gwPort uint32, clock clock.WithTicker) (*fqdnController, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my comment was wrong: the workqueue requires a ticker
} | ||
if mustCacheResponse { | ||
|
||
if len(ipWithExpirationMap) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"or the IPs are not in response and have expired" can you give an example?
I cannot think of a case where the FQDN matches at last one selector, yet ipWithExpirationMap
is empty. The function returns early if responseIPs
is empty, and as long as responseIPs
is non empty and the FQDN matches a selector, can ipWithExpirationMap
be empty? We will always take all IPs from responseIPs
and add them to ipWithExpirationMap
(regardless of expiration time actually, even though presumably it will always be in the future).
} | ||
if mustCacheResponse { | ||
|
||
if len(ipWithExpirationMap) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to clarify, this is just the comment that I think is wrong here, not the implementation
This is a draft PR for work towards #6722 .