@@ -27,6 +27,7 @@ import (
2727
2828import (
2929 "github.com/dubbogo/go-zookeeper/zk"
30+
3031 "github.com/stretchr/testify/require"
3132)
3233
@@ -38,15 +39,15 @@ func TestConfigCacheLoadAndExpiry(t *testing.T) {
3839 return configCacheEntry {content : string (rune ('0' + count )), exists : true }, nil , nil
3940 }
4041
41- first , err := cache .load ("/path" , loader )
42+ first , err := cache .load ("/path" , loader , nil )
4243 require .NoError (t , err )
43- second , err := cache .load ("/path" , loader )
44+ second , err := cache .load ("/path" , loader , nil )
4445 require .NoError (t , err )
4546 require .Equal (t , first .content , second .content )
4647 require .Equal (t , int32 (1 ), loads .Load ())
4748
4849 require .Eventually (t , func () bool {
49- entry , loadErr := cache .load ("/path" , loader )
50+ entry , loadErr := cache .load ("/path" , loader , nil )
5051 return loadErr == nil && entry .content == "2"
5152 }, time .Second , 5 * time .Millisecond )
5253 require .Equal (t , int32 (2 ), loads .Load ())
@@ -57,19 +58,18 @@ func TestConfigCacheUsesFixedPathLockShards(t *testing.T) {
5758 locks := make (map [* sync.Mutex ]struct {})
5859 pathLock := cache .pathLock ("/path" )
5960
60- for i := 0 ; i < 4096 ; i ++ {
61+ for i := range 4096 {
6162 locks [cache .pathLock (fmt .Sprintf ("/path/%d" , i ))] = struct {}{}
6263 }
6364
64- require .Equal (t , pathLockShardCount , len (cache .pathLocks ))
6565 require .Same (t , pathLock , cache .pathLock ("/path" ))
6666 require .LessOrEqual (t , len (locks ), pathLockShardCount )
6767}
6868
6969func TestConfigCacheBoundsEntriesUnderKeyChurn (t * testing.T ) {
7070 cache := newConfigCache (time .Minute )
7171
72- for i := 0 ; i < 4096 ; i ++ {
72+ for i := range 4096 {
7373 cache .store (fmt .Sprintf ("/path/%d" , i ), configCacheEntry {
7474 content : fmt .Sprintf ("value-%d" , i ),
7575 exists : true ,
@@ -91,7 +91,7 @@ func TestConfigCacheStoresMissingEntry(t *testing.T) {
9191
9292func TestConfigCacheEvictsLeastRecentlyUsed (t * testing.T ) {
9393 cache := newConfigCache (time .Minute )
94- for i := 0 ; i < maxCacheEntries ; i ++ {
94+ for i := range maxCacheEntries {
9595 cache .store (fmt .Sprintf ("/path/%d" , i ), configCacheEntry {exists : true })
9696 }
9797
@@ -122,16 +122,14 @@ func TestConfigCacheConcurrentLoadsRemainBounded(t *testing.T) {
122122 var wg sync.WaitGroup
123123 errs := make (chan error , 4096 )
124124
125- for i := 0 ; i < 4096 ; i ++ {
125+ for i := range 4096 {
126126 path := fmt .Sprintf ("/path/%d" , i )
127- wg .Add (1 )
128- go func () {
129- defer wg .Done ()
127+ wg .Go (func () {
130128 _ , err := cache .load (path , func (* zk.Watcher , bool ) (configCacheEntry , * zk.Watcher , error ) {
131129 return configCacheEntry {exists : true }, nil , nil
132- })
130+ }, nil )
133131 errs <- err
134- }( )
132+ })
135133 }
136134 wg .Wait ()
137135 close (errs )
@@ -148,20 +146,18 @@ func TestConfigCacheConcurrentAutoWatchRegistrationsRemainBounded(t *testing.T)
148146 var wg sync.WaitGroup
149147 errs := make (chan error , 4096 )
150148
151- for i := 0 ; i < 4096 ; i ++ {
149+ for i := range 4096 {
152150 path := fmt .Sprintf ("/watch/%d" , i )
153- wg .Add (1 )
154- go func () {
155- defer wg .Done ()
151+ wg .Go (func () {
156152 _ , err := cache .load (path , func (_ * zk.Watcher , registerWatch bool ) (configCacheEntry , * zk.Watcher , error ) {
157153 if registerWatch {
158154 registrations .Add (1 )
159155 return configCacheEntry {exists : true }, & zk.Watcher {}, nil
160156 }
161157 return configCacheEntry {exists : true }, nil , nil
162- })
158+ }, nil )
163159 errs <- err
164- }( )
160+ })
165161 }
166162 wg .Wait ()
167163 close (errs )
@@ -185,7 +181,7 @@ func TestConfigCacheWatchUpdateWinsOverLoad(t *testing.T) {
185181 close (loadStarted )
186182 <- releaseLoad
187183 return configCacheEntry {content : "old" , exists : true }, watcher , nil
188- })
184+ }, nil )
189185 }()
190186
191187 <- loadStarted
@@ -205,35 +201,93 @@ func TestConfigCacheWatchUpdateWinsOverLoad(t *testing.T) {
205201
206202func TestConfigCacheResetDiscardsInFlightLoad (t * testing.T ) {
207203 cache := newConfigCache (time .Minute )
208- cache .setWatch ("/path" , configWatchState {watcher : & zk.Watcher {}, auto : true })
209204 loadStarted := make (chan struct {})
210205 releaseLoad := make (chan struct {})
211- result := make (chan configCacheEntry , 1 )
206+ removed := make (chan * zk.Watcher , 1 )
207+ type loadResult struct {
208+ entry configCacheEntry
209+ err error
210+ }
211+ result := make (chan loadResult , 1 )
212212 var loads atomic.Int32
213+ staleWatcher := & zk.Watcher {}
214+ currentWatcher := & zk.Watcher {}
213215
214216 go func () {
215- entry , _ := cache .load ("/path" , func (watcher * zk.Watcher , _ bool ) (configCacheEntry , * zk.Watcher , error ) {
217+ entry , err := cache .load ("/path" , func (_ * zk.Watcher , _ bool ) (configCacheEntry , * zk.Watcher , error ) {
216218 if loads .Add (1 ) == 1 {
217219 close (loadStarted )
218220 <- releaseLoad
219- return configCacheEntry {content : "old" , exists : true }, watcher , nil
221+ return configCacheEntry {content : "old" , exists : true }, staleWatcher , nil
220222 }
221- return configCacheEntry {content : "new" , exists : true }, watcher , nil
223+ return configCacheEntry {content : "new" , exists : true }, currentWatcher , nil
224+ }, func (watcher * zk.Watcher ) {
225+ removed <- watcher
222226 })
223- result <- entry
227+ result <- loadResult { entry : entry , err : err }
224228 }()
225229
226230 <- loadStarted
227- cache .reset ()
231+ require . Empty ( t , cache .reset () )
228232 _ , ok := cache .getFresh ("/path" )
229233 require .False (t , ok )
230234 _ , watchState := cache .snapshot ("/path" )
231235 require .Nil (t , watchState .watcher )
232236 close (releaseLoad )
233237
234- require .Equal (t , "new" , (<- result ).content )
238+ load := <- result
239+ require .NoError (t , load .err )
240+ require .Equal (t , "new" , load .entry .content )
235241 require .Equal (t , int32 (2 ), loads .Load ())
242+ select {
243+ case watcher := <- removed :
244+ require .Same (t , staleWatcher , watcher )
245+ case <- time .After (time .Second ):
246+ t .Fatal ("stale watcher was not removed" )
247+ }
236248 entry , ok := cache .getFresh ("/path" )
237249 require .True (t , ok )
238250 require .Equal (t , "new" , entry .content )
251+ _ , watchState = cache .snapshot ("/path" )
252+ require .Same (t , currentWatcher , watchState .watcher )
253+ }
254+
255+ func TestConfigCacheResetDiscardsInFlightBusinessWatch (t * testing.T ) {
256+ cache := newConfigCache (time .Minute )
257+ registerStarted := make (chan struct {})
258+ releaseRegister := make (chan struct {})
259+ removed := make (chan * zk.Watcher , 1 )
260+ result := make (chan error , 1 )
261+ var registrations atomic.Int32
262+ staleWatcher := & zk.Watcher {}
263+ currentWatcher := & zk.Watcher {}
264+
265+ go func () {
266+ result <- cache .ensureBusinessWatch ("/path" , func () (* zk.Watcher , error ) {
267+ if registrations .Add (1 ) == 1 {
268+ close (registerStarted )
269+ <- releaseRegister
270+ return staleWatcher , nil
271+ }
272+ return currentWatcher , nil
273+ }, func (watcher * zk.Watcher ) {
274+ removed <- watcher
275+ })
276+ }()
277+
278+ <- registerStarted
279+ require .Empty (t , cache .reset ())
280+ close (releaseRegister )
281+
282+ require .NoError (t , <- result )
283+ require .Equal (t , int32 (2 ), registrations .Load ())
284+ select {
285+ case watcher := <- removed :
286+ require .Same (t , staleWatcher , watcher )
287+ case <- time .After (time .Second ):
288+ t .Fatal ("stale business watcher was not removed" )
289+ }
290+ _ , watchState := cache .snapshot ("/path" )
291+ require .Same (t , currentWatcher , watchState .watcher )
292+ require .False (t , watchState .auto )
239293}
0 commit comments