@@ -33,6 +33,7 @@ import (
3333 "github.com/cockroachdb/pebble/internal/testkeys"
3434 "github.com/cockroachdb/pebble/objstorage"
3535 "github.com/cockroachdb/pebble/objstorage/objstorageprovider"
36+ "github.com/cockroachdb/pebble/objstorage/remote"
3637 "github.com/cockroachdb/pebble/sstable/block"
3738 "github.com/cockroachdb/pebble/sstable/valblk"
3839 "github.com/cockroachdb/pebble/vfs"
@@ -2535,3 +2536,68 @@ func newReader(r ReadableFile, o ReaderOptions) (*Reader, error) {
25352536 }
25362537 return NewReader (context .Background (), readable , o )
25372538}
2539+
2540+ // TestReaderReportsCorruption tests that the reader reports corruption when
2541+ // an external file goes missing after obtaining a remote.ObjectReader for it.
2542+ func TestReaderReportsCorruption (t * testing.T ) {
2543+ defer leaktest .AfterTest (t )()
2544+
2545+ ctx := context .Background ()
2546+ remoteStorage := remote .NewInMem ()
2547+ settings := objstorageprovider .DefaultSettings (vfs .NewMem (), "" )
2548+ settings .Remote .StorageFactory = remote .MakeSimpleFactory (map [remote.Locator ]remote.Storage {
2549+ "locator" : remoteStorage ,
2550+ })
2551+ settings .Remote .CreateOnSharedLocator = "locator"
2552+ settings .Remote .CreateOnShared = remote .CreateOnSharedAll
2553+ provider , err := objstorageprovider .Open (settings )
2554+ require .NoError (t , err )
2555+ defer provider .Close ()
2556+ require .NoError (t , provider .SetCreatorID (1 ))
2557+
2558+ writable , objMeta , err := provider .Create (ctx , base .FileTypeTable , 1 , objstorage.CreateOptions {
2559+ PreferSharedStorage : true ,
2560+ })
2561+ require .NoError (t , err )
2562+ require .NotNil (t , objMeta .Remote .Storage )
2563+
2564+ // Create an sst file with multiple data blocks.
2565+ w := NewWriter (writable , WriterOptions {BlockSize : 1 })
2566+ for i := range 100 {
2567+ require .NoError (t , w .Set ([]byte (fmt .Sprintf ("%04d" , i )), []byte (fmt .Sprintf ("value%04d" , i ))))
2568+ }
2569+ require .NoError (t , w .Close ())
2570+
2571+ readable , err := provider .OpenForReading (ctx , base .FileTypeTable , 1 , objstorage.OpenOptions {})
2572+ require .NoError (t , err )
2573+ r , err := NewReader (context .Background (), readable , ReaderOptions {})
2574+ require .NoError (t , err )
2575+ defer r .Close ()
2576+
2577+ var lastReportedCorruption error
2578+ env := block.ReadEnv {
2579+ ReportCorruptionFn : func (opaque any , err error ) error {
2580+ lastReportedCorruption = err
2581+ return errors .Wrap (err , "error passed through ReportCorruptionFn" )
2582+ },
2583+ }
2584+ iter , err := r .NewPointIter (context .Background (), NoTransforms , nil , nil , nil , 0 , env , nil )
2585+ require .NoError (t , err )
2586+ defer iter .Close ()
2587+ kv := iter .First ()
2588+ require .NotNil (t , kv )
2589+
2590+ // Delete all objects from the store and expect to get a corruption error.
2591+ objects , err := remoteStorage .List ("" , "" )
2592+ require .NoError (t , err )
2593+ for _ , o := range objects {
2594+ require .NoError (t , remoteStorage .Delete (o ))
2595+ }
2596+ for ; kv != nil ; kv = iter .Next () {
2597+ }
2598+ iterErr := iter .Error ()
2599+ require .ErrorContains (t , iterErr , "error passed through ReportCorruptionFn: in-mem remote storage object does not exist" )
2600+ require .True (t , base .IsCorruptionError (iterErr ))
2601+ require .ErrorContains (t , lastReportedCorruption , "in-mem remote storage object does not exist" )
2602+ require .True (t , base .IsCorruptionError (lastReportedCorruption ))
2603+ }
0 commit comments