github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/batcheval/cmd_recompute_stats.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package batcheval 12 13 import ( 14 "context" 15 16 "github.com/cockroachdb/cockroach/pkg/clusterversion" 17 "github.com/cockroachdb/cockroach/pkg/keys" 18 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/batcheval/result" 19 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/rditer" 20 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/spanset" 21 "github.com/cockroachdb/cockroach/pkg/roachpb" 22 "github.com/cockroachdb/cockroach/pkg/storage" 23 "github.com/cockroachdb/cockroach/pkg/storage/enginepb" 24 "github.com/cockroachdb/cockroach/pkg/util/uuid" 25 "github.com/cockroachdb/errors" 26 ) 27 28 func init() { 29 RegisterReadOnlyCommand(roachpb.RecomputeStats, declareKeysRecomputeStats, RecomputeStats) 30 } 31 32 func declareKeysRecomputeStats( 33 desc *roachpb.RangeDescriptor, 34 header roachpb.Header, 35 req roachpb.Request, 36 latchSpans, _ *spanset.SpanSet, 37 ) { 38 // We don't declare any user key in the range. This is OK since all we're doing is computing a 39 // stats delta, and applying this delta commutes with other operations on the same key space. 40 // 41 // But we want two additional properties: 42 // 1) prevent interleaving with splits, and 43 // 2) prevent interleaving between different incarnations of `RecomputeStats`. 44 // 45 // This is achieved by declaring 46 // 1) a read on the range descriptor key (thus blocking splits) and 47 // 2) a write on a transaction anchored at the range descriptor key (thus blocking any other 48 // incarnation of RecomputeStats). 49 // 50 // Note that we're also accessing the range stats key, but we don't declare it for the same 51 // reasons as above. 52 rdKey := keys.RangeDescriptorKey(desc.StartKey) 53 latchSpans.AddNonMVCC(spanset.SpanReadOnly, roachpb.Span{Key: rdKey}) 54 latchSpans.AddNonMVCC(spanset.SpanReadWrite, roachpb.Span{Key: keys.TransactionKey(rdKey, uuid.Nil)}) 55 } 56 57 // RecomputeStats recomputes the MVCCStats stored for this range and adjust them accordingly, 58 // returning the MVCCStats delta obtained in the process. 59 func RecomputeStats( 60 ctx context.Context, _ storage.Reader, cArgs CommandArgs, resp roachpb.Response, 61 ) (result.Result, error) { 62 desc := cArgs.EvalCtx.Desc() 63 args := cArgs.Args.(*roachpb.RecomputeStatsRequest) 64 if !desc.StartKey.AsRawKey().Equal(args.Key) { 65 return result.Result{}, errors.New("descriptor mismatch; range likely merged") 66 } 67 dryRun := args.DryRun 68 69 args = nil // avoid accidental use below 70 71 // Open a snapshot from which we will read everything (including the 72 // MVCCStats). This is necessary because a batch does not provide us 73 // with a consistent view of the data -- reading from the batch, we 74 // could see skew between the stats recomputation and the MVCCStats 75 // we read from the range state if concurrent writes are inflight[1]. 76 // 77 // Note that in doing so, we also circumvent the assertions (present in both 78 // the EvalContext and the batch in some builds) which check that all reads 79 // were previously declared. See the comment in `declareKeysRecomputeStats` 80 // for details on this. 81 // 82 // [1]: see engine.TestBatchReadLaterWrite. 83 snap := cArgs.EvalCtx.Engine().NewSnapshot() 84 defer snap.Close() 85 86 actualMS, err := rditer.ComputeStatsForRange(desc, snap, cArgs.Header.Timestamp.WallTime) 87 if err != nil { 88 return result.Result{}, err 89 } 90 91 currentStats, err := MakeStateLoader(cArgs.EvalCtx).LoadMVCCStats(ctx, snap) 92 if err != nil { 93 return result.Result{}, err 94 } 95 96 delta := actualMS 97 delta.Subtract(currentStats) 98 99 if !dryRun { 100 // TODO(tschottdorf): do we not want to run at all if we have estimates in 101 // this range? I think we want to as this would give us much more realistic 102 // stats for timeseries ranges (which go cold and the approximate stats are 103 // wildly overcounting) and this is paced by the consistency checker, but it 104 // means some extra engine churn. 105 if !cArgs.EvalCtx.ClusterSettings().Version.IsActive(ctx, clusterversion.VersionContainsEstimatesCounter) { 106 // We are running with the older version of MVCCStats.ContainsEstimates 107 // which was a boolean, so we should keep it in {0,1} and not reset it 108 // to avoid racing with another command that sets it to true. 109 delta.ContainsEstimates = currentStats.ContainsEstimates 110 } 111 cArgs.Stats.Add(delta) 112 } 113 114 resp.(*roachpb.RecomputeStatsResponse).AddedDelta = enginepb.MVCCStatsDelta(delta) 115 return result.Result{}, nil 116 }