func runDebugSplitKey(cmd *cobra.Command, args []string) error { stopper := stop.NewStopper() defer stopper.Stop() if len(args) != 2 { return errors.New("store and rangeID must be specified") } db, err := openStore(cmd, args[0], stopper) if err != nil { return err } rangeID, err := parseRangeID(args[1]) if err != nil { return err } snap := db.NewSnapshot() defer snap.Close() var desc roachpb.RangeDescriptor if err := storage.IterateRangeDescriptors(snap, func(descInside roachpb.RangeDescriptor) (bool, error) { if descInside.RangeID == rangeID { desc = descInside return true, nil } return false, nil }); err != nil { return err } if desc.RangeID != rangeID { return fmt.Errorf("range %d not found", rangeID) } if splitKey, err := engine.MVCCFindSplitKey(context.Background(), snap, rangeID, desc.StartKey, desc.EndKey, func(msg string, args ...interface{}) { fmt.Printf(msg+"\n", args...) }); err != nil { fmt.Println("No SplitKey found:", err) } else { fmt.Println("Computed SplitKey:", splitKey) } return nil }
// AdminSplit divides the range into into two ranges, using either // args.SplitKey (if provided) or an internally computed key that aims to // roughly equipartition the range by size. The split is done inside of // a distributed txn which writes updated and new range descriptors, and // updates the range addressing metadata. The handover of responsibility for // the reassigned key range is carried out seamlessly through a split trigger // carried out as part of the commit of that transaction. func (r *Range) AdminSplit(args *proto.AdminSplitRequest, reply *proto.AdminSplitResponse) { // Only allow a single split per range at a time. r.metaLock.Lock() defer r.metaLock.Unlock() // Determine split key if not provided with args. This scan is // allowed to be relatively slow because admin commands don't block // other commands. desc := r.Desc() splitKey := proto.Key(args.SplitKey) if len(splitKey) == 0 { snap := r.rm.NewSnapshot() defer snap.Close() var err error if splitKey, err = engine.MVCCFindSplitKey(snap, desc.RaftID, desc.StartKey, desc.EndKey); err != nil { reply.SetGoError(util.Errorf("unable to determine split key: %s", err)) return } } // First verify this condition so that it will not return // proto.NewRangeKeyMismatchError if splitKey equals to desc.EndKey, // otherwise it will cause infinite retry loop. if splitKey.Equal(desc.StartKey) || splitKey.Equal(desc.EndKey) { reply.SetGoError(util.Errorf("range is already split at key %s", splitKey)) return } // Verify some properties of split key. if !r.ContainsKey(splitKey) { reply.SetGoError(proto.NewRangeKeyMismatchError(splitKey, splitKey, desc)) return } if !engine.IsValidSplitKey(splitKey) { reply.SetGoError(util.Errorf("cannot split range at key %s", splitKey)) return } // Create new range descriptor with newly-allocated replica IDs and Raft IDs. newDesc, err := r.rm.NewRangeDescriptor(splitKey, desc.EndKey, desc.Replicas) if err != nil { reply.SetGoError(util.Errorf("unable to allocate new range descriptor: %s", err)) return } // Init updated version of existing range descriptor. updatedDesc := *desc updatedDesc.EndKey = splitKey log.Infof("initiating a split of %s at key %s", r, splitKey) if err = r.rm.DB().Txn(func(txn *client.Txn) error { // Create range descriptor for second half of split. // Note that this put must go first in order to locate the // transaction record on the correct range. b := &client.Batch{} desc1Key := keys.RangeDescriptorKey(newDesc.StartKey) if err := updateRangeDescriptor(b, desc1Key, nil, newDesc); err != nil { return err } // Update existing range descriptor for first half of split. desc2Key := keys.RangeDescriptorKey(updatedDesc.StartKey) if err := updateRangeDescriptor(b, desc2Key, desc, &updatedDesc); err != nil { return err } // Update range descriptor addressing record(s). if err := splitRangeAddressing(b, newDesc, &updatedDesc); err != nil { return err } if err := txn.Run(b); err != nil { return err } // Update the RangeTree. b = &client.Batch{} if err := InsertRange(txn, b, newDesc.StartKey); err != nil { return err } // End the transaction manually, instead of letting RunTransaction // loop do it, in order to provide a split trigger. b.InternalAddCall(proto.Call{ Args: &proto.EndTransactionRequest{ RequestHeader: proto.RequestHeader{Key: args.Key}, Commit: true, InternalCommitTrigger: &proto.InternalCommitTrigger{ SplitTrigger: &proto.SplitTrigger{ UpdatedDesc: updatedDesc, NewDesc: *newDesc, }, Intents: []proto.Key{desc1Key, desc2Key}, }, }, Reply: &proto.EndTransactionResponse{}, }) return txn.Run(b) }); err != nil { reply.SetGoError(util.Errorf("split at key %s failed: %s", splitKey, err)) } }