-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
Copy pathrecord.go
111 lines (103 loc) · 3.87 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
104
105
106
107
108
109
110
111
// 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,
readWriter storage.ReadWriter,
) 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 := readWriter.PutUnversioned(
keys.StoreUnsafeReplicaRecoveryKey(uuid), data); err != nil {
return err
}
return nil
}
// RegisterOfflineRecoveryEvents checks if recovery data was captured in the
// store and notifies callback about all registered events. Its up to the
// callback function to send events where appropriate.
// This function is called on startup to ensure that any offline replica
// recovery actions are properly reflected in server logs as needed.
// If removeEvents is true, events are removed from store and won't be processed
// on subsequent runs of this function.
// If registerEvent returns error, value associated with a key is not removed
// from store even if removeEvents is true.
//
// 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) error,
removeEvents 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 err := registerEvent(ctx, record); err == nil {
eventCount++
if removeEvents {
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()
}
if err != nil {
return eventCount, errors.Wrapf(err, "failed to iterate replica recovery record keys")
}
return eventCount, nil
}