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 }