@@ -16,6 +16,8 @@ import (
16
16
"github.com/cockroachdb/pebble/internal/humanize"
17
17
"github.com/cockroachdb/pebble/internal/invariants"
18
18
"github.com/cockroachdb/pebble/internal/manifest"
19
+ "github.com/cockroachdb/pebble/objstorage"
20
+ "github.com/cockroachdb/pebble/objstorage/remote"
19
21
"github.com/cockroachdb/pebble/vfs"
20
22
"github.com/cockroachdb/redact"
21
23
)
@@ -45,6 +47,35 @@ func formatFileNums(tables []TableInfo) string {
45
47
return buf .String ()
46
48
}
47
49
50
+ // DataCorruptionInfo contains the information for a DataCorruption event.
51
+ type DataCorruptionInfo struct {
52
+ // Path of the file that is corrupted. For remote files the path starts with
53
+ // "remote://".
54
+ Path string
55
+ IsRemote bool
56
+ // Locator is only set when IsRemote is true (note that an empty Locator is
57
+ // valid even then).
58
+ Locator remote.Locator
59
+ // Bounds indicates the keyspace range that is affected.
60
+ Bounds base.UserKeyBounds
61
+ // Details of the error. See cockroachdb/error for how to format with or
62
+ // without redaction.
63
+ Details error
64
+ }
65
+
66
+ func (i DataCorruptionInfo ) String () string {
67
+ return redact .StringWithoutMarkers (i )
68
+ }
69
+
70
+ // SafeFormat implements redact.SafeFormatter.
71
+ func (i DataCorruptionInfo ) SafeFormat (w redact.SafePrinter , _ rune ) {
72
+ w .Printf ("on-disk corruption: %s" , redact .Safe (i .Path ))
73
+ if i .IsRemote {
74
+ w .Printf (" (remote locator %q)" , redact .Safe (i .Locator ))
75
+ }
76
+ w .Printf ("; bounds: %s; details: %+v" , i .Bounds .String (), i .Details )
77
+ }
78
+
48
79
// LevelInfo contains info pertaining to a particular level.
49
80
type LevelInfo struct {
50
81
Level int
@@ -693,6 +724,10 @@ type EventListener struct {
693
724
// operation such as flush or compaction.
694
725
BackgroundError func (error )
695
726
727
+ // DataCorruption is invoked when an on-disk corruption is detected. It should
728
+ // not block, as it is called synchronously in read paths.
729
+ DataCorruption func (DataCorruptionInfo )
730
+
696
731
// CompactionBegin is invoked after the inputs to a compaction have been
697
732
// determined, but before the compaction has produced any output.
698
733
CompactionBegin func (CompactionInfo )
@@ -797,6 +832,15 @@ func (l *EventListener) EnsureDefaults(logger Logger) {
797
832
l .BackgroundError = func (error ) {}
798
833
}
799
834
}
835
+ if l .DataCorruption == nil {
836
+ if logger != nil {
837
+ l .DataCorruption = func (info DataCorruptionInfo ) {
838
+ logger .Fatalf ("%s" , info )
839
+ }
840
+ } else {
841
+ l .DataCorruption = func (info DataCorruptionInfo ) {}
842
+ }
843
+ }
800
844
if l .CompactionBegin == nil {
801
845
l .CompactionBegin = func (info CompactionInfo ) {}
802
846
}
@@ -873,6 +917,9 @@ func MakeLoggingEventListener(logger Logger) EventListener {
873
917
BackgroundError : func (err error ) {
874
918
backgroundError (logger , err )
875
919
},
920
+ DataCorruption : func (info DataCorruptionInfo ) {
921
+ logger .Errorf ("%s" , info )
922
+ },
876
923
CompactionBegin : func (info CompactionInfo ) {
877
924
logger .Infof ("%s" , info )
878
925
},
@@ -948,6 +995,10 @@ func TeeEventListener(a, b EventListener) EventListener {
948
995
a .BackgroundError (err )
949
996
b .BackgroundError (err )
950
997
},
998
+ DataCorruption : func (info DataCorruptionInfo ) {
999
+ a .DataCorruption (info )
1000
+ b .DataCorruption (info )
1001
+ },
951
1002
CompactionBegin : func (info CompactionInfo ) {
952
1003
a .CompactionBegin (info )
953
1004
b .CompactionBegin (info )
@@ -1098,3 +1149,35 @@ func (r *lowDiskSpaceReporter) findThreshold(
1098
1149
}
1099
1150
return threshold , ok
1100
1151
}
1152
+
1153
+ func (d * DB ) reportSSTableCorruption (meta * manifest.TableMetadata , err error ) {
1154
+ if invariants .Enabled && err == nil {
1155
+ panic ("nil error" )
1156
+ }
1157
+ objMeta , lookupErr := d .objProvider .Lookup (base .FileTypeTable , meta .FileBacking .DiskFileNum )
1158
+ if lookupErr != nil {
1159
+ // If the object is not known to the provider, it must be a local object
1160
+ // that was missing when we opened the store. Remote objects have their
1161
+ // metadata in a catalog, so even if the backing object is deleted, the
1162
+ // DiskFileNum would still be known.
1163
+ objMeta = objstorage.ObjectMetadata {DiskFileNum : meta .FileBacking .DiskFileNum , FileType : base .FileTypeTable }
1164
+ }
1165
+ path := d .objProvider .Path (objMeta )
1166
+ if objMeta .IsRemote () {
1167
+ // Remote path (which include the locator and full path) might not always be
1168
+ // safe.
1169
+ err = errors .WithHintf (err , "path: %s" , path )
1170
+ } else {
1171
+ // Local paths are safe: they start with the store directory and the
1172
+ // filename is generated by Pebble.
1173
+ err = errors .WithHintf (err , "path: %s" , redact .Safe (path ))
1174
+ }
1175
+ info := DataCorruptionInfo {
1176
+ Path : path ,
1177
+ IsRemote : objMeta .IsRemote (),
1178
+ Locator : objMeta .Remote .Locator ,
1179
+ Bounds : meta .UserKeyBounds (),
1180
+ Details : err ,
1181
+ }
1182
+ d .opts .EventListener .DataCorruption (info )
1183
+ }
0 commit comments