// New returns Dispatcher with cluster interface(usually raft.Node). // NOTE: each handler which does something with raft must add to Dispatcher.wg func New(cluster Cluster, c *Config) *Dispatcher { return &Dispatcher{ nodes: newNodeStore(c.HeartbeatPeriod, c.HeartbeatEpsilon, c.GracePeriodMultiplier, c.RateLimitPeriod), store: cluster.MemoryStore(), cluster: cluster, mgrQueue: watch.NewQueue(16), keyMgrQueue: watch.NewQueue(16), taskUpdates: make(map[string]*api.TaskStatus), processTaskUpdatesTrigger: make(chan struct{}, 1), config: c, } }
// New returns Dispatcher with cluster interface(usually raft.Node). // NOTE: each handler which does something with raft must add to Dispatcher.wg func New(cluster Cluster, c *Config) *Dispatcher { return &Dispatcher{ addr: c.Addr, nodes: newNodeStore(c.HeartbeatPeriod, c.HeartbeatEpsilon, c.GracePeriodMultiplier), store: cluster.MemoryStore(), cluster: cluster, mgrQueue: watch.NewQueue(16), keyMgrQueue: watch.NewQueue(16), lastSeenManagers: getWeightedPeers(cluster), taskUpdates: make(map[string]*api.TaskStatus), processTaskUpdatesTrigger: make(chan struct{}, 1), config: c, } }
// NewCluster creates a new Cluster neighbors // list for a raft Member func NewCluster() *Cluster { // TODO(abronan): generate Cluster ID for federation return &Cluster{ members: make(map[uint64]*Member), removed: make(map[uint64]bool), PeersBroadcast: watch.NewQueue(), } }
// NewNode generates a new Raft node func NewNode(opts NodeOptions) *Node { cfg := opts.Config if cfg == nil { cfg = DefaultNodeConfig() } if opts.TickInterval == 0 { opts.TickInterval = time.Second } if opts.SendTimeout == 0 { opts.SendTimeout = 2 * time.Second } raftStore := raft.NewMemoryStorage() n := &Node{ cluster: membership.NewCluster(2 * cfg.ElectionTick), raftStore: raftStore, opts: opts, Config: &raft.Config{ ElectionTick: cfg.ElectionTick, HeartbeatTick: cfg.HeartbeatTick, Storage: raftStore, MaxSizePerMsg: cfg.MaxSizePerMsg, MaxInflightMsgs: cfg.MaxInflightMsgs, Logger: cfg.Logger, }, doneCh: make(chan struct{}), removeRaftCh: make(chan struct{}), stopped: make(chan struct{}), leadershipBroadcast: watch.NewQueue(), lastSendToMember: make(map[uint64]chan struct{}), } n.memoryStore = store.NewMemoryStore(n) if opts.ClockSource == nil { n.ticker = clock.NewClock().NewTicker(opts.TickInterval) } else { n.ticker = opts.ClockSource.NewTicker(opts.TickInterval) } n.reqIDGen = idutil.NewGenerator(uint16(n.Config.ID), time.Now()) n.wait = newWait() n.removeRaftFunc = func(n *Node) func() { var removeRaftOnce sync.Once return func() { removeRaftOnce.Do(func() { close(n.removeRaftCh) }) } }(n) return n }
// NewCluster creates a new Cluster neighbors list for a raft Member. // Member marked as inactive if there was no call ReportActive for heartbeatInterval. func NewCluster(heartbeatTicks int) *Cluster { // TODO(abronan): generate Cluster ID for federation return &Cluster{ members: make(map[uint64]*Member), removed: make(map[uint64]bool), deferedConns: make(map[*deferredConn]struct{}), heartbeatTicks: heartbeatTicks, PeersBroadcast: watch.NewQueue(), } }
// NewMemoryStore returns an in-memory store. The argument is an optional // Proposer which will be used to propagate changes to other members in a // cluster. func NewMemoryStore(proposer state.Proposer) *MemoryStore { memDB, err := memdb.NewMemDB(schema) if err != nil { // This shouldn't fail panic(err) } return &MemoryStore{ memDB: memDB, queue: watch.NewQueue(0), proposer: proposer, } }
// NewNode generates a new Raft node func NewNode(ctx context.Context, opts NewNodeOptions) *Node { cfg := opts.Config if cfg == nil { cfg = DefaultNodeConfig() } if opts.TickInterval == 0 { opts.TickInterval = time.Second } raftStore := raft.NewMemoryStorage() ctx, cancel := context.WithCancel(ctx) n := &Node{ Ctx: ctx, cancel: cancel, cluster: membership.NewCluster(), tlsCredentials: opts.TLSCredentials, raftStore: raftStore, Address: opts.Addr, opts: opts, Config: &raft.Config{ ElectionTick: cfg.ElectionTick, HeartbeatTick: cfg.HeartbeatTick, Storage: raftStore, MaxSizePerMsg: cfg.MaxSizePerMsg, MaxInflightMsgs: cfg.MaxInflightMsgs, Logger: cfg.Logger, }, stopCh: make(chan struct{}), doneCh: make(chan struct{}), removeRaftCh: make(chan struct{}), StateDir: opts.StateDir, joinAddr: opts.JoinAddr, sendTimeout: 2 * time.Second, leadershipBroadcast: watch.NewQueue(), } n.memoryStore = store.NewMemoryStore(n) if opts.ClockSource == nil { n.ticker = clock.NewClock().NewTicker(opts.TickInterval) } else { n.ticker = opts.ClockSource.NewTicker(opts.TickInterval) } if opts.SendTimeout != 0 { n.sendTimeout = opts.SendTimeout } n.reqIDGen = idutil.NewGenerator(uint16(n.Config.ID), time.Now()) n.wait = newWait() n.removeRaftFunc = func(n *Node) func() { var removeRaftOnce sync.Once return func() { removeRaftOnce.Do(func() { close(n.removeRaftCh) }) } }(n) return n }
// Run runs dispatcher tasks which should be run on leader dispatcher. // Dispatcher can be stopped with cancelling ctx or calling Stop(). func (d *Dispatcher) Run(ctx context.Context) error { d.mu.Lock() if d.isRunning() { d.mu.Unlock() return errors.New("dispatcher is already running") } ctx = log.WithModule(ctx, "dispatcher") if err := d.markNodesUnknown(ctx); err != nil { log.G(ctx).Errorf(`failed to move all nodes to "unknown" state: %v`, err) } configWatcher, cancel, err := store.ViewAndWatch( d.store, func(readTx store.ReadTx) error { clusters, err := store.FindClusters(readTx, store.ByName(store.DefaultClusterName)) if err != nil { return err } if err == nil && len(clusters) == 1 { heartbeatPeriod, err := ptypes.Duration(clusters[0].Spec.Dispatcher.HeartbeatPeriod) if err == nil && heartbeatPeriod > 0 { d.config.HeartbeatPeriod = heartbeatPeriod } if clusters[0].NetworkBootstrapKeys != nil { d.networkBootstrapKeys = clusters[0].NetworkBootstrapKeys } } return nil }, state.EventUpdateCluster{}, ) if err != nil { d.mu.Unlock() return err } // set queues here to guarantee that Close will close them d.mgrQueue = watch.NewQueue() d.keyMgrQueue = watch.NewQueue() peerWatcher, peerCancel := d.cluster.SubscribePeers() defer peerCancel() d.lastSeenManagers = getWeightedPeers(d.cluster) defer cancel() d.ctx, d.cancel = context.WithCancel(ctx) d.mu.Unlock() publishManagers := func(peers []*api.Peer) { var mgrs []*api.WeightedPeer for _, p := range peers { mgrs = append(mgrs, &api.WeightedPeer{ Peer: p, Weight: remotes.DefaultObservationWeight, }) } d.mu.Lock() d.lastSeenManagers = mgrs d.mu.Unlock() d.mgrQueue.Publish(mgrs) } batchTimer := time.NewTimer(maxBatchInterval) defer batchTimer.Stop() for { select { case ev := <-peerWatcher: publishManagers(ev.([]*api.Peer)) case <-d.processUpdatesTrigger: d.processUpdates() batchTimer.Reset(maxBatchInterval) case <-batchTimer.C: d.processUpdates() batchTimer.Reset(maxBatchInterval) case v := <-configWatcher: cluster := v.(state.EventUpdateCluster) d.mu.Lock() if cluster.Cluster.Spec.Dispatcher.HeartbeatPeriod != nil { // ignore error, since Spec has passed validation before heartbeatPeriod, _ := ptypes.Duration(cluster.Cluster.Spec.Dispatcher.HeartbeatPeriod) if heartbeatPeriod != d.config.HeartbeatPeriod { // only call d.nodes.updatePeriod when heartbeatPeriod changes d.config.HeartbeatPeriod = heartbeatPeriod d.nodes.updatePeriod(d.config.HeartbeatPeriod, d.config.HeartbeatEpsilon, d.config.GracePeriodMultiplier) } } d.networkBootstrapKeys = cluster.Cluster.NetworkBootstrapKeys d.mu.Unlock() d.keyMgrQueue.Publish(cluster.Cluster.NetworkBootstrapKeys) case <-d.ctx.Done(): return nil } } }