github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvnemesis/applier.go (about) 1 // Copyright 2020 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 kvnemesis 12 13 import ( 14 "context" 15 16 "github.com/cockroachdb/cockroach/pkg/kv" 17 "github.com/cockroachdb/cockroach/pkg/roachpb" 18 "github.com/cockroachdb/cockroach/pkg/util/hlc" 19 "github.com/cockroachdb/cockroach/pkg/util/log" 20 "github.com/cockroachdb/cockroach/pkg/util/retry" 21 "github.com/cockroachdb/cockroach/pkg/util/syncutil" 22 "github.com/cockroachdb/errors" 23 ) 24 25 // Applier executes Steps. 26 type Applier struct { 27 dbs []*kv.DB 28 mu struct { 29 dbIdx int 30 syncutil.Mutex 31 txns map[string]*kv.Txn 32 } 33 } 34 35 // MakeApplier constructs an Applier that executes against the given DB. 36 func MakeApplier(dbs ...*kv.DB) *Applier { 37 a := &Applier{ 38 dbs: dbs, 39 } 40 a.mu.txns = make(map[string]*kv.Txn) 41 return a 42 } 43 44 // Apply executes the given Step and mutates it with the result of execution. An 45 // error is only returned from Apply if there is an internal coding error within 46 // Applier, errors from a Step execution are saved in the Step itself. 47 func (a *Applier) Apply(ctx context.Context, step *Step) (retErr error) { 48 var db *kv.DB 49 db, step.DBID = a.getNextDBRoundRobin() 50 51 step.Before = db.Clock().Now() 52 defer func() { 53 step.After = db.Clock().Now() 54 if p := recover(); p != nil { 55 retErr = errors.Errorf(`panic applying step %s: %v`, step, p) 56 } 57 }() 58 applyOp(ctx, db, &step.Op) 59 return nil 60 } 61 62 func (a *Applier) getNextDBRoundRobin() (*kv.DB, int32) { 63 a.mu.Lock() 64 dbIdx := a.mu.dbIdx 65 a.mu.dbIdx = (a.mu.dbIdx + 1) % len(a.dbs) 66 a.mu.Unlock() 67 return a.dbs[dbIdx], int32(dbIdx) 68 } 69 70 func applyOp(ctx context.Context, db *kv.DB, op *Operation) { 71 switch o := op.GetValue().(type) { 72 case *GetOperation, *PutOperation, *BatchOperation: 73 applyClientOp(ctx, db, op) 74 case *SplitOperation: 75 err := db.AdminSplit(ctx, o.Key, o.Key, hlc.MaxTimestamp) 76 o.Result = resultError(ctx, err) 77 case *MergeOperation: 78 err := db.AdminMerge(ctx, o.Key) 79 o.Result = resultError(ctx, err) 80 case *ChangeReplicasOperation: 81 desc := getRangeDesc(ctx, o.Key, db) 82 _, err := db.AdminChangeReplicas(ctx, o.Key, desc, o.Changes) 83 // TODO(dan): Save returned desc? 84 o.Result = resultError(ctx, err) 85 case *ClosureTxnOperation: 86 var savedTxn *kv.Txn 87 txnErr := db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { 88 savedTxn = txn 89 for i := range o.Ops { 90 op := &o.Ops[i] 91 applyClientOp(ctx, txn, op) 92 // The KV api disallows use of a txn after an operation on it errors. 93 if r := op.Result(); r.Type == ResultType_Error { 94 return errors.DecodeError(ctx, *r.Err) 95 } 96 } 97 if o.CommitInBatch != nil { 98 b := txn.NewBatch() 99 applyBatchOp(ctx, b, txn.CommitInBatch, o.CommitInBatch) 100 // The KV api disallows use of a txn after an operation on it errors. 101 if r := o.CommitInBatch.Result; r.Type == ResultType_Error { 102 return errors.DecodeError(ctx, *r.Err) 103 } 104 } 105 switch o.Type { 106 case ClosureTxnType_Commit: 107 return nil 108 case ClosureTxnType_Rollback: 109 return errors.New("rollback") 110 default: 111 panic(errors.AssertionFailedf(`unknown closure txn type: %s`, o.Type)) 112 } 113 }) 114 o.Result = resultError(ctx, txnErr) 115 if txnErr == nil { 116 o.Txn = savedTxn.Sender().TestingCloneTxn() 117 } 118 default: 119 panic(errors.AssertionFailedf(`unknown operation type: %T %v`, o, o)) 120 } 121 } 122 123 type clientI interface { 124 Get(context.Context, interface{}) (kv.KeyValue, error) 125 Put(context.Context, interface{}, interface{}) error 126 Run(context.Context, *kv.Batch) error 127 } 128 129 func applyClientOp(ctx context.Context, db clientI, op *Operation) { 130 switch o := op.GetValue().(type) { 131 case *GetOperation: 132 result, err := db.Get(ctx, o.Key) 133 if err != nil { 134 o.Result = resultError(ctx, err) 135 } else { 136 o.Result.Type = ResultType_Value 137 if result.Value != nil { 138 o.Result.Value = result.Value.RawBytes 139 } 140 } 141 case *PutOperation: 142 err := db.Put(ctx, o.Key, o.Value) 143 o.Result = resultError(ctx, err) 144 case *BatchOperation: 145 b := &kv.Batch{} 146 applyBatchOp(ctx, b, db.Run, o) 147 default: 148 panic(errors.AssertionFailedf(`unknown batch operation type: %T %v`, o, o)) 149 } 150 } 151 152 func applyBatchOp( 153 ctx context.Context, b *kv.Batch, runFn func(context.Context, *kv.Batch) error, o *BatchOperation, 154 ) { 155 for i := range o.Ops { 156 switch subO := o.Ops[i].GetValue().(type) { 157 case *GetOperation: 158 b.Get(subO.Key) 159 case *PutOperation: 160 b.Put(subO.Key, subO.Value) 161 default: 162 panic(errors.AssertionFailedf(`unknown batch operation type: %T %v`, subO, subO)) 163 } 164 } 165 runErr := runFn(ctx, b) 166 o.Result = resultError(ctx, runErr) 167 for i := range o.Ops { 168 switch subO := o.Ops[i].GetValue().(type) { 169 case *GetOperation: 170 if b.Results[i].Err != nil { 171 subO.Result = resultError(ctx, b.Results[i].Err) 172 } else { 173 subO.Result.Type = ResultType_Value 174 result := b.Results[i].Rows[0] 175 if result.Value != nil { 176 subO.Result.Value = result.Value.RawBytes 177 } 178 } 179 case *PutOperation: 180 err := b.Results[i].Err 181 subO.Result = resultError(ctx, err) 182 default: 183 panic(errors.AssertionFailedf(`unknown batch operation type: %T %v`, subO, subO)) 184 } 185 } 186 } 187 188 func resultError(ctx context.Context, err error) Result { 189 if err == nil { 190 return Result{Type: ResultType_NoError} 191 } 192 ee := errors.EncodeError(ctx, err) 193 return Result{ 194 Type: ResultType_Error, 195 Err: &ee, 196 } 197 } 198 199 func getRangeDesc(ctx context.Context, key roachpb.Key, dbs ...*kv.DB) roachpb.RangeDescriptor { 200 var dbIdx int 201 var opts = retry.Options{} 202 for r := retry.StartWithCtx(ctx, opts); r.Next(); dbIdx = (dbIdx + 1) % len(dbs) { 203 sender := dbs[dbIdx].NonTransactionalSender() 204 descs, _, err := kv.RangeLookup(ctx, sender, key, roachpb.CONSISTENT, 0, false) 205 if err != nil { 206 log.Infof(ctx, "looking up descriptor for %s: %+v", key, err) 207 continue 208 } 209 if len(descs) != 1 { 210 log.Infof(ctx, "unexpected number of descriptors for %s: %d", key, len(descs)) 211 continue 212 } 213 return descs[0] 214 } 215 panic(`unreachable`) 216 } 217 218 func newGetReplicasFn(dbs ...*kv.DB) GetReplicasFn { 219 ctx := context.Background() 220 return func(key roachpb.Key) []roachpb.ReplicationTarget { 221 desc := getRangeDesc(ctx, key, dbs...) 222 replicas := desc.Replicas().All() 223 targets := make([]roachpb.ReplicationTarget, len(replicas)) 224 for i, replica := range replicas { 225 targets[i] = roachpb.ReplicationTarget{ 226 NodeID: replica.NodeID, 227 StoreID: replica.StoreID, 228 } 229 } 230 return targets 231 } 232 }