// 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( ctx context.Context, engines []engine.Engine, stopper *stop.Stopper, bootstrapped bool, ) error { var bootstraps []*storage.Store if len(engines) == 0 { return errors.Errorf("no engines") } for _, e := range engines { s := storage.NewStore(n.storeCfg, e, &n.Descriptor) log.Eventf(ctx, "created store for engine: %s", e) if bootstrapped { s.NotifyBootstrapped() } // Initialize each store in turn, handling un-bootstrapped errors by // adding the store to the bootstraps list. if err := s.Start(ctx, stopper); err != nil { if _, ok := err.(*storage.NotBootstrappedError); ok { log.Infof(ctx, "store %s not bootstrapped", s) bootstraps = append(bootstraps, s) continue } return errors.Errorf("failed to start store: %s", err) } if s.Ident.ClusterID == *uuid.EmptyUUID || s.Ident.NodeID == 0 { return errors.Errorf("unidentified store: %s", s) } capacity, err := s.Capacity() if err != nil { return errors.Errorf("could not query store capacity: %s", err) } log.Infof(ctx, "initialized store %s: %+v", s, capacity) n.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.storeCfg.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 } log.Event(ctx, "validated stores") // 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.storeCfg.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(ctx) log.Event(ctx, "connected to gossip") // 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) n.initialBoot = true log.Eventf(ctx, "allocated node ID %d", n.Descriptor.NodeID) } // Bootstrap any uninitialized stores asynchronously. if len(bootstraps) > 0 { if err := stopper.RunAsyncTask(ctx, func(ctx context.Context) { n.bootstrapStores(ctx, bootstraps, stopper) }); err != nil { return err } } return nil }