github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/protectedts/ptverifier/verifier.go (about)

     1  // Copyright 2019 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 ptverifier
    12  
    13  import (
    14  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/kv"
    17  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/protectedts"
    18  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/protectedts/ptpb"
    19  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    20  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    21  	"github.com/cockroachdb/cockroach/pkg/util/uuid"
    22  	"github.com/cockroachdb/errors"
    23  )
    24  
    25  // verifier implements protectedts.Verifier.
    26  type verifier struct {
    27  	db *kv.DB
    28  	s  protectedts.Storage
    29  }
    30  
    31  // New returns a new Verifier.
    32  func New(db *kv.DB, s protectedts.Storage) protectedts.Verifier {
    33  	return &verifier{db: db, s: s}
    34  }
    35  
    36  // Verify verifies that a record with the provided id is verified.
    37  // If it is not verified this call will perform verification and mark the
    38  // record as verified.
    39  func (v *verifier) Verify(ctx context.Context, id uuid.UUID) error {
    40  	// First we go read the record and note the timestamp at which we read it.
    41  	r, ts, err := getRecordWithTimestamp(ctx, v.s, v.db, id)
    42  	if err != nil {
    43  		return errors.Wrapf(err, "failed to fetch record %s", id)
    44  	}
    45  
    46  	if r.Verified { // already verified
    47  		return nil
    48  	}
    49  
    50  	b := makeVerificationBatch(r, ts)
    51  	if err := v.db.Run(ctx, &b); err != nil {
    52  		return err
    53  	}
    54  
    55  	// Check the responses and synthesize an error if one occurred.
    56  	if err := parseResponse(&b, r); err != nil {
    57  		return err
    58  	}
    59  	// Mark the record as verified.
    60  	return errors.Wrapf(v.db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error {
    61  		return v.s.MarkVerified(ctx, txn, id)
    62  	}), "failed to mark %v as verified", id)
    63  }
    64  
    65  // getRecordWithTimestamp fetches the record with the provided id and returns
    66  // the hlc timestamp at which that read occurred.
    67  func getRecordWithTimestamp(
    68  	ctx context.Context, s protectedts.Storage, db *kv.DB, id uuid.UUID,
    69  ) (r *ptpb.Record, readAt hlc.Timestamp, err error) {
    70  	if err = db.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error {
    71  		r, err = s.GetRecord(ctx, txn, id)
    72  		readAt = txn.ReadTimestamp()
    73  		return err
    74  	}); err != nil {
    75  		return nil, hlc.Timestamp{}, err
    76  	}
    77  	return r, readAt, nil
    78  }
    79  
    80  func makeVerificationBatch(r *ptpb.Record, aliveAt hlc.Timestamp) kv.Batch {
    81  	// Need to perform validation, build a batch and run it.
    82  	mergedSpans, _ := roachpb.MergeSpans(r.Spans)
    83  	var b kv.Batch
    84  	for _, s := range mergedSpans {
    85  		var req roachpb.AdminVerifyProtectedTimestampRequest
    86  		req.RecordAliveAt = aliveAt
    87  		req.Protected = r.Timestamp
    88  		req.RecordID = r.ID
    89  		req.Key = s.Key
    90  		req.EndKey = s.EndKey
    91  		b.AddRawRequest(&req)
    92  	}
    93  	return b
    94  }
    95  
    96  func parseResponse(b *kv.Batch, r *ptpb.Record) error {
    97  	rawResponse := b.RawResponse()
    98  	var failed []roachpb.RangeDescriptor
    99  	for _, r := range rawResponse.Responses {
   100  		resp := r.GetInner().(*roachpb.AdminVerifyProtectedTimestampResponse)
   101  		if len(resp.FailedRanges) == 0 {
   102  			continue
   103  		}
   104  		if len(failed) == 0 {
   105  			failed = resp.FailedRanges
   106  		} else {
   107  			failed = append(failed, resp.FailedRanges...)
   108  		}
   109  	}
   110  	if len(failed) > 0 {
   111  		return errors.Errorf("failed to verify protection %v on %v", r, failed)
   112  	}
   113  	return nil
   114  }