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  }