github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/dispatcher/nodes.go (about) 1 package dispatcher 2 3 import ( 4 "sync" 5 "time" 6 7 "github.com/docker/swarmkit/api" 8 "github.com/docker/swarmkit/identity" 9 "github.com/docker/swarmkit/manager/dispatcher/heartbeat" 10 "google.golang.org/grpc/codes" 11 "google.golang.org/grpc/status" 12 ) 13 14 const rateLimitCount = 3 15 16 type registeredNode struct { 17 SessionID string 18 Heartbeat *heartbeat.Heartbeat 19 Registered time.Time 20 Attempts int 21 Node *api.Node 22 Disconnect chan struct{} // signal to disconnect 23 mu sync.Mutex 24 } 25 26 // checkSessionID determines if the SessionID has changed and returns the 27 // appropriate GRPC error code. 28 // 29 // This may not belong here in the future. 30 func (rn *registeredNode) checkSessionID(sessionID string) error { 31 rn.mu.Lock() 32 defer rn.mu.Unlock() 33 34 // Before each message send, we need to check the nodes sessionID hasn't 35 // changed. If it has, we will the stream and make the node 36 // re-register. 37 if sessionID == "" || rn.SessionID != sessionID { 38 return status.Errorf(codes.InvalidArgument, ErrSessionInvalid.Error()) 39 } 40 41 return nil 42 } 43 44 type nodeStore struct { 45 periodChooser *periodChooser 46 gracePeriodMultiplierNormal time.Duration 47 gracePeriodMultiplierUnknown time.Duration 48 rateLimitPeriod time.Duration 49 nodes map[string]*registeredNode 50 mu sync.RWMutex 51 } 52 53 func newNodeStore(hbPeriod, hbEpsilon time.Duration, graceMultiplier int, rateLimitPeriod time.Duration) *nodeStore { 54 return &nodeStore{ 55 nodes: make(map[string]*registeredNode), 56 periodChooser: newPeriodChooser(hbPeriod, hbEpsilon), 57 gracePeriodMultiplierNormal: time.Duration(graceMultiplier), 58 gracePeriodMultiplierUnknown: time.Duration(graceMultiplier) * 2, 59 rateLimitPeriod: rateLimitPeriod, 60 } 61 } 62 63 func (s *nodeStore) updatePeriod(hbPeriod, hbEpsilon time.Duration, gracePeriodMultiplier int) { 64 s.mu.Lock() 65 s.periodChooser = newPeriodChooser(hbPeriod, hbEpsilon) 66 s.gracePeriodMultiplierNormal = time.Duration(gracePeriodMultiplier) 67 s.gracePeriodMultiplierUnknown = s.gracePeriodMultiplierNormal * 2 68 s.mu.Unlock() 69 } 70 71 func (s *nodeStore) Len() int { 72 s.mu.Lock() 73 defer s.mu.Unlock() 74 return len(s.nodes) 75 } 76 77 func (s *nodeStore) AddUnknown(n *api.Node, expireFunc func()) error { 78 s.mu.Lock() 79 defer s.mu.Unlock() 80 rn := ®isteredNode{ 81 Node: n, 82 } 83 s.nodes[n.ID] = rn 84 rn.Heartbeat = heartbeat.New(s.periodChooser.Choose()*s.gracePeriodMultiplierUnknown, expireFunc) 85 return nil 86 } 87 88 // CheckRateLimit returns error if node with specified id is allowed to re-register 89 // again. 90 func (s *nodeStore) CheckRateLimit(id string) error { 91 s.mu.Lock() 92 defer s.mu.Unlock() 93 if existRn, ok := s.nodes[id]; ok { 94 if time.Since(existRn.Registered) > s.rateLimitPeriod { 95 existRn.Attempts = 0 96 } 97 existRn.Attempts++ 98 if existRn.Attempts > rateLimitCount { 99 return status.Errorf(codes.Unavailable, "node %s exceeded rate limit count of registrations", id) 100 } 101 existRn.Registered = time.Now() 102 } 103 return nil 104 } 105 106 // Add adds new node and returns it, it replaces existing without notification. 107 func (s *nodeStore) Add(n *api.Node, expireFunc func()) *registeredNode { 108 s.mu.Lock() 109 defer s.mu.Unlock() 110 var attempts int 111 var registered time.Time 112 if existRn, ok := s.nodes[n.ID]; ok { 113 attempts = existRn.Attempts 114 registered = existRn.Registered 115 existRn.Heartbeat.Stop() 116 delete(s.nodes, n.ID) 117 } 118 if registered.IsZero() { 119 registered = time.Now() 120 } 121 rn := ®isteredNode{ 122 SessionID: identity.NewID(), // session ID is local to the dispatcher. 123 Node: n, 124 Registered: registered, 125 Attempts: attempts, 126 Disconnect: make(chan struct{}), 127 } 128 s.nodes[n.ID] = rn 129 rn.Heartbeat = heartbeat.New(s.periodChooser.Choose()*s.gracePeriodMultiplierNormal, expireFunc) 130 return rn 131 } 132 133 func (s *nodeStore) Get(id string) (*registeredNode, error) { 134 s.mu.RLock() 135 rn, ok := s.nodes[id] 136 s.mu.RUnlock() 137 if !ok { 138 return nil, status.Errorf(codes.NotFound, ErrNodeNotRegistered.Error()) 139 } 140 return rn, nil 141 } 142 143 func (s *nodeStore) GetWithSession(id, sid string) (*registeredNode, error) { 144 s.mu.RLock() 145 rn, ok := s.nodes[id] 146 s.mu.RUnlock() 147 if !ok { 148 return nil, status.Errorf(codes.NotFound, ErrNodeNotRegistered.Error()) 149 } 150 return rn, rn.checkSessionID(sid) 151 } 152 153 func (s *nodeStore) Heartbeat(id, sid string) (time.Duration, error) { 154 rn, err := s.GetWithSession(id, sid) 155 if err != nil { 156 return 0, err 157 } 158 period := s.periodChooser.Choose() // base period for node 159 grace := period * s.gracePeriodMultiplierNormal 160 rn.mu.Lock() 161 rn.Heartbeat.Update(grace) 162 rn.Heartbeat.Beat() 163 rn.mu.Unlock() 164 return period, nil 165 } 166 167 func (s *nodeStore) Delete(id string) *registeredNode { 168 s.mu.Lock() 169 var node *registeredNode 170 if rn, ok := s.nodes[id]; ok { 171 delete(s.nodes, id) 172 rn.Heartbeat.Stop() 173 node = rn 174 } 175 s.mu.Unlock() 176 return node 177 } 178 179 func (s *nodeStore) Disconnect(id string) { 180 s.mu.Lock() 181 if rn, ok := s.nodes[id]; ok { 182 close(rn.Disconnect) 183 rn.Heartbeat.Stop() 184 } 185 s.mu.Unlock() 186 } 187 188 // Clean removes all nodes and stops their heartbeats. 189 // It's equivalent to invalidate all sessions. 190 func (s *nodeStore) Clean() { 191 s.mu.Lock() 192 for _, rn := range s.nodes { 193 rn.Heartbeat.Stop() 194 } 195 s.nodes = make(map[string]*registeredNode) 196 s.mu.Unlock() 197 }