-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
Copy pathrecord.go
103 lines (95 loc) · 3.64 KB
/
record.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// Copyright 2021 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package loqrecovery
import (
"context"
"github.com/cockroachdb/cockroach/pkg/keys"
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/loqrecovery/loqrecoverypb"
"github.com/cockroachdb/cockroach/pkg/storage"
"github.com/cockroachdb/cockroach/pkg/util/protoutil"
"github.com/cockroachdb/cockroach/pkg/util/uuid"
"github.com/cockroachdb/errors"
)
// writeReplicaRecoveryStoreRecord adds a replica recovery record to store local
// part of key range. This entry is subsequently used on node startup to
// log the data and preserve this information for subsequent debugging as
// needed.
// Record keys have an index suffix. Every recovery run will find first unused
// slot and write records in keys with sequential index.
// See RegisterOfflineRecoveryEvents for details on where these records
// are read and deleted.
func writeReplicaRecoveryStoreRecord(
uuid uuid.UUID,
timestamp int64,
update loqrecoverypb.ReplicaUpdate,
report PrepareReplicaReport,
updater *replicaPrepareBatch,
) error {
record := loqrecoverypb.ReplicaRecoveryRecord{
Timestamp: timestamp,
RangeID: report.RangeID,
StartKey: update.StartKey,
EndKey: update.StartKey,
OldReplicaID: report.OldReplica.ReplicaID,
NewReplica: update.NewReplica,
}
data, err := protoutil.Marshal(&record)
if err != nil {
return errors.Wrap(err, "failed to marshal update record entry")
}
if err := updater.readWriter.PutUnversioned(
keys.StoreReplicaUnsafeRecoveryKey(uuid.GetBytes(), updater.nextUpdateRecordIndex), data); err != nil {
return err
}
updater.nextUpdateRecordIndex++
return nil
}
// RegisterOfflineRecoveryEvents checks if recovery data was captured in the store and writes
// appropriate structured entries to the log.
// This function is called on startup to ensure that any offline replica recovery actions
// are properly reflected in server logs as needed.
// Any new destinations for this info should be added to registerEvent function.
// If registerEvent returns true, value associated with a key is removed from store.
//
// TODO(oleg): #73679 add events to the rangelog. That would require registering events
// at the later stage of startup when SQL is already available.
func RegisterOfflineRecoveryEvents(
ctx context.Context,
readWriter storage.ReadWriter,
registerEvent func(context.Context, loqrecoverypb.ReplicaRecoveryRecord) bool,
) (int, error) {
eventCount := 0
iter := readWriter.NewMVCCIterator(
storage.MVCCKeyIterKind, storage.IterOptions{
LowerBound: keys.LocalStoreUnsafeReplicaRecoveryKeyMin,
UpperBound: keys.LocalStoreUnsafeReplicaRecoveryKeyMax,
})
defer iter.Close()
iter.SeekGE(storage.MVCCKey{Key: keys.LocalStoreUnsafeReplicaRecoveryKeyMin})
valid, err := iter.Valid()
for ; valid && err == nil; valid, err = iter.Valid() {
record := loqrecoverypb.ReplicaRecoveryRecord{}
if err := iter.ValueProto(&record); err != nil {
return eventCount, errors.Wrapf(
err, "failed to deserialize replica recovery event at key %s",
iter.UnsafeRawKey())
}
if registerEvent(ctx, record) {
eventCount++
if err := readWriter.ClearUnversioned(iter.UnsafeKey().Key); err != nil {
return eventCount, errors.Wrapf(
err, "failed to delete replica recovery record at index %s",
iter.UnsafeRawKey())
}
}
iter.Next()
}
return eventCount, nil
}