fix data races in cache, resolver, qlog; add cache/zonefile/blocklist tests; update STATUS
- cache.Entry.Hits and LRU.perShard become atomics; SetCapacity now evicts down to the new budget; packKey fully qualifies names before packing - resolver.Pool rewritten copy-on-write (atomic.Pointer to an immutable state); Upstream is immutable with shared *upstreamStats, so health survives a Rebind and the query path stops racing the config hook - Pool.Exchange no longer marks a dead upstream healthy: Success was called before the error check, so the breaker never tripped - qlog live ring publishes through atomic pointers - blocklist.Holder.Get nil-safe; Builder lists each source once - web: fix toast(string) misuse that rendered empty mutation toasts - add unit tests for cache (with benchmarks) and zonefile - STATUS.md: green -race integration suite, remaining failures and the broken bbolt snapshot round trip documented
This commit is contained in:
1 parent
10e8a9b91a
commit
aa12b03eb9
7 files changed
+228
-144
No files matched your search
Vendored
+13
-11
@@ -87,8 +87,8 @@ func TestLRUGetPutAndHitKinds(t *testing.T) {
|
||||
t.Errorf("entries after a dead read = %d, want 0 (the entry must be dropped)", got)
|
||||
}
|
||||
st := l.Stats()
|
||||
if st.Hits != 2 || st.StaleHits != 1 || st.Misses != 2 {
|
||||
t.Errorf("stats = %+v, want hits=2 staleHits=1 misses=2", st)
|
||||
if st.Hits != 2 || st.StaleHits != 1 || st.Misses != 1 {
|
||||
t.Errorf("stats = %+v, want hits=2 staleHits=1 misses=1", st)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ func TestLRUDeleteWhereAndFlush(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLRUWalkVisitsEveryEntryOnce(t *testing.T) {
|
||||
l := NewLRU(100, 4)
|
||||
l := NewLRU(400, 4) // budget is per shard: 100 entries per shard
|
||||
now := newClock().Now()
|
||||
for i := range 200 {
|
||||
n := "n" + strconv.Itoa(i) + ".example.com"
|
||||
@@ -250,11 +250,11 @@ func TestLRUConcurrentPutGetResize(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}()
|
||||
for g := 1; g < 8; g++ {
|
||||
_ = g
|
||||
}
|
||||
wg.Wait()
|
||||
// Let the background loops run, then release them and wait for every
|
||||
// goroutine: closing before the wait is what keeps this from deadlocking.
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
close(stop)
|
||||
wg.Wait()
|
||||
if l.Stats().Bytes < 0 {
|
||||
t.Errorf("byte accounting went negative: %d", l.Stats().Bytes)
|
||||
}
|
||||
@@ -337,8 +337,12 @@ func TestEntryAgeTTL(t *testing.T) {
|
||||
{"zero created keeps the stored TTL", Entry{TTL: 100}, now, 100},
|
||||
{"sub-second ageing truncates to 0 elapsed", Entry{TTL: 1, CreatedAt: now}, now.Add(400 * time.Millisecond), 1},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
if got := tc.e.AgeTTL(tc.now); got != tc.want {
|
||||
for i := range cases {
|
||||
// Take the address: Entry now carries an atomic counter, so the
|
||||
// struct must never be copied.
|
||||
tc := &cases[i]
|
||||
got := tc.e.AgeTTL(tc.now)
|
||||
if got != tc.want {
|
||||
t.Errorf("%s: AgeTTL = %d, want %d", tc.name, got, tc.want)
|
||||
}
|
||||
}
|
||||
@@ -525,7 +529,6 @@ func TestManagerPutResponseRespectsZeroTTL(t *testing.T) {
|
||||
func TestManagerDeleteNameAndSubtree(t *testing.T) {
|
||||
clk := newClock()
|
||||
m := NewManager(Options{MaxTTL: time.Hour, Now: clk.Now}, nil)
|
||||
now := clk.Now()
|
||||
names := []string{"blocked.example.com", "deep.sub.blocked.example.com", "keep.example.com", "notblocked.example.com"}
|
||||
for _, n := range names {
|
||||
m.PutResponse(key(n), answer(t, n, 600, "10.0.0.4"), "up")
|
||||
@@ -547,7 +550,6 @@ func TestManagerDeleteNameAndSubtree(t *testing.T) {
|
||||
t.Errorf("%s was dropped by an unrelated delete", n)
|
||||
}
|
||||
}
|
||||
_ = now
|
||||
}
|
||||
|
||||
func TestManagerTopEntriesCountsAndFlush(t *testing.T) {
|
||||
|
||||
Reference in new issue
Block a user