diff --git a/docs/generated/settings/settings-for-tenants.txt b/docs/generated/settings/settings-for-tenants.txt index ea3165c6eab4..acf108ec80a3 100644 --- a/docs/generated/settings/settings-for-tenants.txt +++ b/docs/generated/settings/settings-for-tenants.txt @@ -282,4 +282,4 @@ trace.jaeger.agent string the address of a Jaeger agent to receive traces using trace.opentelemetry.collector string address of an OpenTelemetry trace collector to receive traces using the otel gRPC protocol, as :. If no port is specified, 4317 will be used. trace.span_registry.enabled boolean true if set, ongoing traces can be seen at https:///#/debug/tracez trace.zipkin.collector string the address of a Zipkin instance to receive traces, as :. If no port is specified, 9411 will be used. -version version 22.1-1002 set the active cluster version in the format '.' +version version 22.1-1004 set the active cluster version in the format '.' diff --git a/docs/generated/settings/settings.html b/docs/generated/settings/settings.html index 6930bfafd134..85733a1f3dd0 100644 --- a/docs/generated/settings/settings.html +++ b/docs/generated/settings/settings.html @@ -213,6 +213,6 @@ trace.opentelemetry.collectorstringaddress of an OpenTelemetry trace collector to receive traces using the otel gRPC protocol, as :. If no port is specified, 4317 will be used. trace.span_registry.enabledbooleantrueif set, ongoing traces can be seen at https:///#/debug/tracez trace.zipkin.collectorstringthe address of a Zipkin instance to receive traces, as :. If no port is specified, 9411 will be used. -versionversion22.1-1002set the active cluster version in the format '.' +versionversion22.1-1004set the active cluster version in the format '.' diff --git a/pkg/clusterversion/cockroach_versions.go b/pkg/clusterversion/cockroach_versions.go index 8b68163fb921..fbc161a47805 100644 --- a/pkg/clusterversion/cockroach_versions.go +++ b/pkg/clusterversion/cockroach_versions.go @@ -358,6 +358,9 @@ const ( // version is guaranteed to reside in a cluster where all nodes support range // keys at the Pebble layer. EnablePebbleFormatVersionRangeKeys + // ExperimentalMVCCRangeTombstones enables the use of highly experimental MVCC + // range tombstones. + ExperimentalMVCCRangeTombstones // ************************************************* // Step (1): Add new versions here. @@ -627,6 +630,10 @@ var versionsSingleton = keyedVersions{ Key: EnablePebbleFormatVersionRangeKeys, Version: roachpb.Version{Major: 22, Minor: 1, Internal: 1002}, }, + { + Key: ExperimentalMVCCRangeTombstones, + Version: roachpb.Version{Major: 22, Minor: 1, Internal: 1004}, + }, } // TODO(irfansharif): clusterversion.binary{,MinimumSupported}Version diff --git a/pkg/clusterversion/key_string.go b/pkg/clusterversion/key_string.go index d8ed7546907f..4a2ccc6d86e7 100644 --- a/pkg/clusterversion/key_string.go +++ b/pkg/clusterversion/key_string.go @@ -65,11 +65,12 @@ func _() { _ = x[V22_1-54] _ = x[EnsurePebbleFormatVersionRangeKeys-55] _ = x[EnablePebbleFormatVersionRangeKeys-56] + _ = x[ExperimentalMVCCRangeTombstones-57] } -const _Key_name = "V21_2Start22_1TargetBytesAvoidExcessAvoidDrainingNamesDrainingNamesMigrationTraceIDDoesntImplyStructuredRecordingAlterSystemTableStatisticsAddAvgSizeColAlterSystemStmtDiagReqsMVCCAddSSTableInsertPublicSchemaNamespaceEntryOnRestoreUnsplitRangesInAsyncGCJobsValidateGrantOptionPebbleFormatBlockPropertyCollectorProbeRequestSelectRPCsTakeTracingInfoInbandPreSeedTenantSpanConfigsSeedTenantSpanConfigsPublicSchemasWithDescriptorsEnsureSpanConfigReconciliationEnsureSpanConfigSubscriptionEnableSpanConfigStoreScanWholeRowsSCRAMAuthenticationUnsafeLossOfQuorumRecoveryRangeLogAlterSystemProtectedTimestampAddColumnEnableProtectedTimestampsForTenantDeleteCommentsWithDroppedIndexesRemoveIncompatibleDatabasePrivilegesAddRaftAppliedIndexTermMigrationPostAddRaftAppliedIndexTermMigrationDontProposeWriteTimestampForLeaseTransfersTenantSettingsTableEnablePebbleFormatVersionBlockPropertiesDisableSystemConfigGossipTriggerMVCCIndexBackfillerEnableLeaseHolderRemovalBackupResolutionInJobLooselyCoupledRaftLogTruncationChangefeedIdlenessBackupDoesNotOverwriteLatestAndCheckpointEnableDeclarativeSchemaChangerRowLevelTTLPebbleFormatSplitUserKeysMarkedIncrementalBackupSubdirDateStyleIntervalStyleCastRewriteEnableNewStoreRebalancerClusterLocksVirtualTableAutoStatsTableSettingsForecastStatsSuperRegionsEnableNewChangefeedOptionsSpanCountTablePreSeedSpanCountTableSeedSpanCountTableV22_1EnsurePebbleFormatVersionRangeKeysEnablePebbleFormatVersionRangeKeys" +const _Key_name = "V21_2Start22_1TargetBytesAvoidExcessAvoidDrainingNamesDrainingNamesMigrationTraceIDDoesntImplyStructuredRecordingAlterSystemTableStatisticsAddAvgSizeColAlterSystemStmtDiagReqsMVCCAddSSTableInsertPublicSchemaNamespaceEntryOnRestoreUnsplitRangesInAsyncGCJobsValidateGrantOptionPebbleFormatBlockPropertyCollectorProbeRequestSelectRPCsTakeTracingInfoInbandPreSeedTenantSpanConfigsSeedTenantSpanConfigsPublicSchemasWithDescriptorsEnsureSpanConfigReconciliationEnsureSpanConfigSubscriptionEnableSpanConfigStoreScanWholeRowsSCRAMAuthenticationUnsafeLossOfQuorumRecoveryRangeLogAlterSystemProtectedTimestampAddColumnEnableProtectedTimestampsForTenantDeleteCommentsWithDroppedIndexesRemoveIncompatibleDatabasePrivilegesAddRaftAppliedIndexTermMigrationPostAddRaftAppliedIndexTermMigrationDontProposeWriteTimestampForLeaseTransfersTenantSettingsTableEnablePebbleFormatVersionBlockPropertiesDisableSystemConfigGossipTriggerMVCCIndexBackfillerEnableLeaseHolderRemovalBackupResolutionInJobLooselyCoupledRaftLogTruncationChangefeedIdlenessBackupDoesNotOverwriteLatestAndCheckpointEnableDeclarativeSchemaChangerRowLevelTTLPebbleFormatSplitUserKeysMarkedIncrementalBackupSubdirDateStyleIntervalStyleCastRewriteEnableNewStoreRebalancerClusterLocksVirtualTableAutoStatsTableSettingsForecastStatsSuperRegionsEnableNewChangefeedOptionsSpanCountTablePreSeedSpanCountTableSeedSpanCountTableV22_1EnsurePebbleFormatVersionRangeKeysEnablePebbleFormatVersionRangeKeysExperimentalMVCCRangeTombstones" -var _Key_index = [...]uint16{0, 5, 14, 36, 54, 76, 113, 152, 175, 189, 230, 256, 275, 309, 321, 352, 376, 397, 425, 455, 483, 504, 517, 536, 570, 608, 642, 674, 710, 742, 778, 820, 839, 879, 911, 930, 954, 975, 1006, 1024, 1065, 1095, 1106, 1137, 1160, 1193, 1217, 1241, 1263, 1276, 1288, 1314, 1328, 1349, 1367, 1372, 1406, 1440} +var _Key_index = [...]uint16{0, 5, 14, 36, 54, 76, 113, 152, 175, 189, 230, 256, 275, 309, 321, 352, 376, 397, 425, 455, 483, 504, 517, 536, 570, 608, 642, 674, 710, 742, 778, 820, 839, 879, 911, 930, 954, 975, 1006, 1024, 1065, 1095, 1106, 1137, 1160, 1193, 1217, 1241, 1263, 1276, 1288, 1314, 1328, 1349, 1367, 1372, 1406, 1440, 1471} func (i Key) String() string { if i < 0 || i >= Key(len(_Key_index)-1) { diff --git a/pkg/kv/batch.go b/pkg/kv/batch.go index 3c248d9ab3a8..4f2bbd1298be 100644 --- a/pkg/kv/batch.go +++ b/pkg/kv/batch.go @@ -649,6 +649,34 @@ func (b *Batch) DelRange(s, e interface{}, returnKeys bool) { b.initResult(1, 0, notRaw, nil) } +// ExperimentalDelRangeUsingTombstone deletes the rows between begin (inclusive) +// and end (exclusive) using an MVCC range tombstone. The caller must check +// the ExperimentalMVCCRangeTombstones version gate before using this. +// +// This method is EXPERIMENTAL: range tombstones are under active development, +// and have severe limitations including being ignored by all KV and MVCC APIs +// and only being stored in memory. +func (b *Batch) ExperimentalDelRangeUsingTombstone(s, e interface{}) { + start, err := marshalKey(s) + if err != nil { + b.initResult(0, 0, notRaw, err) + return + } + end, err := marshalKey(e) + if err != nil { + b.initResult(0, 0, notRaw, err) + return + } + b.appendReqs(&roachpb.DeleteRangeRequest{ + RequestHeader: roachpb.RequestHeader{ + Key: start, + EndKey: end, + }, + UseExperimentalRangeTombstone: true, + }) + b.initResult(1, 0, notRaw, nil) +} + // adminMerge is only exported on DB. It is here for symmetry with the // other operations. func (b *Batch) adminMerge(key interface{}) { diff --git a/pkg/kv/db.go b/pkg/kv/db.go index 66adb530daa0..93aa2a01a1e8 100644 --- a/pkg/kv/db.go +++ b/pkg/kv/db.go @@ -548,6 +548,22 @@ func (db *DB) DelRange( return r.Keys, err } +// ExperimentalDelRangeUsingTombstone deletes the rows between begin (inclusive) +// and end (exclusive) using an MVCC range tombstone. The caller must check +// the ExperimentalMVCCRangeTombstones version gate before using this. +// +// This method is EXPERIMENTAL: range tombstones are under active development, +// and have severe limitations including being ignored by all KV and MVCC APIs +// and only being stored in memory. +func (db *DB) ExperimentalDelRangeUsingTombstone( + ctx context.Context, begin, end interface{}, +) error { + b := &Batch{} + b.ExperimentalDelRangeUsingTombstone(begin, end) + _, err := getOneResult(db.Run(ctx, b), b) + return err +} + // AdminMerge merges the range containing key and the subsequent range. After // the merge operation is complete, the range containing key will contain all of // the key/value pairs of the subsequent range and the subsequent range will no diff --git a/pkg/kv/kvserver/batcheval/BUILD.bazel b/pkg/kv/kvserver/batcheval/BUILD.bazel index 17c32ded28bb..084924f6694f 100644 --- a/pkg/kv/kvserver/batcheval/BUILD.bazel +++ b/pkg/kv/kvserver/batcheval/BUILD.bazel @@ -100,6 +100,7 @@ go_test( srcs = [ "cmd_add_sstable_test.go", "cmd_clear_range_test.go", + "cmd_delete_range_test.go", "cmd_end_transaction_test.go", "cmd_export_test.go", "cmd_get_test.go", diff --git a/pkg/kv/kvserver/batcheval/cmd_add_sstable_test.go b/pkg/kv/kvserver/batcheval/cmd_add_sstable_test.go index 352c7b1807bd..807ecd5aeaa7 100644 --- a/pkg/kv/kvserver/batcheval/cmd_add_sstable_test.go +++ b/pkg/kv/kvserver/batcheval/cmd_add_sstable_test.go @@ -1477,12 +1477,11 @@ func engineStats(t *testing.T, engine storage.Engine, nowNanos int64) *enginepb. t.Helper() iter := engine.NewMVCCIterator(storage.MVCCKeyAndIntentsIterKind, storage.IterOptions{ + KeyTypes: storage.IterKeyTypePointsAndRanges, LowerBound: keys.LocalMax, UpperBound: keys.MaxKey, }) defer iter.Close() - // We don't care about nowNanos, because the SST can't contain intents or - // tombstones and all existing intents will be resolved. stats, err := storage.ComputeStatsForRange(iter, keys.LocalMax, keys.MaxKey, nowNanos) require.NoError(t, err) return &stats diff --git a/pkg/kv/kvserver/batcheval/cmd_clear_range.go b/pkg/kv/kvserver/batcheval/cmd_clear_range.go index 41755777c690..89667309f4e9 100644 --- a/pkg/kv/kvserver/batcheval/cmd_clear_range.go +++ b/pkg/kv/kvserver/batcheval/cmd_clear_range.go @@ -157,8 +157,15 @@ func computeStatsDelta( // If we can't use the fast stats path, or race test is enabled, // compute stats across the key span to be cleared. + // + // TODO(erikgrinaker): This must handle range key stats adjustments when + // ClearRange is extended to clear them. if !fast || util.RaceEnabled { - iter := readWriter.NewMVCCIterator(storage.MVCCKeyAndIntentsIterKind, storage.IterOptions{UpperBound: to}) + iter := readWriter.NewMVCCIterator(storage.MVCCKeyAndIntentsIterKind, storage.IterOptions{ + KeyTypes: storage.IterKeyTypePointsAndRanges, + LowerBound: from, + UpperBound: to, + }) computed, err := iter.ComputeStats(from, to, delta.LastUpdateNanos) iter.Close() if err != nil { diff --git a/pkg/kv/kvserver/batcheval/cmd_delete_range.go b/pkg/kv/kvserver/batcheval/cmd_delete_range.go index 60554d27ad4b..f15219f9a7d4 100644 --- a/pkg/kv/kvserver/batcheval/cmd_delete_range.go +++ b/pkg/kv/kvserver/batcheval/cmd_delete_range.go @@ -14,11 +14,13 @@ import ( "context" "time" + "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result" "github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/storage" "github.com/cockroachdb/cockroach/pkg/util/hlc" + "github.com/cockroachdb/errors" ) func init() { @@ -38,6 +40,22 @@ func declareKeysDeleteRange( } else { DefaultDeclareIsolatedKeys(rs, header, req, latchSpans, lockSpans, maxOffset) } + + // When writing range tombstones, we must look for adjacent range tombstones + // that we merge with or fragment, to update MVCC stats accordingly. But we + // make sure to stay within the range bounds. + if args.UseExperimentalRangeTombstone { + // NB: The range end key is not available, so this will pessimistically + // latch up to args.EndKey.Next(). If EndKey falls on the range end key, the + // span will be tightened during evaluation. + l, r := rangeTombstonePeekBounds(args.Key, args.EndKey, rs.GetStartKey().AsRawKey(), nil) + latchSpans.AddMVCC(spanset.SpanReadOnly, roachpb.Span{Key: l, EndKey: r}, header.Timestamp) + + // We need to read the range descriptor to determine the bounds during eval. + latchSpans.AddNonMVCC(spanset.SpanReadOnly, roachpb.Span{ + Key: keys.RangeDescriptorKey(rs.GetStartKey()), + }) + } } // DeleteRange deletes the range of key/value pairs specified by @@ -49,6 +67,29 @@ func DeleteRange( h := cArgs.Header reply := resp.(*roachpb.DeleteRangeResponse) + // Use experimental MVCC range tombstone if requested. + if args.UseExperimentalRangeTombstone { + if cArgs.Header.Txn != nil { + return result.Result{}, ErrTransactionUnsupported + } + if args.Inline { + return result.Result{}, errors.AssertionFailedf("Inline can't be used with range tombstones") + } + if args.ReturnKeys { + return result.Result{}, errors.AssertionFailedf( + "ReturnKeys can't be used with range tombstones") + } + + desc := cArgs.EvalCtx.Desc() + leftPeekBound, rightPeekBound := rangeTombstonePeekBounds( + args.Key, args.EndKey, desc.StartKey.AsRawKey(), desc.EndKey.AsRawKey()) + maxIntents := storage.MaxIntentsPerWriteIntentError.Get(&cArgs.EvalCtx.ClusterSettings().SV) + + err := storage.ExperimentalMVCCDeleteRangeUsingTombstone(ctx, readWriter, cArgs.Stats, + args.Key, args.EndKey, h.Timestamp, leftPeekBound, rightPeekBound, maxIntents) + return result.Result{}, err + } + var timestamp hlc.Timestamp if !args.Inline { timestamp = h.Timestamp @@ -76,3 +117,23 @@ func DeleteRange( // error is not consumed by the caller because the result will be discarded. return result.FromAcquiredLocks(h.Txn, deleted...), err } + +// rangeTombstonePeekBounds returns the left and right bounds that +// ExperimentalMVCCDeleteRangeUsingTombstone can read in order to detect +// adjacent range tombstones to merge with or fragment. The bounds will be +// truncated to the Raft range bounds if given. +func rangeTombstonePeekBounds( + startKey, endKey, rangeStart, rangeEnd roachpb.Key, +) (roachpb.Key, roachpb.Key) { + leftPeekBound := startKey.Prevish(roachpb.PrevishKeyLength) + if len(rangeStart) > 0 && leftPeekBound.Compare(rangeStart) <= 0 { + leftPeekBound = rangeStart + } + + rightPeekBound := endKey.Next() + if len(rangeEnd) > 0 && rightPeekBound.Compare(rangeEnd) >= 0 { + rightPeekBound = rangeEnd + } + + return leftPeekBound.Clone(), rightPeekBound.Clone() +} diff --git a/pkg/kv/kvserver/batcheval/cmd_delete_range_test.go b/pkg/kv/kvserver/batcheval/cmd_delete_range_test.go new file mode 100644 index 000000000000..50a25f904754 --- /dev/null +++ b/pkg/kv/kvserver/batcheval/cmd_delete_range_test.go @@ -0,0 +1,279 @@ +// Copyright 2022 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package batcheval + +import ( + "context" + "testing" + + "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset" + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/settings/cluster" + "github.com/cockroachdb/cockroach/pkg/storage" + "github.com/cockroachdb/cockroach/pkg/storage/enginepb" + "github.com/cockroachdb/cockroach/pkg/util/hlc" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" + "github.com/cockroachdb/cockroach/pkg/util/log" + "github.com/cockroachdb/errors" + "github.com/stretchr/testify/require" +) + +// TestDeleteRangeTombstone tests DeleteRange range tombstones directly, using +// only a Pebble engine. +// +// Most MVCC range tombstone logic is tested exhaustively in the MVCC history +// tests, this just tests the RPC plumbing. +func TestDeleteRangeTombstone(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + // Initial data for each test. x is point tombstone, [] is intent, + // o---o is range tombstone. + // + // 5 [i5] + // 4 c4 + // 3 x + // 2 b2 d2 o-------o + // 1 + // a b c d e f g h i + // + // We also write two range tombstones abutting the Raft range a-z at [Z-a)@100 + // and [z-|)@100. Writing a range tombstone should not merge with these. + writeInitialData := func(t *testing.T, ctx context.Context, rw storage.ReadWriter) { + t.Helper() + txn := roachpb.MakeTransaction("test", nil /* baseKey */, roachpb.NormalUserPriority, hlc.Timestamp{WallTime: 5e9}, 0, 0) + require.NoError(t, storage.MVCCPut(ctx, rw, nil, roachpb.Key("b"), hlc.Timestamp{WallTime: 2e9}, roachpb.MakeValueFromString("b2"), nil)) + require.NoError(t, storage.MVCCPut(ctx, rw, nil, roachpb.Key("c"), hlc.Timestamp{WallTime: 4e9}, roachpb.MakeValueFromString("c4"), nil)) + require.NoError(t, storage.MVCCPut(ctx, rw, nil, roachpb.Key("d"), hlc.Timestamp{WallTime: 2e9}, roachpb.MakeValueFromString("d2"), nil)) + require.NoError(t, storage.MVCCDelete(ctx, rw, nil, roachpb.Key("d"), hlc.Timestamp{WallTime: 3e9}, nil)) + require.NoError(t, storage.MVCCPut(ctx, rw, nil, roachpb.Key("i"), hlc.Timestamp{WallTime: 5e9}, roachpb.MakeValueFromString("i5"), &txn)) + require.NoError(t, storage.ExperimentalMVCCDeleteRangeUsingTombstone(ctx, rw, nil, roachpb.Key("f"), roachpb.Key("h"), hlc.Timestamp{WallTime: 3e9}, nil, nil, 0)) + require.NoError(t, storage.ExperimentalMVCCDeleteRangeUsingTombstone(ctx, rw, nil, roachpb.Key("Z"), roachpb.Key("a"), hlc.Timestamp{WallTime: 100e9}, nil, nil, 0)) + require.NoError(t, storage.ExperimentalMVCCDeleteRangeUsingTombstone(ctx, rw, nil, roachpb.Key("z"), roachpb.Key("|"), hlc.Timestamp{WallTime: 100e9}, nil, nil, 0)) + } + + rangeStart, rangeEnd := roachpb.Key("a"), roachpb.Key("z") + + testcases := map[string]struct { + start string + end string + ts int64 + txn bool + inline bool + returnKeys bool + expectErr interface{} // error type, substring, or true (any) + }{ + "above points succeed": { + start: "a", + end: "f", + ts: 10e9, + }, + "above range tombstone succeed": { + start: "f", + end: "h", + ts: 10e9, + expectErr: nil, + }, + "merging succeeds": { + start: "e", + end: "f", + ts: 3e9, + }, + "adjacent to external LHS range key": { + start: "a", + end: "f", + ts: 100e9, + }, + "adjacent to external RHS range key": { + start: "q", + end: "z", + ts: 100e9, + }, + "transaction errors": { + start: "a", + end: "f", + ts: 10e9, + txn: true, + expectErr: ErrTransactionUnsupported, + }, + "inline errors": { + start: "a", + end: "f", + ts: 10e9, + inline: true, + expectErr: "Inline can't be used with range tombstones", + }, + "returnKeys errors": { + start: "a", + end: "f", + ts: 10e9, + returnKeys: true, + expectErr: "ReturnKeys can't be used with range tombstones", + }, + "intent errors with WriteIntentError": { + start: "i", + end: "j", + ts: 10e9, + expectErr: &roachpb.WriteIntentError{}, + }, + "below point errors with WriteTooOldError": { + start: "a", + end: "d", + ts: 1e9, + expectErr: &roachpb.WriteTooOldError{}, + }, + "below range tombstone errors with WriteTooOldError": { + start: "f", + end: "h", + ts: 1e9, + expectErr: &roachpb.WriteTooOldError{}, + }, + } + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + ctx := context.Background() + st := cluster.MakeTestingClusterSettings() + engine := storage.NewDefaultInMemForTesting() + defer engine.Close() + + writeInitialData(t, ctx, engine) + + rangeKey := storage.MVCCRangeKey{ + StartKey: roachpb.Key(tc.start), + EndKey: roachpb.Key(tc.end), + Timestamp: hlc.Timestamp{WallTime: tc.ts}, + } + + // Prepare the request and environment. + evalCtx := &MockEvalCtx{ + ClusterSettings: st, + Desc: &roachpb.RangeDescriptor{ + StartKey: roachpb.RKey(rangeStart), + EndKey: roachpb.RKey(rangeEnd), + }, + } + + h := roachpb.Header{ + Timestamp: rangeKey.Timestamp, + } + if tc.txn { + txn := roachpb.MakeTransaction("txn", nil /* baseKey */, roachpb.NormalUserPriority, rangeKey.Timestamp, 0, 0) + h.Txn = &txn + } + + req := &roachpb.DeleteRangeRequest{ + RequestHeader: roachpb.RequestHeader{ + Key: rangeKey.StartKey, + EndKey: rangeKey.EndKey, + }, + UseExperimentalRangeTombstone: true, + Inline: tc.inline, + ReturnKeys: tc.returnKeys, + } + + ms := computeStats(t, engine, rangeStart, rangeEnd, rangeKey.Timestamp.WallTime) + + // Use a spanset batch to assert latching of all accesses. In particular, + // the additional seeks necessary to check for adjacent range keys that we + // may merge with (for stats purposes) which should not cross the range + // bounds. + var latchSpans, lockSpans spanset.SpanSet + declareKeysDeleteRange(evalCtx.Desc, &h, req, &latchSpans, &lockSpans, 0) + batch := spanset.NewBatchAt(engine.NewBatch(), &latchSpans, h.Timestamp) + defer batch.Close() + + // Run the request. + resp := &roachpb.DeleteRangeResponse{} + _, err := DeleteRange(ctx, batch, CommandArgs{ + EvalCtx: evalCtx.EvalContext(), + Stats: &ms, + Header: h, + Args: req, + }, resp) + + // Check the error. + if tc.expectErr != nil { + require.Error(t, err) + if b, ok := tc.expectErr.(bool); ok && b { + // any error is fine + } else if expectMsg, ok := tc.expectErr.(string); ok { + require.Contains(t, err.Error(), expectMsg) + } else if e, ok := tc.expectErr.(error); ok { + require.True(t, errors.HasType(err, e), "expected %T, got %v", e, err) + } else { + require.Fail(t, "invalid expectErr", "expectErr=%v", tc.expectErr) + } + return + } + require.NoError(t, err) + require.NoError(t, batch.Commit(true)) + + // Check that the range tombstone was written successfully. + iter := engine.NewMVCCIterator(storage.MVCCKeyAndIntentsIterKind, storage.IterOptions{ + KeyTypes: storage.IterKeyTypeRangesOnly, + LowerBound: rangeKey.StartKey, + UpperBound: rangeKey.EndKey, + }) + defer iter.Close() + iter.SeekGE(storage.MVCCKey{Key: rangeKey.StartKey}) + + var endSeen roachpb.Key + for { + ok, err := iter.Valid() + require.NoError(t, err) + if !ok { + break + } + require.True(t, ok) + for _, rk := range iter.RangeKeys() { + if rk.Timestamp.Equal(rangeKey.Timestamp) { + endSeen = rk.EndKey.Clone() + break + } + } + iter.Next() + } + require.Equal(t, rangeKey.EndKey, endSeen) + + // Check that range tombstone stats were updated correctly. + require.Equal(t, computeStats(t, engine, rangeStart, rangeEnd, rangeKey.Timestamp.WallTime), ms) + }) + } +} + +// computeStats computes MVCC stats for the given range. +// +// TODO(erikgrinaker): This, storage.computeStats(), and engineStats() should be +// moved into a testutils package, somehow avoiding import cycles with storage +// tests. +func computeStats( + t *testing.T, reader storage.Reader, from, to roachpb.Key, nowNanos int64, +) enginepb.MVCCStats { + t.Helper() + + if len(from) == 0 { + from = keys.LocalMax + } + if len(to) == 0 { + to = keys.MaxKey + } + + iter := reader.NewMVCCIterator(storage.MVCCKeyAndIntentsIterKind, storage.IterOptions{ + KeyTypes: storage.IterKeyTypePointsAndRanges, + LowerBound: from, + UpperBound: to, + }) + defer iter.Close() + ms, err := storage.ComputeStatsForRange(iter, from, to, nowNanos) + require.NoError(t, err) + return ms +} diff --git a/pkg/kv/kvserver/batcheval/cmd_end_transaction.go b/pkg/kv/kvserver/batcheval/cmd_end_transaction.go index 62d919dc9305..febb95592819 100644 --- a/pkg/kv/kvserver/batcheval/cmd_end_transaction.go +++ b/pkg/kv/kvserver/batcheval/cmd_end_transaction.go @@ -170,6 +170,19 @@ func declareKeysEndTxn( latchSpans.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{ Key: keys.RangePriorReadSummaryKey(mt.LeftDesc.RangeID), }) + // Merges need to adjust MVCC stats for merged MVCC range tombstones + // that straddle the ranges, by peeking to the left and right of the RHS + // start key. Since Prevish() is imprecise, we must also ensure we don't + // go outside of the LHS bounds. + leftPeekBound := mt.RightDesc.StartKey.AsRawKey().Prevish(roachpb.PrevishKeyLength) + rightPeekBound := mt.RightDesc.StartKey.AsRawKey().Next() + if leftPeekBound.Compare(mt.LeftDesc.StartKey.AsRawKey()) < 0 { + leftPeekBound = mt.LeftDesc.StartKey.AsRawKey() + } + latchSpans.AddNonMVCC(spanset.SpanReadOnly, roachpb.Span{ + Key: leftPeekBound, + EndKey: rightPeekBound, + }) } } } @@ -934,9 +947,18 @@ func splitTrigger( "unable to determine whether right hand side of split is empty") } + rangeKeyDeltaMS, err := computeSplitRangeKeyStatsDelta( + batch, split.LeftDesc, split.RightDesc, ts.WallTime) + if err != nil { + return enginepb.MVCCStats{}, result.Result{}, errors.Wrap(err, + "unable to compute range key stats delta for RHS") + } + log.Event(ctx, "computed range key delta stats for right hand side range") + h := splitStatsHelperInput{ AbsPreSplitBothEstimated: rec.GetMVCCStats(), DeltaBatchEstimated: bothDeltaMS, + DeltaRangeKey: rangeKeyDeltaMS, AbsPostSplitLeftFn: makeScanStatsFn(ctx, batch, ts, &split.LeftDesc, "left hand side"), AbsPostSplitRightFn: makeScanStatsFn(ctx, batch, ts, &split.RightDesc, "right hand side"), ScanRightFirst: splitScansRightForStatsFirst || emptyRHS, @@ -1210,15 +1232,27 @@ func mergeTrigger( } } - // The stats for the merged range are the sum of the LHS and RHS stats, less - // the RHS's replicated range ID stats. The only replicated range ID keys we - // copy from the RHS are the keys in the abort span, and we've already - // accounted for those stats above. + // The stats for the merged range are the sum of the LHS and RHS stats + // adjusted for range key merges (which is the inverse of the split + // adjustment). The RHS's replicated range ID stats are subtracted -- the only + // replicated range ID keys we copy from the RHS are the keys in the abort + // span, and we've already accounted for those stats above. ms.Add(merge.RightMVCCStats) + msRangeKeyDelta, err := computeSplitRangeKeyStatsDelta( + batch, merge.LeftDesc, merge.RightDesc, ts.WallTime) + if err != nil { + return result.Result{}, err + } + ms.Subtract(msRangeKeyDelta) + { ridPrefix := keys.MakeRangeIDReplicatedPrefix(merge.RightDesc.RangeID) // NB: Range-ID local keys have no versions and no intents. - iter := batch.NewMVCCIterator(storage.MVCCKeyIterKind, storage.IterOptions{UpperBound: ridPrefix.PrefixEnd()}) + iter := batch.NewMVCCIterator(storage.MVCCKeyIterKind, storage.IterOptions{ + KeyTypes: storage.IterKeyTypePointsAndRanges, + LowerBound: ridPrefix, + UpperBound: ridPrefix.PrefixEnd(), + }) defer iter.Close() sysMS, err := iter.ComputeStats(ridPrefix, ridPrefix.PrefixEnd(), 0 /* nowNanos */) if err != nil { @@ -1264,6 +1298,72 @@ func changeReplicasTrigger( return pd } +// computeSplitRangeKeyStatsDelta computes the delta in MVCCStats caused by +// the splitting of range keys that straddle the range split point. The inverse +// applies during range merges. Consider a range key [a-foo)@1 split at cc: +// +// Before: [a-foo)@1 RangeKeyCount=1 RangeKeyBytes=15 +// LHS: [a-cc)@1 RangeKeyCount=1 RangeKeyBytes=14 +// RHS: [cc-foo)@1 RangeKeyCount=1 RangeKeyBytes=16 +// +// If the LHS is computed directly then the RHS is calculated as: +// +// RHS = Before - LHS = RangeKeyCount=0 RangeKeyBytes=1 +// +// This is clearly incorrect. This function determines the delta such that: +// +// RHS = Before - LHS + Delta = RangeKeyCount=1 RangeKeyBytes=16 +func computeSplitRangeKeyStatsDelta( + r storage.Reader, lhs, rhs roachpb.RangeDescriptor, nowNanos int64, +) (enginepb.MVCCStats, error) { + var delta enginepb.MVCCStats + delta.AgeTo(nowNanos) + + // NB: When called during a merge trigger (for the inverse adjustment), lhs + // will contain the descriptor for the full, merged range. We therefore have + // to use the rhs start key as the reference split point. We also have to make + // sure the bounds fall within the ranges, since Prevish is imprecise. + splitKey := rhs.StartKey.AsRawKey() + lowerBound := splitKey.Prevish(roachpb.PrevishKeyLength) + if lowerBound.Compare(lhs.StartKey.AsRawKey()) < 0 { + lowerBound = lhs.StartKey.AsRawKey() + } + upperBound := splitKey.Next() + + // Check for range keys that straddle the split point. + iter := r.NewMVCCIterator(storage.MVCCKeyIterKind, storage.IterOptions{ + KeyTypes: storage.IterKeyTypeRangesOnly, + LowerBound: lowerBound, + UpperBound: upperBound, + }) + defer iter.Close() + + iter.SeekGE(storage.MVCCKey{Key: splitKey}) + if ok, err := iter.Valid(); err != nil { + return enginepb.MVCCStats{}, err + } else if !ok { + return delta, nil + } else if rangeStart, _ := iter.RangeBounds(); rangeStart.Equal(splitKey) { + return delta, nil + } + + // Calculate the RHS adjustment, which turns out to be equivalent to the stats + // contribution of the range key fragmentation. The naïve calculation would be + // rhs.EncodedSize() - (keyLen(rhs.EndKey) - keyLen(lhs.EndKey)) + // which simplifies to 2 * keyLen(rhs.StartKey) + tsLen(rhs.Timestamp). + for i, rk := range iter.RangeKeys() { + keyBytes := int64(storage.EncodedMVCCTimestampSuffixLength(rk.Timestamp)) + if i == 0 { + delta.RangeKeyCount++ + keyBytes += 2 * int64(storage.EncodedMVCCKeyPrefixLength(splitKey)) + } + delta.RangeKeyBytes += keyBytes + delta.GCBytesAge += keyBytes * (nowNanos/1e9 - rk.Timestamp.WallTime/1e9) + } + + return delta, nil +} + // txnAutoGC controls whether Transaction entries are automatically gc'ed upon // EndTxn if they only have local locks (which can be resolved synchronously // with EndTxn). Certain tests become simpler with this being turned off. diff --git a/pkg/kv/kvserver/batcheval/cmd_end_transaction_test.go b/pkg/kv/kvserver/batcheval/cmd_end_transaction_test.go index 19cb34232cda..5aadfd0be780 100644 --- a/pkg/kv/kvserver/batcheval/cmd_end_transaction_test.go +++ b/pkg/kv/kvserver/batcheval/cmd_end_transaction_test.go @@ -1183,3 +1183,84 @@ func TestCommitWaitBeforeIntentResolutionIfCommitTrigger(t *testing.T) { } }) } + +func TestComputeSplitRangeKeyStatsDelta(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + rangeKey := func(start, end string, ts int) storage.MVCCRangeKey { + return storage.MVCCRangeKey{ + StartKey: roachpb.Key(start), + EndKey: roachpb.Key(end), + Timestamp: hlc.Timestamp{WallTime: int64(ts)}, + } + } + + const nowNanos = 10e9 + lhsDesc := roachpb.RangeDescriptor{StartKey: roachpb.RKey("a"), EndKey: roachpb.RKey("l")} + rhsDesc := roachpb.RangeDescriptor{StartKey: roachpb.RKey("l"), EndKey: roachpb.RKey("z").PrefixEnd()} + + testcases := map[string]struct { + rangeKeys []storage.MVCCRangeKey + expect enginepb.MVCCStats + }{ + // Empty stats shouldn't do anything. + "empty": {nil, enginepb.MVCCStats{}}, + // a-z splits into a-l and l-z: simple +1 range key + "full": {[]storage.MVCCRangeKey{rangeKey("a", "z", 1e9)}, enginepb.MVCCStats{ + RangeKeyCount: 1, + RangeKeyBytes: 13, + GCBytesAge: 117, + }}, + // foo-zz splits into foo-l and l-zzzz: contribution is same as for short + // keys, because we have to adjust for the change in LHS end key which ends + // up only depending on the split key, and that doesn't change. + "different key length": {[]storage.MVCCRangeKey{rangeKey("foo", "zzzz", 1e9)}, enginepb.MVCCStats{ + RangeKeyCount: 1, + RangeKeyBytes: 13, + GCBytesAge: 117, + }}, + // Two abutting keys at different timestamps at the split point should not + // require a delta. + "no straddling": {[]storage.MVCCRangeKey{ + rangeKey("a", "l", 1e9), + rangeKey("l", "z", 2e9), + }, enginepb.MVCCStats{}}, + // Fragments at split point should be equivalent to a single key. + "fragments at split": {[]storage.MVCCRangeKey{ + rangeKey("a", "l", 1e9), + rangeKey("l", "z", 1e9), + }, enginepb.MVCCStats{ + RangeKeyCount: 1, + RangeKeyBytes: 13, + GCBytesAge: 117, + }}, + // Multiple straddling keys. + "multiple": { + []storage.MVCCRangeKey{ + rangeKey("a", "z", 1e9), + rangeKey("k", "p", 2e9), + rangeKey("foo", "m", 3e9), + }, enginepb.MVCCStats{ + RangeKeyCount: 1, + RangeKeyBytes: 31, + GCBytesAge: 244, + }}, + } + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + engine := storage.NewDefaultInMemForTesting() + defer engine.Close() + + for _, rk := range tc.rangeKeys { + require.NoError(t, engine.ExperimentalPutMVCCRangeKey(rk)) + } + + tc.expect.LastUpdateNanos = nowNanos + + msDelta, err := computeSplitRangeKeyStatsDelta(engine, lhsDesc, rhsDesc, nowNanos) + require.NoError(t, err) + require.Equal(t, tc.expect, msDelta) + }) + } +} diff --git a/pkg/kv/kvserver/batcheval/cmd_truncate_log.go b/pkg/kv/kvserver/batcheval/cmd_truncate_log.go index 633231a2cb5f..72d1aca50edb 100644 --- a/pkg/kv/kvserver/batcheval/cmd_truncate_log.go +++ b/pkg/kv/kvserver/batcheval/cmd_truncate_log.go @@ -120,7 +120,11 @@ func TruncateLog( // Note that any sideloaded payloads that may be removed by this truncation // are not tracked in the raft log delta. The delta will be adjusted below // raft. - iter := readWriter.NewMVCCIterator(storage.MVCCKeyIterKind, storage.IterOptions{UpperBound: end}) + iter := readWriter.NewMVCCIterator(storage.MVCCKeyIterKind, storage.IterOptions{ + KeyTypes: storage.IterKeyTypePointsAndRanges, + LowerBound: start, + UpperBound: end, + }) defer iter.Close() // We can pass zero as nowNanos because we're only interested in SysBytes. ms, err := iter.ComputeStats(start, end, 0 /* nowNanos */) diff --git a/pkg/kv/kvserver/batcheval/split_stats_helper.go b/pkg/kv/kvserver/batcheval/split_stats_helper.go index d238d0704428..29dbe2e65039 100644 --- a/pkg/kv/kvserver/batcheval/split_stats_helper.go +++ b/pkg/kv/kvserver/batcheval/split_stats_helper.go @@ -31,6 +31,9 @@ import "github.com/cockroachdb/cockroach/pkg/storage/enginepb" // practice, we obtain this by recomputing the stats using the corresponding // AbsPostSplit{Left,Right}Fn, and so we don't expect ContainsEstimates to be // set in them. The choice of which side to scan is controlled by ScanRightFirst. +// - DeltaRangeKey: the stats delta that must be added to the non-computed +// half's stats to account for the splitting of range keys straddling the split +// point. See computeSplitRangeKeyStatsDelta() for details. // // We are interested in computing from this the quantities // @@ -60,7 +63,7 @@ import "github.com/cockroachdb/cockroach/pkg/storage/enginepb" // The two unknown quantities can be expressed in terms of the known quantities // because // -// (1) AbsPreSplitBoth + DeltaBatch +// (1) AbsPreSplitBoth + DeltaBatch + DeltaRangeKeyRight // - CombinedErrorDelta = AbsPostSplitLeft + AbsPostSplitRight // // In words, this corresponds to "all bytes are accounted for": from the initial @@ -88,14 +91,16 @@ import "github.com/cockroachdb/cockroach/pkg/storage/enginepb" // // For AbsPostSplitRight(), there are two cases. First, due to the identity // -// CombinedErrorDelta = AbsPreSplitBothEstimated + DeltaBatchEstimated +// CombinedErrorDelta = AbsPreSplitBothEstimated + DeltaBatchEstimated // -(AbsPostSplitLeft + AbsPostSplitRight) +// + DeltaRangeKeyRight. // -// and the fact that the second line contains no estimates, we know that -// CombinedErrorDelta is zero if the first line contains no estimates. Using -// this, we can rearrange as +// and the fact that the second and third lines contain no estimates, we know +// that CombinedErrorDelta is zero if the first line contains no estimates. +// Using this, we can rearrange as // -// AbsPostSplitRight() = AbsPreSplitBoth + DeltaBatch - AbsPostSplitLeft. +// AbsPostSplitRight() = AbsPreSplitBoth + DeltaBatch - AbsPostSplitLeft +// + DeltaRangeKeyRight. // // where all quantities on the right are known. If CombinedErrorDelta is // nonzero, we effectively have one more unknown in our linear system and we @@ -116,6 +121,7 @@ type splitStatsScanFn func() (enginepb.MVCCStats, error) type splitStatsHelperInput struct { AbsPreSplitBothEstimated enginepb.MVCCStats DeltaBatchEstimated enginepb.MVCCStats + DeltaRangeKey enginepb.MVCCStats // AbsPostSplitLeftFn returns the stats for the left hand side of the // split. AbsPostSplitLeftFn splitStatsScanFn @@ -160,6 +166,7 @@ func makeSplitStatsHelper(input splitStatsHelperInput) (splitStatsHelper, error) ms := h.in.AbsPreSplitBothEstimated ms.Subtract(absPostSplitFirst) ms.Add(h.in.DeltaBatchEstimated) + ms.Add(h.in.DeltaRangeKey) if h.in.ScanRightFirst { h.absPostSplitLeft = &ms } else { diff --git a/pkg/kv/kvserver/below_raft_protos_test.go b/pkg/kv/kvserver/below_raft_protos_test.go index 02c5e8027863..07b55aafd9d2 100644 --- a/pkg/kv/kvserver/below_raft_protos_test.go +++ b/pkg/kv/kvserver/below_raft_protos_test.go @@ -78,7 +78,7 @@ var belowRaftGoldenProtos = map[reflect.Type]fixture{ return enginepb.NewPopulatedRangeAppliedState(r, false) }, emptySum: 615555020845646359, - populatedSum: 12125419916111069931, + populatedSum: 10260703972438987883, }, reflect.TypeOf(&raftpb.HardState{}): { populatedConstructor: func(r *rand.Rand) protoutil.Message { diff --git a/pkg/kv/kvserver/client_merge_test.go b/pkg/kv/kvserver/client_merge_test.go index d4a6b0d0e10a..634c2d808905 100644 --- a/pkg/kv/kvserver/client_merge_test.go +++ b/pkg/kv/kvserver/client_merge_test.go @@ -1310,15 +1310,13 @@ func TestStoreRangeMergeStats(t *testing.T) { base.TestClusterArgs{ ReplicationMode: base.ReplicationManual, }) - defer tc.Stopper().Stop(context.Background()) + defer tc.Stopper().Stop(ctx) scratch := tc.ScratchRange(t) store := tc.GetFirstStoreFromServer(t, 0) // Split the range. lhsDesc, rhsDesc, err := createSplitRanges(ctx, scratch, store) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // Write some values left and right of the proposed split key. kvserver.WriteRandomDataToRange(t, store, lhsDesc.RangeID, scratchKey("aaa")) @@ -1329,30 +1327,18 @@ func TestStoreRangeMergeStats(t *testing.T) { // tests whether the merge code properly accounts for merging abort span // records for the same transaction. txn1 := kv.NewTxn(ctx, store.DB(), 0 /* gatewayNodeID */) - if err := txn1.Put(ctx, scratchKey("a-txn1"), "val"); err != nil { - t.Fatal(err) - } + require.NoError(t, txn1.Put(ctx, scratchKey("a-txn1"), "val")) txn2 := kv.NewTxn(ctx, store.DB(), 0 /* gatewayNodeID */) - if err := txn2.Put(ctx, scratchKey("c-txn2"), "val"); err != nil { - t.Fatal(err) - } + require.NoError(t, txn2.Put(ctx, scratchKey("c-txn2"), "val")) txn3 := kv.NewTxn(ctx, store.DB(), 0 /* gatewayNodeID */) - if err := txn3.Put(ctx, scratchKey("a-txn3"), "val"); err != nil { - t.Fatal(err) - } - if err := txn3.Put(ctx, scratchKey("c-txn3"), "val"); err != nil { - t.Fatal(err) - } + require.NoError(t, txn3.Put(ctx, scratchKey("a-txn3"), "val")) + require.NoError(t, txn3.Put(ctx, scratchKey("c-txn3"), "val")) hiPriTxn := kv.NewTxn(ctx, store.DB(), 0 /* gatewayNodeID */) hiPriTxn.TestingSetPriority(enginepb.MaxTxnPriority) for _, key := range []string{"a-txn1", "c-txn2", "a-txn3", "c-txn3"} { - if err := hiPriTxn.Put(ctx, scratchKey(key), "val"); err != nil { - t.Fatal(err) - } - } - if err := hiPriTxn.Commit(ctx); err != nil { - t.Fatal(err) + require.NoError(t, hiPriTxn.Put(ctx, scratchKey(key), "val")) } + require.NoError(t, hiPriTxn.Commit(ctx)) // Leave txn1-txn3 open so that their abort span records exist during the // merge below. @@ -1360,43 +1346,30 @@ func TestStoreRangeMergeStats(t *testing.T) { snap := store.Engine().NewSnapshot() defer snap.Close() msA, err := stateloader.Make(lhsDesc.RangeID).LoadMVCCStats(ctx, snap) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) msB, err := stateloader.Make(rhsDesc.RangeID).LoadMVCCStats(ctx, snap) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // Stats should agree with recomputation. - if err := verifyRecomputedStats(snap, lhsDesc, msA, tc.Servers[0].Clock().Now().WallTime); err != nil { - t.Fatalf("failed to verify range A's stats before split: %+v", err) - } - if err := verifyRecomputedStats(snap, rhsDesc, msB, tc.Servers[0].Clock().Now().WallTime); err != nil { - t.Fatalf("failed to verify range B's stats before split: %+v", err) - } + assertRecomputedStats(t, "range A before split", snap, lhsDesc, msA, store.Clock().PhysicalNow()) + assertRecomputedStats(t, "range B before split", snap, rhsDesc, msB, store.Clock().PhysicalNow()) // Merge the b range back into the a range. args := adminMergeArgs(lhsDesc.StartKey.AsRawKey()) - if _, err := kv.SendWrapped(ctx, store.TestSender(), args); err != nil { - t.Fatal(err) - } + _, pErr := kv.SendWrapped(ctx, store.TestSender(), args) + require.NoError(t, pErr.GoError()) replMerged := store.LookupReplica(lhsDesc.StartKey) // Get the range stats for the merged range and verify. snap = store.Engine().NewSnapshot() defer snap.Close() msMerged, err := stateloader.Make(replMerged.RangeID).LoadMVCCStats(ctx, snap) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // Merged stats should agree with recomputation. nowNanos := tc.Servers[0].Clock().Now().WallTime msMerged.AgeTo(nowNanos) - if err := verifyRecomputedStats(snap, replMerged.Desc(), msMerged, nowNanos); err != nil { - t.Errorf("failed to verify range's stats after merge: %+v", err) - } + assertRecomputedStats(t, "merged range", snap, replMerged.Desc(), msMerged, nowNanos) } func TestStoreRangeMergeInFlightTxns(t *testing.T) { diff --git a/pkg/kv/kvserver/client_split_test.go b/pkg/kv/kvserver/client_split_test.go index 693c5033f8aa..f1c52c9dd1b6 100644 --- a/pkg/kv/kvserver/client_split_test.go +++ b/pkg/kv/kvserver/client_split_test.go @@ -746,12 +746,13 @@ func TestStoreRangeSplitIdempotency(t *testing.T) { } } -// 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) { +// TestStoreRangeSplitMergeStats 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 appropriate stats. +// Finally, it merges the ranges back and asserts that the stats equal the +// original stats. +func TestStoreRangeSplitMergeStats(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -769,61 +770,52 @@ func TestStoreRangeSplitStats(t *testing.T) { store, err := s.Stores().GetStore(s.GetFirstStoreID()) require.NoError(t, err) - start := s.Clock().Now() - // Split the range after the last table data key. keyPrefix := keys.SystemSQLCodec.TablePrefix(bootstrap.TestingUserDescID(0)) args := adminSplitArgs(keyPrefix) - if _, pErr := kv.SendWrapped(ctx, store.TestSender(), args); pErr != nil { - t.Fatal(pErr) - } + _, pErr := kv.SendWrapped(ctx, store.TestSender(), args) + require.NoError(t, pErr.GoError()) + // Verify empty range has empty stats. repl := store.LookupReplica(roachpb.RKey(keyPrefix)) - // 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. - empty := enginepb.MVCCStats{LastUpdateNanos: start.WallTime} - if err := verifyRangeStats(store.Engine(), repl.RangeID, empty); err != nil { - t.Fatal(err) - } + assertRangeStats(t, "empty stats", store.Engine(), repl.RangeID, enginepb.MVCCStats{}) // Write random data. - midKey := kvserver.WriteRandomDataToRange(t, store, repl.RangeID, keyPrefix) + splitKey := kvserver.WriteRandomDataToRange(t, store, repl.RangeID, keyPrefix) + + start := s.Clock().Now() // Get the range stats now that we have data. snap := store.Engine().NewSnapshot() defer snap.Close() ms, err := stateloader.Make(repl.RangeID).LoadMVCCStats(ctx, snap) - if err != nil { - t.Fatal(err) - } - if err := verifyRecomputedStats(snap, repl.Desc(), ms, start.WallTime); err != nil { - t.Fatalf("failed to verify range's stats before split: %+v", err) - } - if inMemMS := repl.GetMVCCStats(); inMemMS != ms { - t.Fatalf("in-memory and on-disk diverged:\n%+v\n!=\n%+v", inMemMS, ms) - } + require.NoError(t, err) + assertRecomputedStats(t, "before split", snap, repl.Desc(), ms, start.WallTime) + require.Equal(t, repl.GetMVCCStats(), ms, "in-memory and on-disk stats diverge") // Split the range at approximate halfway point. - args = adminSplitArgs(midKey) - if _, pErr := kv.SendWrappedWith(ctx, store.TestSender(), roachpb.Header{ - RangeID: repl.RangeID, - }, args); pErr != nil { - t.Fatal(pErr) - } + _, pErr = kv.SendWrapped(ctx, store.TestSender(), adminSplitArgs(splitKey)) + require.NoError(t, pErr.GoError()) snap = store.Engine().NewSnapshot() defer snap.Close() msLeft, err := stateloader.Make(repl.RangeID).LoadMVCCStats(ctx, snap) - if err != nil { - t.Fatal(err) - } - replRight := store.LookupReplica(midKey) + require.NoError(t, err) + replRight := store.LookupReplica(splitKey) msRight, err := stateloader.Make(replRight.RangeID).LoadMVCCStats(ctx, snap) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) + + // Stats should both have the new timestamp. + require.Less(t, start.WallTime, msLeft.LastUpdateNanos, "LHS stats have old timestamp") + require.Less(t, start.WallTime, msRight.LastUpdateNanos, "RHS stats have old timestamp") + + // We don't care about system data. + ms.SysBytes, ms.SysCount, ms.AbortSpanBytes = 0, 0, 0 - // The stats should be exactly equal when added. + // The point key stats should be exactly equal when added. + pointMS := ms + pointMS.LastUpdateNanos = 0 + pointMS.RangeKeyCount, pointMS.RangeKeyBytes, pointMS.GCBytesAge = 0, 0, 0 expMS := enginepb.MVCCStats{ LiveBytes: msLeft.LiveBytes + msRight.LiveBytes, KeyBytes: msLeft.KeyBytes + msRight.KeyBytes, @@ -834,27 +826,33 @@ func TestStoreRangeSplitStats(t *testing.T) { ValCount: msLeft.ValCount + msRight.ValCount, IntentCount: msLeft.IntentCount + msRight.IntentCount, } - ms.SysBytes, ms.SysCount, ms.AbortSpanBytes = 0, 0, 0 - ms.LastUpdateNanos = 0 - if expMS != ms { - t.Errorf("expected left plus right ranges to equal original, but\n %+v\n+\n %+v\n!=\n %+v", msLeft, msRight, ms) - } + require.Equal(t, expMS, pointMS, "left plus right point key stats does not match original") - // Stats should both have the new timestamp. - if lTs := msLeft.LastUpdateNanos; lTs < start.WallTime { - t.Errorf("expected left range stats to have new timestamp, want %d, got %d", start.WallTime, lTs) - } - if rTs := msRight.LastUpdateNanos; rTs < start.WallTime { - t.Errorf("expected right range stats to have new timestamp, want %d, got %d", start.WallTime, rTs) - } + // The range key stats should be equal or greater. + require.GreaterOrEqual(t, msLeft.RangeKeyCount+msRight.RangeKeyCount, ms.RangeKeyCount) + require.GreaterOrEqual(t, msLeft.RangeKeyBytes+msRight.RangeKeyBytes, ms.RangeKeyBytes) + require.GreaterOrEqual(t, msLeft.GCBytesAge+msRight.GCBytesAge, ms.GCBytesAge) // Stats should agree with recomputation. - if err := verifyRecomputedStats(snap, repl.Desc(), msLeft, s.Clock().PhysicalNow()); err != nil { - t.Fatalf("failed to verify left range's stats after split: %+v", err) - } - if err := verifyRecomputedStats(snap, replRight.Desc(), msRight, s.Clock().PhysicalNow()); err != nil { - t.Fatalf("failed to verify right range's stats after split: %+v", err) - } + assertRecomputedStats(t, "LHS after split", snap, repl.Desc(), msLeft, s.Clock().PhysicalNow()) + assertRecomputedStats(t, "RHS after split", snap, replRight.Desc(), msRight, s.Clock().PhysicalNow()) + + // Merge the ranges back together, and assert that the merged stats + // agree with the pre-split stats. + _, pErr = kv.SendWrapped(ctx, store.TestSender(), adminMergeArgs(repl.Desc().StartKey.AsRawKey())) + require.NoError(t, pErr.GoError()) + + repl = store.LookupReplica(roachpb.RKey(keyPrefix)) + snap = store.Engine().NewSnapshot() + defer snap.Close() + + msMerged, err := stateloader.Make(repl.RangeID).LoadMVCCStats(ctx, snap) + require.NoError(t, err) + assertRecomputedStats(t, "in-mem after merge", snap, repl.Desc(), msMerged, s.Clock().PhysicalNow()) + + msMerged.SysBytes, msMerged.SysCount, msMerged.AbortSpanBytes = 0, 0, 0 + ms.AgeTo(msMerged.LastUpdateNanos) + require.Equal(t, ms, msMerged, "post-merge stats differ from pre-split") } // RaftMessageHandlerInterceptor wraps a storage.RaftMessageHandler. It @@ -977,56 +975,41 @@ func TestStoreRangeSplitStatsWithMerges(t *testing.T) { // Split the range after the last table data key. keyPrefix := keys.SystemSQLCodec.TablePrefix(bootstrap.TestingUserDescID(0)) args := adminSplitArgs(keyPrefix) - if _, pErr := kv.SendWrapped(ctx, store.TestSender(), args); pErr != nil { - t.Fatal(pErr) - } + _, pErr := kv.SendWrapped(ctx, store.TestSender(), args) + require.NoError(t, pErr.GoError()) + // Verify empty range has empty stats. repl := store.LookupReplica(roachpb.RKey(keyPrefix)) // 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. empty := enginepb.MVCCStats{LastUpdateNanos: start.WallTime} - if err := verifyRangeStats(store.Engine(), repl.RangeID, empty); err != nil { - t.Fatal(err) - } + assertRangeStats(t, "empty stats", store.Engine(), repl.RangeID, empty) // Write random TimeSeries data. midKey := writeRandomTimeSeriesDataToRange(t, store, repl.RangeID, keyPrefix) // Split the range at approximate halfway point. args = adminSplitArgs(midKey) - if _, pErr := kv.SendWrappedWith(ctx, store.TestSender(), roachpb.Header{ + _, pErr = kv.SendWrappedWith(ctx, store.TestSender(), roachpb.Header{ RangeID: repl.RangeID, - }, args); pErr != nil { - t.Fatal(pErr) - } + }, args) + require.NoError(t, pErr.GoError()) snap := store.Engine().NewSnapshot() defer snap.Close() msLeft, err := stateloader.Make(repl.RangeID).LoadMVCCStats(ctx, snap) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) replRight := store.LookupReplica(midKey) msRight, err := stateloader.Make(replRight.RangeID).LoadMVCCStats(ctx, snap) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // Stats should both have the new timestamp. - if lTs := msLeft.LastUpdateNanos; lTs < start.WallTime { - t.Errorf("expected left range stats to have new timestamp, want %d, got %d", start.WallTime, lTs) - } - if rTs := msRight.LastUpdateNanos; rTs < start.WallTime { - t.Errorf("expected right range stats to have new timestamp, want %d, got %d", start.WallTime, rTs) - } + require.Less(t, start.WallTime, msLeft.LastUpdateNanos, "LHS stats have old timestamp") + require.Less(t, start.WallTime, msRight.LastUpdateNanos, "RHS stats have old timestamp") // Stats should agree with recomputation. - if err := verifyRecomputedStats(snap, repl.Desc(), msLeft, s.Clock().PhysicalNow()); err != nil { - t.Fatalf("failed to verify left range's stats after split: %+v", err) - } - if err := verifyRecomputedStats(snap, replRight.Desc(), msRight, s.Clock().PhysicalNow()); err != nil { - t.Fatalf("failed to verify right range's stats after split: %+v", err) - } + assertRecomputedStats(t, "LHS after split", snap, repl.Desc(), msLeft, s.Clock().PhysicalNow()) + assertRecomputedStats(t, "RHS after split", snap, replRight.Desc(), msRight, s.Clock().PhysicalNow()) } // fillRange writes keys with the given prefix and associated values diff --git a/pkg/kv/kvserver/client_test.go b/pkg/kv/kvserver/client_test.go index 54405929f758..53e4549a40f0 100644 --- a/pkg/kv/kvserver/client_test.go +++ b/pkg/kv/kvserver/client_test.go @@ -31,8 +31,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/storage/enginepb" "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/util/hlc" - "github.com/cockroachdb/errors" - "github.com/kr/pretty" + "github.com/stretchr/testify/require" ) // getArgs returns a GetRequest and GetResponse pair addressed to @@ -135,38 +134,38 @@ func adminTransferLeaseArgs(key roachpb.Key, target roachpb.StoreID) roachpb.Req } } -func verifyRangeStats( - reader storage.Reader, rangeID roachpb.RangeID, expMS enginepb.MVCCStats, -) error { - ms, err := stateloader.Make(rangeID).LoadMVCCStats(context.Background(), reader) - if err != nil { - return err - } +func assertRangeStats( + t *testing.T, name string, r storage.Reader, rangeID roachpb.RangeID, expMS enginepb.MVCCStats, +) { + t.Helper() + + ms, err := stateloader.Make(rangeID).LoadMVCCStats(context.Background(), r) + require.NoError(t, err) // When used with a real wall clock these will not be the same, since it // takes time to load stats. expMS.AgeTo(ms.LastUpdateNanos) // Clear system counts as these are expected to vary. ms.SysBytes, ms.SysCount, ms.AbortSpanBytes = 0, 0, 0 - if ms != expMS { - return errors.Errorf("expected and actual stats differ:\n%s", pretty.Diff(expMS, ms)) - } - return nil + require.Equal(t, expMS, ms, "%s: stats differ", name) } -func verifyRecomputedStats( - reader storage.Reader, d *roachpb.RangeDescriptor, expMS enginepb.MVCCStats, nowNanos int64, -) error { - ms, err := rditer.ComputeStatsForRange(d, reader, nowNanos) - if err != nil { - return err - } +func assertRecomputedStats( + t *testing.T, + name string, + r storage.Reader, + desc *roachpb.RangeDescriptor, + expMS enginepb.MVCCStats, + nowNanos int64, +) { + t.Helper() + + ms, err := rditer.ComputeStatsForRange(desc, r, nowNanos) + require.NoError(t, err) + // When used with a real wall clock these will not be the same, since it // takes time to load stats. expMS.AgeTo(ms.LastUpdateNanos) - if expMS != ms { - return fmt.Errorf("expected range's stats to agree with recomputation: got\n%+v\nrecomputed\n%+v", expMS, ms) - } - return nil + require.Equal(t, expMS, ms, "%s: recomputed stats diverge", name) } func waitForTombstone( diff --git a/pkg/kv/kvserver/helpers_test.go b/pkg/kv/kvserver/helpers_test.go index 7e812a5593fd..eb8a50dedf56 100644 --- a/pkg/kv/kvserver/helpers_test.go +++ b/pkg/kv/kvserver/helpers_test.go @@ -18,7 +18,6 @@ package kvserver import ( "context" "fmt" - "math/rand" "testing" "time" @@ -45,6 +44,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/util/randutil" "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/errors" + "github.com/stretchr/testify/require" "go.etcd.io/etcd/raft/v3" ) @@ -496,24 +496,40 @@ func (t *RaftTransport) GetCircuitBreaker( } func WriteRandomDataToRange( - t testing.TB, store *Store, rangeID roachpb.RangeID, keyPrefix []byte, -) (midpoint []byte) { - src := rand.New(rand.NewSource(0)) - for i := 0; i < 100; i++ { - key := append([]byte(nil), keyPrefix...) - key = append(key, randutil.RandBytes(src, int(src.Int31n(1<<7)))...) - val := randutil.RandBytes(src, int(src.Int31n(1<<8))) - pArgs := putArgs(key, val) - if _, pErr := kv.SendWrappedWith(context.Background(), store.TestSender(), roachpb.Header{ - RangeID: rangeID, - }, &pArgs); pErr != nil { - t.Fatal(pErr) + t testing.TB, store *Store, rangeID roachpb.RangeID, keyPrefix roachpb.Key, +) (splitKey []byte) { + t.Helper() + + ctx := context.Background() + src, _ := randutil.NewTestRand() + for i := 0; i < 1000; i++ { + var req roachpb.Request + if src.Float64() < 0.05 { + // Write some occasional range tombstones. + startKey := append(keyPrefix.Clone(), randutil.RandBytes(src, int(src.Int31n(1<<4)))...) + var endKey roachpb.Key + for startKey.Compare(endKey) >= 0 { + endKey = append(keyPrefix.Clone(), randutil.RandBytes(src, int(src.Int31n(1<<4)))...) + } + req = &roachpb.DeleteRangeRequest{ + RequestHeader: roachpb.RequestHeader{ + Key: startKey, + EndKey: endKey, + }, + UseExperimentalRangeTombstone: true, + } + } else { + // Write regular point keys. + key := append(keyPrefix.Clone(), randutil.RandBytes(src, int(src.Int31n(1<<4)))...) + val := randutil.RandBytes(src, int(src.Int31n(1<<8))) + pArgs := putArgs(key, val) + req = &pArgs } + _, pErr := kv.SendWrappedWith(ctx, store.TestSender(), roachpb.Header{RangeID: rangeID}, req) + require.NoError(t, pErr.GoError()) } - // Return approximate midway point ("Z" in string "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"). - midKey := append([]byte(nil), keyPrefix...) - midKey = append(midKey, []byte("Z")...) - return midKey + // Return a random non-empty split key. + return append(keyPrefix.Clone(), randutil.RandBytes(src, int(src.Int31n(1<<4))+1)...) } func WatchForDisappearingReplicas(t testing.TB, store *Store) { diff --git a/pkg/kv/kvserver/rangefeed/task_test.go b/pkg/kv/kvserver/rangefeed/task_test.go index 87b695a7a228..1e9ddb38a42e 100644 --- a/pkg/kv/kvserver/rangefeed/task_test.go +++ b/pkg/kv/kvserver/rangefeed/task_test.go @@ -190,6 +190,21 @@ func (s *testIterator) curKV() storage.MVCCKeyValue { return s.kvs[s.cur] } +// HasPointAndRange implements SimpleMVCCIterator. +func (s *testIterator) HasPointAndRange() (bool, bool) { + panic("not implemented") +} + +// RangeBounds implements SimpleMVCCIterator. +func (s *testIterator) RangeBounds() (roachpb.Key, roachpb.Key) { + panic("not implemented") +} + +// RangeTombstones implements SimpleMVCCIterator. +func (s *testIterator) RangeKeys() []storage.MVCCRangeKey { + panic("not implemented") +} + func TestInitResolvedTSScan(t *testing.T) { defer leaktest.AfterTest(t)() startKey := roachpb.RKey("d") diff --git a/pkg/kv/kvserver/rditer/stats.go b/pkg/kv/kvserver/rditer/stats.go index 8cf5ce11c1b7..20ae426748bd 100644 --- a/pkg/kv/kvserver/rditer/stats.go +++ b/pkg/kv/kvserver/rditer/stats.go @@ -26,8 +26,11 @@ func ComputeStatsForRange( var err error for _, keyRange := range MakeReplicatedKeyRangesExceptLockTable(d) { func() { - iter := reader.NewMVCCIterator(storage.MVCCKeyAndIntentsIterKind, - storage.IterOptions{UpperBound: keyRange.End}) + iter := reader.NewMVCCIterator(storage.MVCCKeyAndIntentsIterKind, storage.IterOptions{ + KeyTypes: storage.IterKeyTypePointsAndRanges, + LowerBound: keyRange.Start, + UpperBound: keyRange.End, + }) defer iter.Close() var msDelta enginepb.MVCCStats diff --git a/pkg/kv/kvserver/replica_consistency.go b/pkg/kv/kvserver/replica_consistency.go index 58fb8794839e..861696ab2edb 100644 --- a/pkg/kv/kvserver/replica_consistency.go +++ b/pkg/kv/kvserver/replica_consistency.go @@ -598,7 +598,8 @@ func (*Replica) sha512( var timestampBuf []byte hasher := sha512.New() - visitor := func(unsafeKey storage.MVCCKey, unsafeValue []byte) error { + // TODO(erikgrinaker): add a range key visitor to hash range keys. + pointKeyVisitor := func(unsafeKey storage.MVCCKey, unsafeValue []byte) error { // Rate Limit the scan through the range if err := limiter.WaitN(ctx, int64(len(unsafeKey.Key)+len(unsafeValue))); err != nil { return err @@ -653,11 +654,13 @@ func (*Replica) sha512( // we will probably not have any interleaved intents so we could stop // using MVCCKeyAndIntentsIterKind and consider all locks here. for _, span := range rditer.MakeReplicatedKeyRangesExceptLockTable(&desc) { - iter := snap.NewMVCCIterator(storage.MVCCKeyAndIntentsIterKind, - storage.IterOptions{UpperBound: span.End}) - spanMS, err := storage.ComputeStatsForRange( - iter, span.Start, span.End, 0 /* nowNanos */, visitor, - ) + iter := snap.NewMVCCIterator(storage.MVCCKeyAndIntentsIterKind, storage.IterOptions{ + KeyTypes: storage.IterKeyTypePointsAndRanges, + LowerBound: span.Start, + UpperBound: span.End, + }) + spanMS, err := storage.ComputeStatsForRangeWithVisitors( + iter, span.Start, span.End, 0 /* nowNanos */, pointKeyVisitor, nil /* rangeKeyVisitor */) iter.Close() if err != nil { return nil, err diff --git a/pkg/kv/kvserver/replica_raft.go b/pkg/kv/kvserver/replica_raft.go index 8b8a1ddbf106..916171e81186 100644 --- a/pkg/kv/kvserver/replica_raft.go +++ b/pkg/kv/kvserver/replica_raft.go @@ -2137,6 +2137,7 @@ func ComputeRaftLogSize( prefix := keys.RaftLogPrefix(rangeID) prefixEnd := prefix.PrefixEnd() iter := reader.NewMVCCIterator(storage.MVCCKeyIterKind, storage.IterOptions{ + KeyTypes: storage.IterKeyTypePointsAndRanges, LowerBound: prefix, UpperBound: prefixEnd, }) diff --git a/pkg/kv/kvserver/replica_test.go b/pkg/kv/kvserver/replica_test.go index 47a2a6d067d8..cd2422221c6f 100644 --- a/pkg/kv/kvserver/replica_test.go +++ b/pkg/kv/kvserver/replica_test.go @@ -11495,7 +11495,7 @@ func TestRangeStatsRequest(t *testing.T) { defer stopper.Stop(ctx) tc.Start(ctx, t, stopper) - keyPrefix := roachpb.RKey("dummy-prefix") + keyPrefix := roachpb.Key("dummy-prefix") // Write some random data to the range and verify that a RangeStatsRequest // returns the same MVCC stats as the replica's in-memory state. diff --git a/pkg/kv/kvserver/spanset/batch.go b/pkg/kv/kvserver/spanset/batch.go index 109c045bacd4..b33d99d11672 100644 --- a/pkg/kv/kvserver/spanset/batch.go +++ b/pkg/kv/kvserver/spanset/batch.go @@ -176,6 +176,21 @@ func (i *MVCCIterator) UnsafeValue() []byte { return i.i.UnsafeValue() } +// HasPointAndRange implements SimpleMVCCIterator. +func (i *MVCCIterator) HasPointAndRange() (bool, bool) { + return i.i.HasPointAndRange() +} + +// RangeBounds implements SimpleMVCCIterator. +func (i *MVCCIterator) RangeBounds() (roachpb.Key, roachpb.Key) { + return i.i.RangeBounds() +} + +// RangeKeys implements SimpleMVCCIterator. +func (i *MVCCIterator) RangeKeys() []storage.MVCCRangeKey { + return i.i.RangeKeys() +} + // ComputeStats is part of the storage.MVCCIterator interface. func (i *MVCCIterator) ComputeStats( start, end roachpb.Key, nowNanos int64, @@ -476,6 +491,11 @@ func (s spanSetReader) ConsistentIterators() bool { return s.r.ConsistentIterators() } +// SupportsRangeKeys implements the storage.Reader interface. +func (s spanSetReader) SupportsRangeKeys() bool { + return s.r.SupportsRangeKeys() +} + // PinEngineStateForIterators implements the storage.Reader interface. func (s spanSetReader) PinEngineStateForIterators() error { return s.r.PinEngineStateForIterators() @@ -589,6 +609,24 @@ func (s spanSetWriter) ClearIterRange(start, end roachpb.Key) error { return s.w.ClearIterRange(start, end) } +func (s spanSetWriter) ExperimentalPutMVCCRangeKey(rangeKey storage.MVCCRangeKey) error { + if err := s.checkAllowedRange(rangeKey.StartKey, rangeKey.EndKey); err != nil { + return err + } + return s.w.ExperimentalPutMVCCRangeKey(rangeKey) +} + +func (s spanSetWriter) ExperimentalClearMVCCRangeKey(rangeKey storage.MVCCRangeKey) error { + if err := s.checkAllowedRange(rangeKey.StartKey, rangeKey.EndKey); err != nil { + return err + } + return s.w.ExperimentalClearMVCCRangeKey(rangeKey) +} + +func (s spanSetWriter) ExperimentalClearAllMVCCRangeKeys(start, end roachpb.Key) error { + panic("not implemented") +} + func (s spanSetWriter) Merge(key storage.MVCCKey, value []byte) error { if s.spansOnly { if err := s.spans.CheckAllowed(SpanReadWrite, roachpb.Span{Key: key.Key}); err != nil { diff --git a/pkg/roachpb/api.go b/pkg/roachpb/api.go index 63db9786c728..0eb62a70d81b 100644 --- a/pkg/roachpb/api.go +++ b/pkg/roachpb/api.go @@ -1262,6 +1262,10 @@ func (*DeleteRequest) flags() flag { } func (drr *DeleteRangeRequest) flags() flag { + // DeleteRangeRequest using MVCC range tombstones cannot be transactional. + if drr.UseExperimentalRangeTombstone { + return isWrite | isRange | isAlone | appliesTSCache + } // DeleteRangeRequest has different properties if the "inline" flag is set. // This flag indicates that the request is deleting inline MVCC values, // which cannot be deleted transactionally - inline DeleteRange will thus diff --git a/pkg/roachpb/api.proto b/pkg/roachpb/api.proto index 9aaa83f888a2..8954965f55c5 100644 --- a/pkg/roachpb/api.proto +++ b/pkg/roachpb/api.proto @@ -348,6 +348,18 @@ message DeleteRangeRequest { // Inline values cannot be deleted transactionally; a DeleteRange with // "inline" set to true will fail if it is executed within a transaction. bool inline = 4; + // If enabled, the range is deleted using an MVCC range tombstone, which is a + // cheaper constant-time write operation, but still requires a scan to check + // for conflicts. This option cannot be used in a transaction, and it cannot + // be combined with Inline or ReturnKeys. + // + // The caller must check the ExperimentalMVCCRangeTombstones version gate + // before using this parameter, as it is new in 22.2. + // + // This parameter is EXPERIMENTAL: range tombstones are under active + // development, and have severe limitations including being ignored by all + // KV and MVCC APIs and only being stored in memory. + bool use_experimental_range_tombstone = 5; } // A DeleteRangeResponse is the return value from the DeleteRange() diff --git a/pkg/roachpb/api_test.go b/pkg/roachpb/api_test.go index 8d370f531650..9a76bd45eec2 100644 --- a/pkg/roachpb/api_test.go +++ b/pkg/roachpb/api_test.go @@ -366,6 +366,7 @@ func TestFlagCombinations(t *testing.T) { reqVariants := []Request{ &AddSSTableRequest{SSTTimestampToRequestTimestamp: hlc.Timestamp{Logical: 1}}, &DeleteRangeRequest{Inline: true}, + &DeleteRangeRequest{UseExperimentalRangeTombstone: true}, &GetRequest{KeyLocking: lock.Exclusive}, &ReverseScanRequest{KeyLocking: lock.Exclusive}, &ScanRequest{KeyLocking: lock.Exclusive}, diff --git a/pkg/roachpb/data.go b/pkg/roachpb/data.go index fc6b94dafd37..840c41482a08 100644 --- a/pkg/roachpb/data.go +++ b/pkg/roachpb/data.go @@ -50,6 +50,12 @@ const ( localPrefixByte = '\x01' // LocalMaxByte is the end of the local key range. LocalMaxByte = '\x02' + // PrevishKeyLength is a reasonable key length to use for Key.Prevish(), + // typically when peeking to the left of a known key. We want this to be as + // tight as possible, since it can e.g. be used for latch spans. However, the + // exact previous key has infinite length, so we assume that most keys are + // less than 8192 bytes, or have a fairly unique 8192-byte prefix. + PrevishKeyLength = 8192 ) var ( @@ -140,6 +146,23 @@ func (rk RKey) StringWithDirs(valDirs []encoding.Direction, maxLen int) string { // messages which refer to Cockroach keys. type Key []byte +// BytesNext returns the next possible byte slice, using the extra capacity +// of the provided slice if possible, and if not, appending an \x00. +func BytesNext(b []byte) []byte { + if cap(b) > len(b) { + bNext := b[:len(b)+1] + if bNext[len(bNext)-1] == 0 { + return bNext + } + } + // TODO(spencer): Do we need to enforce KeyMaxLength here? + // Switched to "make and copy" pattern in #4963 for performance. + bn := make([]byte, len(b)+1) + copy(bn, b) + bn[len(bn)-1] = 0 + return bn +} + // Clone returns a copy of the key. func (k Key) Clone() Key { if k == nil { @@ -157,6 +180,21 @@ func (k Key) Next() Key { return Key(encoding.BytesNext(k)) } +// Prevish returns a previous key in lexicographic sort order. It is impossible +// in general to find the exact immediate predecessor key, because it has an +// infinite number of 0xff bytes at the end, so this returns the nearest +// previous key right-padded with 0xff up to length bytes. An infinite number of +// keys may exist between Key and Key.Prevish(), as keys have unbounded length. +// This also implies that k.Prevish().IsPrev(k) will often be false. +// +// PrevishKeyLength can be used as a reasonable length in most situations. +// +// The method may only take a shallow copy of the Key, so both the receiver and +// the return value should be treated as immutable after. +func (k Key) Prevish(length int) Key { + return Key(encoding.BytesPrevish(k, length)) +} + // IsPrev is a more efficient version of k.Next().Equal(m). func (k Key) IsPrev(m Key) bool { l := len(m) - 1 diff --git a/pkg/roachpb/data_test.go b/pkg/roachpb/data_test.go index 2a3f51f10418..ab3a960d7f9f 100644 --- a/pkg/roachpb/data_test.go +++ b/pkg/roachpb/data_test.go @@ -12,6 +12,7 @@ package roachpb import ( "bytes" + "encoding/hex" "math" "math/rand" "reflect" @@ -221,6 +222,27 @@ func TestNextKey(t *testing.T) { } } +func TestPrevish(t *testing.T) { + const length = 4 + testcases := []struct { + key Key + expect Key + }{ + {nil, nil}, + {[]byte{}, []byte{}}, + {[]byte{0x00}, []byte{}}, + {[]byte{0x01, 0x00}, []byte{0x01}}, + {[]byte{0x01}, []byte{0x00, 0xff, 0xff, 0xff}}, + {[]byte{0x01, 0x01}, []byte{0x01, 0x00, 0xff, 0xff}}, + {[]byte{0xff, 0xff, 0xff, 0xff}, []byte{0xff, 0xff, 0xff, 0xfe}}, + } + for _, tc := range testcases { + t.Run(hex.EncodeToString(tc.key), func(t *testing.T) { + require.Equal(t, tc.expect, tc.key.Prevish(length)) + }) + } +} + func TestIsPrev(t *testing.T) { for i, tc := range []struct { k, m Key diff --git a/pkg/sql/logictest/testdata/logic_test/builtin_function_notenant b/pkg/sql/logictest/testdata/logic_test/builtin_function_notenant index cc31761c66a9..4bbf12461801 100644 --- a/pkg/sql/logictest/testdata/logic_test/builtin_function_notenant +++ b/pkg/sql/logictest/testdata/logic_test/builtin_function_notenant @@ -19,7 +19,8 @@ SELECT crdb_internal.check_consistency(true, '\x03', '\x02') query ITT SELECT range_id, status, regexp_replace(detail, '[0-9]+', '', 'g') FROM crdb_internal.check_consistency(true, '\x02', '\xffff') WHERE range_id = 1 ---- -1 RANGE_CONSISTENT stats: {ContainsEstimates: LastUpdateNanos: IntentAge: GCBytesAge: LiveBytes: LiveCount: KeyBytes: KeyCount: ValBytes: ValCount: IntentBytes: IntentCount: SeparatedIntentCount: SysBytes: SysCount: AbortSpanBytes:} +1 RANGE_CONSISTENT stats: {ContainsEstimates: LastUpdateNanos: IntentAge: GCBytesAge: LiveBytes: LiveCount: KeyBytes: KeyCount: ValBytes: ValCount: IntentBytes: IntentCount: SeparatedIntentCount: RangeKeyCount: RangeKeyBytes: SysBytes: SysCount: AbortSpanBytes:} + # Without explicit keys, scans all ranges (we don't test this too precisely to # avoid flaking the test when the range count changes, just want to know that diff --git a/pkg/storage/BUILD.bazel b/pkg/storage/BUILD.bazel index a92b50a04867..5b394749baa8 100644 --- a/pkg/storage/BUILD.bazel +++ b/pkg/storage/BUILD.bazel @@ -28,6 +28,7 @@ go_library( "pebble_iterator.go", "pebble_merge.go", "pebble_mvcc_scanner.go", + "point_synthesizing_iter.go", "replicas_storage.go", "resource_limiter.go", "row_counter.go", @@ -141,7 +142,6 @@ go_test( "//pkg/testutils/skip", "//pkg/testutils/zerofields", "//pkg/util", - "//pkg/util/caller", "//pkg/util/encoding", "//pkg/util/fileutil", "//pkg/util/hlc", diff --git a/pkg/storage/bench_test.go b/pkg/storage/bench_test.go index 3ddf6086cdef..12a45a997dfd 100644 --- a/pkg/storage/bench_test.go +++ b/pkg/storage/bench_test.go @@ -853,7 +853,11 @@ func runMVCCScan(ctx context.Context, b *testing.B, emk engineMaker, opts benchS // Pull all of the sstables into the RocksDB cache in order to make the // timings more stable. Otherwise, the first run will be penalized pulling // data into the cache while later runs will not. - iter := eng.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{UpperBound: roachpb.KeyMax}) + iter := eng.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + LowerBound: keys.LocalMax, + UpperBound: roachpb.KeyMax, + }) _, _ = iter.ComputeStats(keys.LocalMax, roachpb.KeyMax, 0) iter.Close() } @@ -1321,7 +1325,11 @@ func runMVCCComputeStats(ctx context.Context, b *testing.B, emk engineMaker, val var stats enginepb.MVCCStats var err error for i := 0; i < b.N; i++ { - iter := eng.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{UpperBound: roachpb.KeyMax}) + iter := eng.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + LowerBound: keys.LocalMax, + UpperBound: roachpb.KeyMax, + }) stats, err = iter.ComputeStats(keys.LocalMax, roachpb.KeyMax, 0) iter.Close() if err != nil { diff --git a/pkg/storage/engine.go b/pkg/storage/engine.go index 7cfac155bb58..6c9ccd3eae64 100644 --- a/pkg/storage/engine.go +++ b/pkg/storage/engine.go @@ -45,8 +45,11 @@ func init() { type SimpleMVCCIterator interface { // Close frees up resources held by the iterator. Close() - // SeekGE advances the iterator to the first key in the engine which - // is >= the provided key. + // SeekGE advances the iterator to the first key in the engine which is >= the + // provided key. If range keys are enabled and a range key straddles the seek + // point, it will be surfaced before any point keys unless seeking directly to + // a specific version that exists (including intents, which have version 0 and + // are considered colocated with the bare key). SeekGE(key MVCCKey) // Valid must be called after any call to Seek(), Next(), Prev(), or // similar methods. It returns (true, nil) if the iterator points to @@ -65,6 +68,10 @@ type SimpleMVCCIterator interface { // or the next key if the iterator is currently located at the last version // for a key. NextKey must not be used to switch iteration direction from // reverse iteration to forward iteration. + // + // If range keys are enabled, range and point keys are treated separately, + // except for intents which are colocated with the start of a range key that + // are surfaced together since they have the same Pebble key position. NextKey() // UnsafeKey returns the same value as Key, but the memory is invalidated on // the next call to {Next,NextKey,Prev,SeekGE,SeekLT,Close}. @@ -72,6 +79,43 @@ type SimpleMVCCIterator interface { // UnsafeValue returns the same value as Value, but the memory is // invalidated on the next call to {Next,NextKey,Prev,SeekGE,SeekLT,Close}. UnsafeValue() []byte + // HasPointAndRange returns whether the current iterator position has a point + // key and/or a range key. If Valid() returns true, one of these will be true, + // otherwise they are both false. Range keys are only emitted when requested + // via IterOptions.KeyTypes. + HasPointAndRange() (bool, bool) + // RangeBounds returns the range bounds for the current range key, or + // (nil, nil) if there are none. The returned keys are only valid until + // the next iterator call. See RangeKeys() for more info on range keys. + RangeBounds() (roachpb.Key, roachpb.Key) + // RangeKeys returns all range keys (for different timestamps) at the current + // key position, or an empty list if there are none. When at a point key, it + // will return all range keys overlapping that point key. The keys are only + // valid until the next iterator operation. Currently, all range keys are MVCC + // range tombstones with an implied value of nil, and the value is therefore + // not exposed. + // + // Range keys are fragmented by Pebble such that all overlapping range keys + // between two fragment bounds form a stack of range key fragments at + // different timestamps. For example, writing [a-e)@1 and [c-g)@2 will yield + // this fragment structure: + // + // 2: |---|---| + // 1: |---|---| + // a b c d e f g + // + // Fragmentation makes all range key properties local, which avoids incurring + // unnecessary access costs across SSTs and CRDB ranges. This fragmentation is + // deterministic on the current range key state, and does not depend on write + // history. Stacking allows easy access to all range keys that overlap a given + // point key. + // + // Range keys may merge or fragment due to other range keys, split and merge + // along with CRDB ranges, can be partially removed by GC, and may be + // truncated by iterator bounds. + // + // TODO(erikgrinaker): Write a tech note on range keys and link it here. + RangeKeys() []MVCCRangeKey } // IteratorStats is returned from {MVCCIterator,EngineIterator}.Stats. @@ -128,7 +172,9 @@ type MVCCIterator interface { // keys by avoiding the need to iterate over many deleted intents. SeekIntentGE(key roachpb.Key, txnUUID uuid.UUID) - // Key returns the current key. + // Key returns the current key. If the iterator is on a range key only, this + // returns its start key, or the seek key when calling SeekGE within the range + // key bounds. Key() MVCCKey // UnsafeRawKey returns the current raw key which could be an encoded // MVCCKey, or the more general EngineKey (for a lock table key). @@ -154,11 +200,14 @@ type MVCCIterator interface { ValueProto(msg protoutil.Message) error // ComputeStats scans the underlying engine from start to end keys and // computes stats counters based on the values. This method is used after a - // range is split to recompute stats for each subrange. The start key is - // always adjusted to avoid counting local keys in the event stats are being - // recomputed for the first range (i.e. the one with start key == KeyMin). - // The nowNanos arg specifies the wall time in nanoseconds since the - // epoch and is used to compute the total age of all intents. + // range is split to recompute stats for each subrange. The nowNanos arg + // specifies the wall time in nanoseconds since the epoch and is used to + // compute the total age of intents and garbage. + // + // To properly account for intents and range keys, the iterator must be + // created with MVCCKeyAndIntentsIterKind and IterKeyTypePointsAndRanges, + // and the LowerBound and UpperBound must be set equal to start and end + // in order for range keys to be truncated to the bounds. ComputeStats(start, end roachpb.Key, nowNanos int64) (enginepb.MVCCStats, error) // FindSplitKey finds a key from the given span such that the left side of // the split is roughly targetSize bytes. The returned key will never be @@ -285,8 +334,43 @@ type IterOptions struct { // use such an iterator is to use it in concert with an iterator without // timestamp hints, as done by MVCCIncrementalIterator. MinTimestampHint, MaxTimestampHint hlc.Timestamp + // KeyTypes specifies the types of keys to surface: point and/or range keys. + // Use HasPointAndRange() to determine which key type is present at a given + // iterator position, and RangeBounds() and RangeKeys() to access range keys. + // Defaults to IterKeyTypePointsOnly. For more info, see RangeKeys(). + // + // NB: range keys are only supported for use with MVCCIterators, but it is + // legal to enable them for EngineIterators in order to derive cloned + // MVCCIterators from them. Range key behavior for EngineIterators is + // undefined. + KeyTypes IterKeyType + // RangeKeyMaskingBelow enables masking (hiding) of point keys by range keys. + // Any range key with a timestamp at or below the RangeKeyMaskingBelow + // timestamp will mask point keys below it, preventing them from being + // surfaced. Consider the following example: + // + // 4 o---------------o RangeKeyMaskingBelow=4 emits b3 + // 3 b3 d3 RangeKeyMaskingBelow=3 emits b3,d3,f2 + // 2 o---------------o f2 RangeKeyMaskingBelow=2 emits b3,d3,f2 + // 1 a1 b1 o-------o RangeKeyMaskingBelow=1 emits a1,b3,b1,d3,f2 + // a b c d e f g + RangeKeyMaskingBelow hlc.Timestamp } +// IterKeyType configures which types of keys an iterator should surface. +// +// TODO(erikgrinaker): Combine this with MVCCIterKind somehow. +type IterKeyType = pebble.IterKeyType + +const ( + // IterKeyTypePointsOnly iterates over point keys only. + IterKeyTypePointsOnly = pebble.IterKeyTypePointsOnly + // IterKeyTypePointsAndRanges iterates over both point and range keys. + IterKeyTypePointsAndRanges = pebble.IterKeyTypePointsAndRanges + // IterKeyTypeRangesOnly iterates over only range keys. + IterKeyTypeRangesOnly = pebble.IterKeyTypeRangesOnly +) + // MVCCIterKind is used to inform Reader about the kind of iteration desired // by the caller. type MVCCIterKind int @@ -433,22 +517,28 @@ type Reader interface { // timestamp of the end key; all MVCCKeys at end.Key are considered excluded // in the iteration. MVCCIterate(start, end roachpb.Key, iterKind MVCCIterKind, f func(MVCCKeyValue) error) error - // NewMVCCIterator returns a new instance of an MVCCIterator over this - // engine. The caller must invoke MVCCIterator.Close() when finished - // with the iterator to free resources. + // NewMVCCIterator returns a new instance of an MVCCIterator over this engine. + // The iterator has a consistent view of the underlying reader, and will not + // see later writes -- a new iterator must be created to see these. The caller + // must invoke MVCCIterator.Close() when finished with the iterator to free + // resources. NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions) MVCCIterator // NewEngineIterator returns a new instance of an EngineIterator over this // engine. The caller must invoke EngineIterator.Close() when finished // with the iterator to free resources. The caller can change IterOptions - // after this function returns. + // after this function returns. EngineIterators do not support range keys. NewEngineIterator(opts IterOptions) EngineIterator // ConsistentIterators returns true if the Reader implementation guarantees // that the different iterators constructed by this Reader will see the same - // underlying Engine state. NB: this only applies to iterators without - // timestamp hints (see IterOptions), i.e., even if this returns true, those - // iterators can be "inconsistent" in terms of seeing a different engine - // state. The only exception to this is a Reader created using NewSnapshot. + // underlying Engine state. However, if a non-Engine Reader is also a Writer + // (read: a Batch), new iterators will see subsequent writes made to itself, + // but existing iterators won't. ConsistentIterators() bool + // SupportsRangeKeys returns true if the Reader implementation supports + // range keys. + // + // TODO(erikgrinaker): Remove this after 22.2. + SupportsRangeKeys() bool // PinEngineStateForIterators ensures that the state seen by iterators // without timestamp hints (see IterOptions) is pinned and will not see @@ -528,6 +618,8 @@ type Writer interface { // this method actually removes entries from the storage engine. // // It is safe to modify the contents of the arguments after it returns. + // + // TODO(erikgrinaker): This should clear range keys too. ClearMVCCRangeAndIntents(start, end roachpb.Key) error // ClearMVCCRange removes MVCC keys from start (inclusive) to end // (exclusive). It should not be expected to clear intents, though may clear @@ -543,8 +635,59 @@ type Writer interface { // iterate over point keys and remove them from the storage engine using // per-key storage tombstones (not MVCC tombstones). Any separated // intents/locks will also be cleared. + // + // TODO(erikgrinaker): This should clear range keys too. ClearIterRange(start, end roachpb.Key) error + // ExperimentalClearMVCCRangeKey deletes an MVCC range key from start + // (inclusive) to end (exclusive) at the given timestamp. For any range key + // that straddles the start and end boundaries, only the segments within the + // boundaries will be cleared. Range keys at other timestamps are unaffected. + // Clears are idempotent. + // + // This method is primarily intended for MVCC garbage collection and similar + // internal use. + // + // This method is EXPERIMENTAL: range keys are under active development, and + // have severe limitations including being ignored by all KV and MVCC APIs and + // only being stored in memory. + ExperimentalClearMVCCRangeKey(rangeKey MVCCRangeKey) error + + // ExperimentalClearAllMVCCRangeKeys deletes all MVCC range keys (i.e. all + // versions) from start (inclusive) to end (exclusive). For any range key + // that straddles the start and end boundaries, only the segments within the + // boundaries will be cleared. Clears are idempotent. + // + // This method is primarily intended for MVCC garbage collection and similar + // internal use. + // + // This method is EXPERIMENTAL: range keys are under active development, and + // have severe limitations including being ignored by all KV and MVCC APIs and + // only being stored in memory. + ExperimentalClearAllMVCCRangeKeys(start, end roachpb.Key) error + + // ExperimentalPutMVCCRangeKey writes an MVCC range key. It will replace any + // existing keys, or any segments that it overlaps. This is currently only + // used for range tombstones, which have an implicit value of nil, and the + // Pebble value parameter is not exposed (adding this will need changes to + // MVCC stats, GC, scans/gets, and more). + // + // A range key does not have a distinct identity, but should be considered a + // key continuum. They can be fragmented or merged by overlapping range keys, + // split/merged along with CRDB ranges, partially removed or replaced, + // and truncated during bounded iteration. + // + // Range keys exist separately from point keys in Pebble, and must be accessed + // via special iterator options and methods such as IterOptions.KeyTypes and + // SimpleMVCCIterator.RangeKeys(). + // + // TODO(erikgrinaker): Write a tech note on range keys and link it here. + // + // This method is EXPERIMENTAL: range keys are under active development, and + // have severe limitations including being ignored by all KV and MVCC APIs and + // only being stored in memory. + ExperimentalPutMVCCRangeKey(MVCCRangeKey) error + // Merge is a high-performance write operation used for values which are // accumulated over several writes. Multiple values can be merged // sequentially into a single key; a subsequent read will return a "merged" diff --git a/pkg/storage/engine_test.go b/pkg/storage/engine_test.go index 68df6fb41245..911d44f1dce0 100644 --- a/pkg/storage/engine_test.go +++ b/pkg/storage/engine_test.go @@ -26,6 +26,7 @@ import ( "testing" "time" + "github.com/cockroachdb/cockroach/pkg/clusterversion" "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/settings/cluster" @@ -1661,3 +1662,484 @@ func TestScanIntents(t *testing.T) { }) } } + +// TestEngineConsistentIterators checks iterator consistency for various readers. +// +// 1. All iterators have a consistent view of the reader as of the time of its +// creation. Subsequent writes are never visible to it. +// +// 2. Iterators on readers with ConsistentIterators=true all have a consistent +// view of the Engine as of the time of the first iterator creation or +// PinEngineStateForIterators call: newer Engine writes are never visible to new +// iterators. The opposite is true for ConsistentIterators=false: new iterators +// always see the most recent Engine state at the time of their creation. +// +// 3. New iterators on unindexed Batches never see batch writes. They do satisfy +// ConsistentIterators: they never see new Engine writes after the first +// iterator was created or a PinEngineStateForIterators call. +// +// 4. New iterators on indexed Batches see all batch writes that happened before +// the iterator was created. However, they satisfy ConsistentIterators and +// do not see new Engine writes after the first iterator was created or a +// PinEngineStateForIterators call. Via 1, they never see later batch writes. +func TestEngineConsistentIterators(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + testcases := map[string]struct { + makeReader func(Engine) Reader + expectConsistent bool + canWrite bool + readOwnWrites bool + }{ + "Engine": { + makeReader: func(e Engine) Reader { return e }, + expectConsistent: false, + canWrite: true, + readOwnWrites: true, + }, + "Batch": { + makeReader: func(e Engine) Reader { return e.NewBatch() }, + expectConsistent: true, + canWrite: true, + readOwnWrites: true, + }, + "UnindexedBatch": { + makeReader: func(e Engine) Reader { return e.NewUnindexedBatch(false) }, + expectConsistent: true, + canWrite: true, + readOwnWrites: false, + }, + "ReadOnly": { + makeReader: func(e Engine) Reader { return e.NewReadOnly(StandardDurability) }, + expectConsistent: true, + canWrite: false, + }, + "Snapshot": { + makeReader: func(e Engine) Reader { return e.NewSnapshot() }, + expectConsistent: true, + canWrite: false, + }, + } + keyKinds := []interface{}{MVCCKeyAndIntentsIterKind, MVCCKeyIterKind} + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + testutils.RunValues(t, "IterKind", keyKinds, func(t *testing.T, iterKindI interface{}) { + iterKind := iterKindI.(MVCCIterKind) + eng := NewDefaultInMemForTesting() + defer eng.Close() + + // Write initial point and range keys. + require.NoError(t, eng.PutMVCC(pointKey("a", 1), []byte("a1"))) + require.NoError(t, eng.ExperimentalPutMVCCRangeKey(rangeKey("b", "c", 1))) + + // Set up two readers: one regular and one which will be pinned. + r := tc.makeReader(eng) + defer r.Close() + rPinned := tc.makeReader(eng) + defer rPinned.Close() + + require.Equal(t, tc.expectConsistent, r.ConsistentIterators()) + + // Create an iterator. This will see the old engine state regardless + // of the type of reader. + opts := IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + LowerBound: keys.LocalMax, + UpperBound: keys.MaxKey, + } + iterOld := r.NewMVCCIterator(iterKind, opts) + defer iterOld.Close() + + // Pin the pinned reader, if it supports it. This should ensure later + // iterators see the current state. + if rPinned.ConsistentIterators() { + require.NoError(t, rPinned.PinEngineStateForIterators()) + } else { + require.Error(t, rPinned.PinEngineStateForIterators()) + } + + // Write new point and range keys to the engine, and set up the expected + // old and new results. + require.NoError(t, eng.PutMVCC(pointKey("a", 2), []byte("a2"))) + require.NoError(t, eng.ExperimentalPutMVCCRangeKey(rangeKey("b", "c", 2))) + + expectOld := []interface{}{ + pointKV("a", 1, "a1"), + rangeKey("b", "c", 1), + } + expectNew := []interface{}{ + pointKV("a", 2, "a2"), + pointKV("a", 1, "a1"), + rangeKey("b", "c", 2), + rangeKey("b", "c", 1), + } + + // Opened iterators should all see the old iterator state, regardless of + // reader type. + require.Equal(t, expectOld, scanIter(t, iterOld)) + + // Create new iterators from the pinned readers. Consistent iterators + // should see the old state, others should see the new state. + iterPinned := rPinned.NewMVCCIterator(iterKind, opts) + defer iterPinned.Close() + if rPinned.ConsistentIterators() { + require.Equal(t, expectOld, scanIter(t, iterPinned)) + } else { + require.Equal(t, expectNew, scanIter(t, iterPinned)) + } + + // Create another iterator from the reader. Consistent iterators should + // see the old state, others should see the new state. + iterNew := r.NewMVCCIterator(iterKind, opts) + defer iterNew.Close() + if r.ConsistentIterators() { + require.Equal(t, expectOld, scanIter(t, iterNew)) + } else { + require.Equal(t, expectNew, scanIter(t, iterNew)) + } + + // If the reader is also a writer, check interactions with writes. + // In particular, a Batch should read its own writes for any new + // iterators, but not for any existing iterators. + if tc.canWrite { + w, ok := r.(Writer) + require.Equal(t, tc.canWrite, ok) + + require.NoError(t, w.PutMVCC(pointKey("a", 3), []byte("a3"))) + require.NoError(t, w.ExperimentalPutMVCCRangeKey(rangeKey("b", "c", 3))) + expectNewAndOwn := []interface{}{ + pointKV("a", 3, "a3"), + pointKV("a", 2, "a2"), + pointKV("a", 1, "a1"), + rangeKey("b", "c", 3), + rangeKey("b", "c", 2), + rangeKey("b", "c", 1), + } + expectOldAndOwn := []interface{}{ + pointKV("a", 3, "a3"), + pointKV("a", 1, "a1"), + rangeKey("b", "c", 3), + rangeKey("b", "c", 1), + } + + // TODO(erikgrinaker): existing batch iterators see new writes, and + // new batch iterators don't see new range keys. See: + // https://github.com/cockroachdb/pebble/issues/1638 + if name == "Batch" { + return + } + + // The existing iterators should see the same state as before these + // writes, because they always have a consistent view from when they + // were created. + require.Equal(t, expectOld, scanIter(t, iterOld)) + if r.ConsistentIterators() { + require.Equal(t, expectOld, scanIter(t, iterPinned)) + require.Equal(t, expectOld, scanIter(t, iterNew)) + } else { + require.Equal(t, expectNew, scanIter(t, iterPinned)) + require.Equal(t, expectNew, scanIter(t, iterNew)) + } + + // A new iterator should read our own writes if the reader supports it, + // but consistent iterators should not see the changes to the underlying + // engine either way. + iterOwn := r.NewMVCCIterator(iterKind, opts) + defer iterOwn.Close() + if tc.readOwnWrites { + if r.ConsistentIterators() { + require.Equal(t, expectOldAndOwn, scanIter(t, iterOwn)) + } else { + require.Equal(t, expectNewAndOwn, scanIter(t, iterOwn)) + } + } else { + if r.ConsistentIterators() { + require.Equal(t, expectOld, scanIter(t, iterOwn)) + } else { + require.Equal(t, expectNew, scanIter(t, iterOwn)) + } + } + } + }) + }) + } +} + +// TestEngineRangeKeyMutations tests that range key mutations work as +// expected, both for the engine directly and for batches. +func TestEngineRangeKeyMutations(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + testutils.RunTrueAndFalse(t, "batch", func(t *testing.T, useBatch bool) { + eng := NewDefaultInMemForTesting() + defer eng.Close() + + rw := ReadWriter(eng) + if useBatch { + // TODO(erikgrinaker): Pebble batches do not read back range key writes + // after the iterator was created, which breaks the iterator reuse in + // storage.Batch. See: https://github.com/cockroachdb/pebble/issues/1638 + // + //rw = eng.NewBatch() + //defer rw.Close() + return + } + + require.True(t, rw.SupportsRangeKeys()) + + // Check errors for invalid, empty, and zero-length range keys. Not + // exhaustive, since we assume validation dispatches to + // MVCCRangeKey.Validate() which is tested separately. + empty := MVCCRangeKey{} + invalid := rangeKey("b", "a", 1) + zeroLength := rangeKey("a", "a", 1) + + require.Error(t, rw.ExperimentalPutMVCCRangeKey(empty)) + require.Error(t, rw.ExperimentalPutMVCCRangeKey(invalid)) + require.Error(t, rw.ExperimentalPutMVCCRangeKey(zeroLength)) + + require.Error(t, rw.ExperimentalClearMVCCRangeKey(empty)) + require.Error(t, rw.ExperimentalClearMVCCRangeKey(invalid)) + require.Error(t, rw.ExperimentalClearMVCCRangeKey(zeroLength)) + + require.Error(t, rw.ExperimentalClearAllMVCCRangeKeys(empty.StartKey, empty.EndKey)) + require.Error(t, rw.ExperimentalClearAllMVCCRangeKeys(invalid.StartKey, invalid.EndKey)) + require.Error(t, rw.ExperimentalClearAllMVCCRangeKeys(zeroLength.StartKey, zeroLength.EndKey)) + + require.Empty(t, scanRangeKeys(t, rw)) + + // Write some range keys and read the fragmented keys back. + rangeKeys := []MVCCRangeKey{ + rangeKey("a", "d", 1), + rangeKey("f", "h", 1), + rangeKey("c", "g", 2), + } + for _, rangeKey := range rangeKeys { + require.NoError(t, rw.ExperimentalPutMVCCRangeKey(rangeKey)) + } + require.Equal(t, []MVCCRangeKey{ + rangeKey("a", "c", 1), + rangeKey("c", "d", 2), + rangeKey("c", "d", 1), + rangeKey("d", "f", 2), + rangeKey("f", "g", 2), + rangeKey("f", "g", 1), + rangeKey("g", "h", 1), + }, scanRangeKeys(t, rw)) + + // Clear the f-g portion of [f-h)@1, twice for idempotency. This should not + // affect any other range keys, apart from removing the fragment boundary + // at f for [d-g)@2. + require.NoError(t, rw.ExperimentalClearMVCCRangeKey(rangeKey("f", "g", 1))) + require.NoError(t, rw.ExperimentalClearMVCCRangeKey(rangeKey("f", "g", 1))) + require.Equal(t, []MVCCRangeKey{ + rangeKey("a", "c", 1), + rangeKey("c", "d", 2), + rangeKey("c", "d", 1), + rangeKey("d", "g", 2), + rangeKey("g", "h", 1), + }, scanRangeKeys(t, rw)) + + // Write [e-f)@2 on top of existing [d-g)@2. This should be a noop. + require.NoError(t, rw.ExperimentalPutMVCCRangeKey(rangeKey("e", "f", 2))) + require.Equal(t, []MVCCRangeKey{ + rangeKey("a", "c", 1), + rangeKey("c", "d", 2), + rangeKey("c", "d", 1), + rangeKey("d", "g", 2), + rangeKey("g", "h", 1), + }, scanRangeKeys(t, rw)) + + // Clear all range keys in the [c-f) span. + require.NoError(t, rw.ExperimentalClearAllMVCCRangeKeys(roachpb.Key("c"), roachpb.Key("f"))) + require.Equal(t, []MVCCRangeKey{ + rangeKey("a", "c", 1), + rangeKey("f", "g", 2), + rangeKey("g", "h", 1), + }, scanRangeKeys(t, rw)) + + // Write another range key to bridge the [c-g)@1 gap. + require.NoError(t, rw.ExperimentalPutMVCCRangeKey(rangeKey("c", "g", 1))) + require.Equal(t, []MVCCRangeKey{ + rangeKey("a", "f", 1), + rangeKey("f", "g", 2), + rangeKey("f", "g", 1), + rangeKey("g", "h", 1), + }, scanRangeKeys(t, rw)) + + // If using a batch, make sure nothing has been written to the engine, then + // commit the batch and make sure it gets written to the engine. + if useBatch { + require.Empty(t, scanRangeKeys(t, eng)) + require.NoError(t, rw.(Batch).Commit(true)) + require.Equal(t, []MVCCRangeKey{ + rangeKey("a", "f", 1), + rangeKey("f", "g", 2), + rangeKey("f", "g", 1), + rangeKey("g", "h", 1), + }, scanRangeKeys(t, eng)) + } + }) +} + +// TestEngineRangeKeysUnsupported tests that engines without range key +// support behave as expected, i.e. writes fail but reads degrade gracefully. +func TestEngineRangeKeysUnsupported(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + // Set up an engine with a version that doesn't support range keys. + version := clusterversion.ByKey(clusterversion.EnsurePebbleFormatVersionRangeKeys - 1) + st := cluster.MakeTestingClusterSettingsWithVersions(version, version, true) + + eng := NewDefaultInMemForTesting(Settings(st)) + defer eng.Close() + + require.NoError(t, eng.PutMVCC(pointKey("a", 1), []byte("a1"))) + + batch := eng.NewBatch() + defer batch.Close() + snapshot := eng.NewSnapshot() + defer snapshot.Close() + readOnly := eng.NewReadOnly(StandardDurability) + defer readOnly.Close() + + writers := map[string]Writer{ + "engine": eng, + "batch": batch, + } + readers := map[string]Reader{ + "engine": eng, + "batch": batch, + "snapshot": snapshot, + "readonly": readOnly, + } + + // Range key puts should error, but clears are noops (since old databases + // cannot contain range keys by definition). + for name, w := range writers { + t.Run(fmt.Sprintf("write/%s", name), func(t *testing.T) { + rangeKey := rangeKey("a", "b", 2) + err := w.ExperimentalPutMVCCRangeKey(rangeKey) + require.Error(t, err) + require.Contains(t, err.Error(), "range keys not supported") + require.NoError(t, w.ExperimentalClearMVCCRangeKey(rangeKey)) + require.NoError(t, w.ExperimentalClearAllMVCCRangeKeys(rangeKey.StartKey, rangeKey.EndKey)) + }) + } + + // All range key iterators should degrade gracefully to point key iterators, + // and be empty for IterKeyTypeRangesOnly. + keyTypes := map[string]IterKeyType{ + "PointsOnly": IterKeyTypePointsOnly, + "PointsAndRanges": IterKeyTypePointsAndRanges, + "RangesOnly": IterKeyTypeRangesOnly, + } + for name, r := range readers { + for keyTypeName, keyType := range keyTypes { + t.Run(fmt.Sprintf("read/%s/%s", name, keyTypeName), func(t *testing.T) { + require.False(t, r.SupportsRangeKeys()) + + iter := r.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{ + KeyTypes: keyType, + UpperBound: keys.MaxKey, + RangeKeyMaskingBelow: hlc.Timestamp{WallTime: 1}, // should disable when unsupported + }) + defer iter.Close() + + iter.SeekGE(pointKey("a", 0)) + + ok, err := iter.Valid() + require.NoError(t, err) + + if keyType == IterKeyTypeRangesOnly { + // With RangesOnly, the iterator must be empty. + require.False(t, ok) + hasPoint, hasRange := iter.HasPointAndRange() + require.False(t, hasPoint) + require.False(t, hasRange) + return + } + + require.True(t, ok) + require.Equal(t, pointKey("a", 1), iter.UnsafeKey()) + require.Equal(t, []byte("a1"), iter.UnsafeValue()) + + hasPoint, hasRange := iter.HasPointAndRange() + require.True(t, hasPoint) + require.False(t, hasRange) + rangeStart, rangeEnd := iter.RangeBounds() + require.Nil(t, rangeStart) + require.Nil(t, rangeEnd) + require.Empty(t, iter.RangeKeys()) + + // Exhaust the iterator. + iter.Next() + ok, err = iter.Valid() + require.NoError(t, err) + require.False(t, ok) + }) + } + } +} + +func scanRangeKeys(t *testing.T, r Reader) []MVCCRangeKey { + t.Helper() + + iter := r.NewMVCCIterator(MVCCKeyIterKind, IterOptions{ + KeyTypes: IterKeyTypeRangesOnly, + LowerBound: keys.LocalMax, + UpperBound: keys.MaxKey, + }) + defer iter.Close() + iter.SeekGE(MVCCKey{Key: keys.LocalMax}) + + var rangeKeys []MVCCRangeKey + for { + ok, err := iter.Valid() + require.NoError(t, err) + if !ok { + break + } + for _, rangeKey := range iter.RangeKeys() { + rangeKeys = append(rangeKeys, rangeKey.Clone()) + } + iter.Next() + } + return rangeKeys +} + +func scanIter(t *testing.T, iter MVCCIterator) []interface{} { + t.Helper() + + iter.SeekGE(MVCCKey{Key: keys.LocalMax}) + + var keys []interface{} + var prevRangeStart roachpb.Key + for { + ok, err := iter.Valid() + require.NoError(t, err) + if !ok { + break + } + hasPoint, hasRange := iter.HasPointAndRange() + if hasRange { + if rangeStart, _ := iter.RangeBounds(); !rangeStart.Equal(prevRangeStart) { + for _, rk := range iter.RangeKeys() { + keys = append(keys, rk.Clone()) + } + prevRangeStart = rangeStart.Clone() + } + } + if hasPoint { + keys = append(keys, MVCCKeyValue{ + Key: iter.Key(), + Value: iter.Value(), + }) + } + iter.Next() + } + return keys +} diff --git a/pkg/storage/enginepb/mvcc.go b/pkg/storage/enginepb/mvcc.go index 8e9c9a427633..de3ac5a56103 100644 --- a/pkg/storage/enginepb/mvcc.go +++ b/pkg/storage/enginepb/mvcc.go @@ -90,15 +90,17 @@ func (t TxnMeta) Short() redact.SafeString { } // Total returns the range size as the sum of the key and value -// bytes. This includes all non-live keys and all versioned values. +// bytes. This includes all non-live keys and all versioned values, +// both for point and range keys. func (ms MVCCStats) Total() int64 { - return ms.KeyBytes + ms.ValBytes + return ms.KeyBytes + ms.ValBytes + ms.RangeKeyBytes } // GCBytes is a convenience function which returns the number of gc bytes, -// that is the key and value bytes excluding the live bytes. +// that is the key and value bytes excluding the live bytes, both for +// point keys and range keys. func (ms MVCCStats) GCBytes() int64 { - return ms.KeyBytes + ms.ValBytes - ms.LiveBytes + return ms.Total() - ms.LiveBytes } // AvgIntentAge returns the average age of outstanding intents, @@ -169,6 +171,8 @@ func (ms *MVCCStats) Add(oms MVCCStats) { ms.ValCount += oms.ValCount ms.IntentCount += oms.IntentCount ms.SeparatedIntentCount += oms.SeparatedIntentCount + ms.RangeKeyCount += oms.RangeKeyCount + ms.RangeKeyBytes += oms.RangeKeyBytes ms.SysBytes += oms.SysBytes ms.SysCount += oms.SysCount ms.AbortSpanBytes += oms.AbortSpanBytes @@ -196,6 +200,8 @@ func (ms *MVCCStats) Subtract(oms MVCCStats) { ms.ValCount -= oms.ValCount ms.IntentCount -= oms.IntentCount ms.SeparatedIntentCount -= oms.SeparatedIntentCount + ms.RangeKeyCount -= oms.RangeKeyCount + ms.RangeKeyBytes -= oms.RangeKeyBytes ms.SysBytes -= oms.SysBytes ms.SysCount -= oms.SysCount ms.AbortSpanBytes -= oms.AbortSpanBytes diff --git a/pkg/storage/enginepb/mvcc.proto b/pkg/storage/enginepb/mvcc.proto index ffaf911e898e..b246e7f11550 100644 --- a/pkg/storage/enginepb/mvcc.proto +++ b/pkg/storage/enginepb/mvcc.proto @@ -163,8 +163,8 @@ message MVCCStats { // intent_age is the cumulative age of the tracked intents. // See the comment on MVCCStats. optional sfixed64 intent_age = 2 [(gogoproto.nullable) = false]; - // gc_bytes_age is the cumulative age of the non-live data (i.e. - // data included in key_bytes and val_bytes, but not live_bytes). + // gc_bytes_age is the cumulative age of the non-live data (i.e. data + // included in key_bytes, val_bytes, and range_key_bytes, but not live_bytes). // See the comment on MVCCStats. optional sfixed64 gc_bytes_age = 3 [(gogoproto.nullable) = false, (gogoproto.customname) = "GCBytesAge"]; // live_bytes is the number of bytes stored in keys and values which can in @@ -177,7 +177,7 @@ message MVCCStats { // live_count is the number of meta keys tracked under live_bytes. optional sfixed64 live_count = 5 [(gogoproto.nullable) = false]; // key_bytes is the number of bytes stored in all non-system - // keys, including live, meta, old, and deleted keys. + // point keys, including live, meta, old, and deleted keys. // Only meta keys really account for the "full" key; value // keys only for the timestamp suffix. optional sfixed64 key_bytes = 6 [(gogoproto.nullable) = false]; @@ -201,6 +201,22 @@ message MVCCStats { // intents, so mixed-version clusters with nodes preceding this knowledge // will always have a 0 value for this field. optional sfixed64 separated_intent_count = 16 [(gogoproto.nullable) = false]; + // range_key_count is the number of range keys tracked under range_key_bytes. + // Overlapping range keys get fragmented, such that writing a single range key + // may cause range_key_count to increase by more than 1 due to fragmentation. + // Multiple range key versions count as a single range key. + // + // Range keys that straddle range split boundaries will become two separate + // logical range keys (one in each range), and merge back to one range key + // when the ranges merge. + // + // NB: Currently, all range keys are MVCC range tombstones with no value. + // Therefore, these do not contribute to live_count nor live_bytes, and there + // is no range_val_bytes or range_val_count. + optional sfixed64 range_key_count = 17 [(gogoproto.nullable) = false]; + // range_key_bytes is the encoded size of logical range keys, disregarding + // fragmentation and overlap. + optional sfixed64 range_key_bytes = 18 [(gogoproto.nullable) = false]; // sys_bytes is the number of bytes stored in system-local kv-pairs. // This tracks the same quantity as (key_bytes + val_bytes), but diff --git a/pkg/storage/enginepb/mvcc3.proto b/pkg/storage/enginepb/mvcc3.proto index a528febf764f..2fc8927bb650 100644 --- a/pkg/storage/enginepb/mvcc3.proto +++ b/pkg/storage/enginepb/mvcc3.proto @@ -159,6 +159,8 @@ message MVCCStatsDelta { sint64 intent_bytes = 10; sint64 intent_count = 11; sint64 separated_intent_count = 16; + sint64 range_key_count = 17; + sint64 range_key_bytes = 18; sint64 sys_bytes = 12; sint64 sys_count = 13; sint64 abort_span_bytes = 15; @@ -188,6 +190,8 @@ message MVCCPersistentStats { int64 intent_bytes = 10; int64 intent_count = 11; int64 separated_intent_count = 16; + int64 range_key_count = 17; + int64 range_key_bytes = 18; int64 sys_bytes = 12; int64 sys_count = 13; int64 abort_span_bytes = 15; diff --git a/pkg/storage/intent_interleaving_iter.go b/pkg/storage/intent_interleaving_iter.go index ba038a23be93..ef2da9b85597 100644 --- a/pkg/storage/intent_interleaving_iter.go +++ b/pkg/storage/intent_interleaving_iter.go @@ -233,7 +233,8 @@ func newIntentInterleavingIterator(reader Reader, opts IterOptions) MVCCIterator if reader.ConsistentIterators() { iter = reader.NewMVCCIterator(MVCCKeyIterKind, opts) } else { - iter = newPebbleIterator(nil, intentIter.GetRawIter(), opts, StandardDurability) + iter = newPebbleIterator( + nil, intentIter.GetRawIter(), opts, StandardDurability, reader.SupportsRangeKeys()) } *iiIter = intentInterleavingIter{ @@ -299,6 +300,36 @@ func (i *intentInterleavingIter) makeLowerLimitKey() roachpb.Key { return i.intentLimitKeyBuf } +// maybeSkipIntentRangeKey will step iter once (forwards or backwards) if iter +// is positioned on a bare range key with the same key position (either start +// key or seek key) as the current intentIter intent. We consider an intent with +// timestamp 0 to be colocated with the bare key, such that seeking to the bare +// intent key will not surface the bare range key by itself first (similarly to +// seeking to an existing version of a point key). +// +// In the forward direction, this is necessary when intentIter lands on a new +// intent, to ensure iter is positioned on the provisional value instead of the +// bare range key. This must be done after positioning both iterators. +// +// In the reverse direction, this is necessary to skip over the bare range key +// when leaving the intent. This must be done before stepping either iterator. +// +// NB: This is called before computePos(), and can't rely on intentCmp. +func (i *intentInterleavingIter) maybeSkipIntentRangeKey() error { + if i.iterValid { + hasPoint, hasRange := i.iter.HasPointAndRange() + if hasRange && !hasPoint && i.iterKey.Key.Equal(i.intentKey) { + if i.dir == 1 { + i.iter.Next() + } else { + i.iter.Prev() + } + return i.tryDecodeKey() + } + } + return nil +} + func (i *intentInterleavingIter) SeekGE(key MVCCKey) { i.dir = +1 i.valid = true @@ -332,6 +363,9 @@ func (i *intentInterleavingIter) SeekGE(key MVCCKey) { if err = i.tryDecodeLockKey(iterState, err); err != nil { return } + if err := i.maybeSkipIntentRangeKey(); err != nil { + return + } } i.computePos() } @@ -361,6 +395,9 @@ func (i *intentInterleavingIter) SeekIntentGE(key roachpb.Key, txnUUID uuid.UUID if err = i.tryDecodeLockKey(iterState, err); err != nil { return } + if err := i.maybeSkipIntentRangeKey(); err != nil { + return + } i.computePos() } @@ -406,6 +443,8 @@ func (i *intentInterleavingIter) computePos() { } if i.intentKey == nil { i.intentCmp = i.dir + } else if hasPoint, _ := i.iter.HasPointAndRange(); !hasPoint { + i.intentCmp = 1 // bare range keys sort before intents } else { i.intentCmp = i.intentKey.Compare(i.iterKey.Key) } @@ -494,6 +533,9 @@ func (i *intentInterleavingIter) Next() { if err = i.tryDecodeLockKey(iterState, err); err != nil { return } + if err := i.maybeSkipIntentRangeKey(); err != nil { + return + } i.computePos() return } @@ -512,6 +554,9 @@ func (i *intentInterleavingIter) Next() { if err := i.tryDecodeKey(); err != nil { return } + if err := i.maybeSkipIntentRangeKey(); err != nil { + return + } i.intentCmp = 0 if !i.iterValid { i.err = errors.Errorf("intent has no provisional value") @@ -541,6 +586,9 @@ func (i *intentInterleavingIter) Next() { if err = i.tryDecodeLockKey(iterState, err); err != nil { return } + if err := i.maybeSkipIntentRangeKey(); err != nil { + return + } i.intentCmp = +1 if util.RaceEnabled && iterState == pebble.IterValid { cmp := i.intentKey.Compare(i.iterKey.Key) @@ -579,6 +627,9 @@ func (i *intentInterleavingIter) Next() { if err = i.tryDecodeLockKey(iterState, err); err != nil { return } + if err := i.maybeSkipIntentRangeKey(); err != nil { + return + } i.intentCmp = +1 if util.RaceEnabled && i.intentKey != nil { cmp := i.intentKey.Compare(i.iterKey.Key) @@ -605,6 +656,9 @@ func (i *intentInterleavingIter) Next() { return } } + if err := i.maybeSkipIntentRangeKey(); err != nil { + return + } i.computePos() } } @@ -641,6 +695,9 @@ func (i *intentInterleavingIter) NextKey() { if err := i.tryDecodeLockKey(iterState, err); err != nil { return } + if err := i.maybeSkipIntentRangeKey(); err != nil { + return + } i.computePos() return } @@ -658,6 +715,9 @@ func (i *intentInterleavingIter) NextKey() { return } } + if err := i.maybeSkipIntentRangeKey(); err != nil { + return + } i.computePos() } @@ -715,6 +775,24 @@ func (i *intentInterleavingIter) Value() []byte { return i.iter.Value() } +// HasPointAndRange implements SimpleMVCCIterator. +func (i *intentInterleavingIter) HasPointAndRange() (bool, bool) { + // NB: We assume that EngineIterators, i.e. intentIter, cannot have range keys. + hasPoint, hasRange := i.iter.HasPointAndRange() + hasPoint = hasPoint || (i.valid && i.isCurAtIntentIter()) + return hasPoint, hasRange +} + +// RangeBounds implements SimpleMVCCIterator. +func (i *intentInterleavingIter) RangeBounds() (roachpb.Key, roachpb.Key) { + return i.iter.RangeBounds() +} + +// RangeKeys implements SimpleMVCCIterator. +func (i *intentInterleavingIter) RangeKeys() []MVCCRangeKey { + return i.iter.RangeKeys() +} + func (i *intentInterleavingIter) Close() { i.iter.Close() i.intentIter.Close() @@ -830,6 +908,9 @@ func (i *intentInterleavingIter) Prev() { if err := i.tryDecodeKey(); err != nil { return } + if err := i.maybeSkipIntentRangeKey(); err != nil { + return + } i.intentCmp = +1 if util.RaceEnabled && i.iterValid { cmp := i.intentKey.Compare(i.iterKey.Key) @@ -862,7 +943,11 @@ func (i *intentInterleavingIter) Prev() { // The iterator is positioned at an intent in intentIter, and iter is // exhausted or positioned at a versioned value of a preceding key. // Stepping intentIter backward will ensure that intentKey is <= the key - // of iter (when neither is exhausted). + // of iter (when neither is exhausted). We also skip over the bare range + // key whose start key is colocated with the intent, if any. + if err := i.maybeSkipIntentRangeKey(); err != nil { + return + } var limitKey roachpb.Key if i.iterValid { limitKey = i.makeLowerLimitKey() diff --git a/pkg/storage/intent_reader_writer.go b/pkg/storage/intent_reader_writer.go index 3217153b8950..e2c908d4575c 100644 --- a/pkg/storage/intent_reader_writer.go +++ b/pkg/storage/intent_reader_writer.go @@ -162,7 +162,7 @@ func (imr *intentInterleavingReader) NewMVCCIterator( iterKind == MVCCKeyAndIntentsIterKind { panic("cannot ask for interleaved intents when specifying timestamp hints") } - if iterKind == MVCCKeyIterKind { + if iterKind == MVCCKeyIterKind || opts.KeyTypes == IterKeyTypeRangesOnly { return imr.wrappableReader.NewMVCCIterator(MVCCKeyIterKind, opts) } return newIntentInterleavingIterator(imr.wrappableReader, opts) diff --git a/pkg/storage/multi_iterator.go b/pkg/storage/multi_iterator.go index 9838adf60ec7..51bbb1f1e38d 100644 --- a/pkg/storage/multi_iterator.go +++ b/pkg/storage/multi_iterator.go @@ -14,6 +14,7 @@ import ( "bytes" "github.com/cockroachdb/cockroach/pkg/keys" + "github.com/cockroachdb/cockroach/pkg/roachpb" ) const invalidIdxSentinel = -1 @@ -92,6 +93,21 @@ func (f *multiIterator) UnsafeValue() []byte { return f.iters[f.currentIdx].UnsafeValue() } +// HasPointAndRange implements SimpleMVCCIterator. +func (f *multiIterator) HasPointAndRange() (bool, bool) { + panic("not implemented") +} + +// RangeBounds implements SimpleMVCCIterator. +func (f *multiIterator) RangeBounds() (roachpb.Key, roachpb.Key) { + panic("not implemented") +} + +// RangeKeys implements SimpleMVCCIterator. +func (f *multiIterator) RangeKeys() []MVCCRangeKey { + panic("not implemented") +} + // Next advances the iterator to the next key/value in the iteration. After this // call, Valid() will be true if the iterator was not positioned at the last // key. diff --git a/pkg/storage/mvcc.go b/pkg/storage/mvcc.go index 55f9fe3074a0..9518c08ce962 100644 --- a/pkg/storage/mvcc.go +++ b/pkg/storage/mvcc.go @@ -148,7 +148,7 @@ func isAbortSpanKey(key roachpb.Key) bool { // updateStatsForInline updates stat counters for an inline value // (abort span entries for example). These are simpler as they don't -// involve intents or multiple versions. +// involve intents, multiple versions, or MVCC range tombstones. func updateStatsForInline( ms *enginepb.MVCCStats, key roachpb.Key, @@ -689,12 +689,45 @@ func (opts *MVCCGetOptions) validate() error { return nil } -func newMVCCIterator(reader Reader, inlineMeta bool, opts IterOptions) MVCCIterator { +// newMVCCIterator sets up a suitable iterator for internal MVCC operations: +// +// * Empty timestamp: inline reads, so disables separated intents. +// +// * IterKeyTypePointsOnly: enables MVCC point tombstone synthesis at MVCC +// range tombstone start keys and around point keys that they overlap. +// +// * Prefix: enables MVCC point tombstone synthesis at the SeekGE key too, +// even if no existing point key exists there. Typically used for e.g. point +// gets, to handle conflict/uncertainty checks of MVCC range tombstones at a +// key that does not have an existing point key. +// +// * rangeKeyMasking: enables range key masking, i.e. hides point keys below an +// MVCC range tombstone below the given timestamp. +func newMVCCIterator( + reader Reader, timestamp hlc.Timestamp, rangeKeyMasking bool, opts IterOptions, +) MVCCIterator { + // Disable separated intents if reading inline. iterKind := MVCCKeyAndIntentsIterKind - if inlineMeta { + if timestamp.IsEmpty() { iterKind = MVCCKeyIterKind } - return reader.NewMVCCIterator(iterKind, opts) + // Synthesize MVCC point tombstones when range keys are disabled. + pointSynthesis := opts.KeyTypes == IterKeyTypePointsOnly + if pointSynthesis { + opts.KeyTypes = IterKeyTypePointsAndRanges + } + // Enable range key masking if requested. + if rangeKeyMasking && opts.RangeKeyMaskingBelow.IsEmpty() { + opts.RangeKeyMaskingBelow = timestamp + } + // Construct basic MVCC iterator. + iter := reader.NewMVCCIterator(iterKind, opts) + // Enable MVCC point tombstone synthesis. If Prefix is enabled, also + // synthesize points around a SeekGE key overlapping a range tombstone. + if pointSynthesis { + iter = newPointSynthesizingIter(iter, opts.Prefix) + } + return iter } // MVCCGet returns the most recent value for the specified key whose timestamp @@ -703,7 +736,10 @@ func newMVCCIterator(reader Reader, inlineMeta bool, opts IterOptions) MVCCItera // // In tombstones mode, if the most recent value is a deletion tombstone, the // result will be a non-nil roachpb.Value whose RawBytes field is nil. -// Otherwise, a deletion tombstone results in a nil roachpb.Value. +// Otherwise, a deletion tombstone results in a nil roachpb.Value. MVCC range +// tombstones will be emitted as synthetic point tombstones (these synthetic +// point tombstones may not be visible to a scan if there is no existing point +// key at this key). // // In inconsistent mode, if an intent is encountered, it will be placed in the // dedicated return parameter. By contrast, in consistent mode, an intent will @@ -724,12 +760,13 @@ func newMVCCIterator(reader Reader, inlineMeta bool, opts IterOptions) MVCCItera func MVCCGet( ctx context.Context, reader Reader, key roachpb.Key, timestamp hlc.Timestamp, opts MVCCGetOptions, ) (*roachpb.Value, *roachpb.Intent, error) { - iter := newMVCCIterator(reader, timestamp.IsEmpty(), IterOptions{Prefix: true}) + iter := newMVCCIterator(reader, timestamp, !opts.Tombstones, IterOptions{Prefix: true}) defer iter.Close() value, intent, err := mvccGet(ctx, iter, key, timestamp, opts) return value.ToPointer(), intent, err } +// TODO(erikgrinaker): all callers must use a pointSynthesizingIter. func mvccGet( ctx context.Context, iter MVCCIterator, @@ -830,6 +867,7 @@ func MVCCGetAsTxn( // mvccGetMetadata returns or reconstructs the meta key for the given key. // A prefix scan using the iterator is performed, resulting in one of the // following successful outcomes: +// // 1) iterator finds nothing; returns (false, 0, 0, nil). // 2) iterator finds an explicit meta key; unmarshals and returns its size. // ok is set to true. @@ -839,48 +877,96 @@ func MVCCGetAsTxn( // that is the usual contribution of the meta key). The value size returned // will be zero, as there is no stored MVCCMetadata. // ok is set to true. -// The passed in MVCCMetadata must not be nil. +// 4) iterator finds an MVCC range tombstone above a value. In this case, +// metadata for a synthetic point tombstone is returned. +// +// The timestamp where the real point key last changed is also returned if a +// real point key was found. This may differ from the metadata timestamp when +// the key is covered by multiple MVCC range tombstones, or when a point +// tombstone is covered by a range tombstone. It is needed to correctly account +// for the GCBytesAge contribution of the key prefix. // -// If the supplied iterator is nil, no seek operation is performed. This is -// used by the Blind{Put,ConditionalPut} operations to avoid seeking when the -// metadata is known not to exist. If iterAlreadyPositioned is true, the -// iterator has already been seeked to metaKey, so a wasteful seek can be -// avoided. +// The passed in MVCCMetadata must not be nil. If the supplied iterator is nil, +// no seek operation is performed. This is used by the Blind{Put,ConditionalPut} +// operations to avoid seeking when the metadata is known not to exist. func mvccGetMetadata( - iter MVCCIterator, metaKey MVCCKey, iterAlreadyPositioned bool, meta *enginepb.MVCCMetadata, -) (ok bool, keyBytes, valBytes int64, err error) { + iter MVCCIterator, metaKey MVCCKey, meta *enginepb.MVCCMetadata, +) (ok bool, keyBytes, valBytes int64, realKeyChanged hlc.Timestamp, err error) { if iter == nil { - return false, 0, 0, nil - } - if !iterAlreadyPositioned { - iter.SeekGE(metaKey) + return false, 0, 0, hlc.Timestamp{}, nil } + iter.SeekGE(metaKey) if ok, err := iter.Valid(); !ok { - return false, 0, 0, err + return false, 0, 0, hlc.Timestamp{}, err } unsafeKey := iter.UnsafeKey() if !unsafeKey.Key.Equal(metaKey.Key) { - return false, 0, 0, nil + return false, 0, 0, hlc.Timestamp{}, nil } + hasPoint, hasRange := iter.HasPointAndRange() - if !unsafeKey.IsValue() { + // Check for existing intent metadata. Intents will be emitted colocated with + // a covering range key when seeking to it, and always located above range + // keys, so we don't need to check for range keys here. + if hasPoint && !unsafeKey.IsValue() { if err := iter.ValueProto(meta); err != nil { - return false, 0, 0, err + return false, 0, 0, hlc.Timestamp{}, err } - return true, int64(unsafeKey.EncodedSize()), - int64(len(iter.UnsafeValue())), nil + return true, int64(unsafeKey.EncodedSize()), int64(len(iter.UnsafeValue())), + meta.Timestamp.ToTimestamp(), nil } + // Synthesize metadata for a point key. meta.Reset() - // For values, the size of keys is always accounted for as - // MVCCVersionTimestampSize. The size of the metadata key is - // accounted for separately. meta.KeyBytes = MVCCVersionTimestampSize + + // If we land on a (bare) range key, step to look for a colocated point key. + if hasRange && !hasPoint { + rkTimestamp := iter.RangeKeys()[0].Timestamp + + iter.Next() + if ok, err := iter.Valid(); err != nil { + return false, 0, 0, hlc.Timestamp{}, err + } else if ok { + // NB: For !ok, hasPoint is already false. + hasPoint, hasRange = iter.HasPointAndRange() + unsafeKey = iter.UnsafeKey() + } + // If only a bare range tombstone was found at the seek key, synthesize + // point tombstone metadata for it. realKeyChanged is empty since there + // was no real point key here. + if !hasPoint || !unsafeKey.Key.Equal(metaKey.Key) { + meta.Deleted = true + meta.Timestamp = rkTimestamp.ToLegacyTimestamp() + return true, int64(EncodedMVCCKeyPrefixLength(metaKey.Key)), 0, hlc.Timestamp{}, nil + } + } + + // We're now on a point key. Check if it's covered by an MVCC range tombstone, + // and synthesize point tombstone metadata for it in that case. realKeyChanged + // is set to the timestamp where the point key ceased to exist -- either the + // lowest range tombstone above the key (not the highest which is used for + // metadata), or the point version's timestamp if it was a tombstone. + if hasRange { + rangeKeys := iter.RangeKeys() + if rk, ok := firstRangeKeyAbove(rangeKeys, unsafeKey.Timestamp); ok { + meta.Deleted = true + meta.Timestamp = rangeKeys[0].Timestamp.ToLegacyTimestamp() + keyLastSeen := rk.Timestamp + if len(iter.UnsafeValue()) == 0 { + keyLastSeen = unsafeKey.Timestamp + } + return true, int64(EncodedMVCCKeyPrefixLength(metaKey.Key)), 0, keyLastSeen, nil + } + } + + // Synthesize metadata for a regular point key. meta.ValBytes = int64(len(iter.UnsafeValue())) meta.Deleted = meta.ValBytes == 0 meta.Timestamp = unsafeKey.Timestamp.ToLegacyTimestamp() - return true, int64(unsafeKey.EncodedSize()) - meta.KeyBytes, 0, nil + + return true, int64(EncodedMVCCKeyPrefixLength(metaKey.Key)), 0, unsafeKey.Timestamp, nil } // putBuffer holds pointer data needed by mvccPutInternal. Bundling @@ -995,7 +1081,10 @@ func MVCCPut( var iter MVCCIterator blind := ms == nil && timestamp.IsEmpty() if !blind { - iter = rw.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{Prefix: true}) + iter = newMVCCIterator(rw, timestamp, false /* rangeKeyMasking */, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + Prefix: true, + }) defer iter.Close() } return mvccPutUsingIter(ctx, rw, iter, ms, key, timestamp, value, txn, nil /* valueFn */) @@ -1037,7 +1126,10 @@ func MVCCDelete( timestamp hlc.Timestamp, txn *roachpb.Transaction, ) error { - iter := newMVCCIterator(rw, timestamp.IsEmpty(), IterOptions{Prefix: true}) + iter := newMVCCIterator(rw, timestamp, false /* rangeKeyMasking */, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + Prefix: true, + }) defer iter.Close() return mvccPutUsingIter(ctx, rw, iter, ms, key, timestamp, noValue, txn, nil /* valueFn */) @@ -1246,6 +1338,10 @@ func replayTransactionalWrite( // and vice versa. valueFn can delete by returning nil. Returning // []byte{} will write an empty value, not delete. // +// The given iter must surface range keys to correctly account for +// MVCC range tombstones in MVCC stats. It is not sufficient to pass a +// pointSynthesizingIter, as constructed by newMVCCIterator by default. +// // Note that, when writing transactionally, the txn's timestamps // dictate the timestamp of the operation, and the timestamp parameter // is redundant. Specifically, the intent is written at the txn's @@ -1282,13 +1378,19 @@ func mvccPutInternal( return errors.Errorf("cannot write to %q at timestamp %s", key, timestamp) } + // Fetch or construct synthetic MVCC metadata. This must use an iterator which + // surfaces range keys, to correctly account for MVCC range tombstones in stats. metaKey := MakeMVCCMetadataKey(key) - ok, origMetaKeySize, origMetaValSize, err := - mvccGetMetadata(iter, metaKey, false /* iterAlreadyPositioned */, &buf.meta) + ok, origMetaKeySize, origMetaValSize, origRealKeyChanged, err := + mvccGetMetadata(iter, metaKey, &buf.meta) if err != nil { return err } + // TODO(erikgrinaker): Iterator use below (e.g. mvccGet) must handle MVCC + // range tombstones. This will be implemented using a point synthesizing + // iterator. + // Verify we're not mixing inline and non-inline values. putIsInline := timestamp.IsEmpty() if ok && putIsInline != buf.meta.IsInline() { @@ -1297,6 +1399,9 @@ func mvccPutInternal( } // Handle inline put. No IntentHistory is required for inline writes // as they aren't allowed within transactions. + // + // MVCC range tombstones cannot exist across inline values, so we don't need + // any special handling for them here. if putIsInline { if txn != nil { return errors.Errorf("%q: inline writes not allowed within transactions", metaKey) @@ -1361,6 +1466,8 @@ func mvccPutInternal( meta = &buf.meta metaTimestamp := meta.Timestamp.ToTimestamp() + // Handle intents. MVCC range tombstones should not require any special + // handling, since they cannot be transactional. if meta.Txn != nil { // There is an uncommitted write intent. if txn == nil || meta.Txn.ID != txn.ID { @@ -1622,6 +1729,21 @@ func mvccPutInternal( // Update MVCC stats. if ms != nil { + // Adjust the stats metadata for MVCC range tombstones. The MVCC stats + // update only cares about changes to real point keys, but the above logic + // needs to care about MVCC range tombstones for conflict purposes. + // + // Specifically, if a real point key was covered by a range tombstone, we + // must set meta.Timestamp to the timestamp where the real point key was + // deleted (either by a point tombstone or the lowest range tombstone). If + // there was no real point key, meta must be nil. In all other cases, + // meta.Timestamp will already equal origRealKeyChanged. + if origRealKeyChanged.IsEmpty() { + meta = nil // no real point key was found + } + if meta != nil { + meta.Timestamp = origRealKeyChanged.ToLegacyTimestamp() + } ms.Add(updateStatsOnPut(key, prevValSize, origMetaKeySize, origMetaValSize, metaKeySize, metaValSize, meta, newMeta)) } @@ -1659,7 +1781,10 @@ func MVCCIncrement( txn *roachpb.Transaction, inc int64, ) (int64, error) { - iter := newMVCCIterator(rw, timestamp.IsEmpty(), IterOptions{Prefix: true}) + iter := newMVCCIterator(rw, timestamp, false /* rangeKeyMasking */, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + Prefix: true, + }) defer iter.Close() var int64Val int64 @@ -1731,7 +1856,10 @@ func MVCCConditionalPut( allowIfDoesNotExist CPutMissingBehavior, txn *roachpb.Transaction, ) error { - iter := newMVCCIterator(rw, timestamp.IsEmpty(), IterOptions{Prefix: true}) + iter := newMVCCIterator(rw, timestamp, false /* rangeKeyMasking */, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + Prefix: true, + }) defer iter.Close() return mvccConditionalPutUsingIter(ctx, rw, iter, ms, key, timestamp, value, expVal, allowIfDoesNotExist, txn) @@ -1809,7 +1937,10 @@ func MVCCInitPut( failOnTombstones bool, txn *roachpb.Transaction, ) error { - iter := newMVCCIterator(rw, timestamp.IsEmpty(), IterOptions{Prefix: true}) + iter := newMVCCIterator(rw, timestamp, false /* rangeKeyMasking */, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + Prefix: true, + }) defer iter.Close() return mvccInitPutUsingIter(ctx, rw, iter, ms, key, timestamp, value, failOnTombstones, txn) } @@ -1886,6 +2017,8 @@ func (m mvccKeyFormatter) Format(f fmt.State, c rune) { // concatenates undifferentiated byte slice values, and efficiently // combines time series observations if the roachpb.Value tag value // indicates the value byte slice is of type TIMESERIES. +// +// TODO(erikgrinaker): Needs to handle MVCC range tombstones. func MVCCMerge( _ context.Context, rw ReadWriter, @@ -1946,6 +2079,8 @@ func MVCCMerge( // // If the underlying iterator encounters an intent with a timestamp in the span // (startTime, endTime], or any inline meta, this method will return an error. +// +// TODO(erikgrinaker): This needs to handle MVCC range tombstones (stats too). func MVCCClearTimeRange( _ context.Context, rw ReadWriter, @@ -2184,7 +2319,10 @@ func MVCCDeleteRange( buf := newPutBuffer() defer buf.release() - iter := newMVCCIterator(rw, timestamp.IsEmpty(), IterOptions{Prefix: true}) + iter := newMVCCIterator(rw, timestamp, false /* rangeKeyMasking */, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + Prefix: true, + }) defer iter.Close() var keys []roachpb.Key @@ -2202,6 +2340,247 @@ func MVCCDeleteRange( return keys, res.ResumeSpan, res.NumKeys, nil } +// ExperimentalMVCCDeleteRangeUsingTombstone deletes the given MVCC keyspan at +// the given timestamp using a range tombstone (rather than point tombstones). +// This operation is non-transactional, but will check for existing intents and +// return a WriteIntentError containing up to maxIntents intents. +// +// The leftPeekBound and rightPeekBound parameters are used when looking for +// range tombstones that we'll merge or overlap with. These are provided to +// prevent the command from reading outside of the CRDB range bounds and latch +// bounds. nil means no bounds. +// +// This method is EXPERIMENTAL: range keys are under active development, and +// have severe limitations including being ignored by all KV and MVCC APIs and +// only being stored in memory. +func ExperimentalMVCCDeleteRangeUsingTombstone( + ctx context.Context, + rw ReadWriter, + ms *enginepb.MVCCStats, + startKey, endKey roachpb.Key, + timestamp hlc.Timestamp, + leftPeekBound, rightPeekBound roachpb.Key, + maxIntents int64, +) error { + // Validate the range key. We must do this first, to catch e.g. any bound violations. + rangeKey := MVCCRangeKey{StartKey: startKey, EndKey: endKey, Timestamp: timestamp} + if err := rangeKey.Validate(); err != nil { + return err + } + + // Check for any overlapping intents, and return them to be resolved. + if intents, err := ScanIntents(ctx, rw, startKey, endKey, maxIntents, 0); err != nil { + return err + } else if len(intents) > 0 { + return &roachpb.WriteIntentError{Intents: intents} + } + + // Forward the (empty) stats time to the deletion timestamp first, making the + // range tombstone's own GCBytesAge contributions 0 at this timestamp. + if ms != nil { + ms.Forward(timestamp.WallTime) + } + + // First, set up an iterator covering only the range key span itself, and scan + // it to find conflicts and update MVCC stats within it. + // + // TODO(erikgrinaker): This introduces an O(n) read penalty. We should + // optimize it, in particular by making this optional in cases where we're + // deleting an entire range and the stats can be computed without the scan. + iter := rw.NewMVCCIterator(MVCCKeyIterKind, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + LowerBound: startKey, + UpperBound: endKey, + RangeKeyMaskingBelow: timestamp, + }) + defer iter.Close() + + iter.SeekGE(MVCCKey{Key: startKey}) + prevRangeEnd := startKey.Clone() + for { + if ok, err := iter.Valid(); err != nil { + return err + } else if !ok { + break + } + + hasPoint, hasRange := iter.HasPointAndRange() + + if hasPoint { + // Check for conflict with newer point key. + key, value := iter.UnsafeKey(), iter.UnsafeValue() + if timestamp.LessEq(key.Timestamp) { + return roachpb.NewWriteTooOldError(timestamp, key.Timestamp.Next(), key.Key.Clone()) + } + if key.Timestamp.IsEmpty() { + return errors.Errorf("can't write range tombstone across inline key %s", key) + } + + // Update stats for the covered point key, if it was a live key. + if ms != nil && len(value) > 0 { + ms.LiveCount-- + ms.LiveBytes -= int64(key.EncodedSize()) + int64(len(value)) + } + } + + if hasRange { + // Check if we've encountered a new range key stack. + if rangeStart, rangeEnd := iter.RangeBounds(); !rangeEnd.Equal(prevRangeEnd) { + latest := iter.RangeKeys()[0] + + // Check for conflict with newer range key. + if timestamp.LessEq(latest.Timestamp) { + return roachpb.NewWriteTooOldError( + timestamp, latest.Timestamp.Next(), latest.StartKey.Clone()) + } + + if ms != nil { + // If the encountered range key does not abut the previous range key, + // we'll write a new range key fragment in the gap between them. It + // has no GCBytesAge contribution because it's written at now. + if !rangeStart.Equal(prevRangeEnd) { + ms.RangeKeyCount++ + ms.RangeKeyBytes += int64(EncodedMVCCTimestampSuffixLength(timestamp) + + EncodedMVCCKeyPrefixLength(prevRangeEnd) + EncodedMVCCKeyPrefixLength(rangeStart)) + } + // This range key will create a new version in the current fragment + // stack. It will also move the GCBytesAge contribution of the key + // bounds up from the latest existing range key to this one. + ms.RangeKeyBytes += int64(EncodedMVCCTimestampSuffixLength(timestamp)) + ms.GCBytesAge -= (timestamp.WallTime/1e9 - latest.Timestamp.WallTime/1e9) * + int64(EncodedMVCCKeyPrefixLength(rangeStart)+EncodedMVCCKeyPrefixLength(rangeEnd)) + } + + prevRangeEnd = append(prevRangeEnd[:0], rangeEnd...) + } + } + + iter.NextKey() + } + + // Once we've iterated across the range key span, fill in the final gap + // between the previous existing range key fragment and the end of the range + // key if any. If no existing fragments were found during iteration above, + // this will be the entire new range key. + if ms != nil && !prevRangeEnd.Equal(endKey) { + ms.RangeKeyCount++ + ms.RangeKeyBytes += int64(EncodedMVCCTimestampSuffixLength(timestamp) + + EncodedMVCCKeyPrefixLength(prevRangeEnd) + EncodedMVCCKeyPrefixLength(endKey)) + } + + // Check if the range key will merge with or fragment any existing range keys + // at the bounds. + // + // TODO(erikgrinaker): This could be merged into the scan above to avoid the + // additional seeks. But we do the simple and correct thing for now and leave + // optimizations for later. + if ms != nil { + // fragmentRangeKeys adjusts ms to fragment an existing range key stack + // at the given split point. + fragmentRangeKeys := func(rangeKeys []MVCCRangeKey, splitKey roachpb.Key) { + for i, rk := range rangeKeys { + keyBytes := int64(EncodedMVCCTimestampSuffixLength(rk.Timestamp)) + if i == 0 { + ms.RangeKeyCount++ + keyBytes += 2 * int64(EncodedMVCCKeyPrefixLength(splitKey)) + } + ms.RangeKeyBytes += keyBytes + ms.GCBytesAge += keyBytes * (timestamp.WallTime/1e9 - rk.Timestamp.WallTime/1e9) + } + } + + // maybeMergeRangeKeys adjusts ms to merge two abutting range key + // stacks if they have the same timestamps. It assumes the lhs end key + // equals the rhs start key, and that they are in descending order. + maybeMergeRangeKeys := func(lhs, rhs []MVCCRangeKey) { + if len(lhs) != len(rhs) || len(lhs) == 0 { + return + } + for i, l := range lhs { + if !l.Timestamp.Equal(rhs[i].Timestamp) { + return + } + } + mergeKey := rhs[0].StartKey + for i, rk := range lhs { + keyBytes := int64(EncodedMVCCTimestampSuffixLength(rk.Timestamp)) + if i == 0 { + ms.RangeKeyCount-- + keyBytes += 2 * int64(EncodedMVCCKeyPrefixLength(mergeKey)) + } + ms.RangeKeyBytes -= keyBytes + ms.GCBytesAge -= keyBytes * (timestamp.WallTime/1e9 - rk.Timestamp.WallTime/1e9) + } + } + + // Peek to the left. + if !leftPeekBound.Equal(startKey) { + iter := rw.NewMVCCIterator(MVCCKeyIterKind, IterOptions{ + KeyTypes: IterKeyTypeRangesOnly, + LowerBound: leftPeekBound, + UpperBound: startKey.Next(), + }) + defer iter.Close() + iter.SeekLT(MVCCKey{Key: startKey}) + if ok, err := iter.Valid(); err != nil { + return err + } else if ok { + _, rangeEnd := iter.RangeBounds() + switch rangeEnd.Compare(startKey) { + case 1: // fragment + fragmentRangeKeys(iter.RangeKeys(), startKey) + case 0: // merge + lhs := iter.RangeKeys() + rhs := []MVCCRangeKey{rangeKey} + iter.SeekGE(MVCCKey{Key: startKey}) + if ok, err := iter.Valid(); err != nil { + return err + } else if ok { + rhs = append(rhs, iter.RangeKeys()...) + } + maybeMergeRangeKeys(lhs, rhs) + } + } + } + + // Peek to the right. + if rightPeekBound == nil { + rightPeekBound = keys.MaxKey + } + if !rightPeekBound.Equal(endKey) { + iter := rw.NewMVCCIterator(MVCCKeyIterKind, IterOptions{ + KeyTypes: IterKeyTypeRangesOnly, + LowerBound: endKey.Prevish(roachpb.PrevishKeyLength), + UpperBound: rightPeekBound, + }) + defer iter.Close() + iter.SeekGE(MVCCKey{Key: endKey}) + if ok, err := iter.Valid(); err != nil { + return err + } else if ok { + rangeStart, _ := iter.RangeBounds() + switch rangeStart.Compare(endKey) { + case -1: // fragment + fragmentRangeKeys(iter.RangeKeys(), endKey) + case 0: // merge + lhs := []MVCCRangeKey{rangeKey} + rhs := iter.RangeKeys() + iter.SeekLT(MVCCKey{Key: endKey}) + if ok, err := iter.Valid(); err != nil { + return err + } else if ok { + lhs = append(lhs, iter.RangeKeys()...) + } + maybeMergeRangeKeys(lhs, rhs) + } + } + } + } + + // Write the tombstone. + return rw.ExperimentalPutMVCCRangeKey(rangeKey) +} + func recordIteratorStats(traceSpan *tracing.Span, iteratorStats IteratorStats) { stats := iteratorStats.Stats if traceSpan != nil { @@ -2463,7 +2842,10 @@ type MVCCScanResult struct { // In tombstones mode, if the most recent value for a key is a deletion // tombstone, the scan result will contain a roachpb.KeyValue for that key whose // RawBytes field is nil. Otherwise, the key-value pair will be omitted from the -// result entirely. +// result entirely. For MVCC range tombstones, synthetic point tombstones are +// returned at the start of the range tombstone and when then overlap a point +// key (note that this may emit spurious point tombstones at the MVCCScan start +// key if it truncates an MVCC range tombstone). // // When scanning inconsistently, any encountered intents will be placed in the // dedicated result parameter. By contrast, when scanning consistently, any @@ -2486,7 +2868,10 @@ func MVCCScan( timestamp hlc.Timestamp, opts MVCCScanOptions, ) (MVCCScanResult, error) { - iter := newMVCCIterator(reader, timestamp.IsEmpty(), IterOptions{LowerBound: key, UpperBound: endKey}) + iter := newMVCCIterator(reader, timestamp, !opts.Tombstones, IterOptions{ + LowerBound: key, + UpperBound: endKey, + }) defer iter.Close() return mvccScanToKvs(ctx, iter, key, endKey, timestamp, opts) } @@ -2499,7 +2884,10 @@ func MVCCScanToBytes( timestamp hlc.Timestamp, opts MVCCScanOptions, ) (MVCCScanResult, error) { - iter := newMVCCIterator(reader, timestamp.IsEmpty(), IterOptions{LowerBound: key, UpperBound: endKey}) + iter := newMVCCIterator(reader, timestamp, !opts.Tombstones, IterOptions{ + LowerBound: key, + UpperBound: endKey, + }) defer iter.Close() return mvccScanToBytes(ctx, iter, key, endKey, timestamp, opts) } @@ -2533,7 +2921,9 @@ func MVCCScanAsTxn( // the reverse flag is set, the iterator will be moved in reverse order. If the // scan options specify an inconsistent scan, all "ignored" intents will be // returned. In consistent mode, intents are only ever returned as part of a -// WriteIntentError. +// WriteIntentError. In Tombstones mode, MVCC range tombstones are emitted as +// synthetic point tombstones around overlapping point keys and the start of +// MVCC range tombstones. func MVCCIterate( ctx context.Context, reader Reader, @@ -2542,8 +2932,10 @@ func MVCCIterate( opts MVCCScanOptions, f func(roachpb.KeyValue) error, ) ([]roachpb.Intent, error) { - iter := newMVCCIterator( - reader, timestamp.IsEmpty(), IterOptions{LowerBound: key, UpperBound: endKey}) + iter := newMVCCIterator(reader, timestamp, !opts.Tombstones, IterOptions{ + LowerBound: key, + UpperBound: endKey, + }) defer iter.Close() var intents []roachpb.Intent @@ -2617,7 +3009,10 @@ func MVCCResolveWriteIntent( return false, errors.Errorf("can't resolve range intent as point intent") } - iterAndBuf := GetBufUsingIter(rw.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{Prefix: true})) + iterAndBuf := GetBufUsingIter(rw.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + Prefix: true, + })) iterAndBuf.iter.SeekIntentGE(intent.Key, intent.Txn.ID) ok, err := mvccResolveWriteIntent(ctx, rw, iterAndBuf.iter, ms, intent, iterAndBuf.buf) // Using defer would be more convenient, but it is measurably slower. @@ -2632,14 +3027,53 @@ func unsafeNextVersion(iter MVCCIterator, latestKey MVCCKey) (MVCCKey, []byte, b // Compute the next possible mvcc value for this key. nextKey := latestKey.Next() iter.SeekGE(nextKey) - if ok, err := iter.Valid(); err != nil || !ok || !iter.UnsafeKey().Key.Equal(latestKey.Key) { return MVCCKey{}, nil, false /* never ok */, err } unsafeKey := iter.UnsafeKey() + + // If we land on a range key, find the first matching timestamp. + // + // TODO(erikgrinaker): This should be handled with a point synthesizing + // iterator. + var rkTimestamp hlc.Timestamp + if hasPoint, hasRange := iter.HasPointAndRange(); hasRange { + for _, rk := range iter.RangeKeys() { + if rk.Timestamp.Less(latestKey.Timestamp) { + rkTimestamp = rk.Timestamp + break + } + } + // If this is a bare range key, step forward to look for a point key. + if !hasPoint { + iter.Next() + ok, err := iter.Valid() + if err != nil { + return MVCCKey{}, nil, false, err + } else if ok { + unsafeKey = iter.UnsafeKey() + ok, _ = iter.HasPointAndRange() + ok = ok && unsafeKey.Key.Equal(latestKey.Key) + } + // If we didn't find a point key colocated with the range key, + // then return the matching range tombstone if any. + if !ok { + if rkTimestamp.IsSet() { + return MVCCKey{Key: latestKey.Key, Timestamp: rkTimestamp}, nil, true, nil + } + return MVCCKey{}, nil, false, nil + } + } + } + + // We now have a point key and possibly a matching range key. If the range + // key covers the point key, return it. Otherwise, return the point key. if !unsafeKey.IsValue() { return MVCCKey{}, nil, false, errors.Errorf("expected an MVCC value key: %s", unsafeKey) } + if rkTimestamp.IsSet() && unsafeKey.Timestamp.LessEq(rkTimestamp) { + return MVCCKey{Key: unsafeKey.Key, Timestamp: rkTimestamp}, nil, true, nil + } return unsafeKey, iter.UnsafeValue(), true, nil } @@ -2654,11 +3088,13 @@ func unsafeNextVersion(iter MVCCIterator, latestKey MVCCKey) (MVCCKey, []byte, b // to SeekGE. type iterForKeyVersions interface { Valid() (bool, error) + HasPointAndRange() (bool, bool) SeekGE(key MVCCKey) Next() UnsafeKey() MVCCKey UnsafeValue() []byte ValueProto(msg protoutil.Message) error + RangeKeys() []MVCCRangeKey } // separatedIntentAndVersionIter is an implementation of iterForKeyVersions @@ -2726,6 +3162,18 @@ func (s *separatedIntentAndVersionIter) Valid() (bool, error) { return s.engineIterValid, s.engineIterErr } +func (s *separatedIntentAndVersionIter) HasPointAndRange() (bool, bool) { + hasPoint, hasRange := s.mvccIter.HasPointAndRange() + if !s.atMVCCIter { + hasPoint = s.engineIterValid + } + return hasPoint, hasRange +} + +func (s *separatedIntentAndVersionIter) RangeKeys() []MVCCRangeKey { + return s.mvccIter.RangeKeys() +} + func (s *separatedIntentAndVersionIter) SeekGE(key MVCCKey) { if !key.IsValue() { panic(errors.AssertionFailedf("SeekGE only permitted for values")) @@ -2779,6 +3227,9 @@ func mvccGetIntent( if ok, err := iter.Valid(); !ok { return false, 0, 0, err } + if hasPoint, _ := iter.HasPointAndRange(); !hasPoint { + return false, 0, 0, nil + } unsafeKey := iter.UnsafeKey() if !unsafeKey.Key.Equal(metaKey.Key) { return false, 0, 0, nil @@ -2889,6 +3340,7 @@ func (h singleDelOptimizationHelper) onAbortIntent() bool { // mvccResolveWriteIntent is the core logic for resolving an intent. // REQUIRES: iter is already seeked to intent.Key. +// REQUIRES: iter surfaces range keys via IterKeyTypePointsAndRanges. // Returns whether an intent was found and resolved, false otherwise. func mvccResolveWriteIntent( ctx context.Context, @@ -3068,9 +3520,20 @@ func mvccResolveWriteIntent( // Rewrite the versioned value at the new timestamp. iter.SeekGE(oldKey) - if valid, err := iter.Valid(); err != nil { + valid, err := iter.Valid() + if err != nil { return false, err - } else if !valid || !iter.UnsafeKey().Equal(oldKey) { + } + if hasPoint, hasRange := iter.HasPointAndRange(); hasRange && !hasPoint { + // If the seek lands on a bare range key, attempt to step to a point. + iter.Next() + if valid, err = iter.Valid(); err != nil { + return false, err + } else if valid { + valid, _ = iter.HasPointAndRange() + } + } + if !valid || !iter.UnsafeKey().Equal(oldKey) { return false, errors.Errorf("existing intent value missing: %s", oldKey) } value := iter.UnsafeValue() @@ -3096,12 +3559,18 @@ func mvccResolveWriteIntent( // have to read that version's size. // // Look for the first real versioned key, i.e. the key just below - // the (old) meta's timestamp. + // the (old) meta's timestamp, and for any MVCC range tombstones. iter.Next() if valid, err := iter.Valid(); err != nil { return false, err - } else if valid && iter.UnsafeKey().Key.Equal(oldKey.Key) { - prevValSize = int64(len(iter.UnsafeValue())) + } else if valid { + if hasPoint, hasRange := iter.HasPointAndRange(); hasPoint { + if unsafeKey := iter.UnsafeKey(); unsafeKey.Key.Equal(oldKey.Key) { + if !hasRange || iter.RangeKeys()[0].Timestamp.Less(unsafeKey.Timestamp) { + prevValSize = int64(len(iter.UnsafeValue())) + } + } + } } } @@ -3148,20 +3617,27 @@ func mvccResolveWriteIntent( Key: intent.Key, }) - nextKey := latestKey.Next() ok = false var unsafeNextKey MVCCKey var unsafeNextValue []byte - if nextKey.IsValue() { + if nextKey := latestKey.Next(); nextKey.IsValue() { // The latestKey was not the smallest possible timestamp {WallTime: 0, // Logical: 1}. Practically, this is the only case that will occur in // production. iter.SeekGE(nextKey) - ok, err = iter.Valid() - if err != nil { + if ok, err = iter.Valid(); err != nil { return false, err } - if ok && iter.UnsafeKey().Key.Equal(latestKey.Key) { + // If the seek lands on a bare range key, attempt to step to a point. + if hasPoint, hasRange := iter.HasPointAndRange(); hasRange && !hasPoint { + iter.Next() + if ok, err = iter.Valid(); err != nil { + return false, err + } else if ok { + ok, _ = iter.HasPointAndRange() + } + } + if ok = ok && iter.UnsafeKey().Key.Equal(latestKey.Key); ok { unsafeNextKey = iter.UnsafeKey() if !unsafeNextKey.IsValue() { // Should never see an intent for this key since we seeked to a @@ -3169,8 +3645,15 @@ func mvccResolveWriteIntent( return false, errors.Errorf("expected an MVCC value key: %s", unsafeNextKey) } unsafeNextValue = iter.UnsafeValue() - } else { - ok = false + // If a non-tombstone point key is covered by a range tombstone, then + // synthesize a point tombstone at the lowest range tombstone covering it. + // This is where the point key ceases to exist, contributing to GCBytesAge. + if len(unsafeNextValue) > 0 { + if rk, found := firstRangeKeyAbove(iter.RangeKeys(), unsafeNextKey.Timestamp); found { + unsafeNextKey.Timestamp = rk.Timestamp + unsafeNextValue = nil + } + } } iter = nil // prevent accidental use below } @@ -3326,8 +3809,11 @@ func MVCCResolveWriteIntentRange( ltStart, _ := keys.LockTableSingleKey(intent.Key, nil) ltEnd, _ := keys.LockTableSingleKey(intent.EndKey, nil) engineIter := rw.NewEngineIterator(IterOptions{LowerBound: ltStart, UpperBound: ltEnd}) - iterAndBuf := - GetBufUsingIter(rw.NewMVCCIterator(MVCCKeyIterKind, IterOptions{UpperBound: intent.EndKey})) + iterAndBuf := GetBufUsingIter(rw.NewMVCCIterator(MVCCKeyIterKind, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + LowerBound: intent.Key, + UpperBound: intent.EndKey, + })) defer func() { engineIter.Close() iterAndBuf.Cleanup() @@ -3342,6 +3828,9 @@ func MVCCResolveWriteIntentRange( // only step the iterator and not seek. sepIter.seekEngineKeyGE(EngineKey{Key: ltStart}) } else { + // TODO(erikgrinaker): We don't handle MVCC range keys here, because this + // code path will shortly be removed. See: + // https://github.com/cockroachdb/cockroach/pull/81063 iterAndBuf := GetIterAndBuf(rw, IterOptions{UpperBound: intent.EndKey}) defer iterAndBuf.Cleanup() putBuf = iterAndBuf.buf @@ -3447,6 +3936,8 @@ func MVCCResolveWriteIntentRange( // not a mix of the two. This is to accommodate the implementation below // that creates an iterator with bounds that span from the first to last // key (in sorted order). +// +// TODO(erikgrinaker): This must handle MVCC range tombstones. func MVCCGarbageCollect( ctx context.Context, rw ReadWriter, @@ -3487,8 +3978,7 @@ func MVCCGarbageCollect( meta := &enginepb.MVCCMetadata{} for _, gcKey := range keys { encKey := MakeMVCCMetadataKey(gcKey.Key) - ok, metaKeySize, metaValSize, err := - mvccGetMetadata(iter, encKey, false /* iterAlreadyPositioned */, meta) + ok, metaKeySize, metaValSize, _, err := mvccGetMetadata(iter, encKey, meta) if err != nil { return err } @@ -3762,28 +4252,43 @@ func willOverflow(a, b int64) bool { return math.MinInt64-b > a } -// ComputeStatsForRange scans the underlying engine from start to end keys and -// computes stats counters based on the values. This method is used after a -// range is split to recompute stats for each subrange. The nowNanos arg -// specifies the wall time in nanoseconds since the epoch and is used to compute -// the total age of all intents. +// ComputeStatsForRange scans the iterator from start to end keys and computes +// stats counters based on the values. This method is used after a range is +// split to recompute stats for each subrange. The nowNanos arg specifies the +// wall time in nanoseconds since the epoch and is used to compute the total age +// of all intents. +// +// To account for intents and range keys, the iterator must be created with +// MVCCKeyAndIntentsIterKind and IterKeyTypePointsAndRanges. To correctly +// account for range key truncation bounds, the iterator must have an +// appropriate UpperBound and LowerBound. // -// When optional callbacks are specified, they are invoked for each physical +// TODO(erikgrinaker): Consider removing the start,end parameters, forcing the +// caller to set appropriate bounds on the iterator instead. +func ComputeStatsForRange( + iter SimpleMVCCIterator, start, end roachpb.Key, nowNanos int64, +) (enginepb.MVCCStats, error) { + return ComputeStatsForRangeWithVisitors(iter, start, end, nowNanos, nil, nil) +} + +// ComputeStatsForRangeWithVisitors is like ComputeStatsForRange, but also +// takes a point and/or range key callback that is invoked for each physical // key-value pair (i.e. not for implicit meta records), and iteration is aborted -// on the first error returned from any of them. +// on the first error returned from either of them. // // Callbacks must copy any data they intend to hold on to. -func ComputeStatsForRange( +func ComputeStatsForRangeWithVisitors( iter SimpleMVCCIterator, start, end roachpb.Key, nowNanos int64, - callbacks ...func(MVCCKey, []byte) error, + pointKeyVisitor func(MVCCKey, []byte) error, + rangeKeyVisitor func(MVCCRangeKey) error, ) (enginepb.MVCCStats, error) { var ms enginepb.MVCCStats // Only some callers are providing an MVCCIterator. The others don't have // any intents. var meta enginepb.MVCCMetadata - var prevKey []byte + var prevKey, prevRangeStart []byte first := false // Values start accruing GCBytesAge at the timestamp at which they @@ -3793,22 +4298,60 @@ func ComputeStatsForRange( // of the point in time at which the current key begins to age. var accrueGCAgeNanos int64 mvccEndKey := MakeMVCCMetadataKey(end) + rangeKeys := []MVCCRangeKey{} - iter.SeekGE(MakeMVCCMetadataKey(start)) - for ; ; iter.Next() { - ok, err := iter.Valid() - if err != nil { + for iter.SeekGE(MakeMVCCMetadataKey(start)); ; iter.Next() { + if ok, err := iter.Valid(); err != nil { return ms, err - } - if !ok || !iter.UnsafeKey().Less(mvccEndKey) { + } else if !ok || !iter.UnsafeKey().Less(mvccEndKey) { break } + hasPoint, hasRange := iter.HasPointAndRange() + + if hasRange { + if rangeStart, _ := iter.RangeBounds(); !bytes.Equal(rangeStart, prevRangeStart) { + prevRangeStart = append(prevRangeStart[:0], rangeStart...) + rangeKeys = iter.RangeKeys() + + for i, rk := range rangeKeys { + // Only the top-most fragment contributes the key and its bounds, but + // all versions contribute timestamps. + // + // NB: Point keys always use 12 bytes for the key timestamp, even + // though it is actually variable-length, likely for historical + // reasons. But for range keys we may as well use the actual + // variable-length encoded size. + var keyBytes int64 + if i == 0 { + ms.RangeKeyCount++ + keyBytes += int64(EncodedMVCCKeyPrefixLength(rk.StartKey) + + EncodedMVCCKeyPrefixLength(rk.EndKey)) + } + keyBytes += int64(EncodedMVCCTimestampSuffixLength(rk.Timestamp)) + ms.RangeKeyBytes += keyBytes + ms.GCBytesAge += keyBytes * (nowNanos/1e9 - rk.Timestamp.WallTime/1e9) + + if rangeKeyVisitor != nil { + if err := rangeKeyVisitor(rk); err != nil { + return enginepb.MVCCStats{}, err + } + } + } + } + } else if len(rangeKeys) > 0 { + rangeKeys = rangeKeys[:0] + } + + if !hasPoint { + continue + } + unsafeKey := iter.UnsafeKey() unsafeValue := iter.UnsafeValue() - for _, f := range callbacks { - if err := f(unsafeKey, unsafeValue); err != nil { + if pointKeyVisitor != nil { + if err := pointKeyVisitor(unsafeKey, unsafeValue); err != nil { return enginepb.MVCCStats{}, err } } @@ -3835,6 +4378,16 @@ func ComputeStatsForRange( implicitMeta := isValue && !bytes.Equal(unsafeKey.Key, prevKey) prevKey = append(prevKey[:0], unsafeKey.Key...) + // Find the closest range tombstone above the point key. Range tombstones + // cannot exist above intents, and are undefined across inline values, so we + // only take them into account for versioned values. + var nextRangeTombstone hlc.Timestamp + if isValue { + if rk, ok := firstRangeKeyAbove(rangeKeys, unsafeKey.Timestamp); ok { + nextRangeTombstone = rk.Timestamp + } + } + if implicitMeta { // No MVCCMetadata entry for this series of keys. meta.Reset() @@ -3866,12 +4419,16 @@ func ComputeStatsForRange( ms.AbortSpanBytes += totalBytes } } else { - if !meta.Deleted { - ms.LiveBytes += totalBytes - ms.LiveCount++ - } else { + if meta.Deleted { // First value is deleted, so it's GC'able; add meta key & value bytes to age stat. ms.GCBytesAge += totalBytes * (nowNanos/1e9 - meta.Timestamp.WallTime/1e9) + } else if nextRangeTombstone.IsSet() { + // First value was deleted by a range tombstone, so it accumulates GC age from + // the range tombstone's timestamp. + ms.GCBytesAge += totalBytes * (nowNanos/1e9 - nextRangeTombstone.WallTime/1e9) + } else { + ms.LiveBytes += totalBytes + ms.LiveCount++ } ms.KeyBytes += metaKeySize ms.ValBytes += metaValSize @@ -3891,11 +4448,15 @@ func ComputeStatsForRange( } else { if first { first = false - if !meta.Deleted { - ms.LiveBytes += totalBytes - } else { + if meta.Deleted { // First value is deleted, so it's GC'able; add key & value bytes to age stat. ms.GCBytesAge += totalBytes * (nowNanos/1e9 - meta.Timestamp.WallTime/1e9) + } else if nextRangeTombstone.IsSet() { + // First value was deleted by a range tombstone; add key & value bytes to + // age stat from range tombstone onwards. + ms.GCBytesAge += totalBytes * (nowNanos/1e9 - nextRangeTombstone.WallTime/1e9) + } else { + ms.LiveBytes += totalBytes } if meta.Txn != nil { ms.IntentBytes += totalBytes @@ -3918,6 +4479,10 @@ func ComputeStatsForRange( if isTombstone { // The contribution of the tombstone picks up GCByteAge from its own timestamp on. ms.GCBytesAge += totalBytes * (nowNanos/1e9 - unsafeKey.Timestamp.WallTime/1e9) + } else if nextRangeTombstone.IsSet() && nextRangeTombstone.WallTime < accrueGCAgeNanos { + // The kv pair was deleted by a range tombstone below the next + // version, so it accumulates garbage from the range tombstone. + ms.GCBytesAge += totalBytes * (nowNanos/1e9 - nextRangeTombstone.WallTime/1e9) } else { // The kv pair is an overwritten value, so it became non-live when the closest more // recent value was written. diff --git a/pkg/storage/mvcc_history_test.go b/pkg/storage/mvcc_history_test.go index cd3f571bf4e0..df63c9d36bbb 100644 --- a/pkg/storage/mvcc_history_test.go +++ b/pkg/storage/mvcc_history_test.go @@ -13,6 +13,8 @@ package storage import ( "context" "fmt" + "path/filepath" + "regexp" "strconv" "strings" "testing" @@ -28,6 +30,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/storage/enginepb" "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/testutils/skip" + "github.com/cockroachdb/cockroach/pkg/util" "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/leaktest" "github.com/cockroachdb/cockroach/pkg/util/log" @@ -44,6 +47,8 @@ import ( // // The input files use the following DSL: // +// run [ok|trace|stats|error] +// // txn_begin t= [ts=[,]] [globalUncertaintyLimit=[,]] // txn_remove t= // txn_restart t= @@ -53,15 +58,27 @@ import ( // txn_status t= status= // // resolve_intent t= k= [status=] +// resolve_intent_range t= k= end= [status=] // check_intent k= [none] // -// cput [t=] [ts=[,]] [resolve [status=]] k= v= [raw] [cond=] -// del [t=] [ts=[,]] [resolve [status=]] k= -// del_range [t=] [ts=[,]] [resolve [status=]] k= [end=] [max=] [returnKeys] -// get [t=] [ts=[,]] [resolve [status=]] k= [inconsistent] [tombstones] [failOnMoreRecent] [localUncertaintyLimit=[,]] [globalUncertaintyLimit=[,]] -// increment [t=] [ts=[,]] [resolve [status=]] k= [inc=] -// put [t=] [ts=[,]] [resolve [status=]] k= v= [raw] -// scan [t=] [ts=[,]] [resolve [status=]] k= [end=] [inconsistent] [tombstones] [reverse] [failOnMoreRecent] [localUncertaintyLimit=[,]] [globalUncertaintyLimit=[,]] [max=] [targetbytes=] [avoidExcess] [allowEmpty] +// cput [t=] [ts=[,]] [resolve [status=]] k= v= [raw] [cond=] +// del [t=] [ts=[,]] [resolve [status=]] k= +// del_range [t=] [ts=[,]] [resolve [status=]] k= [end=] [max=] [returnKeys] +// del_range_ts [ts=[,]] k= end= +// get [t=] [ts=[,]] [resolve [status=]] k= [inconsistent] [tombstones] [failOnMoreRecent] [localUncertaintyLimit=[,]] [globalUncertaintyLimit=[,]] +// increment [t=] [ts=[,]] [resolve [status=]] k= [inc=] +// put [t=] [ts=[,]] [resolve [status=]] k= v= [raw] +// put_rangekey k= end= ts=[,] +// scan [t=] [ts=[,]] [resolve [status=]] k= [end=] [inconsistent] [tombstones] [reverse] [failOnMoreRecent] [localUncertaintyLimit=[,]] [globalUncertaintyLimit=[,]] [max=] [targetbytes=] [avoidExcess] [allowEmpty] +// +// iter [k=] [end=] [kind=key|keyAndIntents] [types=pointsOnly|pointsWithRanges|pointsAndRanges|rangesOnly] [pointSynthesis [emitOnSeekGE]] [maskBelow=[,]] +// iter_seek_ge k= [ts=[,]] +// iter_seek_lt k= [ts=[,]] +// iter_seek_intent_ge k= txn= +// iter_next +// iter_next_key +// iter_prev +// iter_scan [reverse] // // merge [ts=[,]] k= v= [raw] // @@ -112,8 +129,35 @@ func TestMVCCHistories(t *testing.T) { defer engine.Close() reportDataEntries := func(buf *redact.StringBuilder) error { - hasData := false - err := engine.MVCCIterate(span.Key, span.EndKey, MVCCKeyAndIntentsIterKind, func(r MVCCKeyValue) error { + var hasData bool + + iter := engine.NewMVCCIterator(MVCCKeyIterKind, IterOptions{ + KeyTypes: IterKeyTypeRangesOnly, + LowerBound: span.Key, + UpperBound: span.EndKey, + }) + defer iter.Close() + iter.SeekGE(MVCCKey{Key: span.Key}) + for { + if ok, err := iter.Valid(); err != nil { + return err + } else if !ok { + break + } + hasData = true + start, end := iter.RangeBounds() + buf.Printf("rangekey: %s/[", roachpb.Span{Key: start, EndKey: end}) + for i, rangeKey := range iter.RangeKeys() { + if i > 0 { + buf.Printf(" ") + } + buf.Printf("%s", rangeKey.Timestamp) + } + buf.Printf("]\n") + iter.Next() + } + + err = engine.MVCCIterate(span.Key, span.EndKey, MVCCKeyAndIntentsIterKind, func(r MVCCKeyValue) error { hasData = true if r.Key.Timestamp.IsEmpty() { // Meta is at timestamp zero. @@ -135,6 +179,7 @@ func TestMVCCHistories(t *testing.T) { } e := newEvalCtx(ctx, engine) + defer e.close() datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string { // We'll be overriding cmd/cmdargs below, because the @@ -164,18 +209,15 @@ func TestMVCCHistories(t *testing.T) { // It stops upon the first error encountered, if any. // // Options: - // "trace" means detail each operation in the output. - // "error" means expect an error to occur. The specific error type/ - // message to expect is spelled out in the expected output. + // - trace: detail each operation in the output. + // - stats: emit MVCC statistics at the end (or after each operation + // if "trace" is also enabled). + // - error: expect an error to occur. The specific error type/ message + // to expect is spelled out in the expected output. // - trace := false - if e.hasArg("trace") { - trace = true - } - expectError := false - if e.hasArg("error") { - expectError = true - } + trace := e.hasArg("trace") + stats := e.hasArg("stats") + expectError := e.hasArg("error") // buf will accumulate the actual output, which the // datadriven driver will use to compare to the expected @@ -299,9 +341,27 @@ func TestMVCCHistories(t *testing.T) { _ = buf.WriteByte('\n') } + var msBefore, msAfter enginepb.MVCCStats + if stats { + msBefore = computeStats(e.t, e.engine, keys.LocalMax, keys.MaxKey, 100e9) + } + // Run the command. + e.ms = &enginepb.MVCCStats{} foundErr = cmd.fn(e) + if stats { + if trace { + buf.Printf("stats: %s\n", formatStats(e.ms, true, true)) + } + msAfter = computeStats(e.t, e.engine, keys.LocalMax, keys.MaxKey, 100e9) + msBefore.Add(*e.ms) + if msAfter != msBefore { + buf.Printf("stats-expected: %s\n", &msBefore) + buf.Printf("stats-computed: %s\n", &msAfter) + } + } + if trace { // If tracing is enabled, we report the intermediate results // after each individual step in the script. @@ -324,6 +384,22 @@ func TestMVCCHistories(t *testing.T) { reportResults(txnChange, dataChange) } + // Calculate and output stats if requested. + if stats { + iter := e.engine.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{ + LowerBound: keys.LocalMax, + UpperBound: keys.MaxKey, + KeyTypes: IterKeyTypePointsAndRanges, + }) + defer iter.Close() + + ms, err := ComputeStatsForRange(iter, keys.LocalMax, keys.MaxKey, 1e12) + if err != nil { + e.t.Errorf("failed to compute stats: %s", err) + } + buf.Printf("stats: %s\n", formatStats(&ms, false, false)) + } + signalError := e.t.Errorf if txnChange || dataChange { // We can't recover from an error and continue @@ -392,19 +468,30 @@ var commands = map[string]cmd{ "txn_step": {typTxnUpdate, cmdTxnStep}, "txn_update": {typTxnUpdate, cmdTxnUpdate}, - "resolve_intent": {typDataUpdate, cmdResolveIntent}, - // TODO(nvanbenschoten): test "resolve_intent_range". - "check_intent": {typReadOnly, cmdCheckIntent}, - - "clear_range": {typDataUpdate, cmdClearRange}, - "cput": {typDataUpdate, cmdCPut}, - "del": {typDataUpdate, cmdDelete}, - "del_range": {typDataUpdate, cmdDeleteRange}, - "get": {typReadOnly, cmdGet}, - "increment": {typDataUpdate, cmdIncrement}, - "merge": {typDataUpdate, cmdMerge}, - "put": {typDataUpdate, cmdPut}, - "scan": {typReadOnly, cmdScan}, + "resolve_intent": {typDataUpdate, cmdResolveIntent}, + "resolve_intent_range": {typDataUpdate, cmdResolveIntentRange}, + "check_intent": {typReadOnly, cmdCheckIntent}, + + "clear_range": {typDataUpdate, cmdClearRange}, + "cput": {typDataUpdate, cmdCPut}, + "del": {typDataUpdate, cmdDelete}, + "del_range": {typDataUpdate, cmdDeleteRange}, + "del_range_ts": {typDataUpdate, cmdDeleteRangeTombstone}, + "get": {typReadOnly, cmdGet}, + "increment": {typDataUpdate, cmdIncrement}, + "merge": {typDataUpdate, cmdMerge}, + "put": {typDataUpdate, cmdPut}, + "put_rangekey": {typDataUpdate, cmdPutRangeKey}, + "scan": {typReadOnly, cmdScan}, + + "iter_new": {typReadOnly, cmdIterNew}, + "iter_seek_ge": {typReadOnly, cmdIterSeekGE}, + "iter_seek_lt": {typReadOnly, cmdIterSeekLT}, + "iter_seek_intent_ge": {typReadOnly, cmdIterSeekIntentGE}, + "iter_next": {typReadOnly, cmdIterNext}, + "iter_next_key": {typReadOnly, cmdIterNextKey}, + "iter_prev": {typReadOnly, cmdIterPrev}, + "iter_scan": {typReadOnly, cmdIterScan}, } func cmdTxnAdvance(e *evalCtx) error { @@ -546,12 +633,33 @@ func cmdResolveIntent(e *evalCtx) error { return e.resolveIntent(e.tryWrapForIntentPrinting(e.engine), key, txn, status) } +func cmdResolveIntentRange(e *evalCtx) error { + txn := e.getTxn(mandatory) + start, end := e.getKeyRange() + status := e.getTxnStatus() + + // TODO(erikgrinaker): We use a batch to hit the ConsistentIterators() path in + // MVCCResolveWriteIntentRange. This function will shortly be rewritten to + // remove the other path, so there's no point implementing it. See: + // https://github.com/cockroachdb/cockroach/pull/81063 + batch := e.engine.NewBatch() + defer batch.Close() + + intent := roachpb.MakeLockUpdate(txn, roachpb.Span{Key: start, EndKey: end}) + intent.Status = status + _, _, err := MVCCResolveWriteIntentRange(e.ctx, e.tryWrapForIntentPrinting(batch), e.ms, intent, 0) + if err != nil { + return err + } + return batch.Commit(true) +} + func (e *evalCtx) resolveIntent( rw ReadWriter, key roachpb.Key, txn *roachpb.Transaction, resolveStatus roachpb.TransactionStatus, ) error { intent := roachpb.MakeLockUpdate(txn, roachpb.Span{Key: key}) intent.Status = resolveStatus - _, err := MVCCResolveWriteIntent(e.ctx, rw, nil, intent) + _, err := MVCCResolveWriteIntent(e.ctx, rw, e.ms, intent) return err } @@ -603,7 +711,7 @@ func cmdCPut(e *evalCtx) error { resolve, resolveStatus := e.getResolve() return e.withWriter("cput", func(rw ReadWriter) error { - if err := MVCCConditionalPut(e.ctx, rw, nil, key, ts, val, expVal, behavior, txn); err != nil { + if err := MVCCConditionalPut(e.ctx, rw, e.ms, key, ts, val, expVal, behavior, txn); err != nil { return err } if resolve { @@ -619,7 +727,7 @@ func cmdDelete(e *evalCtx) error { ts := e.getTs(txn) resolve, resolveStatus := e.getResolve() return e.withWriter("del", func(rw ReadWriter) error { - if err := MVCCDelete(e.ctx, rw, nil, key, ts, txn); err != nil { + if err := MVCCDelete(e.ctx, rw, e.ms, key, ts, txn); err != nil { return err } if resolve { @@ -641,7 +749,7 @@ func cmdDeleteRange(e *evalCtx) error { resolve, resolveStatus := e.getResolve() return e.withWriter("del_range", func(rw ReadWriter) error { - deleted, resumeSpan, num, err := MVCCDeleteRange(e.ctx, rw, nil, key, endKey, int64(max), ts, txn, returnKeys) + deleted, resumeSpan, num, err := MVCCDeleteRange(e.ctx, rw, e.ms, key, endKey, int64(max), ts, txn, returnKeys) if err != nil { return err } @@ -660,6 +768,15 @@ func cmdDeleteRange(e *evalCtx) error { }) } +func cmdDeleteRangeTombstone(e *evalCtx) error { + key, endKey := e.getKeyRange() + ts := e.getTs(nil) + + return e.withWriter("del_range_ts", func(rw ReadWriter) error { + return ExperimentalMVCCDeleteRangeUsingTombstone(e.ctx, rw, e.ms, key, endKey, ts, nil, nil, 0) + }) +} + func cmdGet(e *evalCtx) error { txn := e.getTxn(optional) key := e.getKey() @@ -715,7 +832,7 @@ func cmdIncrement(e *evalCtx) error { resolve, resolveStatus := e.getResolve() return e.withWriter("increment", func(rw ReadWriter) error { - curVal, err := MVCCIncrement(e.ctx, rw, nil, key, ts, txn, inc) + curVal, err := MVCCIncrement(e.ctx, rw, e.ms, key, ts, txn, inc) if err != nil { return err } @@ -739,7 +856,7 @@ func cmdMerge(e *evalCtx) error { } ts := e.getTs(nil) return e.withWriter("merge", func(rw ReadWriter) error { - return MVCCMerge(e.ctx, rw, nil, key, ts, val) + return MVCCMerge(e.ctx, rw, e.ms, key, ts, val) }) } @@ -753,7 +870,7 @@ func cmdPut(e *evalCtx) error { resolve, resolveStatus := e.getResolve() return e.withWriter("put", func(rw ReadWriter) error { - if err := MVCCPut(e.ctx, rw, nil, key, ts, val, txn); err != nil { + if err := MVCCPut(e.ctx, rw, e.ms, key, ts, val, txn); err != nil { return err } if resolve { @@ -832,6 +949,219 @@ func cmdScan(e *evalCtx) error { return err } +func cmdPutRangeKey(e *evalCtx) error { + var rangeKey MVCCRangeKey + rangeKey.StartKey, rangeKey.EndKey = e.getKeyRange() + rangeKey.Timestamp = e.getTs(nil) + + return e.withWriter("put_rangekey", func(rw ReadWriter) error { + return rw.ExperimentalPutMVCCRangeKey(rangeKey) + }) +} + +func cmdIterNew(e *evalCtx) error { + var opts IterOptions + if e.hasArg("k") { + opts.LowerBound, opts.UpperBound = e.getKeyRange() + } + if len(opts.UpperBound) == 0 { + opts.UpperBound = keys.MaxKey + } + kind := MVCCKeyAndIntentsIterKind + if e.hasArg("kind") { + var arg string + e.scanArg("kind", &arg) + switch arg { + case "keys": + kind = MVCCKeyIterKind + case "keysAndIntents": + kind = MVCCKeyAndIntentsIterKind + default: + return errors.Errorf("unknown iterator kind %s", arg) + } + } + if e.hasArg("types") { + var arg string + e.scanArg("types", &arg) + switch arg { + case "pointsOnly": + opts.KeyTypes = IterKeyTypePointsOnly + case "pointsAndRanges": + opts.KeyTypes = IterKeyTypePointsAndRanges + case "rangesOnly": + opts.KeyTypes = IterKeyTypeRangesOnly + default: + return errors.Errorf("unknown key type %s", arg) + } + } + if e.hasArg("maskBelow") { + opts.RangeKeyMaskingBelow = e.getTsWithName(nil, "maskBelow") + } + + var r, closeReader Reader + rType := util.ConstantWithMetamorphicTestChoice( + fmt.Sprintf("iter-reader@%s", filepath.Base(e.td.Pos)), + "engine", "readonly", "batch", "snapshot").(string) + switch rType { + case "engine": + r = e.engine + case "readonly": + r = e.engine.NewReadOnly(StandardDurability) + case "batch": + r = e.engine.NewBatch() + closeReader = r + case "snapshot": + r = e.engine.NewSnapshot() + closeReader = r + default: + return errors.Errorf("unknown reader type %s", rType) + } + + if e.iter != nil { + e.iter.Close() + } + e.iter = &iterWithCloseReader{ + MVCCIterator: r.NewMVCCIterator(kind, opts), + closeReader: closeReader, + } + if e.hasArg("pointSynthesis") { + e.iter = newPointSynthesizingIter(e.iter, e.hasArg("emitOnSeekGE")) + } + return nil +} + +func cmdIterSeekGE(e *evalCtx) error { + key := e.getKey() + ts := e.getTs(nil) + e.iter.SeekGE(MVCCKey{Key: key, Timestamp: ts}) + printIter(e) + return nil +} + +func cmdIterSeekIntentGE(e *evalCtx) error { + key := e.getKey() + var txnName string + e.scanArg("txn", &txnName) + txn := e.txns[txnName] + e.iter.SeekIntentGE(key, txn.ID) + printIter(e) + return nil +} + +func cmdIterSeekLT(e *evalCtx) error { + key := e.getKey() + ts := e.getTs(nil) + e.iter.SeekLT(MVCCKey{Key: key, Timestamp: ts}) + printIter(e) + return nil +} + +func cmdIterNext(e *evalCtx) error { + e.iter.Next() + printIter(e) + return nil +} + +func cmdIterNextKey(e *evalCtx) error { + e.iter.NextKey() + printIter(e) + return nil +} + +func cmdIterPrev(e *evalCtx) error { + e.iter.Prev() + printIter(e) + return nil +} + +func cmdIterScan(e *evalCtx) error { + reverse := e.hasArg("reverse") + for { + printIter(e) + if ok, err := e.iter.Valid(); err != nil { + e.Fatalf("%v", err) + } else if !ok { + return nil + } + if reverse { + e.iter.Prev() + } else { + e.iter.Next() + } + } +} + +func printIter(e *evalCtx) { + e.results.buf.Printf("%s:", e.td.Cmd) + defer e.results.buf.Printf("\n") + + hasPoint, hasRange := e.iter.HasPointAndRange() + ok, err := e.iter.Valid() + if err != nil { + e.results.buf.Printf(" err=%v", err) + return + } + if !ok { + if hasPoint || hasRange { + e.t.Fatalf("invalid iterator gave hasPoint=%t hasRange=%t", hasPoint, hasRange) + } + e.results.buf.Print(" .") + return + } + if !hasPoint && !hasRange { + e.t.Fatalf("valid iterator at %s without point nor range keys", e.iter.UnsafeKey()) + } + + if hasPoint { + if !e.iter.UnsafeKey().IsValue() { + meta := enginepb.MVCCMetadata{} + if err := protoutil.Unmarshal(e.iter.UnsafeValue(), &meta); err != nil { + e.Fatalf("%v", err) + } + e.results.buf.Printf(" %s=%+v", e.iter.UnsafeKey(), &meta) + } else { + e.results.buf.Printf(" %s=%s", + e.iter.UnsafeKey(), roachpb.Value{RawBytes: e.iter.UnsafeValue()}.PrettyPrint()) + } + } + if hasRange { + start, end := e.iter.RangeBounds() + e.results.buf.Printf(" %s/[", roachpb.Span{Key: start, EndKey: end}) + for i, rangeKey := range e.iter.RangeKeys() { + if i > 0 { + e.results.buf.Printf(" ") + } + e.results.buf.Printf("%s", rangeKey.Timestamp) + } + e.results.buf.Printf("]") + } +} + +func formatStats(ms *enginepb.MVCCStats, compact bool, delta bool) string { + var fields []string + reEmpty := regexp.MustCompile(`:0$`) + reDelta := regexp.MustCompile(`:(\d+)`) + for _, field := range strings.Fields(ms.String()) { + if compact && (reEmpty.MatchString(field) || strings.HasPrefix(field, "last_update_nanos")) { + continue + } + if delta { + field = reDelta.ReplaceAllString(field, `:+$1`) + } + fields = append(fields, field) + } + if compact { + return strings.Join(fields, " ") + } + + s := "{\n" + for _, field := range fields { + s += " " + field + "\n" + } + s += "}" + return s +} + // evalCtx stored the current state of the environment of a running // script. type evalCtx struct { @@ -842,10 +1172,12 @@ type evalCtx struct { } ctx context.Context engine Engine + iter MVCCIterator t *testing.T td *datadriven.TestData txns map[string]*roachpb.Transaction txnCounter uint128.Uint128 + ms *enginepb.MVCCStats } func newEvalCtx(ctx context.Context, engine Engine) *evalCtx { @@ -857,6 +1189,13 @@ func newEvalCtx(ctx context.Context, engine Engine) *evalCtx { } } +func (e *evalCtx) close() { + if e.iter != nil { + e.iter.Close() + } + // engine is passed in, so it's the caller's responsibility to close it. +} + func (e *evalCtx) getTxnStatus() roachpb.TransactionStatus { status := roachpb.COMMITTED if e.hasArg("status") { @@ -1085,3 +1424,17 @@ func toKey(s string) roachpb.Key { return roachpb.Key(s) } } + +// iterWithCloseReader will close the underlying reader when the +// iterator is closed. +type iterWithCloseReader struct { + MVCCIterator + closeReader Reader +} + +func (i *iterWithCloseReader) Close() { + i.MVCCIterator.Close() + if i.closeReader != nil { + i.closeReader.Close() + } +} diff --git a/pkg/storage/mvcc_incremental_iterator.go b/pkg/storage/mvcc_incremental_iterator.go index 4004f3d84b58..f64774a085af 100644 --- a/pkg/storage/mvcc_incremental_iterator.go +++ b/pkg/storage/mvcc_incremental_iterator.go @@ -488,6 +488,21 @@ func (i *MVCCIncrementalIterator) UnsafeKey() MVCCKey { return i.iter.UnsafeKey() } +// HasPointAndRange implements SimpleMVCCIterator. +func (i *MVCCIncrementalIterator) HasPointAndRange() (bool, bool) { + panic("not implemented") +} + +// RangeBounds implements SimpleMVCCIterator. +func (i *MVCCIncrementalIterator) RangeBounds() (roachpb.Key, roachpb.Key) { + panic("not implemented") +} + +// RangeKeys implements SimpleMVCCIterator. +func (i *MVCCIncrementalIterator) RangeKeys() []MVCCRangeKey { + panic("not implemented") +} + // UnsafeValue returns the same value as Value, but the memory is invalidated on // the next call to {Next,Reset,Close}. func (i *MVCCIncrementalIterator) UnsafeValue() []byte { diff --git a/pkg/storage/mvcc_incremental_iterator_test.go b/pkg/storage/mvcc_incremental_iterator_test.go index 494dc2d30066..c96bce7bcdf0 100644 --- a/pkg/storage/mvcc_incremental_iterator_test.go +++ b/pkg/storage/mvcc_incremental_iterator_test.go @@ -1647,7 +1647,11 @@ func runIncrementalBenchmark( // Pull all of the sstables into the cache. This // probably defeats a lot of the benefits of the // time-based optimization. - iter := eng.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{UpperBound: roachpb.KeyMax}) + iter := eng.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + LowerBound: roachpb.LocalMax, + UpperBound: roachpb.KeyMax, + }) _, _ = iter.ComputeStats(keys.LocalMax, roachpb.KeyMax, 0) iter.Close() } diff --git a/pkg/storage/mvcc_key.go b/pkg/storage/mvcc_key.go index 40d75745af12..7b8e63612bce 100644 --- a/pkg/storage/mvcc_key.go +++ b/pkg/storage/mvcc_key.go @@ -13,6 +13,7 @@ package storage import ( "encoding/binary" "fmt" + "sort" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/storage/enginepb" @@ -61,6 +62,12 @@ func (k MVCCKey) Next() MVCCKey { } } +// Clone returns a copy of the key. +func (k MVCCKey) Clone() MVCCKey { + k.Key = k.Key.Clone() + return k +} + // Compare returns -1 if this key is less than the given key, 0 if they're // equal, or 1 if this is greater. Comparison is by key,timestamp, where larger // timestamps sort before smaller ones except empty ones which sort first (like @@ -237,8 +244,8 @@ func encodeMVCCTimestampToBuf(buf []byte, ts hlc.Timestamp) { // encodedMVCCKeyLength returns the encoded length of the given MVCCKey. func encodedMVCCKeyLength(key MVCCKey) int { - // NB: We don't call into encodedMVCCKeyPrefixLength() or - // encodedMVCCTimestampSuffixLength() here because the additional function + // NB: We don't call into EncodedMVCCKeyPrefixLength() or + // EncodedMVCCTimestampSuffixLength() here because the additional function // call overhead is significant. keyLen := len(key.Key) + mvccEncodedTimeSentinelLen if !key.Timestamp.IsEmpty() { @@ -253,9 +260,9 @@ func encodedMVCCKeyLength(key MVCCKey) int { return keyLen } -// encodedMVCCKeyPrefixLength returns the encoded length of a roachpb.Key prefix +// EncodedMVCCKeyPrefixLength returns the encoded length of a roachpb.Key prefix // including the sentinel byte. -func encodedMVCCKeyPrefixLength(key roachpb.Key) int { +func EncodedMVCCKeyPrefixLength(key roachpb.Key) int { return len(key) + mvccEncodedTimeSentinelLen } @@ -273,10 +280,10 @@ func encodedMVCCTimestampLength(ts hlc.Timestamp) int { return tsLen } -// encodedMVCCTimestampSuffixLength returns the encoded length of the +// EncodedMVCCTimestampSuffixLength returns the encoded length of the // given MVCC timestamp, including the length suffix. It returns 0 // if the timestamp is empty. -func encodedMVCCTimestampSuffixLength(ts hlc.Timestamp) int { +func EncodedMVCCTimestampSuffixLength(ts hlc.Timestamp) int { // This is backwards, see comment in encodedMVCCTimestampLength() for why. return encodedMVCCKeyLength(MVCCKey{Timestamp: ts}) - mvccEncodedTimeSentinelLen } @@ -327,3 +334,100 @@ func decodeMVCCTimestampSuffix(encodedTS []byte) (hlc.Timestamp, error) { } return decodeMVCCTimestamp(encodedTS[:encodedLen-1]) } + +// MVCCRangeKey is a versioned key span. +type MVCCRangeKey struct { + StartKey roachpb.Key + EndKey roachpb.Key + Timestamp hlc.Timestamp +} + +// Clone returns a copy of the range key. +func (k MVCCRangeKey) Clone() MVCCRangeKey { + // k is already a copy, but byte slices must be cloned. + k.StartKey = k.StartKey.Clone() + k.EndKey = k.EndKey.Clone() + return k +} + +// Compare returns -1 if this key is less than the given key, 0 if they're +// equal, or 1 if this is greater. Comparison is by start,timestamp,end, where +// larger timestamps sort before smaller ones except empty ones which sort first +// (like elsewhere in MVCC). +func (k MVCCRangeKey) Compare(o MVCCRangeKey) int { + if c := k.StartKey.Compare(o.StartKey); c != 0 { + return c + } + if k.Timestamp.IsEmpty() && !o.Timestamp.IsEmpty() { + return -1 + } else if !k.Timestamp.IsEmpty() && o.Timestamp.IsEmpty() { + return 1 + } else if c := k.Timestamp.Compare(o.Timestamp); c != 0 { + return -c // timestamps sort in reverse + } + return k.EndKey.Compare(o.EndKey) +} + +// EncodedSize returns the encoded size of this range key. This does not +// accurately reflect the on-disk size of the key, due to Pebble range key +// stacking and fragmentation. +// +// NB: This calculation differs from MVCCKey in that MVCCKey.EncodedSize() +// incorrectly always uses 13 bytes for the timestamp while this method +// calculates the actual encoded size. +func (k MVCCRangeKey) EncodedSize() int { + return EncodedMVCCKeyPrefixLength(k.StartKey) + + EncodedMVCCKeyPrefixLength(k.EndKey) + + EncodedMVCCTimestampSuffixLength(k.Timestamp) +} + +// String formats the range key. +func (k MVCCRangeKey) String() string { + s := roachpb.Span{Key: k.StartKey, EndKey: k.EndKey}.String() + if !k.Timestamp.IsEmpty() { + s += fmt.Sprintf("/%s", k.Timestamp) + } + return s +} + +// Validate returns an error if the range key is invalid. +// +// This validation is for writing range keys (or checking existing range keys), +// not for filters/bounds, so e.g. specifying an empty start key is invalid even +// though it would be valid to start a range key scan at an empty start key. +func (k MVCCRangeKey) Validate() (err error) { + defer func() { + err = errors.Wrapf(err, "invalid range key %s", k) + }() + + switch { + case len(k.StartKey) == 0: + // We don't allow an empty start key, because we don't allow writing point + // keys at the empty key. The first valid key is 0x00. + return errors.Errorf("no start key") + case len(k.EndKey) == 0: + return errors.Errorf("no end key") + case k.Timestamp.IsEmpty(): + return errors.Errorf("no timestamp") + case k.StartKey.Compare(k.EndKey) >= 0: + return errors.Errorf("start key %s is at or after end key %s", k.StartKey, k.EndKey) + default: + return nil + } +} + +// firstRangeKeyAbove does a binary search for the first range key at or above +// the given timestamp. It assumes the range keys are ordered in descending +// timestamp order, as returned by SimpleMVCCIterator.RangeKeys(). Returns false +// if no matching range key was found. +func firstRangeKeyAbove(rangeKeys []MVCCRangeKey, ts hlc.Timestamp) (MVCCRangeKey, bool) { + // This is kind of odd due to sort.Search() semantics: we do a binary search + // for the first range tombstone that's below the timestamp, then return the + // previous range tombstone if any. + if i := sort.Search(len(rangeKeys), func(i int) bool { + return rangeKeys[i].Timestamp.Less(ts) + }); i > 0 { + return rangeKeys[i-1], true + } + return MVCCRangeKey{}, false +} diff --git a/pkg/storage/mvcc_key_test.go b/pkg/storage/mvcc_key_test.go index 4e8a2ea3d5b0..6ecab69d8363 100644 --- a/pkg/storage/mvcc_key_test.go +++ b/pkg/storage/mvcc_key_test.go @@ -193,7 +193,7 @@ func TestEncodeDecodeMVCCKeyAndTimestampWithLength(t *testing.T) { require.Equal(t, expect, encoded) require.Equal(t, len(encoded), encodedMVCCKeyLength(mvccKey)) require.Equal(t, len(encoded), - encodedMVCCKeyPrefixLength(mvccKey.Key)+encodedMVCCTimestampSuffixLength(mvccKey.Timestamp)) + EncodedMVCCKeyPrefixLength(mvccKey.Key)+EncodedMVCCTimestampSuffixLength(mvccKey.Timestamp)) decoded, err := DecodeMVCCKey(encoded) require.NoError(t, err) @@ -203,7 +203,7 @@ func TestEncodeDecodeMVCCKeyAndTimestampWithLength(t *testing.T) { expectPrefix, err := hex.DecodeString(tc.encoded[:2*len(tc.key)+2]) require.NoError(t, err) require.Equal(t, expectPrefix, EncodeMVCCKeyPrefix(roachpb.Key(tc.key))) - require.Equal(t, len(expectPrefix), encodedMVCCKeyPrefixLength(roachpb.Key(tc.key))) + require.Equal(t, len(expectPrefix), EncodedMVCCKeyPrefixLength(roachpb.Key(tc.key))) // Test encode/decodeMVCCTimestampSuffix too, since we can trivially do so. expectTS, err := hex.DecodeString(tc.encoded[2*len(tc.key)+2:]) @@ -214,7 +214,7 @@ func TestEncodeDecodeMVCCKeyAndTimestampWithLength(t *testing.T) { encodedTS := EncodeMVCCTimestampSuffix(tc.ts) require.Equal(t, expectTS, encodedTS) - require.Equal(t, len(encodedTS), encodedMVCCTimestampSuffixLength(tc.ts)) + require.Equal(t, len(encodedTS), EncodedMVCCTimestampSuffixLength(tc.ts)) decodedTS, err := decodeMVCCTimestampSuffix(encodedTS) require.NoError(t, err) @@ -381,3 +381,175 @@ func BenchmarkDecodeMVCCKey(b *testing.B) { } benchmarkDecodeMVCCKeyResult = mvccKey // avoid compiler optimizing away function call } + +func TestMVCCRangeKeyString(t *testing.T) { + defer leaktest.AfterTest(t)() + + testcases := map[string]struct { + rk MVCCRangeKey + expect string + }{ + "empty": {MVCCRangeKey{}, "/Min"}, + "only start": {MVCCRangeKey{StartKey: roachpb.Key("foo")}, "foo"}, + "only end": {MVCCRangeKey{EndKey: roachpb.Key("foo")}, "{/Min-foo}"}, + "only timestamp": {MVCCRangeKey{Timestamp: hlc.Timestamp{Logical: 1}}, "/Min/0,1"}, + "only span": {MVCCRangeKey{StartKey: roachpb.Key("a"), EndKey: roachpb.Key("z")}, "{a-z}"}, + "all": {MVCCRangeKey{StartKey: roachpb.Key("a"), EndKey: roachpb.Key("z"), Timestamp: hlc.Timestamp{Logical: 1}}, "{a-z}/0,1"}, + "all overlapping": {MVCCRangeKey{StartKey: roachpb.Key("ab"), EndKey: roachpb.Key("af"), Timestamp: hlc.Timestamp{Logical: 1}}, "a{b-f}/0,1"}, + } + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + require.Equal(t, tc.expect, tc.rk.String()) + }) + } +} + +func TestMVCCRangeKeyCompare(t *testing.T) { + defer leaktest.AfterTest(t)() + + ab1 := MVCCRangeKey{roachpb.Key("a"), roachpb.Key("b"), hlc.Timestamp{Logical: 1}} + ac1 := MVCCRangeKey{roachpb.Key("a"), roachpb.Key("c"), hlc.Timestamp{Logical: 1}} + ac2 := MVCCRangeKey{roachpb.Key("a"), roachpb.Key("c"), hlc.Timestamp{Logical: 2}} + bc0 := MVCCRangeKey{roachpb.Key("b"), roachpb.Key("c"), hlc.Timestamp{Logical: 0}} + bc1 := MVCCRangeKey{roachpb.Key("b"), roachpb.Key("c"), hlc.Timestamp{Logical: 1}} + bc3 := MVCCRangeKey{roachpb.Key("b"), roachpb.Key("c"), hlc.Timestamp{Logical: 3}} + bd4 := MVCCRangeKey{roachpb.Key("b"), roachpb.Key("d"), hlc.Timestamp{Logical: 4}} + + testcases := map[string]struct { + a MVCCRangeKey + b MVCCRangeKey + expect int + }{ + "equal": {ac1, ac1, 0}, + "start lt": {ac1, bc1, -1}, + "start gt": {bc1, ac1, 1}, + "end lt": {ab1, ac1, -1}, + "end gt": {ac1, ab1, 1}, + "time lt": {ac2, ac1, -1}, // MVCC timestamps sort in reverse order + "time gt": {ac1, ac2, 1}, // MVCC timestamps sort in reverse order + "empty time lt set": {bc0, bc1, -1}, // empty MVCC timestamps sort before non-empty + "set time gt empty": {bc1, bc0, 1}, // empty MVCC timestamps sort before non-empty + "start time precedence": {ac2, bc3, -1}, // a before b, but 3 before 2; key takes precedence + "time end precedence": {bd4, bc3, -1}, // c before d, but 4 before 3; time takes precedence + } + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + require.Equal(t, tc.expect, tc.a.Compare(tc.b)) + }) + } +} + +func TestMVCCRangeKeyEncodedSize(t *testing.T) { + defer leaktest.AfterTest(t)() + + testcases := map[string]struct { + rk MVCCRangeKey + expect int + }{ + "empty": {MVCCRangeKey{}, 2}, // sentinel byte for start and end + "only start": {MVCCRangeKey{StartKey: roachpb.Key("foo")}, 5}, + "only end": {MVCCRangeKey{EndKey: roachpb.Key("foo")}, 5}, + "only walltime": {MVCCRangeKey{Timestamp: hlc.Timestamp{WallTime: 1}}, 11}, + "only logical": {MVCCRangeKey{Timestamp: hlc.Timestamp{Logical: 1}}, 15}, + "all": {MVCCRangeKey{ + StartKey: roachpb.Key("start"), + EndKey: roachpb.Key("end"), + Timestamp: hlc.Timestamp{WallTime: 1, Logical: 1, Synthetic: true}, + }, 24}, + } + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + require.Equal(t, tc.expect, tc.rk.EncodedSize()) + }) + } +} + +func TestMVCCRangeKeyValidate(t *testing.T) { + defer leaktest.AfterTest(t)() + + a := roachpb.Key("a") + b := roachpb.Key("b") + blank := roachpb.Key("") + ts1 := hlc.Timestamp{Logical: 1} + + testcases := map[string]struct { + rangeKey MVCCRangeKey + expectErr string // empty if no error + }{ + "valid": {MVCCRangeKey{StartKey: a, EndKey: b, Timestamp: ts1}, ""}, + "empty": {MVCCRangeKey{}, "/Min: no start key"}, + "no start": {MVCCRangeKey{EndKey: b, Timestamp: ts1}, "{/Min-b}/0,1: no start key"}, + "no end": {MVCCRangeKey{StartKey: a, Timestamp: ts1}, "a/0,1: no end key"}, + "no timestamp": {MVCCRangeKey{StartKey: a, EndKey: b}, "{a-b}: no timestamp"}, + "blank start": {MVCCRangeKey{StartKey: blank, EndKey: b, Timestamp: ts1}, "{/Min-b}/0,1: no start key"}, + "end at start": {MVCCRangeKey{StartKey: a, EndKey: a, Timestamp: ts1}, `a{-}/0,1: start key "a" is at or after end key "a"`}, + "end before start": {MVCCRangeKey{StartKey: b, EndKey: a, Timestamp: ts1}, `{b-a}/0,1: start key "b" is at or after end key "a"`}, + } + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + err := tc.rangeKey.Validate() + if tc.expectErr == "" { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectErr) + } + }) + } +} + +func TestFirstRangeKeyAbove(t *testing.T) { + defer leaktest.AfterTest(t)() + + rangeKeys := []MVCCRangeKey{ + rangeKey("a", "f", 6), + rangeKey("a", "f", 4), + rangeKey("a", "f", 3), + rangeKey("a", "f", 1), + } + + testcases := []struct { + ts int64 + expect int64 + }{ + {0, 1}, + {1, 1}, + {2, 3}, + {3, 3}, + {4, 4}, + {5, 6}, + {6, 6}, + {7, 0}, + } + for _, tc := range testcases { + t.Run(fmt.Sprintf("%d", tc.ts), func(t *testing.T) { + rk, ok := firstRangeKeyAbove(rangeKeys, hlc.Timestamp{WallTime: tc.ts}) + if tc.expect == 0 { + require.False(t, ok) + require.Empty(t, rk) + } else { + require.True(t, ok) + require.Equal(t, rangeKey("a", "f", int(tc.expect)), rk) + } + }) + } +} + +func pointKey(key string, ts int) MVCCKey { + return MVCCKey{Key: roachpb.Key(key), Timestamp: hlc.Timestamp{WallTime: int64(ts)}} +} + +func pointKV(key string, ts int, value string) MVCCKeyValue { + return MVCCKeyValue{ + Key: pointKey(key, ts), + Value: []byte(value), + } +} + +func rangeKey(start, end string, ts int) MVCCRangeKey { + return MVCCRangeKey{ + StartKey: roachpb.Key(start), + EndKey: roachpb.Key(end), + Timestamp: hlc.Timestamp{WallTime: int64(ts)}, + } +} diff --git a/pkg/storage/mvcc_stats_test.go b/pkg/storage/mvcc_stats_test.go index 5cc09743c86d..406d24f9cf01 100644 --- a/pkg/storage/mvcc_stats_test.go +++ b/pkg/storage/mvcc_stats_test.go @@ -57,7 +57,11 @@ func assertEqImpl( keyMin = keys.LocalMax keyMax = roachpb.KeyMax } - it := rw.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{UpperBound: keyMax}) + it := rw.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + LowerBound: keyMin, + UpperBound: keyMax, + }) defer it.Close() for _, mvccStatsTest := range mvccStatsTests { @@ -1362,6 +1366,108 @@ func TestMVCCStatsSysPutPut(t *testing.T) { } } +func TestMVCCStatsRangeKeysOnly(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + eng := NewDefaultInMemForTesting() + defer eng.Close() + + type stats = enginepb.MVCCStats + const now = 10 * 1e9 + + // Add range keys one by one and assert their stats contribution. + testcases := []struct { + rangeKey MVCCRangeKey + stats enginepb.MVCCStats + }{ + {rangeKey("a", "f", 8e9), stats{RangeKeyCount: 1, RangeKeyBytes: 13, GCBytesAge: 2 * 13}}, + // Historical version of [a-f)@8. + {rangeKey("a", "f", 1e9), stats{RangeKeyBytes: 9, GCBytesAge: 9 * 9}}, + // Fragment [a-f) into [a-e) and [e-f). + {rangeKey("e", "f", 2e9), stats{RangeKeyCount: 1, RangeKeyBytes: 13 + 2*9, GCBytesAge: 2*13 + 8*9 + 9*9}}, + // Fragment [a-e) in the middle into [a-bb), [bb-ccc), and [ccc-e). + {rangeKey("bb", "ccc", 5e9), stats{ + RangeKeyCount: 2, // creates bb-ccc and ccc-e + RangeKeyBytes: 1 + 16 + 15 + 3*9, // [a-e) becomes [a-bb), adds keys [bb-ccc)@8 and [ccc-e)@8, plus versions [bb-ccc)@5, [bb-ccc)@1, [ccc-e)@1 + GCBytesAge: 2*1 + 2*16 + 2*15 + 5*9 + 9*9 + 9*9, + }}, + // Extending the range keys below others should only add itself, but it will + // create new versions across existing fragments. + {rangeKey("a", "p", 3e9), stats{RangeKeyCount: 1, RangeKeyBytes: 13 + 4*9, GCBytesAge: 7 * (13 + 4*9)}}, + // Dropping a range key covering all existing fragments will create new + // versions of each fragment with no GCBytesAge contribution, but will also + // reduce the GCBytesAge contribution of the key bounds of the topmost keys + // since these are now moved up to the latest version at timestamp 10. The + // topmost fragments before this were: + // + // [a-bb)@8 [bb-ccc)@8 [ccc-e)@8 [e-f)@8 [f-p)@3 + {rangeKey("a", "p", 10e9), stats{RangeKeyBytes: 5 * 9, GCBytesAge: -2*(14-9) - 2*(16-9) - 2*(15-9) - 2*(13-9) - 7*(13-9)}}, + } + + for _, tc := range testcases { + // We don't use t.Run() because the test cases are dependant. + before := computeStats(t, eng, roachpb.Key("a"), roachpb.Key("z"), now) + require.NoError(t, eng.ExperimentalPutMVCCRangeKey(tc.rangeKey)) + after := computeStats(t, eng, roachpb.Key("a"), roachpb.Key("z"), now) + + delta := after + delta.Subtract(before) + delta.LastUpdateNanos = 0 + require.Equal(t, tc.stats, delta, "range key %s", tc.rangeKey) + } +} + +func TestMVCCStatsRangeKeysAndPointKeys(t *testing.T) { + defer leaktest.AfterTest(t)() + defer log.Scope(t).Close(t) + + eng := NewDefaultInMemForTesting() + defer eng.Close() + + const now = 10e9 + + rangeKeys := []MVCCRangeKey{ + rangeKey("a", "z", 8e9), // RangeKeyCount:1 RangeKeyBytes:13 GCBytesAge:26 + rangeKey("k", "z", 5e9), // RangeKeyCount:1 RangeKeyBytes:13+9 GCBytesAge:5*13+3*2 + } + for _, rk := range rangeKeys { + require.NoError(t, eng.ExperimentalPutMVCCRangeKey(rk)) + } + + pointKVs := []MVCCKeyValue{ + pointKV("a", 9e9, "a9"), // LiveCount:1 LiveBytes:16 KeyCount:1 KeyBytes:14 ValCount:1 ValBytes:2 + pointKV("b", 5e9, ""), // KeyCount:1 KeyBytes:14 ValCount:1 GCBytesAge:70 + pointKV("c", 5e9, "c5"), // KeyCount:1 KeyBytes:14 ValCount:1 ValBytes:2 GCBytesAge:32 + pointKV("d", 7e9, ""), // KeyCount:1 KeyBytes:14 ValCount:1 GCBytesAge:42 + pointKV("d", 6e9, "d6"), // KeyBytes:12 ValCount:1 ValBytes:2 GCBytesAge:42 + pointKV("d", 5e9, "d5"), // KeyBytes:12 ValCount:1 ValBytes:2 GCBytesAge:56 + pointKV("e", 9e9, "e9"), // LiveCount:1 LiveBytes:16 KeyCount:1 KeyBytes:14 ValCount:1 ValBytes:2 + pointKV("e", 5e9, "e5"), // KeyBytes:12 ValCount:1 ValBytes:2 GCBytesAge:28 + pointKV("m", 6e9, "m6"), // KeyCount:1 KeyBytes:14 ValCount:1 ValBytes:2 GCBytesAge:32 + pointKV("m", 3e9, "m3"), // KeyBytes:12 ValCount:1 ValBytes:2 GCBytesAge:70 + pointKV("o", 9e9, "o9"), // LiveCount:1 LiveBytes:16 KeyCount:1 KeyBytes:14 ValCount:1 ValBytes:2 + pointKV("o", 3e9, ""), // KeyBytes:12 ValCount:1 GCBytesAge:84 + pointKV("o", 1e9, "o1"), // KeyBytes:12 ValCount:1 ValBytes:2 GCBytesAge:98 + } + for _, kv := range pointKVs { + require.NoError(t, eng.PutMVCC(kv.Key, kv.Value)) + } + + require.Equal(t, enginepb.MVCCStats{ + LiveCount: 3, + LiveBytes: 48, + KeyCount: 7, + KeyBytes: 170, + ValCount: 13, + ValBytes: 20, + RangeKeyCount: 2, + RangeKeyBytes: 35, + GCBytesAge: 651, + LastUpdateNanos: now, + }, computeStats(t, eng, nil, nil, now)) +} + var mvccStatsTests = []struct { name string fn func(MVCCIterator, roachpb.Key, roachpb.Key, int64) (enginepb.MVCCStats, error) @@ -1480,6 +1586,8 @@ func (s *randomTest) step(t *testing.T) { } } +// TODO(erikgrinaker): Add ExperimentalMVCCDeleteRangeUsingTombstone operations +// once they are fully integrated with other MVCC operations. func TestMVCCStatsRandomized(t *testing.T) { defer leaktest.AfterTest(t)() defer log.Scope(t).Close(t) @@ -1651,7 +1759,11 @@ func TestMVCCComputeStatsError(t *testing.T) { t.Fatal(err) } - iter := engine.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{UpperBound: roachpb.KeyMax}) + iter := engine.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + LowerBound: roachpb.LocalMax, + UpperBound: roachpb.KeyMax, + }) defer iter.Close() for _, mvccStatsTest := range mvccStatsTests { t.Run(mvccStatsTest.name, func(t *testing.T) { diff --git a/pkg/storage/mvcc_test.go b/pkg/storage/mvcc_test.go index 539d2a0e6f33..d6f63df2392d 100644 --- a/pkg/storage/mvcc_test.go +++ b/pkg/storage/mvcc_test.go @@ -30,7 +30,6 @@ import ( "github.com/cockroachdb/cockroach/pkg/testutils" "github.com/cockroachdb/cockroach/pkg/testutils/skip" "github.com/cockroachdb/cockroach/pkg/testutils/zerofields" - "github.com/cockroachdb/cockroach/pkg/util/caller" "github.com/cockroachdb/cockroach/pkg/util/encoding" "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/leaktest" @@ -124,6 +123,8 @@ func TestMVCCStatsAddSubForward(t *testing.T) { ValCount: 1, IntentBytes: 1, IntentCount: 1, + RangeKeyCount: 1, + RangeKeyBytes: 1, SeparatedIntentCount: 1, IntentAge: 1, GCBytesAge: 1, @@ -134,17 +135,7 @@ func TestMVCCStatsAddSubForward(t *testing.T) { LastUpdateNanos: 1, AbortSpanBytes: 1, } - if err := zerofields.NoZeroField(&goldMS); err != nil { - t.Fatal(err) // prevent rot as fields are added - } - - cmp := func(act, exp enginepb.MVCCStats) { - t.Helper() - f, l, _ := caller.Lookup(1) - if !reflect.DeepEqual(act, exp) { - t.Fatalf("%s:%d: wanted %+v back, got %+v", f, l, exp, act) - } - } + require.NoError(t, zerofields.NoZeroField(&goldMS)) ms := goldMS zeroWithLU := enginepb.MVCCStats{ @@ -153,10 +144,10 @@ func TestMVCCStatsAddSubForward(t *testing.T) { } ms.Subtract(goldMS) - cmp(ms, zeroWithLU) + require.Equal(t, zeroWithLU, ms) ms.Add(goldMS) - cmp(ms, goldMS) + require.Equal(t, goldMS, ms) // Double-add double-sub guards against mistaking `+=` for `=`. ms = zeroWithLU @@ -164,7 +155,7 @@ func TestMVCCStatsAddSubForward(t *testing.T) { ms.Add(goldMS) ms.Subtract(goldMS) ms.Subtract(goldMS) - cmp(ms, zeroWithLU) + require.Equal(t, zeroWithLU, ms) // Run some checks for Forward. goldDelta := enginepb.MVCCStats{ @@ -174,31 +165,27 @@ func TestMVCCStatsAddSubForward(t *testing.T) { } delta := goldDelta - for i, ns := range []int64{1, 1e9 - 1001, 1e9 - 1000, 1e9 - 1, 1e9, 1e9 + 1, 2e9 - 1} { + for _, ns := range []int64{1, 1e9 - 1001, 1e9 - 1000, 1e9 - 1, 1e9, 1e9 + 1, 2e9 - 1} { oldDelta := delta delta.AgeTo(ns) - if delta.LastUpdateNanos < ns { - t.Fatalf("%d: expected LastUpdateNanos < %d, got %d", i, ns, delta.LastUpdateNanos) - } + require.GreaterOrEqual(t, delta.LastUpdateNanos, ns, "LastUpdateNanos") shouldAge := ns/1e9-oldDelta.LastUpdateNanos/1e9 > 0 didAge := delta.IntentAge != oldDelta.IntentAge && delta.GCBytesAge != oldDelta.GCBytesAge - if shouldAge != didAge { - t.Fatalf("%d: should age: %t, but had\n%+v\nand now\n%+v", i, shouldAge, oldDelta, delta) - } + require.Equal(t, shouldAge, didAge) } expDelta := goldDelta expDelta.LastUpdateNanos = 2e9 - 1 expDelta.GCBytesAge = 42 expDelta.IntentAge = 11 - cmp(delta, expDelta) + require.Equal(t, expDelta, delta) delta.AgeTo(2e9) expDelta.LastUpdateNanos = 2e9 expDelta.GCBytesAge += 42 expDelta.IntentAge += 11 - cmp(delta, expDelta) + require.Equal(t, expDelta, delta) { // Verify that AgeTo can go backwards in time. @@ -210,13 +197,13 @@ func TestMVCCStatsAddSubForward(t *testing.T) { expDelta.LastUpdateNanos = 2e9 - 1 expDelta.GCBytesAge -= 42 expDelta.IntentAge -= 11 - cmp(tmpDelta, expDelta) + require.Equal(t, expDelta, tmpDelta) } delta.AgeTo(3e9 - 1) delta.Forward(5) // should be noop expDelta.LastUpdateNanos = 3e9 - 1 - cmp(delta, expDelta) + require.Equal(t, expDelta, delta) // Check that Add calls Forward appropriately. mss := []enginepb.MVCCStats{goldMS, goldMS} @@ -227,13 +214,13 @@ func TestMVCCStatsAddSubForward(t *testing.T) { expMS := goldMS expMS.Add(goldMS) expMS.LastUpdateNanos = 10e9 + 1 - expMS.IntentAge += 9 // from aging 9 ticks from 2E9-1 to 10E9+1 - expMS.GCBytesAge += 9 // ditto + expMS.IntentAge += 9 // from aging 9 ticks from 2E9-1 to 10E9+1 + expMS.GCBytesAge += 2 * 9 // ditto for i := range mss[:1] { ms := mss[(1+i)%2] ms.Add(mss[i]) - cmp(ms, expMS) + require.Equal(t, expMS, ms) } // Finally, check Forward with negative counts (can happen). @@ -244,9 +231,9 @@ func TestMVCCStatsAddSubForward(t *testing.T) { neg.AgeTo(2e9) exp.LastUpdateNanos = 2e9 - exp.GCBytesAge = -3 + exp.GCBytesAge = -5 exp.IntentAge = -3 - cmp(neg, exp) + require.Equal(t, exp, neg) } func TestMVCCGetNotExist(t *testing.T) { @@ -911,7 +898,10 @@ func TestMVCCInvalidateIterator(t *testing.T) { switch which { case "get": iterOptions.Prefix = true - case "scan", "findSplitKey", "computeStats": + case "computeStats": + iterOptions.KeyTypes = IterKeyTypePointsAndRanges + iterOptions.UpperBound = roachpb.KeyMax + case "scan", "findSplitKey": iterOptions.UpperBound = roachpb.KeyMax } @@ -2302,13 +2292,23 @@ func computeStats( t *testing.T, reader Reader, from, to roachpb.Key, nowNanos int64, ) enginepb.MVCCStats { t.Helper() - iter := reader.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{UpperBound: to}) - defer iter.Close() - s, err := ComputeStatsForRange(iter, from, to, nowNanos) - if err != nil { - t.Fatalf("%+v", err) + + if len(from) == 0 { + from = keys.LocalMax + } + if len(to) == 0 { + to = keys.MaxKey } - return s + + iter := reader.NewMVCCIterator(MVCCKeyAndIntentsIterKind, IterOptions{ + KeyTypes: IterKeyTypePointsAndRanges, + LowerBound: from, + UpperBound: to, + }) + defer iter.Close() + ms, err := ComputeStatsForRange(iter, from, to, nowNanos) + require.NoError(t, err) + return ms } // TestMVCCClearTimeRangeOnRandomData sets up mostly random KVs and then picks diff --git a/pkg/storage/pebble.go b/pkg/storage/pebble.go index 2ee1c045f3e8..54e5de610f07 100644 --- a/pkg/storage/pebble.go +++ b/pkg/storage/pebble.go @@ -590,6 +590,8 @@ func DefaultPebbleOptions() *pebble.Options { TablePropertyCollectors: PebbleTablePropertyCollectors, BlockPropertyCollectors: PebbleBlockPropertyCollectors, } + // Used for experimental MVCC range tombstones. + opts.Experimental.RangeKeys = new(pebble.RangeKeysArena) // Automatically flush 10s after the first range tombstone is added to a // memtable. This ensures that we can reclaim space even when there's no // activity on the database generating flushes. @@ -1112,7 +1114,7 @@ func (p *Pebble) NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions) MVCCIt return iter } - iter := newPebbleIterator(p.db, nil, opts, StandardDurability) + iter := newPebbleIterator(p.db, nil, opts, StandardDurability, p.SupportsRangeKeys()) if iter == nil { panic("couldn't create a new iterator") } @@ -1124,7 +1126,7 @@ func (p *Pebble) NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions) MVCCIt // NewEngineIterator implements the Engine interface. func (p *Pebble) NewEngineIterator(opts IterOptions) EngineIterator { - iter := newPebbleIterator(p.db, nil, opts, StandardDurability) + iter := newPebbleIterator(p.db, nil, opts, StandardDurability, p.SupportsRangeKeys()) if iter == nil { panic("couldn't create a new iterator") } @@ -1136,6 +1138,11 @@ func (p *Pebble) ConsistentIterators() bool { return false } +// SupportsRangeKeys implements the Engine interface. +func (p *Pebble) SupportsRangeKeys() bool { + return p.db.FormatMajorVersion() >= pebble.FormatRangeKeys +} + // PinEngineStateForIterators implements the Engine interface. func (p *Pebble) PinEngineStateForIterators() error { return errors.AssertionFailedf( @@ -1237,6 +1244,52 @@ func (p *Pebble) ClearIterRange(start, end roachpb.Key) error { return batch.Commit(true) } +// ExperimentalClearMVCCRangeKey implements the Engine interface. +func (p *Pebble) ExperimentalClearMVCCRangeKey(rangeKey MVCCRangeKey) error { + if !p.SupportsRangeKeys() { + // These databases cannot contain range keys, so clearing is a noop. + return nil + } + if err := rangeKey.Validate(); err != nil { + return err + } + return p.db.Experimental().RangeKeyUnset( + EncodeMVCCKeyPrefix(rangeKey.StartKey), + EncodeMVCCKeyPrefix(rangeKey.EndKey), + EncodeMVCCTimestampSuffix(rangeKey.Timestamp), + pebble.Sync) +} + +// ExperimentalClearAllMVCCRangeKeys implements the Engine interface. +func (p *Pebble) ExperimentalClearAllMVCCRangeKeys(start, end roachpb.Key) error { + if !p.SupportsRangeKeys() { + return nil // noop + } + rangeKey := MVCCRangeKey{StartKey: start, EndKey: end, Timestamp: hlc.MinTimestamp} + if err := rangeKey.Validate(); err != nil { + return err + } + return p.db.Experimental().RangeKeyDelete( + EncodeMVCCKeyPrefix(start), EncodeMVCCKeyPrefix(end), pebble.Sync) +} + +// ExperimentalPutMVCCRangeKey implements the Engine interface. +func (p *Pebble) ExperimentalPutMVCCRangeKey(rangeKey MVCCRangeKey) error { + if !p.SupportsRangeKeys() { + return errors.Errorf("range keys not supported by Pebble database version %s", + p.db.FormatMajorVersion()) + } + if err := rangeKey.Validate(); err != nil { + return err + } + return p.db.Experimental().RangeKeySet( + EncodeMVCCKeyPrefix(rangeKey.StartKey), + EncodeMVCCKeyPrefix(rangeKey.EndKey), + EncodeMVCCTimestampSuffix(rangeKey.Timestamp), + nil, + pebble.Sync) +} + // Merge implements the Engine interface. func (p *Pebble) Merge(key MVCCKey, value []byte) error { if len(key.Key) == 0 { @@ -1544,7 +1597,7 @@ func (p *Pebble) NewUnindexedBatch(writeOnly bool) Batch { func (p *Pebble) NewSnapshot() Reader { return &pebbleSnapshot{ snapshot: p.db.NewSnapshot(), - settings: p.settings, + parent: p, } } @@ -1926,13 +1979,13 @@ func (p *pebbleReadOnly) NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions iter = &p.prefixIter } if iter.inuse { - return newPebbleIterator(p.parent.db, p.iter, opts, p.durability) + return newPebbleIterator(p.parent.db, p.iter, opts, p.durability, p.SupportsRangeKeys()) } if iter.iter != nil { iter.setOptions(opts, p.durability) } else { - iter.init(p.parent.db, p.iter, p.iterUnused, opts, p.durability) + iter.init(p.parent.db, p.iter, p.iterUnused, opts, p.durability, p.SupportsRangeKeys()) if p.iter == nil { // For future cloning. p.iter = iter.iter @@ -1960,13 +2013,13 @@ func (p *pebbleReadOnly) NewEngineIterator(opts IterOptions) EngineIterator { iter = &p.prefixEngineIter } if iter.inuse { - return newPebbleIterator(p.parent.db, p.iter, opts, p.durability) + return newPebbleIterator(p.parent.db, p.iter, opts, p.durability, p.SupportsRangeKeys()) } if iter.iter != nil { iter.setOptions(opts, p.durability) } else { - iter.init(p.parent.db, p.iter, p.iterUnused, opts, p.durability) + iter.init(p.parent.db, p.iter, p.iterUnused, opts, p.durability, p.SupportsRangeKeys()) if p.iter == nil { // For future cloning. p.iter = iter.iter @@ -1984,6 +2037,11 @@ func (p *pebbleReadOnly) ConsistentIterators() bool { return true } +// SupportsRangeKeys implements the Engine interface. +func (p *pebbleReadOnly) SupportsRangeKeys() bool { + return p.parent.SupportsRangeKeys() +} + // PinEngineStateForIterators implements the Engine interface. func (p *pebbleReadOnly) PinEngineStateForIterators() error { if p.iter == nil { @@ -2046,6 +2104,18 @@ func (p *pebbleReadOnly) ClearIterRange(start, end roachpb.Key) error { panic("not implemented") } +func (p *pebbleReadOnly) ExperimentalPutMVCCRangeKey(_ MVCCRangeKey) error { + panic("not implemented") +} + +func (p *pebbleReadOnly) ExperimentalClearMVCCRangeKey(_ MVCCRangeKey) error { + panic("not implemented") +} + +func (p *pebbleReadOnly) ExperimentalClearAllMVCCRangeKeys(_, _ roachpb.Key) error { + panic("not implemented") +} + func (p *pebbleReadOnly) Merge(key MVCCKey, value []byte) error { panic("not implemented") } @@ -2079,7 +2149,7 @@ func (p *pebbleReadOnly) LogLogicalOp(op MVCCLogicalOpType, details MVCCLogicalO // pebbleSnapshot represents a snapshot created using Pebble.NewSnapshot(). type pebbleSnapshot struct { snapshot *pebble.Snapshot - settings *cluster.Settings + parent *Pebble closed bool } @@ -2102,7 +2172,7 @@ func (p *pebbleSnapshot) ExportMVCCToSst( ) (roachpb.BulkOpSummary, roachpb.Key, hlc.Timestamp, error) { r := wrapReader(p) // Doing defer r.Free() does not inline. - summary, k, err := pebbleExportToSst(ctx, p.settings, r, exportOptions, dest) + summary, k, err := pebbleExportToSst(ctx, p.parent.settings, r, exportOptions, dest) r.Free() return summary, k.Key, k.Timestamp, err } @@ -2166,7 +2236,9 @@ func (p *pebbleSnapshot) NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions } return iter } - iter := MVCCIterator(newPebbleIterator(p.snapshot, nil, opts, StandardDurability)) + + iter := MVCCIterator(newPebbleIterator( + p.snapshot, nil, opts, StandardDurability, p.SupportsRangeKeys())) if util.RaceEnabled { iter = wrapInUnsafeIter(iter) } @@ -2175,7 +2247,8 @@ func (p *pebbleSnapshot) NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions // NewEngineIterator implements the Reader interface. func (p pebbleSnapshot) NewEngineIterator(opts IterOptions) EngineIterator { - return newPebbleIterator(p.snapshot, nil, opts, StandardDurability) + return newPebbleIterator( + p.snapshot, nil, opts, StandardDurability, p.SupportsRangeKeys()) } // ConsistentIterators implements the Reader interface. @@ -2183,6 +2256,11 @@ func (p pebbleSnapshot) ConsistentIterators() bool { return true } +// SupportsRangeKeys implements the Reader interface. +func (p *pebbleSnapshot) SupportsRangeKeys() bool { + return p.parent.SupportsRangeKeys() +} + // PinEngineStateForIterators implements the Reader interface. func (p *pebbleSnapshot) PinEngineStateForIterators() error { // Snapshot already pins state, so nothing to do. diff --git a/pkg/storage/pebble_batch.go b/pkg/storage/pebble_batch.go index 6bc4043fd66f..9dfdde3b210b 100644 --- a/pkg/storage/pebble_batch.go +++ b/pkg/storage/pebble_batch.go @@ -220,13 +220,13 @@ func (p *pebbleBatch) NewMVCCIterator(iterKind MVCCIterKind, opts IterOptions) M handle = p.db } if iter.inuse { - return newPebbleIterator(handle, p.iter, opts, StandardDurability) + return newPebbleIterator(handle, p.iter, opts, StandardDurability, p.SupportsRangeKeys()) } if iter.iter != nil { iter.setOptions(opts, StandardDurability) } else { - iter.init(handle, p.iter, p.iterUnused, opts, StandardDurability) + iter.init(handle, p.iter, p.iterUnused, opts, StandardDurability, p.SupportsRangeKeys()) if p.iter == nil { // For future cloning. p.iter = iter.iter @@ -257,13 +257,13 @@ func (p *pebbleBatch) NewEngineIterator(opts IterOptions) EngineIterator { handle = p.db } if iter.inuse { - return newPebbleIterator(handle, p.iter, opts, StandardDurability) + return newPebbleIterator(handle, p.iter, opts, StandardDurability, p.SupportsRangeKeys()) } if iter.iter != nil { iter.setOptions(opts, StandardDurability) } else { - iter.init(handle, p.iter, p.iterUnused, opts, StandardDurability) + iter.init(handle, p.iter, p.iterUnused, opts, StandardDurability, p.SupportsRangeKeys()) if p.iter == nil { // For future cloning. p.iter = iter.iter @@ -280,6 +280,11 @@ func (p *pebbleBatch) ConsistentIterators() bool { return true } +// SupportsRangeKeys implements the Batch interface. +func (p *pebbleBatch) SupportsRangeKeys() bool { + return p.db.FormatMajorVersion() >= pebble.FormatRangeKeys +} + // PinEngineStateForIterators implements the Batch interface. func (p *pebbleBatch) PinEngineStateForIterators() error { if p.iter == nil { @@ -403,6 +408,51 @@ func (p *pebbleBatch) ClearIterRange(start, end roachpb.Key) error { return nil } +// ExperimentalClearMVCCRangeKey implements the Engine interface. +func (p *pebbleBatch) ExperimentalClearMVCCRangeKey(rangeKey MVCCRangeKey) error { + if !p.SupportsRangeKeys() { + return nil // noop + } + if err := rangeKey.Validate(); err != nil { + return err + } + return p.batch.Experimental().RangeKeyUnset( + EncodeMVCCKeyPrefix(rangeKey.StartKey), + EncodeMVCCKeyPrefix(rangeKey.EndKey), + EncodeMVCCTimestampSuffix(rangeKey.Timestamp), + nil) +} + +// ExperimentalClearAllMVCCRangeKeys implements the Engine interface. +func (p *pebbleBatch) ExperimentalClearAllMVCCRangeKeys(start, end roachpb.Key) error { + if !p.SupportsRangeKeys() { + return nil // noop + } + rangeKey := MVCCRangeKey{StartKey: start, EndKey: end, Timestamp: hlc.MinTimestamp} + if err := rangeKey.Validate(); err != nil { + return err + } + return p.batch.Experimental().RangeKeyDelete( + EncodeMVCCKeyPrefix(start), EncodeMVCCKeyPrefix(end), nil) +} + +// ExperimentalPutMVCCRangeKey implements the Batch interface. +func (p *pebbleBatch) ExperimentalPutMVCCRangeKey(rangeKey MVCCRangeKey) error { + if !p.SupportsRangeKeys() { + return errors.Errorf("range keys not supported by Pebble database version %s", + p.db.FormatMajorVersion()) + } + if err := rangeKey.Validate(); err != nil { + return err + } + return p.batch.Experimental().RangeKeySet( + EncodeMVCCKeyPrefix(rangeKey.StartKey), + EncodeMVCCKeyPrefix(rangeKey.EndKey), + EncodeMVCCTimestampSuffix(rangeKey.Timestamp), + nil, + nil) +} + // Merge implements the Batch interface. func (p *pebbleBatch) Merge(key MVCCKey, value []byte) error { if len(key.Key) == 0 { diff --git a/pkg/storage/pebble_iterator.go b/pkg/storage/pebble_iterator.go index f8f57000657c..621628d88aff 100644 --- a/pkg/storage/pebble_iterator.go +++ b/pkg/storage/pebble_iterator.go @@ -18,6 +18,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/keys" "github.com/cockroachdb/cockroach/pkg/roachpb" "github.com/cockroachdb/cockroach/pkg/storage/enginepb" + "github.com/cockroachdb/cockroach/pkg/util/hlc" "github.com/cockroachdb/cockroach/pkg/util/protoutil" "github.com/cockroachdb/cockroach/pkg/util/uuid" "github.com/cockroachdb/errors" @@ -35,10 +36,18 @@ type pebbleIterator struct { // Reusable buffer for MVCCKey or EngineKey encoding. keyBuf []byte // Buffers for copying iterator bounds to. Note that the underlying memory - // is not GCed upon Close(), to reduce the number of overall allocations. - lowerBoundBuf []byte - upperBoundBuf []byte - + // is not GCed upon Close(), to reduce the number of overall allocations. We + // use two slices for each of the bounds since this caller should not change + // the slice holding the current bounds, that the callee (pebble.MVCCIterator) + // is currently using, until after the caller has made the SetOptions call. + lowerBoundBuf [2][]byte + upperBoundBuf [2][]byte + curBuf int + + // True if the iterator's underlying reader supports range keys. + // + // TODO(erikgrinaker): Remove after 22.2. + supportsRangeKeys bool // Set to true to govern whether to call SeekPrefixGE or SeekGE. Skips // SSTables based on MVCC/Engine key when true. prefix bool @@ -80,10 +89,11 @@ func newPebbleIterator( iterToClone cloneableIter, opts IterOptions, durability DurabilityRequirement, + supportsRangeKeys bool, ) *pebbleIterator { iter := pebbleIterPool.Get().(*pebbleIterator) iter.reusable = false // defensive - iter.init(handle, iterToClone, false /* iterUnused */, opts, durability) + iter.init(handle, iterToClone, false /* iterUnused */, opts, durability, supportsRangeKeys) return iter } @@ -98,12 +108,14 @@ func (p *pebbleIterator) init( iterUnused bool, opts IterOptions, durability DurabilityRequirement, + supportsRangeKeys bool, // TODO(erikgrinaker): remove after 22.2. ) { *p = pebbleIterator{ - keyBuf: p.keyBuf, - lowerBoundBuf: p.lowerBoundBuf, - upperBoundBuf: p.upperBoundBuf, - reusable: p.reusable, + keyBuf: p.keyBuf, + lowerBoundBuf: p.lowerBoundBuf, + upperBoundBuf: p.upperBoundBuf, + reusable: p.reusable, + supportsRangeKeys: supportsRangeKeys, } if iterToClone != nil { @@ -138,42 +150,33 @@ func (p *pebbleIterator) setOptions(opts IterOptions, durability DurabilityRequi panic("min timestamp hint set without max timestamp hint") } - // Omit setting the options if there's nothing to change, since calling - // pebble.Iterator.SetOptions() can make the next seek more expensive. - // - // We don't generate a pebble.Options for comparison, because we want to reuse - // the byte slices of the existing p.options for any key encoding. - optsChanged := p.options.OnlyReadGuaranteedDurable != (durability == GuaranteedDurability) || - // NB: Don't be tempted to omit SetOptions() if only the prefix option - // changes. This check has the side-effect of ensuring newly cloned - // iterators (with unknown existing options) always has SetOptions() called - // on them: we require either Prefix, UpperBound, or LowerBound to be set, - // so one of these won't match the zero value of a new iterator. - opts.Prefix != p.prefix || - (opts.UpperBound == nil) != (p.options.UpperBound == nil) || - (opts.LowerBound == nil) != (p.options.LowerBound == nil) || - // If p.options.UpperBound or LowerBound is set, they are encoded with a - // trailing 0x00 byte which must be omitted in comparisons. - (p.options.UpperBound != nil && - !bytes.Equal(opts.UpperBound, p.options.UpperBound[:len(p.options.UpperBound)-1])) || - (p.options.LowerBound != nil && - !bytes.Equal(opts.LowerBound, p.options.LowerBound[:len(p.options.LowerBound)-1])) || - // We can't compare these filters, so if any existing or new filters are set - // we consider them changed. - p.options.TableFilter != nil || - p.options.PointKeyFilters != nil || - opts.MaxTimestampHint.IsSet() || - opts.MinTimestampHint.IsSet() - if !optsChanged { - return + // If this Pebble database does not support range keys yet, fall back to + // only iterating over point keys to avoid panics. This is effectively the + // same, since a database without range key support contains no range keys, + // except in the case of RangesOnly where the iterator must always be empty. + if !p.supportsRangeKeys { + if opts.KeyTypes == IterKeyTypeRangesOnly { + opts.LowerBound = nil + opts.UpperBound = []byte{0} + } + opts.KeyTypes = IterKeyTypePointsOnly + opts.RangeKeyMaskingBelow = hlc.Timestamp{} } - // Reset the existing options. - p.options = pebble.IterOptions{ + // Generate new Pebble iterator options. + // + // NB: Make sure new options are accounted for in the optsChanged check below. + // Otherwise, the option may not take effect. + newOptions := pebble.IterOptions{ OnlyReadGuaranteedDurable: durability == GuaranteedDurability, + KeyTypes: opts.KeyTypes, + RangeKeyMasking: pebble.RangeKeyMasking{ + // TODO(erikgrinaker): Consider reusing a buffer if necessary. + Suffix: EncodeMVCCTimestampSuffix(opts.RangeKeyMaskingBelow), + }, } - p.prefix = opts.Prefix + newBuf := 1 - p.curBuf if opts.LowerBound != nil { // This is the same as // p.options.LowerBound = EncodeKeyToBuf(p.lowerBoundBuf[0][:0], MVCCKey{Key: opts.LowerBound}) @@ -181,21 +184,21 @@ func (p *pebbleIterator) setOptions(opts IterOptions, durability DurabilityRequi // Since we are encoding keys with an empty version anyway, we can just // append the NUL byte instead of calling the above encode functions which // will do the same thing. - p.lowerBoundBuf = append(p.lowerBoundBuf[:0], opts.LowerBound...) - p.lowerBoundBuf = append(p.lowerBoundBuf, 0x00) - p.options.LowerBound = p.lowerBoundBuf + p.lowerBoundBuf[newBuf] = append(p.lowerBoundBuf[newBuf][:0], opts.LowerBound...) + p.lowerBoundBuf[newBuf] = append(p.lowerBoundBuf[newBuf], 0x00) + newOptions.LowerBound = p.lowerBoundBuf[newBuf] } if opts.UpperBound != nil { // Same as above. - p.upperBoundBuf = append(p.upperBoundBuf[:0], opts.UpperBound...) - p.upperBoundBuf = append(p.upperBoundBuf, 0x00) - p.options.UpperBound = p.upperBoundBuf + p.upperBoundBuf[newBuf] = append(p.upperBoundBuf[newBuf][:0], opts.UpperBound...) + p.upperBoundBuf[newBuf] = append(p.upperBoundBuf[newBuf], 0x00) + newOptions.UpperBound = p.upperBoundBuf[newBuf] } if opts.MaxTimestampHint.IsSet() { encodedMinTS := string(encodeMVCCTimestamp(opts.MinTimestampHint)) encodedMaxTS := string(encodeMVCCTimestamp(opts.MaxTimestampHint)) - p.options.TableFilter = func(userProps map[string]string) bool { + newOptions.TableFilter = func(userProps map[string]string) bool { tableMinTS := userProps["crdb.ts.min"] if len(tableMinTS) == 0 { if opts.WithStats { @@ -219,13 +222,40 @@ func (p *pebbleIterator) setOptions(opts IterOptions, durability DurabilityRequi // We are given an inclusive [MinTimestampHint, MaxTimestampHint]. The // MVCCWAllTimeIntervalCollector has collected the WallTimes and we need // [min, max), i.e., exclusive on the upper bound. - p.options.PointKeyFilters = []pebble.BlockPropertyFilter{ + newOptions.PointKeyFilters = []pebble.BlockPropertyFilter{ sstable.NewBlockIntervalFilter(mvccWallTimeIntervalCollector, uint64(opts.MinTimestampHint.WallTime), uint64(opts.MaxTimestampHint.WallTime)+1), } } + // Omit setting the options if there's nothing to change, since calling + // pebble.Iterator.SetOptions() can make the next seek more expensive. + // + // NB: Don't be tempted to omit SetOptions() if only the prefix option + // changes. This check has the side-effect of ensuring newly cloned iterators + // (with unknown existing options) always has SetOptions() called on them: we + // require either Prefix, UpperBound, or LowerBound to be set, so one of these + // won't match the zero value of a new iterator. + optsChanged := opts.Prefix != p.prefix || + newOptions.OnlyReadGuaranteedDurable != p.options.OnlyReadGuaranteedDurable || + newOptions.KeyTypes != p.options.KeyTypes || + !bytes.Equal(newOptions.UpperBound, p.options.UpperBound) || + !bytes.Equal(newOptions.LowerBound, p.options.LowerBound) || + !bytes.Equal(newOptions.RangeKeyMasking.Suffix, p.options.RangeKeyMasking.Suffix) || + // We can't compare these filters, so if any existing or new filters are set + // we consider them changed. + newOptions.TableFilter != nil || p.options.TableFilter != nil || + newOptions.PointKeyFilters != nil || p.options.PointKeyFilters != nil + if !optsChanged { + return + } + + // Set the new iterator options. + p.options = newOptions + p.prefix = opts.Prefix + p.curBuf = newBuf + if p.iter != nil { p.iter.SetOptions(&p.options) } @@ -395,14 +425,31 @@ func (p *pebbleIterator) NextKey() { if valid, err := p.Valid(); err != nil || !valid { return } + wasPoint, _ := p.HasPointAndRange() p.keyBuf = append(p.keyBuf[:0], p.UnsafeKey().Key...) if !p.iter.Next() { return } - if bytes.Equal(p.keyBuf, p.UnsafeKey().Key) { + isPoint, _ := p.HasPointAndRange() + + // NB: a range key and point key both starting at a given key are considered + // separate keys during iteration, so calling NextKey() at the range key + // should land on the point key. + if wasPoint && isPoint && bytes.Equal(p.keyBuf, p.UnsafeKey().Key) { // This is equivalent to: // p.iter.SeekGE(EncodeKey(MVCCKey{p.UnsafeKey().Key.Next(), hlc.Timestamp{}})) p.iter.SeekGE(append(p.keyBuf, 0, 0)) + // If there's a range key straddling the seek point (e.g. a-c when seeking + // to b), it will be surfaced first. In that case, we skip past it to the + // next key, which may be either a point or range key but one starting past + // the seek key. + if isPoint, _ = p.HasPointAndRange(); !isPoint { + if rangeStart, _ := p.RangeBounds(); rangeStart.Compare(p.keyBuf) <= 0 { + if !p.iter.Next() { + return + } + } + } } } @@ -560,6 +607,63 @@ func (p *pebbleIterator) ValueProto(msg protoutil.Message) error { return protoutil.Unmarshal(value, msg) } +// HasPointAndRange implements the MVCCIterator interface. +func (p *pebbleIterator) HasPointAndRange() (bool, bool) { + // TODO(erikgrinaker): The MVCCIterator contract mandates returning false for + // an invalid iterator. We should improve pebbleIterator validity and error + // checking by doing it once per iterator operation and propagating errors. + if ok, err := p.Valid(); !ok || err != nil { + return false, false + } + return p.iter.HasPointAndRange() +} + +// RangeBounds implements the MVCCIterator interface. +func (p *pebbleIterator) RangeBounds() (roachpb.Key, roachpb.Key) { + start, end := p.iter.RangeBounds() + + // Avoid decoding empty keys: DecodeMVCCKey() will return errors for these, + // which are expensive to construct. + if len(start) == 0 && len(end) == 0 { + return nil, nil + } + + // TODO(erikgrinaker): We should surface this error somehow, but for now we + // follow UnsafeKey()'s example and silently return empty bounds. + startKey, err := DecodeMVCCKey(start) + if err != nil { + return nil, nil + } + endKey, err := DecodeMVCCKey(end) + if err != nil { + return nil, nil + } + + return startKey.Key, endKey.Key +} + +// RangeKeys implements the MVCCIterator interface. +func (p *pebbleIterator) RangeKeys() []MVCCRangeKey { + startKey, endKey := p.RangeBounds() + rangeKeys := p.iter.RangeKeys() + rangeValues := make([]MVCCRangeKey, 0, len(rangeKeys)) + + for _, rangeKey := range rangeKeys { + timestamp, err := decodeMVCCTimestampSuffix(rangeKey.Suffix) + if err != nil { + // TODO(erikgrinaker): We should surface this error somehow, but for now + // we follow UnsafeKey()'s example and silently skip them. + continue + } + rangeValues = append(rangeValues, MVCCRangeKey{ + StartKey: startKey, + EndKey: endKey, + Timestamp: timestamp, + }) + } + return rangeValues +} + // ComputeStats implements the MVCCIterator interface. func (p *pebbleIterator) ComputeStats( start, end roachpb.Key, nowNanos int64, diff --git a/pkg/storage/pebble_mvcc_scanner.go b/pkg/storage/pebble_mvcc_scanner.go index b5a4ecb12e09..c51f5385b050 100644 --- a/pkg/storage/pebble_mvcc_scanner.go +++ b/pkg/storage/pebble_mvcc_scanner.go @@ -281,8 +281,9 @@ func extractResultKey(repr []byte) roachpb.Key { return key } -// Go port of mvccScanner in libroach/mvcc.h. Stores all variables relating to -// one MVCCGet / MVCCScan call. +// pebbleMVCCScanner performs an MVCCGet / MVCCScan. It does not support MVCC +// range tombstones, and relies on a pointSynthesizingIter to synthesize point +// tombstones for range tombstones. type pebbleMVCCScanner struct { parent MVCCIterator // memAccount is used to account for the size of the scan results. @@ -1067,6 +1068,11 @@ func (p *pebbleMVCCScanner) iterValid() bool { } return false } + // We don't support range keys, and expect the caller to give us a + // pointSynthesizingIter for these. + if _, hasRange := p.parent.HasPointAndRange(); hasRange { + panic(errors.AssertionFailedf("unexpected range key at %s", p.parent.UnsafeKey())) + } return true } diff --git a/pkg/storage/point_synthesizing_iter.go b/pkg/storage/point_synthesizing_iter.go new file mode 100644 index 000000000000..3b3e8c09cdab --- /dev/null +++ b/pkg/storage/point_synthesizing_iter.go @@ -0,0 +1,614 @@ +// Copyright 2022 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package storage + +import ( + "sort" + + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/storage/enginepb" + "github.com/cockroachdb/cockroach/pkg/util" + "github.com/cockroachdb/cockroach/pkg/util/hlc" + "github.com/cockroachdb/cockroach/pkg/util/protoutil" + "github.com/cockroachdb/cockroach/pkg/util/uuid" + "github.com/cockroachdb/errors" +) + +// pointSynthesizingIter wraps an MVCCIterator, and synthesizes MVCC point +// tombstones for MVCC range tombstones above/below existing point keys, and at +// the start keys of MVCC range tombstones (truncated to iterator bounds). If +// emitOnSeekGE is set, it will also synthesize point tombstones around the +// SeekGE key if it does not have an existing point key. +// +// It does not emit MVCC range keys at all, since these would appear to conflict +// with the synthesized point keys. +type pointSynthesizingIter struct { + iter MVCCIterator + + // emitOnSeekGE will synthesize point tombstones for the SeekGE seek key if it + // overlaps with a range tombstone even if no point key exists. The primary + // use-case is to synthesize point tombstones for e.g. an MVCCGet that does + // not match a point key but overlaps a range key, which is necessary for + // conflict checks. + // + // This is optional, because e.g. pebbleMVCCScanner often uses seeks as an + // optimization to skip over old versions of a key, and we don't want to keep + // synthesizing point tombstones every time it skips ahead. + // + // TODO(erikgrinaker): This could instead check for prefix iterators, or a + // separate SeekPrefixGE() method, but we don't currently have APIs for it. + emitOnSeekGE bool + + // rangeKeys contains the timestamps of MVCC range tombstones at the current + // key position, for which point tombstones will be synthesized. + rangeKeys []hlc.Timestamp + + // rangeKeysPos is the current key (along the rangeKeys span) that points will + // be synthesized for. It is only set if rangeKeys is non-empty, and may + // differ from the underlying iterator position. See atPoint for details. + rangeKeysPos roachpb.Key + + // rangeKeysIdx is the rangeKeys index of the current/pending range tombstone + // to synthesize a point for. See atPoint for details. + rangeKeysIdx int + + // rangeKeysStart contains the start key of the current rangeKeys stack. It is + // only used to memoize rangeKeys for adjacent keys. + rangeKeysStart roachpb.Key + + // atPoint is true if the synthesizing iterator is positioned on a real point + // key in the underlying iterator. In that case, rangeKeysIdx points to a + // range key following the point key, or beyond the slice bounds when there + // are no further range keys at this key position. + // + // When false, the synthesizing iterator is positioned on a synthetic point + // tombstone given by rangeKeysIdx. In that case, the underlying iterator + // points to a following key (either a different version of the current key or + // a different point/range key) unless it is exhausted. + // + // The relative positioning is mirrored in the forward and reverse direction. + // For example, when atPoint is true and rangeKeys are exhausted, rangeKeysIdx + // will be len(rangeKeys) in the forward direction and -1 in the reverse + // direction. Similarly, the underlying iterator is always >= rangeKeysPos in + // the forward direction and <= in reverse. + // + // See also assertInvariants() which asserts positioning invariants. + atPoint bool + + // reverse is true when the current iterator direction is in reverse, i.e. + // following a SeekLT or Prev call. + reverse bool +} + +var _ MVCCIterator = new(pointSynthesizingIter) + +// newPointSynthesizingIter creates a new pointSynthesizingIter. +func newPointSynthesizingIter(iter MVCCIterator, emitOnSeekGE bool) *pointSynthesizingIter { + return &pointSynthesizingIter{ + iter: iter, + emitOnSeekGE: emitOnSeekGE, + } +} + +// updateRangeKeys updates i.rangeKeys and related fields with range keys from +// the underlying iterator. rangeKeysIdx is reset to the first/last range key. +func (i *pointSynthesizingIter) updateRangeKeys() { + if _, hasRange := i.iter.HasPointAndRange(); hasRange { + i.rangeKeysPos = append(i.rangeKeysPos[:0], i.iter.UnsafeKey().Key...) + if rangeStart, _ := i.iter.RangeBounds(); !rangeStart.Equal(i.rangeKeysStart) { + i.rangeKeys = i.rangeKeys[:0] + for _, rk := range i.iter.RangeKeys() { + i.rangeKeys = append(i.rangeKeys, rk.Timestamp) + } + i.rangeKeysStart = append(i.rangeKeysStart[:0], rangeStart...) + } + } else if len(i.rangeKeys) != 0 { + i.rangeKeys = i.rangeKeys[:0] + i.rangeKeysPos = i.rangeKeysPos[:0] + i.rangeKeysStart = i.rangeKeysStart[:0] + } + if !i.reverse { + i.rangeKeysIdx = 0 + } else { + i.rangeKeysIdx = len(i.rangeKeys) - 1 // NB: -1 is correct with no range keys + } +} + +// updateAtPoint updates i.atPoint according to whether the synthesizing +// iterator is positioned on the real point key in the underlying iterator. +// Requires i.rangeKeys to have been positioned first. +func (i *pointSynthesizingIter) updateAtPoint() { + if hasPoint, _ := i.iter.HasPointAndRange(); !hasPoint { + i.atPoint = false + } else if len(i.rangeKeys) == 0 { + i.atPoint = true + } else if point := i.iter.UnsafeKey(); !point.Key.Equal(i.rangeKeysPos) { + i.atPoint = false + } else if !i.reverse { + i.atPoint = i.rangeKeysIdx >= len(i.rangeKeys) || + point.Timestamp.IsEmpty() || + i.rangeKeys[i.rangeKeysIdx].LessEq(point.Timestamp) + } else { + i.atPoint = i.rangeKeysIdx < 0 || (point.Timestamp.IsSet() && + point.Timestamp.LessEq(i.rangeKeys[i.rangeKeysIdx])) + } +} + +// updatePosition updates the synthesizing iterator with the position of the +// underlying iterator. This may step the underlying iterator to position it +// correctly relative to bare range keys. +func (i *pointSynthesizingIter) updatePosition() { + if !i.reverse { + i.updateRangeKeys() + // If we're on a bare range key in the forward direction, we populate the + // range keys but then step iter ahead before updating the point position. + // The next position may be a point key colocated with the current range key + // position, which must be interleaved with the synthetic points. + if hasPoint, hasRange := i.iter.HasPointAndRange(); hasRange && !hasPoint { + i.iter.Next() + } + i.updateAtPoint() + } else { + // If we're on a bare range key in the reverse direction, and we've already + // emitted synthetic points for this key (as evidenced by rangeKeysPos), + // then we skip over the bare range key to avoid duplicates. + if hasPoint, hasRange := i.iter.HasPointAndRange(); hasRange && !hasPoint { + if i.iter.UnsafeKey().Key.Equal(i.rangeKeysPos) { + i.iter.Prev() + } + } + i.updateRangeKeys() + i.updateAtPoint() + } +} + +// SeekGE implements MVCCIterator. +func (i *pointSynthesizingIter) SeekGE(key MVCCKey) { + i.reverse = false + i.iter.SeekGE(key) + + // If we land in the middle of a bare range key and emitOnSeekGE is disabled, + // then skip over it to the next point/range key. However, if we're seeking to + // a specific version and don't find an older point key at the seek key, then + // we also need to peek backwards for an existing point key above us, which + // would mandate that we synthesize point keys here after all. + // + // TODO(erikgrinaker): It might be faster to first do an unversioned seek to + // look for previous points and then a versioned seek. + var positioned bool + if !i.emitOnSeekGE { + if hasPoint, hasRange := i.iter.HasPointAndRange(); hasRange && !hasPoint { + if rangeStart, _ := i.iter.RangeBounds(); !rangeStart.Equal(i.iter.UnsafeKey().Key) { + i.iter.Next() + + if key.Timestamp.IsSet() { + ok, err := i.iter.Valid() + if err == nil && (!ok || !key.Key.Equal(i.iter.UnsafeKey().Key)) { + i.iter.Prev() + if hasP, _ := i.iter.HasPointAndRange(); hasP && key.Key.Equal(i.iter.UnsafeKey().Key) { + i.updateRangeKeys() + positioned = true + } + i.iter.Next() + } + } + } + } + } + + if !positioned { + i.updateRangeKeys() + + // If we're now at a bare range key, it must either be at the start of it, + // or in the middle with emitOnSeekGE enabled. In either case, we want to + // move the iterator ahead to look for a point key that may be colocated + // with the start/seek key in order to interleave it. + if hasPoint, hasRange := i.iter.HasPointAndRange(); hasRange && !hasPoint { + i.iter.Next() + } + } + + // If we're seeking to a specific version, skip newer range tombstones. + if len(i.rangeKeys) > 0 && key.Timestamp.IsSet() && key.Key.Equal(i.rangeKeysPos) { + i.rangeKeysIdx = sort.Search(len(i.rangeKeys), func(idx int) bool { + return i.rangeKeys[idx].LessEq(key.Timestamp) + }) + } + + i.updateAtPoint() + + // It's possible that we seeked past all of the range key versions. In this + // case, we have to reposition on the next key (current iter key). + if !i.atPoint && i.rangeKeysIdx >= len(i.rangeKeys) { + i.updatePosition() + } +} + +// SeekIntentGE implements MVCCIterator. +func (i *pointSynthesizingIter) SeekIntentGE(key roachpb.Key, txnUUID uuid.UUID) { + i.reverse = false + i.iter.SeekIntentGE(key, txnUUID) + + // If we land in the middle of a bare range key and emitOnSeekGE is disabled, + // then skip over it to the next point/range key. + if !i.emitOnSeekGE { + if hasPoint, hasRange := i.iter.HasPointAndRange(); hasRange && !hasPoint { + if rangeStart, _ := i.iter.RangeBounds(); !rangeStart.Equal(i.iter.UnsafeKey().Key) { + i.iter.Next() + } + } + } + + i.updatePosition() +} + +// Next implements MVCCIterator. +func (i *pointSynthesizingIter) Next() { + // When changing direction, flip the relative positioning with iter. + if i.reverse { + i.reverse = false + if !i.atPoint && len(i.rangeKeys) == 0 { // iterator was exhausted + i.iter.Next() + i.updatePosition() + return + } else if i.atPoint { + i.rangeKeysIdx++ + } else { + i.iter.Next() + } + } + + // Step off the current point, either real or synthetic. + if i.atPoint { + i.iter.Next() + } else { + i.rangeKeysIdx++ + } + i.updateAtPoint() + + // If we've exhausted the current range keys, update with the underlying + // iterator position (which must now be at a later key). + if !i.atPoint && i.rangeKeysIdx >= len(i.rangeKeys) { + i.updatePosition() + } +} + +// NextKey implements MVCCIterator. +func (i *pointSynthesizingIter) NextKey() { + // When changing direction, flip the relative positioning with iter. + if i.reverse { + i.reverse = false + if !i.atPoint { + i.iter.Next() + } + } + // Don't call NextKey() if the underlying iterator is already on the next key. + if i.atPoint || i.rangeKeysPos.Equal(i.iter.UnsafeKey().Key) { + i.iter.NextKey() + } + i.updatePosition() +} + +// SeekLT implements MVCCIterator. +func (i *pointSynthesizingIter) SeekLT(key MVCCKey) { + i.reverse = true + i.iter.SeekLT(key) + + // If we did a versioned seek and find a range key that overlaps the seek key, + // we may have skipped over existing point key versions of the seek key. These + // would mandate that we synthesize point keys for the seek key after all, so + // we peek ahead to check for them. + // + // TODO(erikgrinaker): It might be faster to do an unversioned seek from the + // next key first to look for points. + var positioned bool + if key.Timestamp.IsSet() { + if hasPoint, hasRange := i.iter.HasPointAndRange(); hasRange { + if !hasPoint || !i.iter.UnsafeKey().Key.Equal(key.Key) { + if _, rangeEnd := i.iter.RangeBounds(); key.Key.Compare(rangeEnd) < 0 { + i.iter.Next() + if hasP, _ := i.iter.HasPointAndRange(); hasP && i.iter.UnsafeKey().Key.Equal(key.Key) { + i.updateRangeKeys() + positioned = true + } + i.iter.Prev() + } + } + } + } + + if !positioned { + i.updateRangeKeys() + } + + // If we're seeking to a specific version, skip over older range tombstones. + if key.Timestamp.IsSet() && key.Key.Equal(i.rangeKeysPos) { + i.rangeKeysIdx = sort.Search(len(i.rangeKeys), func(idx int) bool { + return i.rangeKeys[idx].LessEq(key.Timestamp) + }) - 1 + } + + i.updateAtPoint() + + // It's possible that we seeked past all of the range key versions. In this + // case, we have to reposition on the previous key (current iter key). + if !i.atPoint && i.rangeKeysIdx < 0 { + i.updatePosition() + } +} + +// Prev implements MVCCIterator. +func (i *pointSynthesizingIter) Prev() { + // When changing direction, flip the relative positioning with iter. + if !i.reverse { + i.reverse = true + if !i.atPoint && len(i.rangeKeys) == 0 { // iterator was exhausted + i.iter.Prev() + i.updatePosition() + return + } else if i.atPoint { + i.rangeKeysIdx-- + } else { + i.iter.Prev() + } + } + + // Step off the current point key (real or synthetic). + if i.atPoint { + i.iter.Prev() + } else { + i.rangeKeysIdx-- + } + i.updateAtPoint() + + // If we've exhausted the current range keys, and we're not positioned on a + // point key at the current range key position, then update with the + // underlying iter position (which must be before the current key). + if i.rangeKeysIdx < 0 && (!i.atPoint || !i.rangeKeysPos.Equal(i.iter.UnsafeKey().Key)) { + i.updatePosition() + } +} + +// Valid implements MVCCIterator. +func (i *pointSynthesizingIter) Valid() (bool, error) { + if util.RaceEnabled { + if err := i.assertInvariants(); err != nil { + panic(err) + } + } + if !i.atPoint && i.rangeKeysIdx >= 0 && i.rangeKeysIdx < len(i.rangeKeys) { + return true, nil // on synthetic point tombstone + } + return i.iter.Valid() +} + +// Key implements MVCCIterator. +func (i *pointSynthesizingIter) Key() MVCCKey { + return i.UnsafeKey().Clone() +} + +// UnsafeKey implements MVCCIterator. +func (i *pointSynthesizingIter) UnsafeKey() MVCCKey { + if i.atPoint { + return i.iter.UnsafeKey() + } + if i.rangeKeysIdx >= len(i.rangeKeys) || i.rangeKeysIdx < 0 { + return MVCCKey{} + } + return MVCCKey{ + Key: i.rangeKeysPos, + Timestamp: i.rangeKeys[i.rangeKeysIdx], + } +} + +// UnsafeRawKey implements MVCCIterator. +func (i *pointSynthesizingIter) UnsafeRawKey() []byte { + if i.atPoint { + return i.iter.UnsafeRawKey() + } + return EncodeMVCCKeyPrefix(i.rangeKeysPos) +} + +// UnsafeRawMVCCKey implements MVCCIterator. +func (i *pointSynthesizingIter) UnsafeRawMVCCKey() []byte { + if i.atPoint { + return i.iter.UnsafeRawMVCCKey() + } + return EncodeMVCCKey(i.UnsafeKey()) +} + +// Value implements MVCCIterator. +func (i *pointSynthesizingIter) Value() []byte { + if v := i.UnsafeValue(); v != nil { + return append([]byte(nil), v...) + } + return nil +} + +// UnsafeValue implements MVCCIterator. +func (i *pointSynthesizingIter) UnsafeValue() []byte { + if i.atPoint { + return i.iter.UnsafeValue() + } + return nil +} + +// ValueProto implements MVCCIterator. +func (i *pointSynthesizingIter) ValueProto(msg protoutil.Message) error { + if i.atPoint { + return i.iter.ValueProto(msg) + } + // Tombstones have no value, but ValueProto() also resets the message. + msg.Reset() + return nil +} + +// HasPointAndRange implements MVCCIterator. +func (i *pointSynthesizingIter) HasPointAndRange() (bool, bool) { + ok, err := i.Valid() + return ok && err == nil, false +} + +// RangeBounds implements MVCCIterator. +func (i *pointSynthesizingIter) RangeBounds() (roachpb.Key, roachpb.Key) { + return nil, nil +} + +// RangeKeys implements MVCCIterator. +func (i *pointSynthesizingIter) RangeKeys() []MVCCRangeKey { + return nil +} + +// Close implements MVCCIterator. +func (i *pointSynthesizingIter) Close() { + i.iter.Close() +} + +// ComputeStats implements MVCCIterator. +func (i *pointSynthesizingIter) ComputeStats( + start, end roachpb.Key, nowNanos int64, +) (enginepb.MVCCStats, error) { + return i.iter.ComputeStats(start, end, nowNanos) +} + +// FindSplitKey implements MVCCIterator. +func (i *pointSynthesizingIter) FindSplitKey( + start, end, minSplitKey roachpb.Key, targetSize int64, +) (MVCCKey, error) { + return i.iter.FindSplitKey(start, end, minSplitKey, targetSize) +} + +// Stats implements MVCCIterator. +func (i *pointSynthesizingIter) Stats() IteratorStats { + return i.iter.Stats() +} + +// SupportsPrev implements MVCCIterator. +func (i *pointSynthesizingIter) SupportsPrev() bool { + return i.iter.SupportsPrev() +} + +// assertInvariants asserts iterator invariants. +func (i *pointSynthesizingIter) assertInvariants() error { + // If the underlying iterator has errored, make sure we're not positioned on a + // synthetic point such that Valid() will surface the error. + if _, err := i.iter.Valid(); err != nil { + if !i.atPoint && i.rangeKeysIdx >= 0 && i.rangeKeysIdx < len(i.rangeKeys) { + return errors.NewAssertionErrorWithWrappedErrf(err, "iterator error with synthetic point %s", + i.rangeKeysPos) + } + return nil + } + + // When atPoint is true, the underlying iterator must be valid and on a point. + if i.atPoint { + if ok, _ := i.iter.Valid(); !ok { + return errors.AssertionFailedf("atPoint with invalid iter") + } + if hasPoint, _ := i.iter.HasPointAndRange(); !hasPoint { + return errors.AssertionFailedf("atPoint at non-point position %s", i.iter.UnsafeKey()) + } + } + + // rangeKeysIdx is never more than 1 outside of the slice bounds, and the + // excess depends on the direction: len(rangeKeys) in the forward direction, + // -1 in the reverse. + if i.rangeKeysIdx < 0 || i.rangeKeysIdx >= len(i.rangeKeys) { + if (!i.reverse && i.rangeKeysIdx != len(i.rangeKeys)) || (i.reverse && i.rangeKeysIdx != -1) { + return errors.AssertionFailedf("invalid rangeKeysIdx %d with length %d and reverse=%t", + i.rangeKeysIdx, len(i.rangeKeys), i.reverse) + } + } + + // If rangeKeys is empty, atPoint is true unless exhausted and other state is + // cleared. In this case, there's nothing more to check. + if len(i.rangeKeys) == 0 { + if ok, _ := i.iter.Valid(); ok && !i.atPoint { + return errors.AssertionFailedf("no rangeKeys nor atPoint") + } + if len(i.rangeKeysPos) > 0 { + return errors.AssertionFailedf("no rangeKeys but rangeKeysPos %s", i.rangeKeysPos) + } + if len(i.rangeKeysStart) > 0 { + return errors.AssertionFailedf("no rangeKeys but rangeKeysStart %s", i.rangeKeysStart) + } + return nil + } + + // rangeKeysStart must be set, and rangeKeysPos must be at or after it. This + // implies that rangeKeysPos must also be set. + if len(i.rangeKeysStart) == 0 { + return errors.AssertionFailedf("no rangeKeysStart at %s", i.iter.UnsafeKey()) + } + if i.rangeKeysPos.Compare(i.rangeKeysStart) < 0 { + return errors.AssertionFailedf("rangeKeysPos %s not after rangeKeysStart %s", + i.rangeKeysPos, i.rangeKeysStart) + } + + // rangeKeysIdx must be valid if we're not on a point. + if !i.atPoint && (i.rangeKeysIdx < 0 || i.rangeKeysIdx >= len(i.rangeKeys)) { + return errors.AssertionFailedf("not atPoint with invalid rangeKeysIdx %d at %s", + i.rangeKeysIdx, i.rangeKeysPos) + } + + // If the underlying iterator is exhausted, then there's nothing more to + // check. We must either be on a synthetic point key or exhausted iterator. + if ok, _ := i.iter.Valid(); !ok { + return nil + } + + // We now have range keys and a non-exhausted iterator. Check their relative + // positioning as minimum and maximum iter keys (in MVCC order). We can assume + // that overlapping range keys and point keys don't have the same timestamp, + // since this is enforced by MVCC mutations. + var minKey, maxKey MVCCKey + + // The iterator should never lag behind the range key position. + if !i.reverse { + minKey = MVCCKey{Key: i.rangeKeysPos} + } else { + maxKey = MVCCKey{Key: i.rangeKeysPos, Timestamp: hlc.MinTimestamp} + } + + // If we're not at a real point, then the iterator must be ahead of the + // current synthesized point. If we are on a point, then it must lie between + // the surrounding range keys (if they exist). + minIdx, maxIdx := -1, -1 + if !i.atPoint { + if !i.reverse { + minIdx = i.rangeKeysIdx + } else { + maxIdx = i.rangeKeysIdx + } + } else if !i.reverse { + minIdx = i.rangeKeysIdx - 1 + maxIdx = i.rangeKeysIdx + } else { + minIdx = i.rangeKeysIdx + maxIdx = i.rangeKeysIdx + 1 + } + if minIdx >= 0 && minIdx < len(i.rangeKeys) { + minKey = MVCCKey{Key: i.rangeKeysPos, Timestamp: i.rangeKeys[minIdx]} + } + if maxIdx >= 0 && maxIdx < len(i.rangeKeys) { + maxKey = MVCCKey{Key: i.rangeKeysPos, Timestamp: i.rangeKeys[maxIdx]} + } + + iterKey := i.iter.Key() + if minKey.Key != nil && iterKey.Compare(minKey) < 0 { + return errors.AssertionFailedf("iter %s below minimum key %s", iterKey, minKey) + } + if maxKey.Key != nil && iterKey.Compare(maxKey) > 0 { + return errors.AssertionFailedf("iter %s above maximum key %s", iterKey, maxKey) + } + + return nil +} diff --git a/pkg/storage/sst_iterator.go b/pkg/storage/sst_iterator.go index 9bd8b49b1b39..8b03acec9580 100644 --- a/pkg/storage/sst_iterator.go +++ b/pkg/storage/sst_iterator.go @@ -56,6 +56,10 @@ func NewSSTIterator(file sstable.ReadableFile) (SimpleMVCCIterator, error) { // It's compatible with sstables written by `RocksDBSstFileWriter` and // Pebble's `sstable.Writer`, and assumes the keys use Cockroach's MVCC // format. +// +// TODO(erikgrinaker): When this gets support for iterating over range keys, all +// call sites that use ComputeStatsForRange() must be updated to enable range +// keys for the iterators. func NewMemSSTIterator(data []byte, verify bool) (SimpleMVCCIterator, error) { sst, err := sstable.NewReader(vfs.NewMemFile(data), sstable.ReaderOptions{ Comparer: EngineComparer, @@ -158,3 +162,20 @@ func (r *sstIterator) UnsafeKey() MVCCKey { func (r *sstIterator) UnsafeValue() []byte { return r.value } + +// HasPointAndRange implements SimpleMVCCIterator. +// +// TODO(erikgrinaker): implement range key support. +func (r *sstIterator) HasPointAndRange() (bool, bool) { + return true, false +} + +// RangeBounds implements SimpleMVCCIterator. +func (r *sstIterator) RangeBounds() (roachpb.Key, roachpb.Key) { + return nil, nil +} + +// RangeKeys implements SimpleMVCCIterator. +func (r *sstIterator) RangeKeys() []MVCCRangeKey { + return nil +} diff --git a/pkg/storage/sst_writer.go b/pkg/storage/sst_writer.go index 38067b313dad..0938af17484a 100644 --- a/pkg/storage/sst_writer.go +++ b/pkg/storage/sst_writer.go @@ -142,6 +142,21 @@ func (fw *SSTWriter) ClearMVCCRange(start, end MVCCKey) error { return fw.clearRange(start, end) } +// ExperimentalPutMVCCRangeKey implements the Writer interface. +func (fw *SSTWriter) ExperimentalPutMVCCRangeKey(rangeKey MVCCRangeKey) error { + panic("not implemented") +} + +// ExperimentalClearMVCCRangeKey implements the Writer interface. +func (fw *SSTWriter) ExperimentalClearMVCCRangeKey(rangeKey MVCCRangeKey) error { + panic("not implemented") +} + +// ExperimentalClearAllMVCCRangeKeys implements the Writer interface. +func (fw *SSTWriter) ExperimentalClearAllMVCCRangeKeys(start, end roachpb.Key) error { + panic("not implemented") +} + func (fw *SSTWriter) clearRange(start, end MVCCKey) error { if fw.fw == nil { return errors.New("cannot call ClearRange on a closed writer") diff --git a/pkg/storage/testdata/mvcc_histories/range_key_iter b/pkg/storage/testdata/mvcc_histories/range_key_iter new file mode 100644 index 000000000000..70bd88406711 --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/range_key_iter @@ -0,0 +1,912 @@ +# Tests range key handling in MVCC iterators. +# +# Sets up following dataset, where x is tombstone, o-o is range tombstone, [] is intent. +# +# T +# 7 [a7] [d7] [j7] [l7] +# 6 f6 +# 5 o---------------o k5 +# 4 x x d4 f4 g4 +# 3 o-------o e3 o-------oh3 +# 2 a2 f2 g2 +# 1 o---------------------------------------o +# a b c d e f g h i j k +# +run ok +put_rangekey k=a end=k ts=1 +put_rangekey k=l end=m ts=1 +put_rangekey k=b end=d ts=3 +put k=a ts=2 v=a2 +del k=a ts=4 +del k=b ts=4 +put k=d ts=4 v=d4 +put k=e ts=3 v=e3 +put k=f ts=2 v=f2 +put k=g ts=2 v=g2 +put_rangekey k=f end=h ts=3 +put k=f ts=4 v=f4 +put k=f ts=6 v=f6 +put k=g ts=4 v=g4 +put_rangekey k=c end=g ts=5 +put k=h ts=3 v=h3 +del k=h ts=4 +put k=k ts=5 v=k5 +with t=A + txn_begin ts=7 + put k=a v=a7 + put k=d v=d7 + put k=j v=j7 + put k=l v=l7 +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=7.000000000,0 wto=false gul=0,0 +rangekey: {a-b}/[1.000000000,0] +rangekey: {b-c}/[3.000000000,0 1.000000000,0] +rangekey: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +rangekey: {d-f}/[5.000000000,0 1.000000000,0] +rangekey: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +rangekey: {g-h}/[3.000000000,0 1.000000000,0] +rangekey: {h-k}/[1.000000000,0] +rangekey: {l-m}/[1.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/7.000000000,0 -> /BYTES/a7 +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/4.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "e"/3.000000000,0 -> /BYTES/e3 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "f"/2.000000000,0 -> /BYTES/f2 +data: "g"/4.000000000,0 -> /BYTES/g4 +data: "g"/2.000000000,0 -> /BYTES/g2 +data: "h"/4.000000000,0 -> / +data: "h"/3.000000000,0 -> /BYTES/h3 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "j"/7.000000000,0 -> /BYTES/j7 +data: "k"/5.000000000,0 -> /BYTES/k5 +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "l"/7.000000000,0 -> /BYTES/l7 + +# Iterate across the entire span for all key types, and without intents. +run ok +iter_new types=pointsOnly +iter_seek_ge k=a +iter_scan +---- +iter_seek_ge: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "a"/7.000000000,0=/BYTES/a7 +iter_scan: "a"/4.000000000,0=/ +iter_scan: "a"/2.000000000,0=/BYTES/a2 +iter_scan: "b"/4.000000000,0=/ +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "d"/7.000000000,0=/BYTES/d7 +iter_scan: "d"/4.000000000,0=/BYTES/d4 +iter_scan: "e"/3.000000000,0=/BYTES/e3 +iter_scan: "f"/6.000000000,0=/BYTES/f6 +iter_scan: "f"/4.000000000,0=/BYTES/f4 +iter_scan: "f"/2.000000000,0=/BYTES/f2 +iter_scan: "g"/4.000000000,0=/BYTES/g4 +iter_scan: "g"/2.000000000,0=/BYTES/g2 +iter_scan: "h"/4.000000000,0=/ +iter_scan: "h"/3.000000000,0=/BYTES/h3 +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "j"/7.000000000,0=/BYTES/j7 +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "l"/7.000000000,0=/BYTES/l7 +iter_scan: . + +run ok +iter_new types=pointsAndRanges +iter_seek_ge k=a +iter_scan +---- +iter_seek_ge: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] +iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0] +iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0] +iter_scan: {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: {h-k}/[1.000000000,0] +iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0] +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {h-k}/[1.000000000,0] +iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0] +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {l-m}/[1.000000000,0] +iter_scan: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_scan: . + +run ok +iter_new types=rangesOnly +iter_seek_ge k=a +iter_scan +---- +iter_seek_ge: {a-b}/[1.000000000,0] +iter_scan: {a-b}/[1.000000000,0] +iter_scan: {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: {h-k}/[1.000000000,0] +iter_scan: {l-m}/[1.000000000,0] +iter_scan: . + +run ok +iter_new kind=keys types=pointsAndRanges +iter_seek_ge k=a +iter_scan +---- +iter_seek_ge: {a-b}/[1.000000000,0] +iter_scan: {a-b}/[1.000000000,0] +iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] +iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0] +iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0] +iter_scan: {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: {h-k}/[1.000000000,0] +iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0] +iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0] +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: {l-m}/[1.000000000,0] +iter_scan: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_scan: . + +# And do the same in reverse. +run ok +iter_new types=pointsOnly +iter_seek_lt k=z +iter_scan reverse +---- +iter_seek_lt: "l"/7.000000000,0=/BYTES/l7 +iter_scan: "l"/7.000000000,0=/BYTES/l7 +iter_scan: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "j"/7.000000000,0=/BYTES/j7 +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "h"/3.000000000,0=/BYTES/h3 +iter_scan: "h"/4.000000000,0=/ +iter_scan: "g"/2.000000000,0=/BYTES/g2 +iter_scan: "g"/4.000000000,0=/BYTES/g4 +iter_scan: "f"/2.000000000,0=/BYTES/f2 +iter_scan: "f"/4.000000000,0=/BYTES/f4 +iter_scan: "f"/6.000000000,0=/BYTES/f6 +iter_scan: "e"/3.000000000,0=/BYTES/e3 +iter_scan: "d"/4.000000000,0=/BYTES/d4 +iter_scan: "d"/7.000000000,0=/BYTES/d7 +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "b"/4.000000000,0=/ +iter_scan: "a"/2.000000000,0=/BYTES/a2 +iter_scan: "a"/4.000000000,0=/ +iter_scan: "a"/7.000000000,0=/BYTES/a7 +iter_scan: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: . + +run ok +iter_new types=pointsAndRanges +iter_seek_lt k=z +iter_scan reverse +---- +iter_seek_lt: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_scan: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_scan: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {l-m}/[1.000000000,0] +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0] +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {h-k}/[1.000000000,0] +iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0] +iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_scan: {h-k}/[1.000000000,0] +iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0] +iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0] +iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] +iter_scan: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: . + +run ok +iter_new types=rangesOnly +iter_seek_lt k=z +iter_scan reverse +---- +iter_seek_lt: {l-m}/[1.000000000,0] +iter_scan: {l-m}/[1.000000000,0] +iter_scan: {h-k}/[1.000000000,0] +iter_scan: {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: {a-b}/[1.000000000,0] +iter_scan: . + +run ok +iter_new kind=keys types=pointsAndRanges +iter_seek_lt k=z +iter_scan reverse +---- +iter_seek_lt: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_scan: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_scan: {l-m}/[1.000000000,0] +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0] +iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0] +iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_scan: {h-k}/[1.000000000,0] +iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0] +iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0] +iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] +iter_scan: {a-b}/[1.000000000,0] +iter_scan: . + +# Bounded scans. +run ok +iter_new types=pointsAndRanges k=bbb end=fff +iter_seek_ge k=a +iter_scan +iter_seek_lt k=z +iter_scan reverse +---- +iter_seek_ge: {bbb-c}/[3.000000000,0 1.000000000,0] +iter_scan: {bbb-c}/[3.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: f{-ff}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/6.000000000,0=/BYTES/f6 f{-ff}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/4.000000000,0=/BYTES/f4 f{-ff}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/2.000000000,0=/BYTES/f2 f{-ff}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: . +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 f{-ff}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/2.000000000,0=/BYTES/f2 f{-ff}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/4.000000000,0=/BYTES/f4 f{-ff}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/6.000000000,0=/BYTES/f6 f{-ff}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: f{-ff}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {bbb-c}/[3.000000000,0 1.000000000,0] +iter_scan: . + +# Try some masked scans at increasing timestamps. +run ok +iter_new types=pointsAndRanges maskBelow=1 +iter_seek_ge k=a +iter_scan +---- +iter_seek_ge: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] +iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0] +iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0] +iter_scan: {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: {h-k}/[1.000000000,0] +iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0] +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {h-k}/[1.000000000,0] +iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0] +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {l-m}/[1.000000000,0] +iter_scan: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_scan: . + +run ok +iter_new types=pointsAndRanges maskBelow=2 +iter_seek_ge k=a +iter_scan +---- +iter_seek_ge: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] +iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0] +iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0] +iter_scan: {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/2.000000000,0=/BYTES/f2 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: {h-k}/[1.000000000,0] +iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0] +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {h-k}/[1.000000000,0] +iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0] +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {l-m}/[1.000000000,0] +iter_scan: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_scan: . + +run ok +iter_new types=pointsAndRanges maskBelow=3 +iter_seek_ge k=a +iter_scan +---- +iter_seek_ge: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] +iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0] +iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0] +iter_scan: {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: {h-k}/[1.000000000,0] +iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0] +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {h-k}/[1.000000000,0] +iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0] +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {l-m}/[1.000000000,0] +iter_scan: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_scan: . + +run ok +iter_new types=pointsAndRanges maskBelow=4 +iter_seek_ge k=a +iter_scan +---- +iter_seek_ge: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] +iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0] +iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0] +iter_scan: {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/4.000000000,0=/BYTES/f4 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: {h-k}/[1.000000000,0] +iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0] +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {h-k}/[1.000000000,0] +iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0] +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {l-m}/[1.000000000,0] +iter_scan: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_scan: . + +run ok +iter_new types=pointsAndRanges maskBelow=5 +iter_seek_ge k=a +iter_scan +---- +iter_seek_ge: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] +iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0] +iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0] +iter_scan: {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: {h-k}/[1.000000000,0] +iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0] +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {h-k}/[1.000000000,0] +iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0] +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {l-m}/[1.000000000,0] +iter_scan: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_scan: . + +run ok +iter_new types=pointsAndRanges maskBelow=6 +iter_seek_ge k=a +iter_scan +---- +iter_seek_ge: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_scan: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] +iter_scan: "a"/4.000000000,0=/ {a-b}/[1.000000000,0] +iter_scan: "a"/2.000000000,0=/BYTES/a2 {a-b}/[1.000000000,0] +iter_scan: {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] +iter_scan: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_scan: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_scan: {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0 1.000000000,0] +iter_scan: {h-k}/[1.000000000,0] +iter_scan: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_scan: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0] +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {h-k}/[1.000000000,0] +iter_scan: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0] +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {l-m}/[1.000000000,0] +iter_scan: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_scan: . + +# Seek to d, iterate a few times, then reverse direction and iterate beyond seek point. +run ok +iter_new types=pointsAndRanges +iter_seek_ge k=d +iter_next +iter_next +iter_next +iter_next +iter_prev +iter_prev +iter_prev +iter_prev +iter_prev +iter_prev +---- +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_next: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_next: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_next: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_next: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_prev: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_prev: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] + +# Do a few seeks around an intent/point/range. +run ok +iter_new types=pointsAndRanges +iter_seek_ge k=d +iter_next +iter_seek_ge k=d ts=8 +iter_next +iter_seek_ge k=d ts=7 +iter_seek_ge k=d ts=5 +iter_next +iter_seek_ge k=d ts=4 +---- +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_next: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_ge: {d-f}/[5.000000000,0 1.000000000,0] +iter_next: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_ge: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_ge: {d-f}/[5.000000000,0 1.000000000,0] +iter_next: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_ge: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] + +run ok +iter_new types=pointsAndRanges +iter_seek_lt k=e +iter_seek_lt k=d ts=4 +iter_seek_lt k=d ts=7 +iter_prev +iter_seek_lt k=d +---- +iter_seek_lt: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_lt: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_lt: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_seek_lt: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] + +# Do the same, but switch direction immediately. +run ok +iter_new types=pointsAndRanges +iter_seek_ge k=d +iter_prev +iter_seek_ge k=d ts=8 +iter_prev +iter_prev +iter_seek_ge k=d ts=7 +iter_seek_ge k=d ts=5 +iter_prev +iter_seek_ge k=d ts=4 +---- +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_seek_ge: {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_seek_ge: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_ge: {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_ge: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] + +run ok +iter_new types=pointsAndRanges +iter_seek_lt k=e +iter_next +iter_seek_lt k=d ts=4 +iter_next +iter_seek_lt k=d ts=7 +iter_next +iter_seek_lt k=d +iter_next +---- +iter_seek_lt: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_next: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_lt: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_next: "d"/4.000000000,0=/BYTES/d4 {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_lt: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_next: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_lt: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_next: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] + +# Check that switching direction past an intent will yield the right range key. +# We also reverse seek to the intent, which must surface the correct range key. +run ok +iter_new types=pointsAndRanges +iter_seek_ge k=d +iter_prev +iter_next +iter_next +iter_prev +iter_prev +---- +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_next: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_next: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] + +run ok +iter_new types=pointsAndRanges +iter_seek_lt k=d ts=7 +iter_prev +iter_next +iter_seek_lt k=d ts=7 +iter_next +---- +iter_seek_lt: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_next: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_lt: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_next: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] + +# Seeking to keys with an intent will hit the intent immediately, both when +# it's at the start of a range key and in the middle of one. +run ok +iter_new types=pointsAndRanges +iter_seek_ge k=j +iter_next +iter_prev +iter_prev +iter_prev +iter_seek_ge k=d +iter_next +iter_prev +iter_prev +---- +iter_seek_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {h-k}/[1.000000000,0] +iter_next: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0] +iter_prev: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {h-k}/[1.000000000,0] +iter_prev: "h"/3.000000000,0=/BYTES/h3 {h-k}/[1.000000000,0] +iter_prev: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_next: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_prev: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] + +# Exhaust iterators and then switch directions. +run ok +iter_new types=pointsAndRanges +iter_seek_ge k=a ts=4 +iter_prev +iter_prev +iter_prev +iter_next +iter_next +---- +iter_seek_ge: "a"/4.000000000,0=/ {a-b}/[1.000000000,0] +iter_prev: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] +iter_prev: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_prev: . +iter_next: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_next: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] + +run ok +iter_new types=pointsAndRanges +iter_seek_ge k=l +iter_next +iter_next +iter_prev +iter_prev +iter_prev +---- +iter_seek_ge: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {l-m}/[1.000000000,0] +iter_next: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_next: . +iter_prev: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_prev: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {l-m}/[1.000000000,0] +iter_prev: "k"/5.000000000,0=/BYTES/k5 + +# Test NextKey() without and with intents/range keys, and with some seeks. +run ok +iter_new kind=keys types=pointsAndRanges +iter_seek_ge k=a +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +---- +iter_seek_ge: {a-b}/[1.000000000,0] +iter_next_key: "a"/7.000000000,0=/BYTES/a7 {a-b}/[1.000000000,0] +iter_next_key: {b-c}/[3.000000000,0 1.000000000,0] +iter_next_key: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] +iter_next_key: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_next_key: {d-f}/[5.000000000,0 1.000000000,0] +iter_next_key: "d"/7.000000000,0=/BYTES/d7 {d-f}/[5.000000000,0 1.000000000,0] +iter_next_key: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_next_key: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_next_key: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_next_key: {g-h}/[3.000000000,0 1.000000000,0] +iter_next_key: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0 1.000000000,0] +iter_next_key: {h-k}/[1.000000000,0] +iter_next_key: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_next_key: "j"/7.000000000,0=/BYTES/j7 {h-k}/[1.000000000,0] +iter_next_key: "k"/5.000000000,0=/BYTES/k5 +iter_next_key: {l-m}/[1.000000000,0] +iter_next_key: "l"/7.000000000,0=/BYTES/l7 {l-m}/[1.000000000,0] +iter_next_key: . + +run ok +iter_new types=pointsAndRanges +iter_seek_ge k=a +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +---- +iter_seek_ge: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {a-b}/[1.000000000,0] +iter_next_key: {b-c}/[3.000000000,0 1.000000000,0] +iter_next_key: "b"/4.000000000,0=/ {b-c}/[3.000000000,0 1.000000000,0] +iter_next_key: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_next_key: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_next_key: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] +iter_next_key: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_next_key: "f"/6.000000000,0=/BYTES/f6 {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_next_key: {g-h}/[3.000000000,0 1.000000000,0] +iter_next_key: "g"/4.000000000,0=/BYTES/g4 {g-h}/[3.000000000,0 1.000000000,0] +iter_next_key: {h-k}/[1.000000000,0] +iter_next_key: "h"/4.000000000,0=/ {h-k}/[1.000000000,0] +iter_next_key: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {h-k}/[1.000000000,0] +iter_next_key: "k"/5.000000000,0=/BYTES/k5 +iter_next_key: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {l-m}/[1.000000000,0] +iter_next_key: . + +run ok +iter_new types=pointsOnly +iter_seek_ge k=a +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +---- +iter_seek_ge: "a"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_next_key: "b"/4.000000000,0=/ +iter_next_key: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_next_key: "e"/3.000000000,0=/BYTES/e3 +iter_next_key: "f"/6.000000000,0=/BYTES/f6 +iter_next_key: "g"/4.000000000,0=/BYTES/g4 +iter_next_key: "h"/4.000000000,0=/ +iter_next_key: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_next_key: "k"/5.000000000,0=/BYTES/k5 +iter_next_key: "l"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_next_key: . + +run ok +iter_new types=rangesOnly +iter_seek_ge k=a +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +---- +iter_seek_ge: {a-b}/[1.000000000,0] +iter_next_key: {b-c}/[3.000000000,0 1.000000000,0] +iter_next_key: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_next_key: {d-f}/[5.000000000,0 1.000000000,0] +iter_next_key: {f-g}/[5.000000000,0 3.000000000,0 1.000000000,0] +iter_next_key: {g-h}/[3.000000000,0 1.000000000,0] +iter_next_key: {h-k}/[1.000000000,0] +iter_next_key: {l-m}/[1.000000000,0] +iter_next_key: . + +# Test NextKey() during seeks. +run ok +iter_new types=pointsAndRanges +iter_seek_ge k=g ts=2 +iter_next_key +iter_seek_ge k=d +iter_next_key +---- +iter_seek_ge: "g"/2.000000000,0=/BYTES/g2 {g-h}/[3.000000000,0 1.000000000,0] +iter_next_key: {h-k}/[1.000000000,0] +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_next_key: "e"/3.000000000,0=/BYTES/e3 {d-f}/[5.000000000,0 1.000000000,0] + +# Test SeekIntentGE both with and without intents and range keys. +run ok +iter_new types=pointsAndRanges +iter_seek_intent_ge k=b txn=A +iter_seek_intent_ge k=d txn=A +iter_seek_intent_ge k=i txn=A +iter_seek_intent_ge k=j txn=A +iter_seek_intent_ge k=k txn=A +---- +iter_seek_intent_ge: {b-c}/[3.000000000,0 1.000000000,0] +iter_seek_intent_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_intent_ge: {h-k}/[1.000000000,0] +iter_seek_intent_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true {h-k}/[1.000000000,0] +iter_seek_intent_ge: "k"/5.000000000,0=/BYTES/k5 + +run ok +iter_new kind=keys types=pointsAndRanges +iter_seek_intent_ge k=b txn=A +iter_seek_intent_ge k=d txn=A +iter_seek_intent_ge k=i txn=A +iter_seek_intent_ge k=j txn=A +iter_seek_intent_ge k=k txn=A +---- +iter_seek_intent_ge: {b-c}/[3.000000000,0 1.000000000,0] +iter_seek_intent_ge: {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_intent_ge: {h-k}/[1.000000000,0] +iter_seek_intent_ge: {h-k}/[1.000000000,0] +iter_seek_intent_ge: "k"/5.000000000,0=/BYTES/k5 + +run ok +iter_new types=pointsOnly +iter_seek_intent_ge k=b txn=A +iter_seek_intent_ge k=d txn=A +iter_seek_intent_ge k=i txn=A +iter_seek_intent_ge k=j txn=A +iter_seek_intent_ge k=k txn=A +---- +iter_seek_intent_ge: "b"/4.000000000,0=/ +iter_seek_intent_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "k"/5.000000000,0=/BYTES/k5 + +run ok +iter_new types=rangesOnly +iter_seek_intent_ge k=b txn=A +iter_seek_intent_ge k=d txn=A +iter_seek_intent_ge k=i txn=A +iter_seek_intent_ge k=j txn=A +iter_seek_intent_ge k=k txn=A +---- +iter_seek_intent_ge: {b-c}/[3.000000000,0 1.000000000,0] +iter_seek_intent_ge: {d-f}/[5.000000000,0 1.000000000,0] +iter_seek_intent_ge: {h-k}/[1.000000000,0] +iter_seek_intent_ge: {h-k}/[1.000000000,0] +iter_seek_intent_ge: {l-m}/[1.000000000,0] diff --git a/pkg/storage/testdata/mvcc_histories/range_key_point_synthesis b/pkg/storage/testdata/mvcc_histories/range_key_point_synthesis new file mode 100644 index 000000000000..3b8c97243afd --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/range_key_point_synthesis @@ -0,0 +1,1042 @@ +# Tests pointSynthesizingIter. +# +# Sets up following dataset, where x is tombstone, o-o is range tombstone, [] is intent. +# +# T +# 7 [d7] [j7] +# 6 f6 +# 5 o---------------o k5 o-----------o +# 4 x x d4 f4 g4 +# 3 o-------o e3 o-------oh3 o---o +# 2 a2 f2 g2 +# 1 o-------------------o o-----------o +# a b c d e f g h i j k l m n o p +# +run ok +put_rangekey k=a end=f ts=1 +put_rangekey k=h end=k ts=1 +put_rangekey k=b end=d ts=3 +put_rangekey k=n end=o ts=3 +put_rangekey k=l end=o ts=5 +put k=a ts=2 v=a2 +del k=a ts=4 +del k=b ts=4 +put k=d ts=4 v=d4 +put k=e ts=3 v=e3 +put k=f ts=2 v=f2 +put k=g ts=2 v=g2 +put_rangekey k=f end=h ts=3 +put k=f ts=4 v=f4 +put k=f ts=6 v=f6 +put k=g ts=4 v=g4 +put_rangekey k=c end=g ts=5 +put k=h ts=3 v=h3 +put k=k ts=5 v=k5 +with t=A + txn_begin ts=7 + put k=d v=d7 + put k=j v=j7 +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=7.000000000,0 wto=false gul=0,0 +rangekey: {a-b}/[1.000000000,0] +rangekey: {b-c}/[3.000000000,0 1.000000000,0] +rangekey: {c-d}/[5.000000000,0 3.000000000,0 1.000000000,0] +rangekey: {d-f}/[5.000000000,0 1.000000000,0] +rangekey: {f-g}/[5.000000000,0 3.000000000,0] +rangekey: {g-h}/[3.000000000,0] +rangekey: {h-k}/[1.000000000,0] +rangekey: {l-n}/[5.000000000,0] +rangekey: {n-o}/[5.000000000,0 3.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/4.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "e"/3.000000000,0 -> /BYTES/e3 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "f"/2.000000000,0 -> /BYTES/f2 +data: "g"/4.000000000,0 -> /BYTES/g4 +data: "g"/2.000000000,0 -> /BYTES/g2 +data: "h"/3.000000000,0 -> /BYTES/h3 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "j"/7.000000000,0 -> /BYTES/j7 +data: "k"/5.000000000,0 -> /BYTES/k5 + +# Iterate across the entire span, forward and reverse. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=a +iter_scan +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_scan: "a"/4.000000000,0=/ +iter_scan: "a"/2.000000000,0=/BYTES/a2 +iter_scan: "a"/1.000000000,0=/ +iter_scan: "b"/4.000000000,0=/ +iter_scan: "b"/3.000000000,0=/ +iter_scan: "b"/1.000000000,0=/ +iter_scan: "c"/5.000000000,0=/ +iter_scan: "c"/3.000000000,0=/ +iter_scan: "c"/1.000000000,0=/ +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "d"/7.000000000,0=/BYTES/d7 +iter_scan: "d"/5.000000000,0=/ +iter_scan: "d"/4.000000000,0=/BYTES/d4 +iter_scan: "d"/1.000000000,0=/ +iter_scan: "e"/5.000000000,0=/ +iter_scan: "e"/3.000000000,0=/BYTES/e3 +iter_scan: "e"/1.000000000,0=/ +iter_scan: "f"/6.000000000,0=/BYTES/f6 +iter_scan: "f"/5.000000000,0=/ +iter_scan: "f"/4.000000000,0=/BYTES/f4 +iter_scan: "f"/3.000000000,0=/ +iter_scan: "f"/2.000000000,0=/BYTES/f2 +iter_scan: "g"/4.000000000,0=/BYTES/g4 +iter_scan: "g"/3.000000000,0=/ +iter_scan: "g"/2.000000000,0=/BYTES/g2 +iter_scan: "h"/3.000000000,0=/BYTES/h3 +iter_scan: "h"/1.000000000,0=/ +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "j"/7.000000000,0=/BYTES/j7 +iter_scan: "j"/1.000000000,0=/ +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/5.000000000,0=/ +iter_scan: "n"/5.000000000,0=/ +iter_scan: "n"/3.000000000,0=/ +iter_scan: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=z +iter_scan reverse +---- +iter_seek_lt: "n"/3.000000000,0=/ +iter_scan: "n"/3.000000000,0=/ +iter_scan: "n"/5.000000000,0=/ +iter_scan: "l"/5.000000000,0=/ +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "j"/1.000000000,0=/ +iter_scan: "j"/7.000000000,0=/BYTES/j7 +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "h"/1.000000000,0=/ +iter_scan: "h"/3.000000000,0=/BYTES/h3 +iter_scan: "g"/2.000000000,0=/BYTES/g2 +iter_scan: "g"/3.000000000,0=/ +iter_scan: "g"/4.000000000,0=/BYTES/g4 +iter_scan: "f"/2.000000000,0=/BYTES/f2 +iter_scan: "f"/3.000000000,0=/ +iter_scan: "f"/4.000000000,0=/BYTES/f4 +iter_scan: "f"/5.000000000,0=/ +iter_scan: "f"/6.000000000,0=/BYTES/f6 +iter_scan: "e"/1.000000000,0=/ +iter_scan: "e"/3.000000000,0=/BYTES/e3 +iter_scan: "e"/5.000000000,0=/ +iter_scan: "d"/1.000000000,0=/ +iter_scan: "d"/4.000000000,0=/BYTES/d4 +iter_scan: "d"/5.000000000,0=/ +iter_scan: "d"/7.000000000,0=/BYTES/d7 +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "c"/1.000000000,0=/ +iter_scan: "c"/3.000000000,0=/ +iter_scan: "c"/5.000000000,0=/ +iter_scan: "b"/1.000000000,0=/ +iter_scan: "b"/3.000000000,0=/ +iter_scan: "b"/4.000000000,0=/ +iter_scan: "a"/1.000000000,0=/ +iter_scan: "a"/2.000000000,0=/BYTES/a2 +iter_scan: "a"/4.000000000,0=/ +iter_scan: . + +# Iterate across the entire span using NextKey(). +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=a +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +iter_next_key +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_next_key: "b"/4.000000000,0=/ +iter_next_key: "c"/5.000000000,0=/ +iter_next_key: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_next_key: "e"/5.000000000,0=/ +iter_next_key: "f"/6.000000000,0=/BYTES/f6 +iter_next_key: "g"/4.000000000,0=/BYTES/g4 +iter_next_key: "h"/3.000000000,0=/BYTES/h3 +iter_next_key: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_next_key: "k"/5.000000000,0=/BYTES/k5 +iter_next_key: "l"/5.000000000,0=/ +iter_next_key: "n"/5.000000000,0=/ +iter_next_key: . + +# Unversioned seeks. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=a +iter_seek_ge k=b +iter_seek_ge k=c +iter_seek_ge k=d +iter_seek_ge k=e +iter_seek_ge k=f +iter_seek_ge k=g +iter_seek_ge k=h +iter_seek_ge k=i +iter_seek_ge k=j +iter_seek_ge k=k +iter_seek_ge k=l +iter_seek_ge k=m +iter_seek_ge k=n +iter_seek_ge k=o +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_seek_ge: "b"/4.000000000,0=/ +iter_seek_ge: "c"/5.000000000,0=/ +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "e"/5.000000000,0=/ +iter_seek_ge: "f"/6.000000000,0=/BYTES/f6 +iter_seek_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_ge: "h"/3.000000000,0=/BYTES/h3 +iter_seek_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "k"/5.000000000,0=/BYTES/k5 +iter_seek_ge: "l"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: . + +run ok +iter_new types=pointsAndRanges pointSynthesis emitOnSeekGE +iter_seek_ge k=a +iter_seek_ge k=b +iter_seek_ge k=c +iter_seek_ge k=d +iter_seek_ge k=e +iter_seek_ge k=f +iter_seek_ge k=g +iter_seek_ge k=h +iter_seek_ge k=i +iter_seek_ge k=j +iter_seek_ge k=k +iter_seek_ge k=l +iter_seek_ge k=m +iter_seek_ge k=n +iter_seek_ge k=o +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_seek_ge: "b"/4.000000000,0=/ +iter_seek_ge: "c"/5.000000000,0=/ +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "e"/5.000000000,0=/ +iter_seek_ge: "f"/6.000000000,0=/BYTES/f6 +iter_seek_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_ge: "h"/3.000000000,0=/BYTES/h3 +iter_seek_ge: "i"/1.000000000,0=/ +iter_seek_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "k"/5.000000000,0=/BYTES/k5 +iter_seek_ge: "l"/5.000000000,0=/ +iter_seek_ge: "m"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=o +iter_seek_lt k=n +iter_seek_lt k=m +iter_seek_lt k=l +iter_seek_lt k=k +iter_seek_lt k=j +iter_seek_lt k=i +iter_seek_lt k=h +iter_seek_lt k=g +iter_seek_lt k=f +iter_seek_lt k=e +iter_seek_lt k=d +iter_seek_lt k=c +iter_seek_lt k=b +iter_seek_lt k=a +---- +iter_seek_lt: "n"/3.000000000,0=/ +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 +iter_seek_lt: "j"/1.000000000,0=/ +iter_seek_lt: "h"/1.000000000,0=/ +iter_seek_lt: "h"/1.000000000,0=/ +iter_seek_lt: "g"/2.000000000,0=/BYTES/g2 +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 +iter_seek_lt: "e"/1.000000000,0=/ +iter_seek_lt: "d"/1.000000000,0=/ +iter_seek_lt: "c"/1.000000000,0=/ +iter_seek_lt: "b"/1.000000000,0=/ +iter_seek_lt: "a"/1.000000000,0=/ +iter_seek_lt: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_intent_ge k=a txn=A +iter_seek_intent_ge k=b txn=A +iter_seek_intent_ge k=c txn=A +iter_seek_intent_ge k=d txn=A +iter_seek_intent_ge k=e txn=A +iter_seek_intent_ge k=f txn=A +iter_seek_intent_ge k=g txn=A +iter_seek_intent_ge k=h txn=A +iter_seek_intent_ge k=i txn=A +iter_seek_intent_ge k=j txn=A +iter_seek_intent_ge k=k txn=A +iter_seek_intent_ge k=l txn=A +iter_seek_intent_ge k=m txn=A +iter_seek_intent_ge k=n txn=A +iter_seek_intent_ge k=o txn=A +---- +iter_seek_intent_ge: "a"/4.000000000,0=/ +iter_seek_intent_ge: "b"/4.000000000,0=/ +iter_seek_intent_ge: "c"/5.000000000,0=/ +iter_seek_intent_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "e"/5.000000000,0=/ +iter_seek_intent_ge: "f"/6.000000000,0=/BYTES/f6 +iter_seek_intent_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_intent_ge: "h"/3.000000000,0=/BYTES/h3 +iter_seek_intent_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "k"/5.000000000,0=/BYTES/k5 +iter_seek_intent_ge: "l"/5.000000000,0=/ +iter_seek_intent_ge: "n"/5.000000000,0=/ +iter_seek_intent_ge: "n"/5.000000000,0=/ +iter_seek_intent_ge: . + +run ok +iter_new types=pointsAndRanges pointSynthesis emitOnSeekGE +iter_seek_intent_ge k=a txn=A +iter_seek_intent_ge k=b txn=A +iter_seek_intent_ge k=c txn=A +iter_seek_intent_ge k=d txn=A +iter_seek_intent_ge k=e txn=A +iter_seek_intent_ge k=f txn=A +iter_seek_intent_ge k=g txn=A +iter_seek_intent_ge k=h txn=A +iter_seek_intent_ge k=i txn=A +iter_seek_intent_ge k=j txn=A +iter_seek_intent_ge k=k txn=A +iter_seek_intent_ge k=l txn=A +iter_seek_intent_ge k=m txn=A +iter_seek_intent_ge k=n txn=A +iter_seek_intent_ge k=o txn=A +---- +iter_seek_intent_ge: "a"/4.000000000,0=/ +iter_seek_intent_ge: "b"/4.000000000,0=/ +iter_seek_intent_ge: "c"/5.000000000,0=/ +iter_seek_intent_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "e"/5.000000000,0=/ +iter_seek_intent_ge: "f"/6.000000000,0=/BYTES/f6 +iter_seek_intent_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_intent_ge: "h"/3.000000000,0=/BYTES/h3 +iter_seek_intent_ge: "i"/1.000000000,0=/ +iter_seek_intent_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_intent_ge: "k"/5.000000000,0=/BYTES/k5 +iter_seek_intent_ge: "l"/5.000000000,0=/ +iter_seek_intent_ge: "m"/5.000000000,0=/ +iter_seek_intent_ge: "n"/5.000000000,0=/ +iter_seek_intent_ge: . + +# Versioned seeks. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=a ts=5 +iter_seek_ge k=a ts=4 +iter_seek_ge k=a ts=3 +iter_seek_ge k=a ts=2 +iter_seek_ge k=a ts=1 +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_seek_ge: "a"/4.000000000,0=/ +iter_seek_ge: "a"/2.000000000,0=/BYTES/a2 +iter_seek_ge: "a"/2.000000000,0=/BYTES/a2 +iter_seek_ge: "a"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=b ts=5 +iter_seek_ge k=b ts=4 +iter_seek_ge k=b ts=3 +iter_seek_ge k=b ts=2 +iter_seek_ge k=b ts=1 +---- +iter_seek_ge: "b"/4.000000000,0=/ +iter_seek_ge: "b"/4.000000000,0=/ +iter_seek_ge: "b"/3.000000000,0=/ +iter_seek_ge: "b"/1.000000000,0=/ +iter_seek_ge: "b"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=c ts=6 +iter_seek_ge k=c ts=5 +iter_seek_ge k=c ts=4 +iter_seek_ge k=c ts=3 +iter_seek_ge k=c ts=2 +iter_seek_ge k=c ts=1 +---- +iter_seek_ge: "c"/5.000000000,0=/ +iter_seek_ge: "c"/5.000000000,0=/ +iter_seek_ge: "c"/3.000000000,0=/ +iter_seek_ge: "c"/3.000000000,0=/ +iter_seek_ge: "c"/1.000000000,0=/ +iter_seek_ge: "c"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=d ts=0 +iter_seek_ge k=d ts=8 +iter_seek_ge k=d ts=7 +iter_seek_ge k=d ts=6 +iter_seek_ge k=d ts=5 +iter_seek_ge k=d ts=4 +iter_seek_ge k=d ts=3 +iter_seek_ge k=d ts=2 +iter_seek_ge k=d ts=1 +---- +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "d"/7.000000000,0=/BYTES/d7 +iter_seek_ge: "d"/7.000000000,0=/BYTES/d7 +iter_seek_ge: "d"/5.000000000,0=/ +iter_seek_ge: "d"/5.000000000,0=/ +iter_seek_ge: "d"/4.000000000,0=/BYTES/d4 +iter_seek_ge: "d"/1.000000000,0=/ +iter_seek_ge: "d"/1.000000000,0=/ +iter_seek_ge: "d"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=e ts=6 +iter_seek_ge k=e ts=5 +iter_seek_ge k=e ts=4 +iter_seek_ge k=e ts=3 +iter_seek_ge k=e ts=2 +iter_seek_ge k=e ts=1 +---- +iter_seek_ge: "e"/5.000000000,0=/ +iter_seek_ge: "e"/5.000000000,0=/ +iter_seek_ge: "e"/3.000000000,0=/BYTES/e3 +iter_seek_ge: "e"/3.000000000,0=/BYTES/e3 +iter_seek_ge: "e"/1.000000000,0=/ +iter_seek_ge: "e"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=f ts=7 +iter_seek_ge k=f ts=6 +iter_seek_ge k=f ts=5 +iter_seek_ge k=f ts=4 +iter_seek_ge k=f ts=3 +iter_seek_ge k=f ts=2 +iter_seek_ge k=f ts=1 +---- +iter_seek_ge: "f"/6.000000000,0=/BYTES/f6 +iter_seek_ge: "f"/6.000000000,0=/BYTES/f6 +iter_seek_ge: "f"/5.000000000,0=/ +iter_seek_ge: "f"/4.000000000,0=/BYTES/f4 +iter_seek_ge: "f"/3.000000000,0=/ +iter_seek_ge: "f"/2.000000000,0=/BYTES/f2 +iter_seek_ge: "g"/4.000000000,0=/BYTES/g4 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=g ts=6 +iter_seek_ge k=g ts=5 +iter_seek_ge k=g ts=4 +iter_seek_ge k=g ts=3 +iter_seek_ge k=g ts=2 +iter_seek_ge k=g ts=1 +---- +iter_seek_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_ge: "g"/4.000000000,0=/BYTES/g4 +iter_seek_ge: "g"/3.000000000,0=/ +iter_seek_ge: "g"/2.000000000,0=/BYTES/g2 +iter_seek_ge: "h"/3.000000000,0=/BYTES/h3 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=h ts=4 +iter_seek_ge k=h ts=3 +iter_seek_ge k=h ts=2 +iter_seek_ge k=h ts=1 +---- +iter_seek_ge: "h"/3.000000000,0=/BYTES/h3 +iter_seek_ge: "h"/3.000000000,0=/BYTES/h3 +iter_seek_ge: "h"/1.000000000,0=/ +iter_seek_ge: "h"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=i ts=2 +iter_seek_ge k=i ts=1 +---- +iter_seek_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_ge: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=j ts=8 +iter_seek_ge k=j ts=7 +iter_seek_ge k=j ts=6 +iter_seek_ge k=j ts=1 +---- +iter_seek_ge: "j"/7.000000000,0=/BYTES/j7 +iter_seek_ge: "j"/7.000000000,0=/BYTES/j7 +iter_seek_ge: "j"/1.000000000,0=/ +iter_seek_ge: "j"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=k ts=6 +iter_seek_ge k=k ts=5 +iter_seek_ge k=k ts=4 +---- +iter_seek_ge: "k"/5.000000000,0=/BYTES/k5 +iter_seek_ge: "k"/5.000000000,0=/BYTES/k5 +iter_seek_ge: "l"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=l ts=6 +iter_seek_ge k=l ts=5 +iter_seek_ge k=l ts=4 +---- +iter_seek_ge: "l"/5.000000000,0=/ +iter_seek_ge: "l"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=m ts=6 +iter_seek_ge k=m ts=5 +iter_seek_ge k=m ts=4 +---- +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=n ts=6 +iter_seek_ge k=n ts=5 +iter_seek_ge k=n ts=4 +iter_seek_ge k=n ts=3 +iter_seek_ge k=n ts=2 +---- +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/3.000000000,0=/ +iter_seek_ge: "n"/3.000000000,0=/ +iter_seek_ge: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=o ts=6 +iter_seek_ge k=o ts=5 +iter_seek_ge k=o ts=4 +iter_seek_ge k=o ts=3 +---- +iter_seek_ge: . +iter_seek_ge: . +iter_seek_ge: . +iter_seek_ge: . + +# Versioned seeks with emitOnSeekGE. +run ok +iter_new types=pointsAndRanges pointSynthesis emitOnSeekGE +iter_seek_ge k=l ts=6 +iter_seek_ge k=l ts=5 +iter_seek_ge k=l ts=4 +---- +iter_seek_ge: "l"/5.000000000,0=/ +iter_seek_ge: "l"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis emitOnSeekGE +iter_seek_ge k=m ts=6 +iter_seek_ge k=m ts=5 +iter_seek_ge k=m ts=4 +---- +iter_seek_ge: "m"/5.000000000,0=/ +iter_seek_ge: "m"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis emitOnSeekGE +iter_seek_ge k=n ts=6 +iter_seek_ge k=n ts=5 +iter_seek_ge k=n ts=4 +iter_seek_ge k=n ts=3 +---- +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/5.000000000,0=/ +iter_seek_ge: "n"/3.000000000,0=/ +iter_seek_ge: "n"/3.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis emitOnSeekGE +iter_seek_ge k=o ts=6 +iter_seek_ge k=o ts=5 +iter_seek_ge k=o ts=4 +---- +iter_seek_ge: . +iter_seek_ge: . +iter_seek_ge: . + +# Versioned reverse seeks. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=a ts=1 +iter_seek_lt k=a ts=2 +iter_seek_lt k=a ts=3 +iter_seek_lt k=a ts=4 +iter_seek_lt k=a ts=5 +---- +iter_seek_lt: "a"/2.000000000,0=/BYTES/a2 +iter_seek_lt: "a"/4.000000000,0=/ +iter_seek_lt: "a"/4.000000000,0=/ +iter_seek_lt: . +iter_seek_lt: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=b ts=1 +iter_seek_lt k=b ts=2 +iter_seek_lt k=b ts=3 +iter_seek_lt k=b ts=4 +iter_seek_lt k=b ts=5 +---- +iter_seek_lt: "b"/3.000000000,0=/ +iter_seek_lt: "b"/3.000000000,0=/ +iter_seek_lt: "b"/4.000000000,0=/ +iter_seek_lt: "a"/1.000000000,0=/ +iter_seek_lt: "a"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=c ts=1 +iter_seek_lt k=c ts=2 +iter_seek_lt k=c ts=3 +iter_seek_lt k=c ts=4 +iter_seek_lt k=c ts=5 +iter_seek_lt k=c ts=6 +---- +iter_seek_lt: "c"/3.000000000,0=/ +iter_seek_lt: "c"/3.000000000,0=/ +iter_seek_lt: "c"/5.000000000,0=/ +iter_seek_lt: "c"/5.000000000,0=/ +iter_seek_lt: "b"/1.000000000,0=/ +iter_seek_lt: "b"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=d ts=1 +iter_seek_lt k=d ts=2 +iter_seek_lt k=d ts=3 +iter_seek_lt k=d ts=4 +iter_seek_lt k=d ts=5 +iter_seek_lt k=d ts=6 +iter_seek_lt k=d ts=7 +iter_seek_lt k=d ts=8 +---- +iter_seek_lt: "d"/4.000000000,0=/BYTES/d4 +iter_seek_lt: "d"/4.000000000,0=/BYTES/d4 +iter_seek_lt: "d"/4.000000000,0=/BYTES/d4 +iter_seek_lt: "d"/5.000000000,0=/ +iter_seek_lt: "d"/7.000000000,0=/BYTES/d7 +iter_seek_lt: "d"/7.000000000,0=/BYTES/d7 +iter_seek_lt: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_lt: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=e ts=1 +iter_seek_lt k=e ts=2 +iter_seek_lt k=e ts=3 +iter_seek_lt k=e ts=4 +iter_seek_lt k=e ts=5 +iter_seek_lt k=e ts=6 +---- +iter_seek_lt: "e"/3.000000000,0=/BYTES/e3 +iter_seek_lt: "e"/3.000000000,0=/BYTES/e3 +iter_seek_lt: "e"/5.000000000,0=/ +iter_seek_lt: "e"/5.000000000,0=/ +iter_seek_lt: "d"/1.000000000,0=/ +iter_seek_lt: "d"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=f ts=1 +iter_seek_lt k=f ts=2 +iter_seek_lt k=f ts=3 +iter_seek_lt k=f ts=4 +iter_seek_lt k=f ts=5 +iter_seek_lt k=f ts=6 +iter_seek_lt k=f ts=7 +---- +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 +iter_seek_lt: "f"/3.000000000,0=/ +iter_seek_lt: "f"/4.000000000,0=/BYTES/f4 +iter_seek_lt: "f"/5.000000000,0=/ +iter_seek_lt: "f"/6.000000000,0=/BYTES/f6 +iter_seek_lt: "e"/1.000000000,0=/ +iter_seek_lt: "e"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=g ts=1 +iter_seek_lt k=g ts=2 +iter_seek_lt k=g ts=3 +iter_seek_lt k=g ts=4 +iter_seek_lt k=g ts=5 +iter_seek_lt k=g ts=6 +---- +iter_seek_lt: "g"/2.000000000,0=/BYTES/g2 +iter_seek_lt: "g"/3.000000000,0=/ +iter_seek_lt: "g"/4.000000000,0=/BYTES/g4 +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=h ts=1 +iter_seek_lt k=h ts=2 +iter_seek_lt k=h ts=3 +iter_seek_lt k=h ts=4 +---- +iter_seek_lt: "h"/3.000000000,0=/BYTES/h3 +iter_seek_lt: "h"/3.000000000,0=/BYTES/h3 +iter_seek_lt: "g"/2.000000000,0=/BYTES/g2 +iter_seek_lt: "g"/2.000000000,0=/BYTES/g2 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=i ts=1 +iter_seek_lt k=i ts=2 +---- +iter_seek_lt: "h"/1.000000000,0=/ +iter_seek_lt: "h"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=j ts=1 +iter_seek_lt k=j ts=6 +iter_seek_lt k=j ts=7 +iter_seek_lt k=j ts=8 +---- +iter_seek_lt: "j"/7.000000000,0=/BYTES/j7 +iter_seek_lt: "j"/7.000000000,0=/BYTES/j7 +iter_seek_lt: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_seek_lt: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=k ts=1 +iter_seek_lt k=k ts=4 +iter_seek_lt k=k ts=5 +iter_seek_lt k=k ts=6 +---- +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 +iter_seek_lt: "j"/1.000000000,0=/ +iter_seek_lt: "j"/1.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=l ts=4 +iter_seek_lt k=l ts=5 +iter_seek_lt k=l ts=6 +---- +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=l ts=4 +iter_seek_lt k=l ts=5 +iter_seek_lt k=l ts=6 +---- +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 +iter_seek_lt: "k"/5.000000000,0=/BYTES/k5 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=m ts=4 +iter_seek_lt k=m ts=5 +iter_seek_lt k=m ts=6 +---- +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "l"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=n ts=2 +iter_seek_lt k=n ts=3 +iter_seek_lt k=n ts=4 +iter_seek_lt k=n ts=5 +iter_seek_lt k=n ts=6 +---- +iter_seek_lt: "n"/3.000000000,0=/ +iter_seek_lt: "n"/5.000000000,0=/ +iter_seek_lt: "n"/5.000000000,0=/ +iter_seek_lt: "l"/5.000000000,0=/ +iter_seek_lt: "l"/5.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=o ts=1 +---- +iter_seek_lt: "n"/3.000000000,0=/ + +# Seeks with opposite scans. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=d +iter_scan reverse +---- +iter_seek_ge: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "c"/1.000000000,0=/ +iter_scan: "c"/3.000000000,0=/ +iter_scan: "c"/5.000000000,0=/ +iter_scan: "b"/1.000000000,0=/ +iter_scan: "b"/3.000000000,0=/ +iter_scan: "b"/4.000000000,0=/ +iter_scan: "a"/1.000000000,0=/ +iter_scan: "a"/2.000000000,0=/BYTES/a2 +iter_scan: "a"/4.000000000,0=/ +iter_scan: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=d ts=5 +iter_scan reverse +---- +iter_seek_ge: "d"/5.000000000,0=/ +iter_scan: "d"/5.000000000,0=/ +iter_scan: "d"/7.000000000,0=/BYTES/d7 +iter_scan: "d"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "c"/1.000000000,0=/ +iter_scan: "c"/3.000000000,0=/ +iter_scan: "c"/5.000000000,0=/ +iter_scan: "b"/1.000000000,0=/ +iter_scan: "b"/3.000000000,0=/ +iter_scan: "b"/4.000000000,0=/ +iter_scan: "a"/1.000000000,0=/ +iter_scan: "a"/2.000000000,0=/BYTES/a2 +iter_scan: "a"/4.000000000,0=/ +iter_scan: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=g +iter_scan +---- +iter_seek_lt: "f"/2.000000000,0=/BYTES/f2 +iter_scan: "f"/2.000000000,0=/BYTES/f2 +iter_scan: "g"/4.000000000,0=/BYTES/g4 +iter_scan: "g"/3.000000000,0=/ +iter_scan: "g"/2.000000000,0=/BYTES/g2 +iter_scan: "h"/3.000000000,0=/BYTES/h3 +iter_scan: "h"/1.000000000,0=/ +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "j"/7.000000000,0=/BYTES/j7 +iter_scan: "j"/1.000000000,0=/ +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/5.000000000,0=/ +iter_scan: "n"/5.000000000,0=/ +iter_scan: "n"/3.000000000,0=/ +iter_scan: . + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=g ts=2 +iter_scan +---- +iter_seek_lt: "g"/3.000000000,0=/ +iter_scan: "g"/3.000000000,0=/ +iter_scan: "g"/2.000000000,0=/BYTES/g2 +iter_scan: "h"/3.000000000,0=/BYTES/h3 +iter_scan: "h"/1.000000000,0=/ +iter_scan: "j"/0,0=txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +iter_scan: "j"/7.000000000,0=/BYTES/j7 +iter_scan: "j"/1.000000000,0=/ +iter_scan: "k"/5.000000000,0=/BYTES/k5 +iter_scan: "l"/5.000000000,0=/ +iter_scan: "n"/5.000000000,0=/ +iter_scan: "n"/3.000000000,0=/ +iter_scan: . + +# Try some direction changes. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=e ts=4 +iter_prev +iter_next +iter_next +iter_prev +---- +iter_seek_ge: "e"/3.000000000,0=/BYTES/e3 +iter_prev: "e"/5.000000000,0=/ +iter_next: "e"/3.000000000,0=/BYTES/e3 +iter_next: "e"/1.000000000,0=/ +iter_prev: "e"/3.000000000,0=/BYTES/e3 + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=e ts=4 +iter_next +iter_prev +iter_prev +iter_next +---- +iter_seek_lt: "e"/5.000000000,0=/ +iter_next: "e"/3.000000000,0=/BYTES/e3 +iter_prev: "e"/5.000000000,0=/ +iter_prev: "d"/1.000000000,0=/ +iter_next: "e"/5.000000000,0=/ + +run ok +iter_new kind=keys types=pointsAndRanges pointSynthesis +iter_seek_ge k=e ts=4 +iter_prev +iter_prev +iter_next_key +iter_next +iter_next_key +iter_prev +iter_prev +iter_next_key +iter_next +---- +iter_seek_ge: "e"/3.000000000,0=/BYTES/e3 +iter_prev: "e"/5.000000000,0=/ +iter_prev: "d"/1.000000000,0=/ +iter_next_key: "e"/5.000000000,0=/ +iter_next: "e"/3.000000000,0=/BYTES/e3 +iter_next_key: "f"/6.000000000,0=/BYTES/f6 +iter_prev: "e"/1.000000000,0=/ +iter_prev: "e"/3.000000000,0=/BYTES/e3 +iter_next_key: "f"/6.000000000,0=/BYTES/f6 +iter_next: "f"/5.000000000,0=/ + +run ok +iter_new kind=keys types=pointsAndRanges pointSynthesis +iter_seek_ge k=k ts=4 +iter_next_key +iter_prev +iter_next_key +iter_next +iter_prev +---- +iter_seek_ge: "l"/5.000000000,0=/ +iter_next_key: "n"/5.000000000,0=/ +iter_prev: "l"/5.000000000,0=/ +iter_next_key: "n"/5.000000000,0=/ +iter_next: "n"/3.000000000,0=/ +iter_prev: "n"/5.000000000,0=/ + +run ok +iter_new kind=keys types=pointsAndRanges pointSynthesis +iter_seek_ge k=e ts=3 +iter_prev +iter_next_key +---- +iter_seek_ge: "e"/3.000000000,0=/BYTES/e3 +iter_prev: "e"/5.000000000,0=/ +iter_next_key: "f"/6.000000000,0=/BYTES/f6 + +# Exhausting the iterator then reversing should work in both directions, +# both after a seek and after a step. +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=a +iter_next +---- +iter_seek_lt: . +iter_next: "a"/4.000000000,0=/ + +run ok +iter_new kind=keys types=pointsAndRanges pointSynthesis +iter_seek_lt k=a +iter_next_key +---- +iter_seek_lt: . +iter_next_key: "a"/4.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=z +iter_prev +---- +iter_seek_ge: . +iter_prev: "n"/3.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_lt k=z +iter_next +iter_next +iter_prev +---- +iter_seek_lt: "n"/3.000000000,0=/ +iter_next: . +iter_next: . +iter_prev: "n"/3.000000000,0=/ + +run ok +iter_new kind=keys types=pointsAndRanges pointSynthesis +iter_seek_lt k=z +iter_next_key +iter_next_key +iter_prev +---- +iter_seek_lt: "n"/3.000000000,0=/ +iter_next_key: . +iter_next_key: . +iter_prev: "n"/3.000000000,0=/ + +run ok +iter_new types=pointsAndRanges pointSynthesis +iter_seek_ge k=a +iter_prev +iter_prev +iter_next +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_prev: . +iter_prev: . +iter_next: "a"/4.000000000,0=/ + +run ok +iter_new kind=keys types=pointsAndRanges pointSynthesis +iter_seek_ge k=a +iter_prev +iter_prev +iter_next_key +---- +iter_seek_ge: "a"/4.000000000,0=/ +iter_prev: . +iter_prev: . +iter_next_key: "a"/4.000000000,0=/ diff --git a/pkg/storage/testdata/mvcc_histories/range_key_put b/pkg/storage/testdata/mvcc_histories/range_key_put new file mode 100644 index 000000000000..ed9bee4e13d8 --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/range_key_put @@ -0,0 +1,42 @@ +# Test basic MVCC range key mutations. + +run trace +# These three should merge. +put_rangekey k=c end=e ts=1 +put_rangekey k=a end=c ts=1 +put_rangekey k=e end=f ts=1 +# Write overlapping key causing fragmentation. +put_rangekey k=d end=k ts=2 +# Write key underneath that fragments in the middle. +put_rangekey k=g end=j ts=1 +# Merge keys below. +put_rangekey k=f end=g ts=1 +# Fill in the gaps to make one chunk. +put_rangekey k=a end=d ts=2 +put_rangekey k=j end=k ts=1 +---- +>> put_rangekey k=c end=e ts=1 +rangekey: {c-e}/[1.000000000,0] +>> put_rangekey k=a end=c ts=1 +rangekey: {a-e}/[1.000000000,0] +>> put_rangekey k=e end=f ts=1 +rangekey: {a-f}/[1.000000000,0] +>> put_rangekey k=d end=k ts=2 +rangekey: {a-d}/[1.000000000,0] +rangekey: {d-f}/[2.000000000,0 1.000000000,0] +rangekey: {f-k}/[2.000000000,0] +>> put_rangekey k=g end=j ts=1 +rangekey: {a-d}/[1.000000000,0] +rangekey: {d-f}/[2.000000000,0 1.000000000,0] +rangekey: {f-g}/[2.000000000,0] +rangekey: {g-j}/[2.000000000,0 1.000000000,0] +rangekey: {j-k}/[2.000000000,0] +>> put_rangekey k=f end=g ts=1 +rangekey: {a-d}/[1.000000000,0] +rangekey: {d-j}/[2.000000000,0 1.000000000,0] +rangekey: {j-k}/[2.000000000,0] +>> put_rangekey k=a end=d ts=2 +rangekey: {a-j}/[2.000000000,0 1.000000000,0] +rangekey: {j-k}/[2.000000000,0] +>> put_rangekey k=j end=k ts=1 +rangekey: {a-k}/[2.000000000,0 1.000000000,0] diff --git a/pkg/storage/testdata/mvcc_histories/range_tombstone_gets b/pkg/storage/testdata/mvcc_histories/range_tombstone_gets new file mode 100644 index 000000000000..c02913612b86 --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/range_tombstone_gets @@ -0,0 +1,305 @@ +# Tests MVCC gets across range tombstones. +# +# Sets up following dataset, where x is tombstone, o-o is range tombstone, [] is intent. +# +# T +# 6 [e6] +# 5 f5 +# 4 o-----------------------o +# 3 x d3 f3 +# 2 o---------------o h2 +# 1 a1 x c1 f1 +# a b c d e f g h i +# +run ok +put k=a ts=1 v=a1 +del k=b ts=1 +put k=c ts=1 v=c1 +put k=f ts=1 v=f1 +del_range_ts k=a end=e ts=2 +del k=a ts=3 +put k=d ts=3 v=d3 +put k=f ts=3 v=f3 +put k=h ts=2 v=h2 +del_range_ts k=c end=i ts=4 +put k=f ts=5 v=f5 +with t=A + txn_begin ts=6 + put k=e v=e6 +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=6.000000000,0 wto=false gul=0,0 +rangekey: {a-c}/[2.000000000,0] +rangekey: {c-e}/[4.000000000,0 2.000000000,0] +rangekey: {e-i}/[4.000000000,0] +data: "a"/3.000000000,0 -> / +data: "a"/1.000000000,0 -> /BYTES/a1 +data: "b"/1.000000000,0 -> / +data: "c"/1.000000000,0 -> /BYTES/c1 +data: "d"/3.000000000,0 -> /BYTES/d3 +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> /BYTES/e6 +data: "f"/5.000000000,0 -> /BYTES/f5 +data: "f"/3.000000000,0 -> /BYTES/f3 +data: "f"/1.000000000,0 -> /BYTES/f1 +data: "h"/2.000000000,0 -> /BYTES/h2 + +# Run gets for all keys and all timestamps. +run ok +get k=a ts=1 +get k=a ts=2 +get k=a ts=3 +get k=a ts=1 tombstones +get k=a ts=2 tombstones +get k=a ts=3 tombstones +---- +get: "a" -> /BYTES/a1 @1.000000000,0 +get: "a" -> +get: "a" -> +get: "a" -> /BYTES/a1 @1.000000000,0 +get: "a" -> / @2.000000000,0 +get: "a" -> / @3.000000000,0 + +run ok +get k=b ts=1 +get k=b ts=2 +get k=b ts=3 +get k=b ts=1 tombstones +get k=b ts=2 tombstones +get k=b ts=3 tombstones +---- +get: "b" -> +get: "b" -> +get: "b" -> +get: "b" -> / @1.000000000,0 +get: "b" -> / @2.000000000,0 +get: "b" -> / @2.000000000,0 + +run ok +get k=c ts=1 +get k=c ts=2 +get k=c ts=3 +get k=c ts=4 +get k=c ts=5 +get k=c ts=1 tombstones +get k=c ts=2 tombstones +get k=c ts=3 tombstones +get k=c ts=4 tombstones +get k=c ts=5 tombstones +---- +get: "c" -> /BYTES/c1 @1.000000000,0 +get: "c" -> +get: "c" -> +get: "c" -> +get: "c" -> +get: "c" -> /BYTES/c1 @1.000000000,0 +get: "c" -> / @2.000000000,0 +get: "c" -> / @2.000000000,0 +get: "c" -> / @4.000000000,0 +get: "c" -> / @4.000000000,0 + +run ok +get k=d ts=1 +get k=d ts=2 +get k=d ts=3 +get k=d ts=4 +get k=d ts=5 +get k=d ts=1 tombstones +get k=d ts=2 tombstones +get k=d ts=3 tombstones +get k=d ts=4 tombstones +get k=d ts=5 tombstones +---- +get: "d" -> +get: "d" -> +get: "d" -> /BYTES/d3 @3.000000000,0 +get: "d" -> +get: "d" -> +get: "d" -> +get: "d" -> / @2.000000000,0 +get: "d" -> /BYTES/d3 @3.000000000,0 +get: "d" -> / @4.000000000,0 +get: "d" -> / @4.000000000,0 + +run ok +get k=e ts=3 inconsistent +get k=e ts=4 inconsistent +get k=e ts=5 inconsistent +get k=e ts=6 inconsistent +get k=e ts=3 tombstones inconsistent +get k=e ts=4 tombstones inconsistent +get k=e ts=5 tombstones inconsistent +get k=e ts=6 tombstones inconsistent +---- +get: "e" -> +get: "e" -> +get: "e" -> +get: "e" -> intent {id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} +get: "e" -> +get: "e" -> +get: "e" -> / @4.000000000,0 +get: "e" -> / @4.000000000,0 +get: "e" -> intent {id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} +get: "e" -> / @4.000000000,0 + +run ok +get k=g ts=3 +get k=g ts=4 +get k=g ts=5 +get k=g ts=3 tombstones +get k=g ts=4 tombstones +get k=g ts=5 tombstones +---- +get: "g" -> +get: "g" -> +get: "g" -> +get: "g" -> +get: "g" -> / @4.000000000,0 +get: "g" -> / @4.000000000,0 + +run ok +get k=h ts=1 +get k=h ts=2 +get k=h ts=3 +get k=h ts=4 +get k=h ts=5 +get k=h ts=1 tombstones +get k=h ts=2 tombstones +get k=h ts=3 tombstones +get k=h ts=4 tombstones +get k=h ts=5 tombstones +---- +get: "h" -> +get: "h" -> /BYTES/h2 @2.000000000,0 +get: "h" -> /BYTES/h2 @2.000000000,0 +get: "h" -> +get: "h" -> +get: "h" -> +get: "h" -> /BYTES/h2 @2.000000000,0 +get: "h" -> /BYTES/h2 @2.000000000,0 +get: "h" -> / @4.000000000,0 +get: "h" -> / @4.000000000,0 + +# failOnMoreRecent: c +run error +get k=c ts=1 failOnMoreRecent +---- +get: "c" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "c" at timestamp 1.000000000,0 too old; wrote at 4.000000000,1 + +run error +get k=c ts=2 failOnMoreRecent +---- +get: "c" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "c" at timestamp 2.000000000,0 too old; wrote at 4.000000000,1 + +run error +get k=c ts=3 failOnMoreRecent +---- +get: "c" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "c" at timestamp 3.000000000,0 too old; wrote at 4.000000000,1 + +run error +get k=c ts=4 failOnMoreRecent +---- +get: "c" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "c" at timestamp 4.000000000,0 too old; wrote at 4.000000000,1 + +run ok +get k=c ts=5 failOnMoreRecent +---- +get: "c" -> + +# failOnMoreRecent: e +run error +get k=e ts=3 failOnMoreRecent +---- +get: "e" -> +error: (*roachpb.WriteIntentError:) conflicting intents on "e" + +run error +get k=e ts=4 failOnMoreRecent +---- +get: "e" -> +error: (*roachpb.WriteIntentError:) conflicting intents on "e" + +run error +get k=e ts=5 failOnMoreRecent +---- +get: "e" -> +error: (*roachpb.WriteIntentError:) conflicting intents on "e" + +# failOnMoreRecent: g +run error +get k=g ts=3 failOnMoreRecent +---- +get: "g" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "g" at timestamp 3.000000000,0 too old; wrote at 4.000000000,1 + +run error +get k=g ts=4 failOnMoreRecent +---- +get: "g" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "g" at timestamp 4.000000000,0 too old; wrote at 4.000000000,1 + +run ok +get k=g ts=5 failOnMoreRecent +---- +get: "g" -> + + +# globalUncertaintyLimit: b-d +run ok +get k=g ts=3 globalUncertaintyLimit=3 +---- +get: "g" -> + +run error +get k=g ts=3 globalUncertaintyLimit=4 +---- +get: "g" -> +error: (*roachpb.ReadWithinUncertaintyIntervalError:) ReadWithinUncertaintyIntervalError: read at time 3.000000000,0 encountered previous write with future timestamp 4.000000000,0 within uncertainty interval `t <= (local=0,0, global=0,0)`; observed timestamps: [] + +run ok +get k=g ts=4 globalUncertaintyLimit=5 +---- +get: "g" -> + +# globalUncertaintyLimit: g +run ok +get k=g ts=1 globalUncertaintyLimit=1 +---- +get: "g" -> + +run ok +get k=g ts=1 globalUncertaintyLimit=3 +---- +get: "g" -> + +run error +get k=g ts=1 globalUncertaintyLimit=4 +---- +get: "g" -> +error: (*roachpb.ReadWithinUncertaintyIntervalError:) ReadWithinUncertaintyIntervalError: read at time 1.000000000,0 encountered previous write with future timestamp 4.000000000,0 within uncertainty interval `t <= (local=0,0, global=0,0)`; observed timestamps: [] + +run ok +get k=g ts=4 globalUncertaintyLimit=5 +---- +get: "g" -> + +# globalUncertaintyLimit: h +run ok +get k=h ts=2 globalUncertaintyLimit=2 +---- +get: "h" -> /BYTES/h2 @2.000000000,0 + +run ok +get k=h ts=2 globalUncertaintyLimit=3 +---- +get: "h" -> /BYTES/h2 @2.000000000,0 + +run error +get k=h ts=2 globalUncertaintyLimit=4 +---- +get: "h" -> +error: (*roachpb.ReadWithinUncertaintyIntervalError:) ReadWithinUncertaintyIntervalError: read at time 2.000000000,0 encountered previous write with future timestamp 4.000000000,0 within uncertainty interval `t <= (local=0,0, global=0,0)`; observed timestamps: [] diff --git a/pkg/storage/testdata/mvcc_histories/range_tombstone_mutations b/pkg/storage/testdata/mvcc_histories/range_tombstone_mutations new file mode 100644 index 000000000000..4fa6688415ea --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/range_tombstone_mutations @@ -0,0 +1,619 @@ +# Set up some point keys, point tombstones x, range tombstones o--o, +# and intents []. +# +# 7 [d7] [i7] +# 6 +# 5 +# 4 x d4 f4 x o-------------------o +# 3 +# 2 a2 g2 +# 1 +# 0 h0 +# a b c d e f g h i j k l m n o p +run ok +put k=a ts=2 v=a2 +del k=a ts=4 +put k=b ts=3 v=b3 +put k=d ts=4 v=d4 +put k=f ts=4 v=f4 +put k=g ts=2 v=g2 +del k=g ts=4 +put k=h ts=0 v=inline +del_range_ts k=k end=p ts=4 +with t=A + txn_begin ts=7 + put k=d v=d7 + put k=i v=i7 +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=7.000000000,0 wto=false gul=0,0 +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 + +# Writing invalid range tombstones should error. +run error +del_range_ts k=z end=x ts=3 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*withstack.withStack:) invalid range key {z-x}/3.000000000,0: start key "z" is at or after end key "x" + +run error +del_range_ts k=x end=z ts=0 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*withstack.withStack:) invalid range key {x-z}: no timestamp + +run error +del_range_ts k=x end=x ts=3 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*withstack.withStack:) invalid range key x{-}/3.000000000,0: start key "x" is at or after end key "x" + +# Writing at or below existing point keys should return a WriteTooOldError, +# both at the start key and in the middle of the range key. +run error +del_range_ts k=a end=b ts=3 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "a" at timestamp 3.000000000,0 too old; wrote at 4.000000000,1 + +run error +del_range_ts k=a end=b ts=4 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "a" at timestamp 4.000000000,0 too old; wrote at 4.000000000,1 + +run error +del_range_ts k=e end=g ts=3 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "f" at timestamp 3.000000000,0 too old; wrote at 4.000000000,1 + +# Writing at or below existing range tombstones should return a WriteTooOldError, +# regardless of how they overlap. +run error +del_range_ts k=k end=p ts=3 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "k" at timestamp 3.000000000,0 too old; wrote at 4.000000000,1 + +run error +del_range_ts k=k end=p ts=4 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "k" at timestamp 4.000000000,0 too old; wrote at 4.000000000,1 + +run error +del_range_ts k=j end=m ts=3 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "k" at timestamp 3.000000000,0 too old; wrote at 4.000000000,1 + +run error +del_range_ts k=o end=q ts=3 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "o" at timestamp 3.000000000,0 too old; wrote at 4.000000000,1 + +run error +del_range_ts k=j end=q ts=3 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "k" at timestamp 3.000000000,0 too old; wrote at 4.000000000,1 + +run error +del_range_ts k=k end=n ts=3 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "k" at timestamp 3.000000000,0 too old; wrote at 4.000000000,1 + +# Writing below intents should return a WriteIntentError, both when above and +# below the intent timestamp and any existing values. +run error +del_range_ts k=d end=e ts=3 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteIntentError:) conflicting intents on "d" + +run error +del_range_ts k=d end=e ts=5 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteIntentError:) conflicting intents on "d" + +run error +del_range_ts k=i end=j ts=5 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteIntentError:) conflicting intents on "i" + +run error +del_range_ts k=i end=j ts=7 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteIntentError:) conflicting intents on "i" + +run error +del_range_ts k=i end=j ts=10 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*roachpb.WriteIntentError:) conflicting intents on "i" + +# Writing above an inline value should error. +run error +del_range_ts k=h end=i ts=3 +---- +>> at end: +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +error: (*withstack.withStack:) can't write range tombstone across inline key "h"/0,0 + +# Writing next to or above point keys and tombstones should work, and should +# update MVCC stats accordingly. +run stats trace +del_range_ts k=a end=b ts=10 +del_range_ts k=b end=d ts=4 +del_range_ts k=f end=h ts=10 +---- +>> del_range_ts k=a end=b ts=10 +stats: range_key_count:+1 range_key_bytes:+13 +rangekey: {a-b}/[10.000000000,0] +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +>> del_range_ts k=b end=d ts=4 +stats: live_bytes:-21 live_count:-1 range_key_count:+1 range_key_bytes:+13 +rangekey: {a-b}/[10.000000000,0] +rangekey: {b-d}/[4.000000000,0] +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +>> del_range_ts k=f end=h ts=10 +stats: live_bytes:-21 live_count:-1 range_key_count:+1 range_key_bytes:+13 +rangekey: {a-b}/[10.000000000,0] +rangekey: {b-d}/[4.000000000,0] +rangekey: {f-h}/[10.000000000,0] +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:1986 + gc_bytes_age:177945 + live_bytes:165 + live_count:3 + key_bytes:122 + key_count:7 + val_bytes:170 + val_count:10 + intent_bytes:38 + intent_count:2 + separated_intent_count:2 + range_key_count:4 + range_key_bytes:52 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} + +# Writing a range tombstone above another should not affect stats, except for +# its own contribution. +run stats trace +del_range_ts k=a end=d ts=11 +---- +>> del_range_ts k=a end=d ts=11 +stats: gc_bytes_age:-32 range_key_bytes:+18 +rangekey: {a-b}/[11.000000000,0 10.000000000,0] +rangekey: {b-d}/[11.000000000,0 4.000000000,0] +rangekey: {f-h}/[10.000000000,0] +rangekey: {k-p}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:1986 + gc_bytes_age:195715 + live_bytes:165 + live_count:3 + key_bytes:122 + key_count:7 + val_bytes:170 + val_count:10 + intent_bytes:38 + intent_count:2 + separated_intent_count:2 + range_key_count:4 + range_key_bytes:70 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} + +# Writing range tombstones next to other range tombstones will merge them, and +# stats take this into account. +run stats trace +del_range_ts k=www end=xxx ts=4 +del_range_ts k=xxx end=z ts=4 +del_range_ts k=s end=www ts=4 +del_range_ts k=p end=s ts=4 +---- +>> del_range_ts k=www end=xxx ts=4 +stats: range_key_count:+1 range_key_bytes:+17 +rangekey: {a-b}/[11.000000000,0 10.000000000,0] +rangekey: {b-d}/[11.000000000,0 4.000000000,0] +rangekey: {f-h}/[10.000000000,0] +rangekey: {k-p}/[4.000000000,0] +rangekey: {www-xxx}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +>> del_range_ts k=xxx end=z ts=4 +stats: range_key_bytes:-2 +rangekey: {a-b}/[11.000000000,0 10.000000000,0] +rangekey: {b-d}/[11.000000000,0 4.000000000,0] +rangekey: {f-h}/[10.000000000,0] +rangekey: {k-p}/[4.000000000,0] +rangekey: {www-z}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +>> del_range_ts k=s end=www ts=4 +stats: range_key_bytes:-2 +rangekey: {a-b}/[11.000000000,0 10.000000000,0] +rangekey: {b-d}/[11.000000000,0 4.000000000,0] +rangekey: {f-h}/[10.000000000,0] +rangekey: {k-p}/[4.000000000,0] +rangekey: {s-z}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +>> del_range_ts k=p end=s ts=4 +stats: range_key_count:-1 range_key_bytes:-13 +rangekey: {a-b}/[11.000000000,0 10.000000000,0] +rangekey: {b-d}/[11.000000000,0 4.000000000,0] +rangekey: {f-h}/[10.000000000,0] +rangekey: {k-z}/[4.000000000,0] +data: "a"/4.000000000,0 -> / +data: "a"/2.000000000,0 -> /BYTES/a2 +data: "b"/3.000000000,0 -> /BYTES/b3 +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "d"/7.000000000,0 -> /BYTES/d7 +data: "d"/4.000000000,0 -> /BYTES/d4 +data: "f"/4.000000000,0 -> /BYTES/f4 +data: "g"/4.000000000,0 -> / +data: "g"/2.000000000,0 -> /BYTES/g2 +meta: "h"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/inline mergeTs= txnDidNotUpdateMeta=false +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/7.000000000,0 -> /BYTES/i7 +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:1986 + gc_bytes_age:195715 + live_bytes:165 + live_count:3 + key_bytes:122 + key_count:7 + val_bytes:170 + val_count:10 + intent_bytes:38 + intent_count:2 + separated_intent_count:2 + range_key_count:4 + range_key_bytes:70 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} diff --git a/pkg/storage/testdata/mvcc_histories/range_tombstone_scans b/pkg/storage/testdata/mvcc_histories/range_tombstone_scans new file mode 100644 index 000000000000..ca4b5b0d29bc --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/range_tombstone_scans @@ -0,0 +1,287 @@ +# Tests MVCC scans across range tombstones. +# +# Sets up following dataset, where x is tombstone, o-o is range tombstone, [] is intent. +# +# T +# 6 [e6] +# 5 f5 +# 4 o-----------------------o +# 3 x d3 f3 +# 2 o---------------o h2 +# 1 a1 x c1 f1 +# a b c d e f g h i +# +run ok +put k=a ts=1 v=a1 +del k=b ts=1 +put k=c ts=1 v=c1 +put k=f ts=1 v=f1 +del_range_ts k=a end=e ts=2 +del k=a ts=3 +put k=d ts=3 v=d3 +put k=f ts=3 v=f3 +put k=h ts=2 v=h2 +del_range_ts k=c end=i ts=4 +put k=f ts=5 v=f5 +with t=A + txn_begin ts=6 + put k=e v=e6 +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=6.000000000,0 wto=false gul=0,0 +rangekey: {a-c}/[2.000000000,0] +rangekey: {c-e}/[4.000000000,0 2.000000000,0] +rangekey: {e-i}/[4.000000000,0] +data: "a"/3.000000000,0 -> / +data: "a"/1.000000000,0 -> /BYTES/a1 +data: "b"/1.000000000,0 -> / +data: "c"/1.000000000,0 -> /BYTES/c1 +data: "d"/3.000000000,0 -> /BYTES/d3 +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> /BYTES/e6 +data: "f"/5.000000000,0 -> /BYTES/f5 +data: "f"/3.000000000,0 -> /BYTES/f3 +data: "f"/1.000000000,0 -> /BYTES/f1 +data: "h"/2.000000000,0 -> /BYTES/h2 + +# Run non-tombstone scans at all timestamps. +run ok +scan k=a end=z ts=1 +---- +scan: "a" -> /BYTES/a1 @1.000000000,0 +scan: "c" -> /BYTES/c1 @1.000000000,0 +scan: "f" -> /BYTES/f1 @1.000000000,0 + +run ok +scan k=a end=z ts=2 +---- +scan: "f" -> /BYTES/f1 @1.000000000,0 +scan: "h" -> /BYTES/h2 @2.000000000,0 + +run ok +scan k=a end=z ts=3 +---- +scan: "d" -> /BYTES/d3 @3.000000000,0 +scan: "f" -> /BYTES/f3 @3.000000000,0 +scan: "h" -> /BYTES/h2 @2.000000000,0 + +run ok +scan k=a end=z ts=4 +---- +scan: "a"-"z" -> + +run ok +scan k=a end=z ts=5 +---- +scan: "f" -> /BYTES/f5 @5.000000000,0 + +run ok +scan k=a end=z ts=6 inconsistent +---- +scan: "a" -> intent {id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} +scan: "f" -> /BYTES/f5 @5.000000000,0 + +# Run tombstone scans at all timestamps. +run ok +scan k=a end=z ts=1 tombstones +---- +scan: "a" -> /BYTES/a1 @1.000000000,0 +scan: "b" -> / @1.000000000,0 +scan: "c" -> /BYTES/c1 @1.000000000,0 +scan: "f" -> /BYTES/f1 @1.000000000,0 + +run ok +scan k=a end=z ts=2 tombstones +---- +scan: "a" -> / @2.000000000,0 +scan: "b" -> / @2.000000000,0 +scan: "c" -> / @2.000000000,0 +scan: "d" -> / @2.000000000,0 +scan: "f" -> /BYTES/f1 @1.000000000,0 +scan: "h" -> /BYTES/h2 @2.000000000,0 + +run ok +scan k=a end=z ts=3 tombstones +---- +scan: "a" -> / @3.000000000,0 +scan: "b" -> / @2.000000000,0 +scan: "c" -> / @2.000000000,0 +scan: "d" -> /BYTES/d3 @3.000000000,0 +scan: "f" -> /BYTES/f3 @3.000000000,0 +scan: "h" -> /BYTES/h2 @2.000000000,0 + +run ok +scan k=a end=z ts=4 tombstones +---- +scan: "a" -> / @3.000000000,0 +scan: "b" -> / @2.000000000,0 +scan: "c" -> / @4.000000000,0 +scan: "d" -> / @4.000000000,0 +scan: "e" -> / @4.000000000,0 +scan: "f" -> / @4.000000000,0 +scan: "h" -> / @4.000000000,0 + +run ok +scan k=a end=z ts=5 tombstones +---- +scan: "a" -> / @3.000000000,0 +scan: "b" -> / @2.000000000,0 +scan: "c" -> / @4.000000000,0 +scan: "d" -> / @4.000000000,0 +scan: "e" -> / @4.000000000,0 +scan: "f" -> /BYTES/f5 @5.000000000,0 +scan: "h" -> / @4.000000000,0 + +run ok +scan k=a end=z ts=6 tombstones inconsistent +---- +scan: "a" -> intent {id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} +scan: "a" -> / @3.000000000,0 +scan: "b" -> / @2.000000000,0 +scan: "c" -> / @4.000000000,0 +scan: "d" -> / @4.000000000,0 +scan: "e" -> / @4.000000000,0 +scan: "f" -> /BYTES/f5 @5.000000000,0 +scan: "h" -> / @4.000000000,0 + +# failOnMoreRecent: a-d +run error +scan k=a end=d ts=3 failOnMoreRecent +---- +scan: "a"-"d" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "a" at timestamp 3.000000000,0 too old; wrote at 4.000000000,1 + +run error +scan k=a end=d ts=4 failOnMoreRecent +---- +scan: "a"-"d" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "c" at timestamp 4.000000000,0 too old; wrote at 4.000000000,1 + +run ok +scan k=a end=d ts=5 failOnMoreRecent +---- +scan: "a"-"d" -> + +# failOnMoreRecent: c-d +run error +scan k=c end=d ts=1 failOnMoreRecent +---- +scan: "c"-"d" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "c" at timestamp 1.000000000,0 too old; wrote at 4.000000000,1 + +run error +scan k=c end=d ts=2 failOnMoreRecent +---- +scan: "c"-"d" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "c" at timestamp 2.000000000,0 too old; wrote at 4.000000000,1 + +run error +scan k=c end=d ts=3 failOnMoreRecent +---- +scan: "c"-"d" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "c" at timestamp 3.000000000,0 too old; wrote at 4.000000000,1 + +run error +scan k=c end=d ts=4 failOnMoreRecent +---- +scan: "c"-"d" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "c" at timestamp 4.000000000,0 too old; wrote at 4.000000000,1 + +run ok +scan k=c end=d ts=5 failOnMoreRecent +---- +scan: "c"-"d" -> + +# failOnMoreRecent: e-f +run error +scan k=e end=f ts=3 failOnMoreRecent +---- +scan: "e"-"f" -> +error: (*roachpb.WriteIntentError:) conflicting intents on "e" + +run error +scan k=e end=f ts=4 failOnMoreRecent +---- +scan: "e"-"f" -> +error: (*roachpb.WriteIntentError:) conflicting intents on "e" + +run error +scan k=e end=f ts=5 failOnMoreRecent +---- +scan: "e"-"f" -> +error: (*roachpb.WriteIntentError:) conflicting intents on "e" + +# failOnMoreRecent: g-h +run error +scan k=g end=h ts=3 failOnMoreRecent +---- +scan: "g"-"h" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "g" at timestamp 3.000000000,0 too old; wrote at 4.000000000,1 + +run error +scan k=g end=h ts=4 failOnMoreRecent +---- +scan: "g"-"h" -> +error: (*roachpb.WriteTooOldError:) WriteTooOldError: write for key "g" at timestamp 4.000000000,0 too old; wrote at 4.000000000,1 + +run ok +scan k=g end=h ts=5 failOnMoreRecent +---- +scan: "g"-"h" -> + + +# globalUncertaintyLimit: b-d +run ok +scan k=g end=h ts=3 globalUncertaintyLimit=3 +---- +scan: "g"-"h" -> + +run error +scan k=g end=h ts=3 globalUncertaintyLimit=4 +---- +scan: "g"-"h" -> +error: (*roachpb.ReadWithinUncertaintyIntervalError:) ReadWithinUncertaintyIntervalError: read at time 3.000000000,0 encountered previous write with future timestamp 4.000000000,0 within uncertainty interval `t <= (local=0,0, global=0,0)`; observed timestamps: [] + +run ok +scan k=g end=h ts=4 globalUncertaintyLimit=5 +---- +scan: "g"-"h" -> + +# globalUncertaintyLimit: g-h +run ok +scan k=g end=h ts=1 globalUncertaintyLimit=1 +---- +scan: "g"-"h" -> + +run ok +scan k=g end=h ts=1 globalUncertaintyLimit=3 +---- +scan: "g"-"h" -> + +run error +scan k=g end=h ts=1 globalUncertaintyLimit=4 +---- +scan: "g"-"h" -> +error: (*roachpb.ReadWithinUncertaintyIntervalError:) ReadWithinUncertaintyIntervalError: read at time 1.000000000,0 encountered previous write with future timestamp 4.000000000,0 within uncertainty interval `t <= (local=0,0, global=0,0)`; observed timestamps: [] + +run ok +scan k=g end=h ts=4 globalUncertaintyLimit=5 +---- +scan: "g"-"h" -> + +# globalUncertaintyLimit: h-i +run ok +scan k=h end=i ts=2 globalUncertaintyLimit=2 +---- +scan: "h" -> /BYTES/h2 @2.000000000,0 + +run ok +scan k=h end=i ts=2 globalUncertaintyLimit=3 +---- +scan: "h" -> /BYTES/h2 @2.000000000,0 + +run error +scan k=h end=i ts=2 globalUncertaintyLimit=4 +---- +scan: "h"-"i" -> +error: (*roachpb.ReadWithinUncertaintyIntervalError:) ReadWithinUncertaintyIntervalError: read at time 2.000000000,0 encountered previous write with future timestamp 4.000000000,0 within uncertainty interval `t <= (local=0,0, global=0,0)`; observed timestamps: [] diff --git a/pkg/storage/testdata/mvcc_histories/stats b/pkg/storage/testdata/mvcc_histories/stats new file mode 100644 index 000000000000..324700c775f3 --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/stats @@ -0,0 +1,310 @@ +# Tests MVCC stats calculations for puts, dels, and rangedels. +# +# Intermediate states are tested through stats traces. Final state: +# +# (x is tombstone, o---o is range tombstone, [] is intent) +# +# 6 d6 e6 f6 +# 5 x x x +# 4 b4 x o-----------------------o Two range tombstones: the lowest is the one +# 3 x c3 o-----------------------o that matters for point key GCBytesAge. +# 2 x +# 1 b1 x e1 x h1 x +# 0 a0 +# a b c d e f g h i j + +run stats trace +put k=a ts=0 v=a0 +put k=b ts=1 v=b1 +del k=b ts=2 +del k=b ts=3 +put k=b ts=4 v=b4 +del k=c ts=1 +put k=c ts=3 v=c3 +del k=c ts=4 +put k=e ts=1 v=e1 +del k=f ts=1 +put k=h ts=1 v=h1 +del k=i ts=1 +del_range k=d end=j ts=3 +del_range k=d end=j ts=4 +put k=d ts=6 v=d6 +put k=e ts=6 v=e6 +put k=f ts=6 v=f6 +del k=g ts=5 +del k=h ts=5 +del k=i ts=5 +---- +>> put k=a ts=0 v=a0 +stats: live_bytes:+23 live_count:+1 key_bytes:+2 key_count:+1 val_bytes:+21 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +>> put k=b ts=1 v=b1 +stats: live_bytes:+21 live_count:+1 key_bytes:+14 key_count:+1 val_bytes:+7 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/1.000000000,0 -> /BYTES/b1 +>> del k=b ts=2 +stats: live_bytes:-21 live_count:-1 key_bytes:+12 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +>> del k=b ts=3 +stats: gc_bytes_age:-2 key_bytes:+12 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +>> put k=b ts=4 v=b4 +stats: gc_bytes_age:-2 live_bytes:+21 live_count:+1 key_bytes:+12 val_bytes:+7 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +>> del k=c ts=1 +stats: key_bytes:+14 key_count:+1 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +>> put k=c ts=3 v=c3 +stats: gc_bytes_age:-4 live_bytes:+21 live_count:+1 key_bytes:+12 val_bytes:+7 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +>> del k=c ts=4 +stats: live_bytes:-21 live_count:-1 key_bytes:+12 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +>> put k=e ts=1 v=e1 +stats: live_bytes:+21 live_count:+1 key_bytes:+14 key_count:+1 val_bytes:+7 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +>> del k=f ts=1 +stats: key_bytes:+14 key_count:+1 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +>> put k=h ts=1 v=h1 +stats: live_bytes:+21 live_count:+1 key_bytes:+14 key_count:+1 val_bytes:+7 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "h"/1.000000000,0 -> /BYTES/h1 +>> del k=i ts=1 +stats: key_bytes:+14 key_count:+1 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "h"/1.000000000,0 -> /BYTES/h1 +data: "i"/1.000000000,0 -> / +>> del_range k=d end=j ts=3 +del_range: "d"-"j" -> deleted 2 key(s) +stats: live_bytes:-42 live_count:-2 key_bytes:+24 val_count:+2 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +data: "e"/3.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "h"/3.000000000,0 -> / +data: "h"/1.000000000,0 -> /BYTES/h1 +data: "i"/1.000000000,0 -> / +>> del_range k=d end=j ts=4 +del_range: "d"-"j" -> deleted 0 key(s) +stats: +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +data: "e"/3.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "h"/3.000000000,0 -> / +data: "h"/1.000000000,0 -> /BYTES/h1 +data: "i"/1.000000000,0 -> / +>> put k=d ts=6 v=d6 +stats: live_bytes:+21 live_count:+1 key_bytes:+14 key_count:+1 val_bytes:+7 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> /BYTES/d6 +data: "e"/3.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "h"/3.000000000,0 -> / +data: "h"/1.000000000,0 -> /BYTES/h1 +data: "i"/1.000000000,0 -> / +>> put k=e ts=6 v=e6 +stats: gc_bytes_age:-6 live_bytes:+21 live_count:+1 key_bytes:+12 val_bytes:+7 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> /BYTES/d6 +data: "e"/6.000000000,0 -> /BYTES/e6 +data: "e"/3.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "h"/3.000000000,0 -> / +data: "h"/1.000000000,0 -> /BYTES/h1 +data: "i"/1.000000000,0 -> / +>> put k=f ts=6 v=f6 +stats: gc_bytes_age:-10 live_bytes:+21 live_count:+1 key_bytes:+12 val_bytes:+7 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> /BYTES/d6 +data: "e"/6.000000000,0 -> /BYTES/e6 +data: "e"/3.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/1.000000000,0 -> / +data: "h"/3.000000000,0 -> / +data: "h"/1.000000000,0 -> /BYTES/h1 +data: "i"/1.000000000,0 -> / +>> del k=g ts=5 +stats: key_bytes:+14 key_count:+1 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> /BYTES/d6 +data: "e"/6.000000000,0 -> /BYTES/e6 +data: "e"/3.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/1.000000000,0 -> / +data: "g"/5.000000000,0 -> / +data: "h"/3.000000000,0 -> / +data: "h"/1.000000000,0 -> /BYTES/h1 +data: "i"/1.000000000,0 -> / +>> del k=h ts=5 +stats: gc_bytes_age:-4 key_bytes:+12 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> /BYTES/d6 +data: "e"/6.000000000,0 -> /BYTES/e6 +data: "e"/3.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/1.000000000,0 -> / +data: "g"/5.000000000,0 -> / +data: "h"/5.000000000,0 -> / +data: "h"/3.000000000,0 -> / +data: "h"/1.000000000,0 -> /BYTES/h1 +data: "i"/1.000000000,0 -> / +>> del k=i ts=5 +stats: gc_bytes_age:-8 key_bytes:+12 val_count:+1 +meta: "a"/0,0 -> txn={} ts=0,0 del=false klen=0 vlen=0 raw=/BYTES/a0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/4.000000000,0 -> /BYTES/b4 +data: "b"/3.000000000,0 -> / +data: "b"/2.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/4.000000000,0 -> / +data: "c"/3.000000000,0 -> /BYTES/c3 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> /BYTES/d6 +data: "e"/6.000000000,0 -> /BYTES/e6 +data: "e"/3.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> /BYTES/f6 +data: "f"/1.000000000,0 -> / +data: "g"/5.000000000,0 -> / +data: "h"/5.000000000,0 -> / +data: "h"/3.000000000,0 -> / +data: "h"/1.000000000,0 -> /BYTES/h1 +data: "i"/5.000000000,0 -> / +data: "i"/1.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:0 + gc_bytes_age:215338 + live_bytes:107 + live_count:5 + key_bytes:246 + key_count:9 + val_bytes:77 + val_count:20 + intent_bytes:0 + intent_count:0 + separated_intent_count:0 + range_key_count:0 + range_key_bytes:0 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} diff --git a/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_abort b/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_abort new file mode 100644 index 000000000000..de7af020a644 --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_abort @@ -0,0 +1,768 @@ +# Tests MVCC stats calculations when resolving intents. Intermediate states are +# tested through stats traces. Initial state: +# +# (x is tombstone, o---o is range tombstone, [] is intent) +# +# 7 +# 6 [a6][b6][c6][x] [x] [x] [g6][h6][i6][x] [x] [x] [m6][n6][o6] x x x +# 5 n5 x q5 x +# 4 o-----------------------------------------------o +# 3 o-----------------------------------------------o +# 2 +# 1 b1 x e1 x h1 x k1 x +# a b c d e f g h i j k l m n o p q r s +# +# This uses two range tombstones, since the lowest is the one that matters for +# point key GCBytesAge. It also uses points below/above range tombstones, +# because iterators surface range keys separately from point keys, which can +# cause bugs if callers don't step onto the point key. +# +# TODO(erikgrinaker): This is probably better handled by randomized or +# generative testing, since the combinations are getting unwieldy. But it'll do +# for now. + +run stats +with ts=1 + put k=b v=b1 + del k=c + put k=e v=e1 + del k=f + put k=g v=g1 + del k=h + put k=i v=i1 + del k=j +del_range_ts k=g end=s ts=3 +del_range_ts k=g end=s ts=4 +with ts=5 + put k=n v=n5 + del k=o + put k=q v=q5 + del k=r +with t=A + txn_begin ts=6 + put k=a v=a6 + put k=b v=b6 + put k=c v=c6 + del k=d + del k=e + del k=f + put k=g v=g6 + put k=h v=h6 + put k=i v=i6 + del k=j + del k=k + del k=l + put k=m v=m6 + put k=n v=n6 + put k=o v=o6 + del k=p + del k=q + del k=r +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=6.000000000,0 wto=false gul=0,0 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/6.000000000,0 -> /BYTES/a6 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:17892 + gc_bytes_age:761835 + live_bytes:621 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:969 + val_count:30 + intent_bytes:279 + intent_count:18 + separated_intent_count:18 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} + +run stats trace +with t=A status=ABORTED + resolve_intent k=a + resolve_intent k=b + resolve_intent k=c + resolve_intent k=d + resolve_intent k=e + resolve_intent k=f + resolve_intent k=g + resolve_intent k=h + resolve_intent k=i + resolve_intent k=j + resolve_intent k=k + resolve_intent k=l + resolve_intent k=m + resolve_intent k=n + resolve_intent k=o + resolve_intent k=p + resolve_intent k=q + resolve_intent k=r +---- +>> resolve_intent k=a t=A status=ABORTED +called ClearIntent("a", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-69 live_count:-1 key_bytes:-14 key_count:-1 val_bytes:-55 val_count:-1 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=b t=A status=ABORTED +called ClearIntent("b", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-48 key_bytes:-12 val_bytes:-55 val_count:-1 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=c t=A status=ABORTED +called ClearIntent("c", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: gc_bytes_age:+10 live_bytes:-69 live_count:-1 key_bytes:-12 val_bytes:-55 val_count:-1 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=d t=A status=ABORTED +called ClearIntent("d", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: key_bytes:-14 key_count:-1 val_bytes:-48 val_count:-1 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=e t=A status=ABORTED +called ClearIntent("e", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:+21 live_count:+1 key_bytes:-12 val_bytes:-48 val_count:-1 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=f t=A status=ABORTED +called ClearIntent("f", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: gc_bytes_age:+10 key_bytes:-12 val_bytes:-48 val_count:-1 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=g t=A status=ABORTED +called ClearIntent("g", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: gc_bytes_age:+6 live_bytes:-69 live_count:-1 key_bytes:-12 val_bytes:-55 val_count:-1 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=h t=A status=ABORTED +called ClearIntent("h", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: gc_bytes_age:+10 live_bytes:-69 live_count:-1 key_bytes:-12 val_bytes:-55 val_count:-1 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=i t=A status=ABORTED +called ClearIntent("i", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: gc_bytes_age:+6 live_bytes:-69 live_count:-1 key_bytes:-12 val_bytes:-55 val_count:-1 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/1.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=j t=A status=ABORTED +called ClearIntent("j", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: gc_bytes_age:+10 key_bytes:-12 val_bytes:-48 val_count:-1 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/1.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=k t=A status=ABORTED +called ClearIntent("k", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: key_bytes:-14 key_count:-1 val_bytes:-48 val_count:-1 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/1.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/1.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=l t=A status=ABORTED +called ClearIntent("l", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: key_bytes:-14 key_count:-1 val_bytes:-48 val_count:-1 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/1.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/1.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=m t=A status=ABORTED +called ClearIntent("m", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-69 live_count:-1 key_bytes:-14 key_count:-1 val_bytes:-55 val_count:-1 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/1.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/1.000000000,0 -> / +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=n t=A status=ABORTED +called ClearIntent("n", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-48 key_bytes:-12 val_bytes:-55 val_count:-1 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/1.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/1.000000000,0 -> / +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=o t=A status=ABORTED +called ClearIntent("o", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: gc_bytes_age:+2 live_bytes:-69 live_count:-1 key_bytes:-12 val_bytes:-55 val_count:-1 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/1.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/1.000000000,0 -> / +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=p t=A status=ABORTED +called ClearIntent("p", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: key_bytes:-14 key_count:-1 val_bytes:-48 val_count:-1 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/1.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/1.000000000,0 -> / +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/5.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=q t=A status=ABORTED +called ClearIntent("q", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:+21 live_count:+1 key_bytes:-12 val_bytes:-48 val_count:-1 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/1.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/1.000000000,0 -> / +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/5.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=r t=A status=ABORTED +called ClearIntent("r", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: gc_bytes_age:+2 key_bytes:-12 val_bytes:-48 val_count:-1 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/1.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/1.000000000,0 -> / +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/5.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:0 + gc_bytes_age:147599 + live_bytes:84 + live_count:4 + key_bytes:168 + key_count:12 + val_bytes:42 + val_count:12 + intent_bytes:0 + intent_count:0 + separated_intent_count:0 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} diff --git a/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_commit b/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_commit new file mode 100644 index 000000000000..da852f0e2b5c --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_commit @@ -0,0 +1,939 @@ +# Tests MVCC stats calculations when resolving intents. Intermediate states are +# tested through stats traces. Initial state: +# +# (x is tombstone, o---o is range tombstone, [] is intent) +# +# 7 +# 6 [a6][b6][c6][x] [x] [x] [g6][h6][i6][x] [x] [x] [m6][n6][o6] x x x +# 5 n5 x q5 x +# 4 o-----------------------------------------------o +# 3 o-----------------------------------------------o +# 2 +# 1 b1 x e1 x h1 x k1 x +# a b c d e f g h i j k l m n o p q r s +# +# This uses two range tombstones, since the lowest is the one that matters for +# point key GCBytesAge. It also uses points below/above range tombstones, +# because iterators surface range keys separately from point keys, which can +# cause bugs if callers don't step onto the point key. +# +# TODO(erikgrinaker): This is probably better handled by randomized or +# generative testing, since the combinations are getting unwieldy. But it'll do +# for now. + +run stats +with ts=1 + put k=b v=b1 + del k=c + put k=e v=e1 + del k=f + put k=g v=g1 + del k=h + put k=i v=i1 + del k=j +del_range_ts k=g end=s ts=3 +del_range_ts k=g end=s ts=4 +with ts=5 + put k=n v=n5 + del k=o + put k=q v=q5 + del k=r +with t=A + txn_begin ts=6 + put k=a v=a6 + put k=b v=b6 + put k=c v=c6 + del k=d + del k=e + del k=f + put k=g v=g6 + put k=h v=h6 + put k=i v=i6 + del k=j + del k=k + del k=l + put k=m v=m6 + put k=n v=n6 + put k=o v=o6 + del k=p + del k=q + del k=r +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=6.000000000,0 wto=false gul=0,0 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/6.000000000,0 -> /BYTES/a6 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:17892 + gc_bytes_age:761835 + live_bytes:621 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:969 + val_count:30 + intent_bytes:279 + intent_count:18 + separated_intent_count:18 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} + +run stats trace +with t=A status=COMMITTED + resolve_intent k=a + resolve_intent k=b + resolve_intent k=c + resolve_intent k=d + resolve_intent k=e + resolve_intent k=f + resolve_intent k=g + resolve_intent k=h + resolve_intent k=i + resolve_intent k=j + resolve_intent k=k + resolve_intent k=l + resolve_intent k=m + resolve_intent k=n + resolve_intent k=o + resolve_intent k=p + resolve_intent k=q + resolve_intent k=r +---- +>> resolve_intent k=a t=A status=COMMITTED +called ClearIntent("a", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=b t=A status=COMMITTED +called ClearIntent("b", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=c t=A status=COMMITTED +called ClearIntent("c", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=d t=A status=COMMITTED +called ClearIntent("d", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=e t=A status=COMMITTED +called ClearIntent("e", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=f t=A status=COMMITTED +called ClearIntent("f", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=g t=A status=COMMITTED +called ClearIntent("g", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=h t=A status=COMMITTED +called ClearIntent("h", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=i t=A status=COMMITTED +called ClearIntent("i", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=j t=A status=COMMITTED +called ClearIntent("j", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=k t=A status=COMMITTED +called ClearIntent("k", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=l t=A status=COMMITTED +called ClearIntent("l", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/6.000000000,0 -> / +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=m t=A status=COMMITTED +called ClearIntent("m", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/6.000000000,0 -> / +data: "l"/6.000000000,0 -> / +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=n t=A status=COMMITTED +called ClearIntent("n", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/6.000000000,0 -> / +data: "l"/6.000000000,0 -> / +data: "m"/6.000000000,0 -> /BYTES/m6 +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=o t=A status=COMMITTED +called ClearIntent("o", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/6.000000000,0 -> / +data: "l"/6.000000000,0 -> / +data: "m"/6.000000000,0 -> /BYTES/m6 +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=p t=A status=COMMITTED +called ClearIntent("p", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/6.000000000,0 -> / +data: "l"/6.000000000,0 -> / +data: "m"/6.000000000,0 -> /BYTES/m6 +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=q t=A status=COMMITTED +called ClearIntent("q", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/6.000000000,0 -> / +data: "l"/6.000000000,0 -> / +data: "m"/6.000000000,0 -> /BYTES/m6 +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +data: "p"/6.000000000,0 -> / +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=r t=A status=COMMITTED +called ClearIntent("r", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/6.000000000,0 -> / +data: "l"/6.000000000,0 -> / +data: "m"/6.000000000,0 -> /BYTES/m6 +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +data: "p"/6.000000000,0 -> / +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:0 + gc_bytes_age:332427 + live_bytes:189 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:105 + val_count:30 + intent_bytes:0 + intent_count:0 + separated_intent_count:0 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} diff --git a/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_pushed b/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_pushed new file mode 100644 index 000000000000..56dc3a56ef16 --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_pushed @@ -0,0 +1,943 @@ +# Tests MVCC stats calculations when resolving intents. Intermediate states are +# tested through stats traces. Initial state: +# +# (x is tombstone, o---o is range tombstone, [] is intent) +# +# 7 +# 6 [a6][b6][c6][x] [x] [x] [g6][h6][i6][x] [x] [x] [m6][n6][o6] x x x +# 5 n5 x q5 x +# 4 o-----------------------------------------------o +# 3 o-----------------------------------------------o +# 2 +# 1 b1 x e1 x h1 x k1 x +# a b c d e f g h i j k l m n o p q r s +# +# This uses two range tombstones, since the lowest is the one that matters for +# point key GCBytesAge. It also uses points below/above range tombstones, +# because iterators surface range keys separately from point keys, which can +# cause bugs if callers don't step onto the point key. +# +# TODO(erikgrinaker): This is probably better handled by randomized or +# generative testing, since the combinations are getting unwieldy. But it'll do +# for now. + +run stats +with ts=1 + put k=b v=b1 + del k=c + put k=e v=e1 + del k=f + put k=g v=g1 + del k=h + put k=i v=i1 + del k=j +del_range_ts k=g end=s ts=3 +del_range_ts k=g end=s ts=4 +with ts=5 + put k=n v=n5 + del k=o + put k=q v=q5 + del k=r +with t=A + txn_begin ts=6 + put k=a v=a6 + put k=b v=b6 + put k=c v=c6 + del k=d + del k=e + del k=f + put k=g v=g6 + put k=h v=h6 + put k=i v=i6 + del k=j + del k=k + del k=l + put k=m v=m6 + put k=n v=n6 + put k=o v=o6 + del k=p + del k=q + del k=r +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=6.000000000,0 wto=false gul=0,0 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/6.000000000,0 -> /BYTES/a6 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:17892 + gc_bytes_age:761835 + live_bytes:621 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:969 + val_count:30 + intent_bytes:279 + intent_count:18 + separated_intent_count:18 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} + +run stats trace +with t=A status=COMMITTED + txn_advance ts=7 + resolve_intent k=a + resolve_intent k=b + resolve_intent k=c + resolve_intent k=d + resolve_intent k=e + resolve_intent k=f + resolve_intent k=g + resolve_intent k=h + resolve_intent k=i + resolve_intent k=j + resolve_intent k=k + resolve_intent k=l + resolve_intent k=m + resolve_intent k=n + resolve_intent k=o + resolve_intent k=p + resolve_intent k=q + resolve_intent k=r +---- +>> txn_advance ts=7 t=A status=COMMITTED +stats: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=6.000000000,0 wto=false gul=0,0 +>> resolve_intent k=a t=A status=COMMITTED +called ClearIntent("a", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=b t=A status=COMMITTED +called ClearIntent("b", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-19 live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=c t=A status=COMMITTED +called ClearIntent("c", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=d t=A status=COMMITTED +called ClearIntent("d", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=e t=A status=COMMITTED +called ClearIntent("e", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-81 val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=f t=A status=COMMITTED +called ClearIntent("f", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=g t=A status=COMMITTED +called ClearIntent("g", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=h t=A status=COMMITTED +called ClearIntent("h", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/7.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=i t=A status=COMMITTED +called ClearIntent("i", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/7.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/7.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=j t=A status=COMMITTED +called ClearIntent("j", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/7.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/7.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=k t=A status=COMMITTED +called ClearIntent("k", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/7.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/7.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=l t=A status=COMMITTED +called ClearIntent("l", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/7.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/7.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/7.000000000,0 -> / +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=m t=A status=COMMITTED +called ClearIntent("m", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/7.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/7.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/7.000000000,0 -> / +data: "l"/7.000000000,0 -> / +data: "m"/7.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=n t=A status=COMMITTED +called ClearIntent("n", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-19 live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/7.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/7.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/7.000000000,0 -> / +data: "l"/7.000000000,0 -> / +data: "m"/7.000000000,0 -> /BYTES/m6 +data: "n"/7.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=o t=A status=COMMITTED +called ClearIntent("o", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 live_bytes:-48 val_bytes:-48 intent_bytes:-19 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/7.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/7.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/7.000000000,0 -> / +data: "l"/7.000000000,0 -> / +data: "m"/7.000000000,0 -> /BYTES/m6 +data: "n"/7.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/7.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=p t=A status=COMMITTED +called ClearIntent("p", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/7.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/7.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/7.000000000,0 -> / +data: "l"/7.000000000,0 -> / +data: "m"/7.000000000,0 -> /BYTES/m6 +data: "n"/7.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/7.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=q t=A status=COMMITTED +called ClearIntent("q", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-81 val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/7.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/7.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/7.000000000,0 -> / +data: "l"/7.000000000,0 -> / +data: "m"/7.000000000,0 -> /BYTES/m6 +data: "n"/7.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/7.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +data: "p"/7.000000000,0 -> / +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> resolve_intent k=r t=A status=COMMITTED +called ClearIntent("r", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 val_bytes:-48 intent_bytes:-12 intent_count:-1 separated_intent_count:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/7.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/7.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/7.000000000,0 -> / +data: "l"/7.000000000,0 -> / +data: "m"/7.000000000,0 -> /BYTES/m6 +data: "n"/7.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/7.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +data: "p"/7.000000000,0 -> / +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:0 + gc_bytes_age:332225 + live_bytes:189 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:105 + val_count:30 + intent_bytes:0 + intent_count:0 + separated_intent_count:0 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} diff --git a/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_range_abort b/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_range_abort new file mode 100644 index 000000000000..1327ae7d71f9 --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_range_abort @@ -0,0 +1,189 @@ +# Tests MVCC stats calculations when resolving intents. Intermediate states are +# tested through stats traces. Initial state: +# +# (x is tombstone, o---o is range tombstone, [] is intent) +# +# 7 +# 6 [a6][b6][c6][x] [x] [x] [g6][h6][i6][x] [x] [x] [m6][n6][o6] x x x +# 5 n5 x q5 x +# 4 o-----------------------------------------------o +# 3 o-----------------------------------------------o +# 2 +# 1 b1 x e1 x h1 x k1 x +# a b c d e f g h i j k l m n o p q r s +# +# This uses two range tombstones, since the lowest is the one that matters for +# point key GCBytesAge. It also uses points below/above range tombstones, +# because iterators surface range keys separately from point keys, which can +# cause bugs if callers don't step onto the point key. +# +# TODO(erikgrinaker): This is probably better handled by randomized or +# generative testing, since the combinations are getting unwieldy. But it'll do +# for now. + +run stats +with ts=1 + put k=b v=b1 + del k=c + put k=e v=e1 + del k=f + put k=g v=g1 + del k=h + put k=i v=i1 + del k=j +del_range_ts k=g end=s ts=3 +del_range_ts k=g end=s ts=4 +with ts=5 + put k=n v=n5 + del k=o + put k=q v=q5 + del k=r +with t=A + txn_begin ts=6 + put k=a v=a6 + put k=b v=b6 + put k=c v=c6 + del k=d + del k=e + del k=f + put k=g v=g6 + put k=h v=h6 + put k=i v=i6 + del k=j + del k=k + del k=l + put k=m v=m6 + put k=n v=n6 + put k=o v=o6 + del k=p + del k=q + del k=r +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=6.000000000,0 wto=false gul=0,0 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/6.000000000,0 -> /BYTES/a6 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:17892 + gc_bytes_age:761835 + live_bytes:621 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:969 + val_count:30 + intent_bytes:279 + intent_count:18 + separated_intent_count:18 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} + +run stats trace +resolve_intent_range t=A k=a end=z status=ABORTED +---- +>> resolve_intent_range t=A k=a end=z status=ABORTED +called ClearIntent("a", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("b", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("c", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("d", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("e", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("f", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("g", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("h", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("i", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("j", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("k", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("l", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("m", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("n", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("o", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("p", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("q", TDNUM(false), 00000000-0000-0000-0000-000000000001) +called ClearIntent("r", TDNUM(false), 00000000-0000-0000-0000-000000000001) +stats: gc_bytes_age:+56 live_bytes:-537 live_count:-5 key_bytes:-228 key_count:-6 val_bytes:-927 val_count:-18 intent_bytes:-279 intent_count:-18 separated_intent_count:-18 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/1.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/1.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/1.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/1.000000000,0 -> / +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/5.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:0 + gc_bytes_age:147599 + live_bytes:84 + live_count:4 + key_bytes:168 + key_count:12 + val_bytes:42 + val_count:12 + intent_bytes:0 + intent_count:0 + separated_intent_count:0 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} diff --git a/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_range_commit b/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_range_commit new file mode 100644 index 000000000000..8161acbbdb1b --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_range_commit @@ -0,0 +1,207 @@ +# Tests MVCC stats calculations when resolving intents. Intermediate states are +# tested through stats traces. Initial state: +# +# (x is tombstone, o---o is range tombstone, [] is intent) +# +# 7 +# 6 [a6][b6][c6][x] [x] [x] [g6][h6][i6][x] [x] [x] [m6][n6][o6] x x x +# 5 n5 x q5 x +# 4 o-----------------------------------------------o +# 3 o-----------------------------------------------o +# 2 +# 1 b1 x e1 x h1 x k1 x +# a b c d e f g h i j k l m n o p q r s +# +# This uses two range tombstones, since the lowest is the one that matters for +# point key GCBytesAge. It also uses points below/above range tombstones, +# because iterators surface range keys separately from point keys, which can +# cause bugs if callers don't step onto the point key. +# +# TODO(erikgrinaker): This is probably better handled by randomized or +# generative testing, since the combinations are getting unwieldy. But it'll do +# for now. + +run stats +with ts=1 + put k=b v=b1 + del k=c + put k=e v=e1 + del k=f + put k=g v=g1 + del k=h + put k=i v=i1 + del k=j +del_range_ts k=g end=s ts=3 +del_range_ts k=g end=s ts=4 +with ts=5 + put k=n v=n5 + del k=o + put k=q v=q5 + del k=r +with t=A + txn_begin ts=6 + put k=a v=a6 + put k=b v=b6 + put k=c v=c6 + del k=d + del k=e + del k=f + put k=g v=g6 + put k=h v=h6 + put k=i v=i6 + del k=j + del k=k + del k=l + put k=m v=m6 + put k=n v=n6 + put k=o v=o6 + del k=p + del k=q + del k=r +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=6.000000000,0 wto=false gul=0,0 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/6.000000000,0 -> /BYTES/a6 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:17892 + gc_bytes_age:761835 + live_bytes:621 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:969 + val_count:30 + intent_bytes:279 + intent_count:18 + separated_intent_count:18 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} + +run stats trace +resolve_intent_range t=A k=a end=z status=COMMITTED +---- +>> resolve_intent_range t=A k=a end=z status=COMMITTED +called ClearIntent("a", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("b", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("c", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("d", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("e", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("f", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("g", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("h", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("i", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("j", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("k", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("l", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("m", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("n", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("o", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("p", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("q", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("r", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: live_bytes:-432 val_bytes:-864 intent_bytes:-279 intent_count:-18 separated_intent_count:-18 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/6.000000000,0 -> /BYTES/a6 +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/6.000000000,0 -> / +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/6.000000000,0 -> / +data: "l"/6.000000000,0 -> / +data: "m"/6.000000000,0 -> /BYTES/m6 +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +data: "p"/6.000000000,0 -> / +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:0 + gc_bytes_age:332427 + live_bytes:189 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:105 + val_count:30 + intent_bytes:0 + intent_count:0 + separated_intent_count:0 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} diff --git a/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_range_pushed b/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_range_pushed new file mode 100644 index 000000000000..d3c9669394a9 --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/stats_intent_resolve_range_pushed @@ -0,0 +1,225 @@ +# Tests MVCC stats calculations for intent resolution of pushed txn. +# +# Intermediate states are tested through stats traces. Final state before resolution: +# +# (x is tombstone, o---o is range tombstone, [] is intent) +# +# 6 [a6][b6][c6][x] [x] [x] [g6][h6][i6][x] [x] [x] +# 5 +# 4 o-----------------------o Two range tombstones: the lowest is the +# 3 o-----------------------o one that matters for point key GCBytesAge. +# 2 +# 1 b1 x e1 x h1 x k1 x +# a b c d e f g h i j k l m + +# Tests MVCC stats calculations when resolving intents. Intermediate states are +# tested through stats traces. Initial state: +# +# (x is tombstone, o---o is range tombstone, [] is intent) +# +# 7 +# 6 [a6][b6][c6][x] [x] [x] [g6][h6][i6][x] [x] [x] [m6][n6][o6] x x x +# 5 n5 x q5 x +# 4 o-----------------------------------------------o +# 3 o-----------------------------------------------o +# 2 +# 1 b1 x e1 x h1 x k1 x +# a b c d e f g h i j k l m n o p q r s +# +# This uses two range tombstones, since the lowest is the one that matters for +# point key GCBytesAge. It also uses points below/above range tombstones, +# because iterators surface range keys separately from point keys, which can +# cause bugs if callers don't step onto the point key. +# +# TODO(erikgrinaker): This is probably better handled by randomized or +# generative testing, since the combinations are getting unwieldy. But it'll do +# for now. + +run stats +with ts=1 + put k=b v=b1 + del k=c + put k=e v=e1 + del k=f + put k=g v=g1 + del k=h + put k=i v=i1 + del k=j +del_range_ts k=g end=s ts=3 +del_range_ts k=g end=s ts=4 +with ts=5 + put k=n v=n5 + del k=o + put k=q v=q5 + del k=r +with t=A + txn_begin ts=6 + put k=a v=a6 + put k=b v=b6 + put k=c v=c6 + del k=d + del k=e + del k=f + put k=g v=g6 + put k=h v=h6 + put k=i v=i6 + del k=j + del k=k + del k=l + put k=m v=m6 + put k=n v=n6 + put k=o v=o6 + del k=p + del k=q + del k=r +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=6.000000000,0 wto=false gul=0,0 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/6.000000000,0 -> /BYTES/a6 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:17892 + gc_bytes_age:761835 + live_bytes:621 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:969 + val_count:30 + intent_bytes:279 + intent_count:18 + separated_intent_count:18 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} + +run stats trace +txn_advance t=A ts=7 +resolve_intent_range t=A k=a end=z status=COMMITTED +---- +>> txn_advance t=A ts=7 +stats: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=6.000000000,0 wto=false gul=0,0 +>> resolve_intent_range t=A k=a end=z status=COMMITTED +called ClearIntent("a", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("b", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("c", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("d", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("e", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("f", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("g", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("h", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("i", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("j", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("k", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("l", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("m", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("n", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("o", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("p", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("q", TDNUM(true), 00000000-0000-0000-0000-000000000001) +called ClearIntent("r", TDNUM(true), 00000000-0000-0000-0000-000000000001) +stats: intent_age:-18 gc_bytes_age:-634 live_bytes:-432 val_bytes:-864 intent_bytes:-279 intent_count:-18 separated_intent_count:-18 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +data: "a"/7.000000000,0 -> /BYTES/a6 +data: "b"/7.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +data: "c"/7.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +data: "d"/7.000000000,0 -> / +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +data: "g"/7.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +data: "h"/7.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +data: "i"/7.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +data: "k"/7.000000000,0 -> / +data: "l"/7.000000000,0 -> / +data: "m"/7.000000000,0 -> /BYTES/m6 +data: "n"/7.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +data: "o"/7.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +data: "p"/7.000000000,0 -> / +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:0 + gc_bytes_age:332225 + live_bytes:189 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:105 + val_count:30 + intent_bytes:0 + intent_count:0 + separated_intent_count:0 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} diff --git a/pkg/storage/testdata/mvcc_histories/stats_intent_rewrite b/pkg/storage/testdata/mvcc_histories/stats_intent_rewrite new file mode 100644 index 000000000000..bcfb58165c90 --- /dev/null +++ b/pkg/storage/testdata/mvcc_histories/stats_intent_rewrite @@ -0,0 +1,1788 @@ +# Tests MVCC stats calculations when rewriting intents. Intermediate states are +# tested through stats traces. Initial state: +# +# (x is tombstone, o---o is range tombstone, [] is intent) +# +# 7 +# 6 [a6][b6][c6][x] [x] [x] [g6][h6][i6][x] [x] [x] [m6][n6][o6] x x x +# 5 n5 x q5 x +# 4 o-----------------------------------------------o +# 3 o-----------------------------------------------o +# 2 +# 1 b1 x e1 x h1 x k1 x +# a b c d e f g h i j k l m n o p q r s +# +# This uses two range tombstones, since the lowest is the one that matters for +# point key GCBytesAge. It also uses points below/above range tombstones, +# because iterators surface range keys separately from point keys, which can +# cause bugs if callers don't step onto the point key. +# +# TODO(erikgrinaker): This is probably better handled by randomized or +# generative testing, since the combinations are getting unwieldy. But it'll do +# for now. + +run stats +with ts=1 + put k=b v=b1 + del k=c + put k=e v=e1 + del k=f + put k=g v=g1 + del k=h + put k=i v=i1 + del k=j +del_range_ts k=g end=s ts=3 +del_range_ts k=g end=s ts=4 +with ts=5 + put k=n v=n5 + del k=o + put k=q v=q5 + del k=r +with t=A + txn_begin ts=6 + put k=a v=a6 + put k=b v=b6 + put k=c v=c6 + del k=d + del k=e + del k=f + put k=g v=g6 + put k=h v=h6 + put k=i v=i6 + del k=j + del k=k + del k=l + put k=m v=m6 + put k=n v=n6 + put k=o v=o6 + del k=p + del k=q + del k=r +---- +>> at end: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=6.000000000,0 wto=false gul=0,0 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "a"/6.000000000,0 -> /BYTES/a6 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:17892 + gc_bytes_age:761835 + live_bytes:621 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:969 + val_count:30 + intent_bytes:279 + intent_count:18 + separated_intent_count:18 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} + +# Rewrite the same keys at a higher timestamp. +run stats trace +with t=A ts=7 + txn_advance + txn_restart + put k=a v=a7 + put k=b v=b7 + put k=c v=c7 + del k=d + del k=e + del k=f + put k=g v=g7 + put k=h v=h7 + put k=i v=i7 + del k=j + del k=k + del k=l + put k=m v=m7 + put k=n v=n7 + put k=o v=o7 + del k=p + del k=q + del k=r +---- +>> txn_advance t=A ts=7 +stats: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=0 ts=7.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=6.000000000,0 wto=false gul=0,0 +>> txn_restart t=A ts=7 +stats: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=7.000000000,0 wto=false gul=0,0 +>> put k=a v=a7 t=A ts=7 +called PutIntent("a", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "b"/6.000000000,0 -> /BYTES/b6 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=b v=b7 t=A ts=7 +called PutIntent("b", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-19 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "c"/6.000000000,0 -> /BYTES/c6 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=c v=c7 t=A ts=7 +called PutIntent("c", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "d"/6.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=d t=A ts=7 +called PutIntent("d", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "e"/6.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=e t=A ts=7 +called PutIntent("e", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-81 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "f"/6.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=f t=A ts=7 +called PutIntent("f", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "g"/6.000000000,0 -> /BYTES/g6 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=g v=g7 t=A ts=7 +called PutIntent("g", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "h"/6.000000000,0 -> /BYTES/h6 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=h v=h7 t=A ts=7 +called PutIntent("h", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "i"/6.000000000,0 -> /BYTES/i6 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=i v=i7 t=A ts=7 +called PutIntent("i", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "j"/6.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=j t=A ts=7 +called PutIntent("j", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "k"/6.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=k t=A ts=7 +called PutIntent("k", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "l"/6.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=l t=A ts=7 +called PutIntent("l", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "m"/6.000000000,0 -> /BYTES/m6 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=m v=m7 t=A ts=7 +called PutIntent("m", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "n"/6.000000000,0 -> /BYTES/n6 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=n v=n7 t=A ts=7 +called PutIntent("n", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-19 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true +data: "o"/6.000000000,0 -> /BYTES/o6 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=o v=o7 t=A ts=7 +called PutIntent("o", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "p"/6.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=p t=A ts=7 +called PutIntent("p", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "q"/6.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=q t=A ts=7 +called PutIntent("q", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-81 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=0 ts=6.000000000,0 min=0,0 seq=0} ts=6.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=true +data: "r"/6.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=r t=A ts=7 +called PutIntent("r", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "a"/7.000000000,0 -> /BYTES/a7 +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:17874 + gc_bytes_age:761201 + live_bytes:621 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:969 + val_count:30 + intent_bytes:279 + intent_count:18 + separated_intent_count:18 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} + +# Rewrite keys<->tombstones at a higher timestamp. +run stats trace +with t=A ts=8 + txn_advance + txn_restart + del k=a + del k=b + del k=c + put k=d v=d8 + put k=e v=e8 + put k=f v=f8 + del k=g + del k=h + del k=i + put k=j v=j8 + put k=k v=k8 + put k=l v=l8 +---- +>> txn_advance t=A ts=8 +stats: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=1 ts=8.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=7.000000000,0 wto=false gul=0,0 +>> txn_restart t=A ts=8 +stats: +txn: "A" meta={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=8.000000000,0 wto=false gul=0,0 +>> del k=a t=A ts=8 +called PutIntent("a", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 live_bytes:-69 live_count:-1 val_bytes:-7 intent_bytes:-7 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "a"/8.000000000,0 -> / +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "b"/7.000000000,0 -> /BYTES/b7 +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=b t=A ts=8 +called PutIntent("b", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-19 live_bytes:-69 live_count:-1 val_bytes:-7 intent_bytes:-7 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "a"/8.000000000,0 -> / +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/8.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "c"/7.000000000,0 -> /BYTES/c7 +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=c t=A ts=8 +called PutIntent("c", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 live_bytes:-69 live_count:-1 val_bytes:-7 intent_bytes:-7 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "a"/8.000000000,0 -> / +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/8.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "c"/8.000000000,0 -> / +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "d"/7.000000000,0 -> / +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=d v=d8 t=A ts=8 +called PutIntent("d", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 live_bytes:+69 live_count:+1 val_bytes:+7 intent_bytes:+7 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "a"/8.000000000,0 -> / +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/8.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "c"/8.000000000,0 -> / +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "d"/8.000000000,0 -> /BYTES/d8 +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "e"/7.000000000,0 -> / +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=e v=e8 t=A ts=8 +called PutIntent("e", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-81 live_bytes:+69 live_count:+1 val_bytes:+7 intent_bytes:+7 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "a"/8.000000000,0 -> / +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/8.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "c"/8.000000000,0 -> / +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "d"/8.000000000,0 -> /BYTES/d8 +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "e"/8.000000000,0 -> /BYTES/e8 +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "f"/7.000000000,0 -> / +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=f v=f8 t=A ts=8 +called PutIntent("f", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 live_bytes:+69 live_count:+1 val_bytes:+7 intent_bytes:+7 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "a"/8.000000000,0 -> / +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/8.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "c"/8.000000000,0 -> / +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "d"/8.000000000,0 -> /BYTES/d8 +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "e"/8.000000000,0 -> /BYTES/e8 +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "f"/8.000000000,0 -> /BYTES/f8 +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "g"/7.000000000,0 -> /BYTES/g7 +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=g t=A ts=8 +called PutIntent("g", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 live_bytes:-69 live_count:-1 val_bytes:-7 intent_bytes:-7 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "a"/8.000000000,0 -> / +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/8.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "c"/8.000000000,0 -> / +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "d"/8.000000000,0 -> /BYTES/d8 +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "e"/8.000000000,0 -> /BYTES/e8 +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "f"/8.000000000,0 -> /BYTES/f8 +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "g"/8.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "h"/7.000000000,0 -> /BYTES/h7 +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=h t=A ts=8 +called PutIntent("h", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 live_bytes:-69 live_count:-1 val_bytes:-7 intent_bytes:-7 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "a"/8.000000000,0 -> / +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/8.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "c"/8.000000000,0 -> / +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "d"/8.000000000,0 -> /BYTES/d8 +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "e"/8.000000000,0 -> /BYTES/e8 +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "f"/8.000000000,0 -> /BYTES/f8 +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "g"/8.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "h"/8.000000000,0 -> / +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "i"/7.000000000,0 -> /BYTES/i7 +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> del k=i t=A ts=8 +called PutIntent("i", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 live_bytes:-69 live_count:-1 val_bytes:-7 intent_bytes:-7 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "a"/8.000000000,0 -> / +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/8.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "c"/8.000000000,0 -> / +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "d"/8.000000000,0 -> /BYTES/d8 +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "e"/8.000000000,0 -> /BYTES/e8 +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "f"/8.000000000,0 -> /BYTES/f8 +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "g"/8.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "h"/8.000000000,0 -> / +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "i"/8.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "j"/7.000000000,0 -> / +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=j v=j8 t=A ts=8 +called PutIntent("j", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 live_bytes:+69 live_count:+1 val_bytes:+7 intent_bytes:+7 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "a"/8.000000000,0 -> / +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/8.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "c"/8.000000000,0 -> / +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "d"/8.000000000,0 -> /BYTES/d8 +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "e"/8.000000000,0 -> /BYTES/e8 +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "f"/8.000000000,0 -> /BYTES/f8 +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "g"/8.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "h"/8.000000000,0 -> / +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "i"/8.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "j"/8.000000000,0 -> /BYTES/j8 +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "k"/7.000000000,0 -> / +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=k v=k8 t=A ts=8 +called PutIntent("k", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 live_bytes:+69 live_count:+1 val_bytes:+7 intent_bytes:+7 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "a"/8.000000000,0 -> / +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/8.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "c"/8.000000000,0 -> / +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "d"/8.000000000,0 -> /BYTES/d8 +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "e"/8.000000000,0 -> /BYTES/e8 +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "f"/8.000000000,0 -> /BYTES/f8 +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "g"/8.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "h"/8.000000000,0 -> / +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "i"/8.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "j"/8.000000000,0 -> /BYTES/j8 +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "k"/8.000000000,0 -> /BYTES/k8 +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "l"/7.000000000,0 -> / +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +>> put k=l v=l8 t=A ts=8 +called PutIntent("l", _, 00000000-0000-0000-0000-000000000001) +stats: intent_age:-1 gc_bytes_age:-62 live_bytes:+69 live_count:+1 val_bytes:+7 intent_bytes:+7 +rangekey: {g-s}/[4.000000000,0 3.000000000,0] +meta: "a"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "a"/8.000000000,0 -> / +meta: "b"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "b"/8.000000000,0 -> / +data: "b"/1.000000000,0 -> /BYTES/b1 +meta: "c"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "c"/8.000000000,0 -> / +data: "c"/1.000000000,0 -> / +meta: "d"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "d"/8.000000000,0 -> /BYTES/d8 +meta: "e"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "e"/8.000000000,0 -> /BYTES/e8 +data: "e"/1.000000000,0 -> /BYTES/e1 +meta: "f"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "f"/8.000000000,0 -> /BYTES/f8 +data: "f"/1.000000000,0 -> / +meta: "g"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "g"/8.000000000,0 -> / +data: "g"/1.000000000,0 -> /BYTES/g1 +meta: "h"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "h"/8.000000000,0 -> / +data: "h"/1.000000000,0 -> / +meta: "i"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "i"/8.000000000,0 -> / +data: "i"/1.000000000,0 -> /BYTES/i1 +meta: "j"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "j"/8.000000000,0 -> /BYTES/j8 +data: "j"/1.000000000,0 -> / +meta: "k"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "k"/8.000000000,0 -> /BYTES/k8 +meta: "l"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=2 ts=8.000000000,0 min=0,0 seq=0} ts=8.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "l"/8.000000000,0 -> /BYTES/l8 +meta: "m"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "m"/7.000000000,0 -> /BYTES/m7 +meta: "n"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "n"/7.000000000,0 -> /BYTES/n7 +data: "n"/5.000000000,0 -> /BYTES/n5 +meta: "o"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=false +data: "o"/7.000000000,0 -> /BYTES/o7 +data: "o"/5.000000000,0 -> / +meta: "p"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "p"/7.000000000,0 -> / +meta: "q"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "q"/7.000000000,0 -> / +data: "q"/5.000000000,0 -> /BYTES/q5 +meta: "r"/0,0 -> txn={id=00000000 key=/Min pri=0.00000000 epo=1 ts=7.000000000,0 min=0,0 seq=0} ts=7.000000000,0 del=true klen=12 vlen=0 mergeTs= txnDidNotUpdateMeta=false +data: "r"/7.000000000,0 -> / +data: "r"/5.000000000,0 -> / +stats: { + contains_estimates:0 + last_update_nanos:1000000000000 + intent_age:17862 + gc_bytes_age:760791 + live_bytes:621 + live_count:9 + key_bytes:396 + key_count:18 + val_bytes:969 + val_count:30 + intent_bytes:279 + intent_count:18 + separated_intent_count:18 + range_key_count:1 + range_key_bytes:22 + sys_bytes:0 + sys_count:0 + abort_span_bytes:0 +} diff --git a/pkg/util/encoding/encoding.go b/pkg/util/encoding/encoding.go index ebe39b178477..505b4d66fe49 100644 --- a/pkg/util/encoding/encoding.go +++ b/pkg/util/encoding/encoding.go @@ -3185,3 +3185,29 @@ func BytesNext(b []byte) []byte { bn[len(bn)-1] = 0 return bn } + +// BytesPrevish returns a previous byte slice in lexicographical ordering. It is +// impossible in general to find the exact previous byte slice, because it has +// an infinite number of 0xff bytes at the end, so this returns the nearest +// previous slice right-padded with 0xff up to length bytes. It may reuse the +// given slice when possible. +func BytesPrevish(b []byte, length int) []byte { + bLen := len(b) + // An empty slice has no previous slice. + if bLen == 0 { + return b + } + // If the last byte is 0, just remove it. + if b[bLen-1] == 0 { + return b[:bLen-1] + } + // Otherwise, decrement the last byte and right-pad with 0xff. + if bLen > length { + length = bLen + } + buf := make([]byte, length) + copy(buf, b) + buf[bLen-1]-- + copy(buf[bLen:], bytes.Repeat([]byte{0xff}, length-bLen)) + return buf +}