// CopyFrom copies all the persisted results from the originRangeID // sequence cache into this one. Note that the cache will not be // locked while copying is in progress. Failures decoding individual // entries return an error. The copy is done directly using the engine // instead of interpreting values through MVCC for efficiency. func (sc *SequenceCache) CopyFrom(e engine.Engine, originRangeID roachpb.RangeID) error { originMin := engine.MakeMVCCMetadataKey( keys.SequenceCacheKey(originRangeID, txnIDMin, math.MaxUint32, math.MaxUint32)) originMax := engine.MakeMVCCMetadataKey( keys.SequenceCacheKey(originRangeID, txnIDMax, 0, 0)) return copySeqCache(e, originRangeID, sc.rangeID, originMin, originMax) }
// NewSequenceCache returns a new sequence cache. Every range replica // maintains a sequence cache, not just the leader. func NewSequenceCache(rangeID roachpb.RangeID) *SequenceCache { return &SequenceCache{ rangeID: rangeID, // The epoch and sequence numbers are encoded in decreasing order. min: keys.SequenceCacheKey(rangeID, txnIDMin, math.MaxUint32, math.MaxUint32), max: keys.SequenceCacheKey(rangeID, txnIDMax, 0, 0), } }
func copySeqCache(e engine.Engine, srcID, dstID roachpb.RangeID, keyMin, keyMax engine.MVCCKey) error { var scratch [64]byte return e.Iterate(keyMin, keyMax, func(kv engine.MVCCKeyValue) (bool, error) { // Decode the key into a cmd, skipping on error. Otherwise, // write it to the corresponding key in the new cache. id, epoch, seq, err := decodeSequenceCacheMVCCKey(kv.Key, scratch[:0]) if err != nil { return false, util.Errorf("could not decode a sequence cache key %s: %s", kv.Key, err) } key := keys.SequenceCacheKey(dstID, id, epoch, seq) encKey := engine.MakeMVCCMetadataKey(key) // Decode the value, update the checksum and re-encode. meta := &engine.MVCCMetadata{} if err := proto.Unmarshal(kv.Value, meta); err != nil { return false, util.Errorf("could not decode sequence cache value %s [% x]: %s", kv.Key, kv.Value, err) } value := meta.Value() value.ClearChecksum() value.InitChecksum(key) meta.RawBytes = value.RawBytes _, _, err = engine.PutProto(e, encKey, meta) return false, err }) }
// createRangeData creates sample range data in all possible areas of // the key space. Returns a slice of the encoded keys of all created // data. func createRangeData(r *Replica, t *testing.T) []engine.MVCCKey { ts0 := roachpb.ZeroTimestamp ts := roachpb.Timestamp{WallTime: 1} keyTSs := []struct { key roachpb.Key ts roachpb.Timestamp }{ {keys.SequenceCacheKey(r.Desc().RangeID, testTxnID, testTxnEpoch, 2), ts0}, {keys.SequenceCacheKey(r.Desc().RangeID, testTxnID, testTxnEpoch, 1), ts0}, {keys.RaftHardStateKey(r.Desc().RangeID), ts0}, {keys.RaftLogKey(r.Desc().RangeID, 1), ts0}, {keys.RaftLogKey(r.Desc().RangeID, 2), ts0}, {keys.RangeGCMetadataKey(r.Desc().RangeID), ts0}, {keys.RangeLastVerificationTimestampKey(r.Desc().RangeID), ts0}, {keys.RangeStatsKey(r.Desc().RangeID), ts0}, {keys.RangeDescriptorKey(r.Desc().StartKey), ts}, {keys.TransactionKey(roachpb.Key(r.Desc().StartKey), []byte("1234")), ts0}, {keys.TransactionKey(roachpb.Key(r.Desc().StartKey.Next()), []byte("5678")), ts0}, {keys.TransactionKey(fakePrevKey(r.Desc().EndKey), []byte("2468")), ts0}, // TODO(bdarnell): KeyMin.Next() results in a key in the reserved system-local space. // Once we have resolved https://github.com/cockroachdb/cockroach/issues/437, // replace this with something that reliably generates the first valid key in the range. //{r.Desc().StartKey.Next(), ts}, // The following line is similar to StartKey.Next() but adds more to the key to // avoid falling into the system-local space. {append(append([]byte{}, r.Desc().StartKey...), '\x01'), ts}, {fakePrevKey(r.Desc().EndKey), ts}, } keys := []engine.MVCCKey{} for _, keyTS := range keyTSs { if err := engine.MVCCPut(r.store.Engine(), nil, keyTS.key, keyTS.ts, roachpb.MakeValueFromString("value"), nil); err != nil { t.Fatal(err) } keys = append(keys, engine.MVCCEncodeKey(keyTS.key)) if !keyTS.ts.Equal(ts0) { keys = append(keys, engine.MVCCEncodeVersionKey(keyTS.key, keyTS.ts)) } } return keys }
// Put writes a sequence number for the specified transaction ID. func (sc *SequenceCache) Put(e engine.Engine, id []byte, epoch, seq uint32, txnKey roachpb.Key, txnTS roachpb.Timestamp, pErr *roachpb.Error) error { if seq <= 0 || len(id) == 0 { return errEmptyTxnID } if !sc.shouldCacheError(pErr) { return nil } // Write the response value to the engine. key := keys.SequenceCacheKey(sc.rangeID, id, epoch, seq) sc.scratchEntry = roachpb.SequenceCacheEntry{Key: txnKey, Timestamp: txnTS} return engine.MVCCPutProto(e, nil /* ms */, key, roachpb.ZeroTimestamp, nil /* txn */, &sc.scratchEntry) }
func TestSequenceCacheEncodeDecode(t *testing.T) { defer leaktest.AfterTest(t) const rangeID = 123 const expSeq = 987 key := keys.SequenceCacheKey(rangeID, testTxnID, expSeq) id, seq, err := decodeSequenceCacheKey(key, nil) if err != nil { t.Fatal(err) } if !bytes.Equal(id, testTxnID) { t.Fatalf("expected %q, got %q", testTxnID, id) } if seq != expSeq { t.Fatalf("expected %d, got %d", expSeq, seq) } }
func TestSequenceCacheEncodeDecode(t *testing.T) { defer leaktest.AfterTest(t) const rangeID = 123 const expSeq = 987 key := keys.SequenceCacheKey(rangeID, testTxnID, testTxnEpoch, expSeq) txnID, epoch, seq, err := decodeSequenceCacheKey(key, nil) if err != nil { t.Fatal(err) } if !roachpb.TxnIDEqual(txnID, testTxnID) { t.Fatalf("expected txnID %q, got %q", testTxnID, txnID) } if epoch != testTxnEpoch { t.Fatalf("expected epoch %d, got %d", testTxnEpoch, epoch) } if seq != expSeq { t.Fatalf("expected sequence %d, got %d", expSeq, seq) } }
// Put writes a sequence number for the specified transaction ID. func (sc *SequenceCache) Put( e engine.Engine, ms *engine.MVCCStats, txnID *uuid.UUID, epoch, seq uint32, txnKey roachpb.Key, txnTS roachpb.Timestamp, pErr *roachpb.Error, ) error { if seq <= 0 || txnID == nil { return errEmptyTxnID } if !sc.shouldCacheError(pErr) { return nil } // Write the response value to the engine. key := keys.SequenceCacheKey(sc.rangeID, txnID, epoch, seq) sc.scratchEntry = roachpb.SequenceCacheEntry{Key: txnKey, Timestamp: txnTS} return engine.MVCCPutProto(e, ms, key, roachpb.ZeroTimestamp, nil /* txn */, &sc.scratchEntry) }