// Snapshot implements the raft.Storage interface. // Snapshot requires that the replica lock is held. func (r *Replica) Snapshot() (raftpb.Snapshot, error) { // Copy all the data from a consistent RocksDB snapshot into a RaftSnapshotData. snap := r.store.NewSnapshot() defer snap.Close() var snapData roachpb.RaftSnapshotData firstIndex, err := r.FirstIndex() if err != nil { return raftpb.Snapshot{}, err } // Read the range metadata from the snapshot instead of the members // of the Range struct because they might be changed concurrently. appliedIndex, err := r.loadAppliedIndexLocked(snap) if err != nil { return raftpb.Snapshot{}, err } var desc roachpb.RangeDescriptor // We ignore intents on the range descriptor (consistent=false) because we // know they cannot be committed yet; operations that modify range // descriptors resolve their own intents when they commit. ok, err := engine.MVCCGetProto(snap, keys.RangeDescriptorKey(r.mu.desc.StartKey), r.store.Clock().Now(), false /* !consistent */, nil, &desc) if err != nil { return raftpb.Snapshot{}, util.Errorf("failed to get desc: %s", err) } if !ok { return raftpb.Snapshot{}, util.Errorf("couldn't find range descriptor") } // Store RangeDescriptor as metadata, it will be retrieved by ApplySnapshot() snapData.RangeDescriptor = desc // Iterate over all the data in the range, including local-only data like // the sequence cache. iter := newReplicaDataIterator(&desc, snap, true /* !replicatedOnly */) defer iter.Close() for ; iter.Valid(); iter.Next() { key := iter.Key() snapData.KV = append(snapData.KV, roachpb.RaftSnapshotData_KeyValue{ Key: key.Key, Value: iter.Value(), Timestamp: key.Timestamp, }) } entries, err := r.entries(snap, firstIndex, appliedIndex+1, 0) if err != nil { return raftpb.Snapshot{}, err } snapData.LogEntries = entries data, err := proto.Marshal(&snapData) if err != nil { return raftpb.Snapshot{}, err } // Synthesize our raftpb.ConfState from desc. var cs raftpb.ConfState for _, rep := range desc.Replicas { cs.Nodes = append(cs.Nodes, uint64(rep.ReplicaID)) } term, err := r.Term(appliedIndex) if err != nil { return raftpb.Snapshot{}, util.Errorf("failed to fetch term of %d: %s", appliedIndex, err) } return raftpb.Snapshot{ Data: data, Metadata: raftpb.SnapshotMetadata{ Index: appliedIndex, Term: term, ConfState: cs, }, }, nil }
func snapshot( ctx context.Context, snap engine.Reader, rangeID roachpb.RangeID, eCache *raftEntryCache, startKey roachpb.RKey, ) (raftpb.Snapshot, error) { start := timeutil.Now() var snapData roachpb.RaftSnapshotData truncState, err := loadTruncatedState(ctx, snap, rangeID) if err != nil { return raftpb.Snapshot{}, err } firstIndex := truncState.Index + 1 // Read the range metadata from the snapshot instead of the members // of the Range struct because they might be changed concurrently. appliedIndex, _, err := loadAppliedIndex(ctx, snap, rangeID) if err != nil { return raftpb.Snapshot{}, err } var desc roachpb.RangeDescriptor // We ignore intents on the range descriptor (consistent=false) because we // know they cannot be committed yet; operations that modify range // descriptors resolve their own intents when they commit. ok, err := engine.MVCCGetProto(ctx, snap, keys.RangeDescriptorKey(startKey), hlc.MaxTimestamp, false /* !consistent */, nil, &desc) if err != nil { return raftpb.Snapshot{}, errors.Errorf("failed to get desc: %s", err) } if !ok { return raftpb.Snapshot{}, errors.Errorf("couldn't find range descriptor") } // Store RangeDescriptor as metadata, it will be retrieved by ApplySnapshot() snapData.RangeDescriptor = desc // Iterate over all the data in the range, including local-only data like // the sequence cache. iter := NewReplicaDataIterator(&desc, snap, true /* replicatedOnly */) defer iter.Close() var alloc bufalloc.ByteAllocator for ; iter.Valid(); iter.Next() { var key engine.MVCCKey var value []byte alloc, key, value = iter.allocIterKeyValue(alloc) snapData.KV = append(snapData.KV, roachpb.RaftSnapshotData_KeyValue{ Key: key.Key, Value: value, Timestamp: key.Timestamp, }) } endIndex := appliedIndex + 1 snapData.LogEntries = make([][]byte, 0, endIndex-firstIndex) scanFunc := func(kv roachpb.KeyValue) (bool, error) { bytes, err := kv.Value.GetBytes() if err == nil { snapData.LogEntries = append(snapData.LogEntries, bytes) } return false, err } if err := iterateEntries(ctx, snap, rangeID, firstIndex, endIndex, scanFunc); err != nil { return raftpb.Snapshot{}, err } data, err := protoutil.Marshal(&snapData) if err != nil { return raftpb.Snapshot{}, err } // Synthesize our raftpb.ConfState from desc. var cs raftpb.ConfState for _, rep := range desc.Replicas { cs.Nodes = append(cs.Nodes, uint64(rep.ReplicaID)) } term, err := term(ctx, snap, rangeID, eCache, appliedIndex) if err != nil { return raftpb.Snapshot{}, errors.Errorf("failed to fetch term of %d: %s", appliedIndex, err) } log.Infof(ctx, "generated snapshot for range %s at index %d in %s. encoded size=%d, %d KV pairs, %d log entries", rangeID, appliedIndex, timeutil.Since(start), len(data), len(snapData.KV), len(snapData.LogEntries)) return raftpb.Snapshot{ Data: data, Metadata: raftpb.SnapshotMetadata{ Index: appliedIndex, Term: term, ConfState: cs, }, }, nil }