// executeCmd synchronously runs Store.ExecuteCmd. The store is looked // up from the store map if specified by header.Replica; otherwise, // the command is being executed locally, and the replica is // determined via lookup of header.Key in the ranges slice. func (db *LocalDB) executeCmd(method string, args storage.Request, reply storage.Response) { // If the replica isn't specified in the header, look it up. var err error var store *storage.Store // If we aren't given a Replica, then a little bending over // backwards here. We need to find the Store, but all we have is the // Key. So find its Range locally, and pull out its Replica which we // use to find the Store. This lets us use the same codepath below // (store.ExecuteCmd) for both locally and remotely originated // commands. header := args.Header() if header.Replica.NodeID == 0 { if repl := db.lookupReplica(header.Key); repl != nil { header.Replica = *repl } else { err = util.Errorf("unable to lookup range replica for key %q", string(header.Key)) } } if err == nil { store, err = db.GetStore(&header.Replica) } if err != nil { reply.Header().Error = err } else { store.ExecuteCmd(method, args, reply) } }
// executeCmd looks up the store specified by header.Replica, and runs // Store.ExecuteCmd. func (n *Node) executeCmd(method string, args storage.Request, reply storage.Response) error { store, err := n.localDB.GetStore(&args.Header().Replica) if err != nil { return err } store.ExecuteCmd(method, args, reply) return nil }
func (db *DistDB) routeRPCInternal(method string, args storage.Request, replyChan interface{}) { // Verify permissions. if err := db.verifyPermissions(method, args.Header()); err != nil { sendErrorReply(err, replyChan) return } // Retry logic for lookup of range by key and RPCs to range replicas. go func() { retryOpts := util.RetryOptions{ Tag: fmt.Sprintf("routing %s rpc", method), Backoff: retryBackoff, MaxBackoff: maxRetryBackoff, Constant: 2, MaxAttempts: 0, // retry indefinitely } err := util.RetryWithBackoff(retryOpts, func() (bool, error) { rangeMeta, err := db.rangeCache.LookupRangeMetadata(args.Header().Key) if err == nil { err = db.sendRPC(rangeMeta.Replicas, method, args, replyChan) } if err != nil { // Range metadata might be out of date - evict it. db.rangeCache.EvictCachedRangeMetadata(args.Header().Key) // If retryable, allow outer loop to retry. if retryErr, ok := err.(util.Retryable); ok && retryErr.CanRetry() { log.Warningf("failed to invoke %s: %v", method, err) return false, nil } } return true, err }) if err != nil { sendErrorReply(err, replyChan) } }() }
// routeRPC verifies permissions and looks up the appropriate range // based on the supplied key and sends the RPC according to the // specified options. routeRPC sends asynchronously and returns a // response value on the replyChan channel when the call is // complete. func (db *DistDB) routeRPC(method string, args storage.Request, replyChan interface{}) { if isTransactional(method) { db.coordinator.addRequest(args.Header()) } db.routeRPCInternal(method, args, replyChan) }