// initStores initializes the Stores map from ID to Store. Stores are // added to the local sender if already bootstrapped. A bootstrapped // Store has a valid ident with cluster, node and Store IDs set. If // the Store doesn't yet have a valid ident, it's added to the // bootstraps list for initialization once the cluster and node IDs // have been determined. func (n *Node) initStores(engines []engine.Engine, stopper *stop.Stopper) error { bootstraps := list.New() if len(engines) == 0 { return util.Errorf("no engines") } for _, e := range engines { s := storage.NewStore(n.ctx, e, &n.Descriptor) // Initialize each store in turn, handling un-bootstrapped errors by // adding the store to the bootstraps list. if err := s.Start(stopper); err != nil { if _, ok := err.(*storage.NotBootstrappedError); ok { log.Infof("store %s not bootstrapped", s) bootstraps.PushBack(s) continue } return util.Errorf("failed to start store: %s", err) } if s.Ident.ClusterID == "" || s.Ident.NodeID == 0 { return util.Errorf("unidentified store: %s", s) } capacity, err := s.Capacity() if err != nil { return util.Errorf("could not query store capacity: %s", err) } log.Infof("initialized store %s: %+v", s, capacity) n.stores.AddStore(s) } // Verify all initialized stores agree on cluster and node IDs. if err := n.validateStores(); err != nil { return err } // Set the stores map as the gossip persistent storage, so that // gossip can bootstrap using the most recently persisted set of // node addresses. if err := n.ctx.Gossip.SetStorage(n.stores); err != nil { return fmt.Errorf("failed to initialize the gossip interface: %s", err) } // Connect gossip before starting bootstrap. For new nodes, connecting // to the gossip network is necessary to get the cluster ID. n.connectGossip() // If no NodeID has been assigned yet, allocate a new node ID by // supplying 0 to initNodeID. if n.Descriptor.NodeID == 0 { n.initNodeID(0) } // Bootstrap any uninitialized stores asynchronously. if bootstraps.Len() > 0 { stopper.RunAsyncTask(func() { n.bootstrapStores(bootstraps, stopper) }) } return nil }
// initStores initializes the Stores map from ID to Store. Stores are // added to the local sender if already bootstrapped. A bootstrapped // Store has a valid ident with cluster, node and Store IDs set. If // the Store doesn't yet have a valid ident, it's added to the // bootstraps list for initialization once the cluster and node IDs // have been determined. func (n *Node) initStores(engines []engine.Engine, stopper *stop.Stopper) error { var bootstraps []*storage.Store if len(engines) == 0 { return util.Errorf("no engines") } for _, e := range engines { s := storage.NewStore(n.ctx, e, &n.Descriptor) // Initialize each store in turn, handling un-bootstrapped errors by // adding the store to the bootstraps list. if err := s.Start(stopper); err != nil { if _, ok := err.(*storage.NotBootstrappedError); ok { log.Infof("store %s not bootstrapped", s) bootstraps = append(bootstraps, s) continue } return util.Errorf("failed to start store: %s", err) } if s.Ident.ClusterID == *uuid.EmptyUUID || s.Ident.NodeID == 0 { return util.Errorf("unidentified store: %s", s) } capacity, err := s.Capacity() if err != nil { return util.Errorf("could not query store capacity: %s", err) } log.Infof("initialized store %s: %+v", s, capacity) n.stores.AddStore(s) } // If there are no initialized stores and no gossip resolvers, // bootstrap this node as the seed of a new cluster. if n.stores.GetStoreCount() == 0 { resolvers := n.ctx.Gossip.GetResolvers() // Check for the case of uninitialized node having only itself specified as join host. switch len(resolvers) { case 0: return errNeedsBootstrap case 1: if resolvers[0].Addr() == n.Descriptor.Address.String() { return errCannotJoinSelf } } } // Verify all initialized stores agree on cluster and node IDs. if err := n.validateStores(); err != nil { return err } // Set the stores map as the gossip persistent storage, so that // gossip can bootstrap using the most recently persisted set of // node addresses. if err := n.ctx.Gossip.SetStorage(n.stores); err != nil { return fmt.Errorf("failed to initialize the gossip interface: %s", err) } // Connect gossip before starting bootstrap. For new nodes, connecting // to the gossip network is necessary to get the cluster ID. n.connectGossip() // If no NodeID has been assigned yet, allocate a new node ID by // supplying 0 to initNodeID. if n.Descriptor.NodeID == 0 { n.initNodeID(0) } // Bootstrap any uninitialized stores asynchronously. if len(bootstraps) > 0 { stopper.RunAsyncTask(func() { n.bootstrapStores(bootstraps, stopper) }) } return nil }
// close sends resolve intent commands for all key ranges this // transaction has covered, clears the keys cache and closes the // metadata heartbeat. Any keys listed in the resolved slice have // already been resolved and do not receive resolve intent commands. func (tm *txnMetadata) close(trace *tracer.Trace, txn *proto.Transaction, resolved []proto.Key, sender client.Sender, stopper *stop.Stopper) { close(tm.txnEnd) // stop heartbeat trace.Event("coordinator stops") if tm.keys.Len() > 0 { if log.V(2) { log.Infof("cleaning up %d intent(s) for transaction %s", tm.keys.Len(), txn) } } // TODO(tschottdorf): Should create a Batch here. for _, o := range tm.keys.GetOverlaps(proto.KeyMin, proto.KeyMax) { // If the op was range based, end key != start key: resolve a range. var call proto.Call key := o.Key.Start().(proto.Key) endKey := o.Key.End().(proto.Key) if !key.Next().Equal(endKey) { call.Args = &proto.InternalResolveIntentRangeRequest{ RequestHeader: proto.RequestHeader{ Timestamp: txn.Timestamp, Key: key, EndKey: endKey, User: security.RootUser, Txn: txn, }, } call.Reply = &proto.InternalResolveIntentRangeResponse{} } else { // Check if the key has already been resolved; skip if yes. found := false for _, k := range resolved { if key.Equal(k) { found = true } } if found { continue } call.Args = &proto.InternalResolveIntentRequest{ RequestHeader: proto.RequestHeader{ Timestamp: txn.Timestamp, Key: key, User: security.RootUser, Txn: txn, }, } call.Reply = &proto.InternalResolveIntentResponse{} } // We don't care about the reply channel; these are best // effort. We simply fire and forget, each in its own goroutine. ctx := tracer.ToCtx(context.Background(), trace.Fork()) stopper.RunAsyncTask(func() { if log.V(2) { log.Infof("cleaning up intent %q for txn %s", call.Args.Header().Key, txn) } sender.Send(ctx, call) if call.Reply.Header().Error != nil { log.Warningf("failed to cleanup %q intent: %s", call.Args.Header().Key, call.Reply.Header().GoError()) } }) } tm.keys.Clear() }