// maybeGossipFirstRange adds the sentinel and first range metadata to gossip // if this is the first range and a leader lease can be obtained. The Store // calls this periodically on first range replicas. func (r *Range) maybeGossipFirstRange() error { if !r.IsFirstRange() { return nil } ctx := r.context() // Gossip the cluster ID from all replicas of the first range. log.Infoc(ctx, "gossiping cluster id %s from store %d, range %d", r.rm.ClusterID(), r.rm.StoreID(), r.Desc().RaftID) if err := r.rm.Gossip().AddInfo(gossip.KeyClusterID, r.rm.ClusterID(), clusterIDGossipTTL); err != nil { log.Errorc(ctx, "failed to gossip cluster ID: %s", err) } if ok, err := r.getLeaseForGossip(ctx); !ok || err != nil { return err } log.Infoc(ctx, "gossiping sentinel from store %d, range %d", r.rm.StoreID(), r.Desc().RaftID) if err := r.rm.Gossip().AddInfo(gossip.KeySentinel, r.rm.ClusterID(), clusterIDGossipTTL); err != nil { log.Errorc(ctx, "failed to gossip cluster ID: %s", err) } log.Infoc(ctx, "gossiping first range from store %d, range %d", r.rm.StoreID(), r.Desc().RaftID) if err := r.rm.Gossip().AddInfo(gossip.KeyFirstRangeDescriptor, *r.Desc(), configGossipTTL); err != nil { log.Errorc(ctx, "failed to gossip first range metadata: %s", err) } return nil }
func (r *Range) maybeGossipConfigsLocked(match func(configPrefix proto.Key) bool) { if r.rm.Gossip() == nil || !r.isInitialized() { return } ctx := r.context() for i, cd := range configDescriptors { if match(cd.keyPrefix) { // Check for a bad range split. This should never happen as ranges // cannot be split mid-config. if !r.ContainsKey(cd.keyPrefix.PrefixEnd()) { // If we ever implement configs that span multiple ranges, // we must update store.startGossip accordingly. For the // time being, it will only fire the first range. log.Fatalc(ctx, "range splits configuration values for %s", cd.keyPrefix) } configMap, hash, err := loadConfigMap(r.rm.Engine(), cd.keyPrefix, cd.configI) if err != nil { log.Errorc(ctx, "failed loading %s config map: %s", cd.gossipKey, err) continue } if r.configHashes == nil { r.configHashes = map[int][]byte{} } if prevHash, ok := r.configHashes[i]; !ok || !bytes.Equal(prevHash, hash) { r.configHashes[i] = hash log.Infoc(ctx, "gossiping %s config from store %d, range %d", cd.gossipKey, r.rm.StoreID(), r.Desc().RaftID) if err := r.rm.Gossip().AddInfo(cd.gossipKey, configMap, 0*time.Second); err != nil { log.Errorc(ctx, "failed to gossip %s configMap: %s", cd.gossipKey, err) continue } } } } }
// maybeSetCorrupt is a stand-in for proper handling of failing replicas. Such a // failure is indicated by a call to maybeSetCorrupt with a replicaCorruptionError. // Currently any error is passed through, but prospectively it should stop the // range from participating in progress, trigger a rebalance operation and // decide on an error-by-error basis whether the corruption is limited to the // range, store, node or cluster with corresponding actions taken. func (r *Range) maybeSetCorrupt(err error) error { if cErr, ok := err.(*replicaCorruptionError); ok && cErr != nil { log.Errorc(r.context(), "stalling replica due to: %s", cErr.error) cErr.processed = true return cErr } return err }
// processRaftCommand processes a raft command by unpacking the command // struct to get args and reply and then applying the command to the // state machine via applyRaftCommand(). The error result is sent on // the command's done channel, if available. func (r *Range) processRaftCommand(idKey cmdIDKey, index uint64, raftCmd proto.InternalRaftCommand) error { if index == 0 { log.Fatalc(r.context(), "processRaftCommand requires a non-zero index") } r.Lock() cmd := r.pendingCmds[idKey] delete(r.pendingCmds, idKey) r.Unlock() args := raftCmd.Cmd.GetValue().(proto.Request) var reply proto.Response var ctx context.Context if cmd != nil { // We initiated this command, so use the caller-supplied reply. reply = cmd.Reply ctx = cmd.ctx } else { // This command originated elsewhere so we must create a new reply buffer. reply = args.CreateReply() // TODO(tschottdorf): consider the Trace situation here. ctx = r.context() } execDone := tracer.FromCtx(ctx).Epoch(fmt.Sprintf("applying %s", args.Method())) // applyRaftCommand will return "expected" errors, but may also indicate // replica corruption (as of now, signaled by a replicaCorruptionError). // We feed its return through maybeSetCorrupt to act when that happens. err := r.maybeSetCorrupt( r.applyRaftCommand(ctx, index, proto.RaftNodeID(raftCmd.OriginNodeID), args, reply), ) execDone() if cmd != nil { cmd.done <- err } else if err != nil && log.V(1) { log.Errorc(r.context(), "error executing raft command %s: %s", args.Method(), err) } return err }