func TestRangeStatsInit(t *testing.T) { tc := testContext{} tc.Start(t) defer tc.Stop() ms := engine.MVCCStats{ LiveBytes: 1, KeyBytes: 2, ValBytes: 3, IntentBytes: 4, LiveCount: 5, KeyCount: 6, ValCount: 7, IntentCount: 8, IntentAge: 9, GCBytesAge: 10, LastUpdateNanos: 11, } ms.SetStats(tc.engine, 1) s, err := newRangeStats(1, tc.engine) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(ms, s.MVCCStats) { t.Errorf("mvcc stats mismatch %+v != %+v", ms, s.MVCCStats) } }
// MergeMVCCStats merges the results of an MVCC operation or series of // MVCC operations into the range's stats. The intent age is augmented // by multiplying the previous intent count by the elapsed nanos since // the last update to range stats. func (rs *rangeStats) MergeMVCCStats(e engine.Engine, ms *engine.MVCCStats, nowNanos int64) { // Augment the current intent age. diffSeconds := nowNanos/1E9 - rs.LastUpdateNanos/1E9 ms.LastUpdateNanos = nowNanos - rs.LastUpdateNanos ms.IntentAge += rs.IntentCount * diffSeconds ms.GCBytesAge += engine.MVCCComputeGCBytesAge(rs.KeyBytes+rs.ValBytes-rs.LiveBytes, diffSeconds) ms.MergeStats(e, rs.raftID) }
// MergeMVCCStats merges the results of an MVCC operation or series of // MVCC operations into the range's stats. The intent age is augmented // by multiplying the previous intent count by the elapsed nanos since // the last update to range stats. Stats are stored to the underlying // engine and the rangeStats MVCCStats updated to reflect merged totals. func (rs *rangeStats) MergeMVCCStats(e engine.Engine, ms *engine.MVCCStats, nowNanos int64) error { rs.Lock() defer rs.Unlock() // Augment the current intent age. diffSeconds := nowNanos/1E9 - rs.LastUpdateNanos/1E9 ms.LastUpdateNanos = nowNanos ms.IntentAge += rs.IntentCount * diffSeconds ms.GCBytesAge += engine.MVCCComputeGCBytesAge(rs.KeyBytes+rs.ValBytes-rs.LiveBytes, diffSeconds) rs.MVCCStats.Add(ms) return engine.MVCCSetRangeStats(e, rs.rangeID, &rs.MVCCStats) }
// ComputeStatsForRange computes the stats for a given range by // iterating over all key ranges for the given range that should // be accounted for in its stats. func ComputeStatsForRange(d *roachpb.RangeDescriptor, e engine.Engine, nowNanos int64) (engine.MVCCStats, error) { iter := e.NewIterator(nil) defer iter.Close() ms := engine.MVCCStats{} for _, r := range makeReplicatedKeyRanges(d) { msDelta, err := iter.ComputeStats(r.start, r.end, nowNanos) if err != nil { return engine.MVCCStats{}, err } ms.Add(msDelta) } return ms, nil }
// ComputeMVCCStats immediately computes correct total MVCC usage statistics // for the store, returning the computed values (but without modifying the // store). func (s *Store) ComputeMVCCStats() (engine.MVCCStats, error) { var totalStats engine.MVCCStats var err error visitor := newStoreRangeSet(s) now := s.Clock().PhysicalNow() visitor.Visit(func(r *Replica) bool { var stats engine.MVCCStats stats, err = ComputeStatsForRange(r.Desc(), s.Engine(), now) if err != nil { return false } totalStats.Add(stats) return true }) return totalStats, err }
func copySeqCache( e engine.Engine, ms *engine.MVCCStats, srcID, dstID roachpb.RangeID, keyMin, keyMax engine.MVCCKey, ) (int, error) { var scratch [64]byte var count int var meta engine.MVCCMetadata // TODO(spencer): look into making this an MVCCIteration and writing // the values using MVCC so we can avoid the ugliness of updating // the MVCCStats by hand below. err := e.Iterate(keyMin, keyMax, func(kv engine.MVCCKeyValue) (bool, error) { // Decode the key, skipping on error. Otherwise, write it to the // corresponding key in the new cache. txnID, err := decodeAbortCacheMVCCKey(kv.Key, scratch[:0]) if err != nil { return false, util.Errorf("could not decode an abort cache key %s: %s", kv.Key, err) } key := keys.AbortCacheKey(dstID, txnID) encKey := engine.MakeMVCCMetadataKey(key) // Decode the MVCCMetadata value. if err := proto.Unmarshal(kv.Value, &meta); err != nil { return false, util.Errorf("could not decode mvcc metadata %s [% x]: %s", kv.Key, kv.Value, err) } value := meta.Value() value.ClearChecksum() value.InitChecksum(key) meta.RawBytes = value.RawBytes keyBytes, valBytes, err := engine.PutProto(e, encKey, &meta) if err != nil { return false, err } count++ if ms != nil { ms.SysBytes += keyBytes + valBytes ms.SysCount++ } return false, nil }) return count, err }
func copySeqCache( e engine.Engine, ms *engine.MVCCStats, srcID, dstID roachpb.RangeID, keyMin, keyMax engine.MVCCKey, ) (int, error) { var scratch [64]byte var count int err := 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. txnID, 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, txnID, 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 keyBytes, valBytes, err := engine.PutProto(e, encKey, meta) if err != nil { return false, err } count++ if ms != nil { ms.SysBytes += keyBytes + valBytes ms.SysCount++ } return false, nil }) return count, err }
// TestStoreRangeSplitStats starts by splitting the system keys from user-space // keys and verifying that the user space side of the split (which is empty), // has all zeros for stats. It then writes random data to the user space side, // splits it halfway and verifies the two splits have stats exactly equaling // the pre-split. func TestStoreRangeSplitStats(t *testing.T) { defer leaktest.AfterTest(t)() defer config.TestingDisableTableSplits()() store, stopper, manual := createTestStore(t) defer stopper.Stop() // Split the range after the last table data key. keyPrefix := keys.MakeTablePrefix(keys.MaxReservedDescID + 1) keyPrefix = keys.MakeNonColumnKey(keyPrefix) args := adminSplitArgs(roachpb.KeyMin, keyPrefix) if _, pErr := client.SendWrapped(rg1(store), nil, &args); pErr != nil { t.Fatal(pErr) } // Verify empty range has empty stats. rng := store.LookupReplica(keyPrefix, nil) // NOTE that this value is expected to change over time, depending on what // we store in the sys-local keyspace. Update it accordingly for this test. if err := verifyRangeStats(store.Engine(), rng.RangeID, engine.MVCCStats{LastUpdateNanos: manual.UnixNano()}); err != nil { t.Fatal(err) } // Write random data. writeRandomDataToRange(t, store, rng.RangeID, keyPrefix) // Get the range stats now that we have data. snap := store.Engine().NewSnapshot() defer snap.Close() var ms engine.MVCCStats if err := engine.MVCCGetRangeStats(context.Background(), snap, rng.RangeID, &ms); err != nil { t.Fatal(err) } if err := verifyRecomputedStats(snap, rng.Desc(), ms, manual.UnixNano()); err != nil { t.Fatalf("failed to verify range's stats before split: %v", err) } manual.Increment(100) // Split the range at approximate halfway point ("Z" in string "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"). midKey := append([]byte(nil), keyPrefix...) midKey = append(midKey, []byte("Z")...) midKey = keys.MakeNonColumnKey(midKey) args = adminSplitArgs(keyPrefix, midKey) if _, pErr := client.SendWrappedWith(rg1(store), nil, roachpb.Header{ RangeID: rng.RangeID, }, &args); pErr != nil { t.Fatal(pErr) } snap = store.Engine().NewSnapshot() defer snap.Close() var msLeft, msRight engine.MVCCStats if err := engine.MVCCGetRangeStats(context.Background(), snap, rng.RangeID, &msLeft); err != nil { t.Fatal(err) } rngRight := store.LookupReplica(midKey, nil) if err := engine.MVCCGetRangeStats(context.Background(), snap, rngRight.RangeID, &msRight); err != nil { t.Fatal(err) } // The stats should be exactly equal when added. expMS := engine.MVCCStats{ LiveBytes: msLeft.LiveBytes + msRight.LiveBytes, KeyBytes: msLeft.KeyBytes + msRight.KeyBytes, ValBytes: msLeft.ValBytes + msRight.ValBytes, IntentBytes: msLeft.IntentBytes + msRight.IntentBytes, LiveCount: msLeft.LiveCount + msRight.LiveCount, KeyCount: msLeft.KeyCount + msRight.KeyCount, ValCount: msLeft.ValCount + msRight.ValCount, IntentCount: msLeft.IntentCount + msRight.IntentCount, } ms.SysBytes, ms.SysCount = 0, 0 ms.LastUpdateNanos = 0 if expMS != ms { t.Errorf("expected left and right ranges to equal original: %+v + %+v != %+v", msLeft, msRight, ms) } // Stats should both have the new timestamp. now := manual.UnixNano() if lTs := msLeft.LastUpdateNanos; lTs != now { t.Errorf("expected left range stats to have new timestamp, want %d, got %d", now, lTs) } if rTs := msRight.LastUpdateNanos; rTs != now { t.Errorf("expected right range stats to have new timestamp, want %d, got %d", now, rTs) } // Stats should agree with recomputation. if err := verifyRecomputedStats(snap, rng.Desc(), msLeft, now); err != nil { t.Fatalf("failed to verify left range's stats after split: %v", err) } if err := verifyRecomputedStats(snap, rngRight.Desc(), msRight, now); err != nil { t.Fatalf("failed to verify right range's stats after split: %v", err) } }
// SetStats sets stats wholesale. func (rs *rangeStats) SetMVCCStats(e engine.Engine, ms engine.MVCCStats) { rs.Lock() defer rs.Unlock() rs.MVCCStats = ms ms.SetStats(e, rs.raftID) }