github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/testutils/gossiputil/store_gossiper.go (about) 1 // Copyright 2015 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package gossiputil 12 13 import ( 14 "sync" 15 "testing" 16 17 "github.com/cockroachdb/cockroach/pkg/gossip" 18 "github.com/cockroachdb/cockroach/pkg/roachpb" 19 "github.com/cockroachdb/cockroach/pkg/util/syncutil" 20 ) 21 22 // StoreGossiper allows tests to push storeDescriptors into gossip and 23 // synchronize on their callbacks. There can only be one storeGossiper used per 24 // gossip instance. 25 type StoreGossiper struct { 26 g *gossip.Gossip 27 mu syncutil.Mutex 28 cond *sync.Cond 29 storeKeyMap map[string]struct{} 30 } 31 32 // NewStoreGossiper creates a store gossiper for use by tests. It adds the 33 // callback to gossip. 34 func NewStoreGossiper(g *gossip.Gossip) *StoreGossiper { 35 sg := &StoreGossiper{ 36 g: g, 37 storeKeyMap: make(map[string]struct{}), 38 } 39 sg.cond = sync.NewCond(&sg.mu) 40 // Redundant callbacks are required by StoreGossiper. See GossipWithFunction 41 // which waits for all of the callbacks to be invoked. 42 g.RegisterCallback(gossip.MakePrefixPattern(gossip.KeyStorePrefix), func(key string, _ roachpb.Value) { 43 sg.mu.Lock() 44 defer sg.mu.Unlock() 45 delete(sg.storeKeyMap, key) 46 sg.cond.Broadcast() 47 }, gossip.Redundant) 48 return sg 49 } 50 51 // GossipStores queues up a list of stores to gossip and blocks until each one 52 // is gossiped before returning. 53 func (sg *StoreGossiper) GossipStores(storeDescs []*roachpb.StoreDescriptor, t *testing.T) { 54 storeIDs := make([]roachpb.StoreID, len(storeDescs)) 55 for i, store := range storeDescs { 56 storeIDs[i] = store.StoreID 57 } 58 sg.GossipWithFunction(storeIDs, func() { 59 for i, storeDesc := range storeDescs { 60 if err := sg.g.AddInfoProto(gossip.MakeStoreKey(storeIDs[i]), storeDesc, 0); err != nil { 61 t.Fatal(err) 62 } 63 } 64 }) 65 } 66 67 // GossipWithFunction calls gossipFn and blocks until gossip callbacks have 68 // fired on each of the stores specified by storeIDs. 69 func (sg *StoreGossiper) GossipWithFunction(storeIDs []roachpb.StoreID, gossipFn func()) { 70 sg.mu.Lock() 71 defer sg.mu.Unlock() 72 sg.storeKeyMap = make(map[string]struct{}) 73 for _, storeID := range storeIDs { 74 storeKey := gossip.MakeStoreKey(storeID) 75 sg.storeKeyMap[storeKey] = struct{}{} 76 } 77 78 gossipFn() 79 80 // Wait for gossip callbacks to be invoked on all the stores. 81 for len(sg.storeKeyMap) > 0 { 82 sg.cond.Wait() 83 } 84 }