Skip to content

Commit b33397f

Browse files
committed
db: improve error checking in TestIngestError
Informs #4492
1 parent a514d0f commit b33397f

File tree

1 file changed

+122
-51
lines changed

1 file changed

+122
-51
lines changed

ingest_test.go

Lines changed: 122 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1804,66 +1804,137 @@ func TestIngest(t *testing.T) {
18041804
})
18051805
}
18061806

1807+
// linkAndRemovePredicate is like errorfs.InjectIndex, and injects only on the
1808+
// set of Link and Remove operations, or the complement of that set. Link and
1809+
// Remove operation errors are hidden by Ingest.
1810+
type linkAndRemovePredicate struct {
1811+
isComplement bool
1812+
*errorfs.InjectIndex
1813+
}
1814+
1815+
func (p linkAndRemovePredicate) Evaluate(op errorfs.Op) bool {
1816+
isLinkOrRemove := op.Kind == errorfs.OpLink || op.Kind == errorfs.OpRemove
1817+
if (isLinkOrRemove && p.isComplement) || (!isLinkOrRemove && !p.isComplement) {
1818+
return false
1819+
}
1820+
return p.InjectIndex.Evaluate(op)
1821+
}
1822+
1823+
var _ errorfs.Predicate = &linkAndRemovePredicate{}
1824+
18071825
func TestIngestError(t *testing.T) {
1808-
for i := int32(0); ; i++ {
1809-
mem := vfs.NewMem()
1826+
for _, linkAndRemove := range []bool{false, true} {
1827+
for i := int32(0); ; i++ {
1828+
mem := vfs.NewMem()
18101829

1811-
f0, err := mem.Create("ext0", vfs.WriteCategoryUnspecified)
1812-
require.NoError(t, err)
1813-
w := sstable.NewWriter(objstorageprovider.NewFileWritable(f0), sstable.WriterOptions{})
1814-
require.NoError(t, w.Set([]byte("d"), nil))
1815-
require.NoError(t, w.Close())
1816-
f1, err := mem.Create("ext1", vfs.WriteCategoryUnspecified)
1817-
require.NoError(t, err)
1818-
w = sstable.NewWriter(objstorageprovider.NewFileWritable(f1), sstable.WriterOptions{})
1819-
require.NoError(t, w.Set([]byte("d"), nil))
1820-
require.NoError(t, w.Close())
1830+
f0, err := mem.Create("ext0", vfs.WriteCategoryUnspecified)
1831+
require.NoError(t, err)
1832+
w := sstable.NewWriter(objstorageprovider.NewFileWritable(f0), sstable.WriterOptions{})
1833+
require.NoError(t, w.Set([]byte("d"), []byte("d1")))
1834+
require.NoError(t, w.Close())
1835+
f1, err := mem.Create("ext1", vfs.WriteCategoryUnspecified)
1836+
require.NoError(t, err)
1837+
w = sstable.NewWriter(objstorageprovider.NewFileWritable(f1), sstable.WriterOptions{})
1838+
require.NoError(t, w.Set([]byte("d"), []byte("d2")))
1839+
require.NoError(t, w.Close())
18211840

1822-
ii := errorfs.OnIndex(-1)
1823-
d, err := Open("", &Options{
1824-
FS: errorfs.Wrap(mem, errorfs.ErrInjected.If(ii)),
1825-
Logger: panicLogger{},
1826-
L0CompactionThreshold: 8,
1827-
})
1828-
require.NoError(t, err)
1829-
// Force the creation of an L0 sstable that overlaps with the tables
1830-
// we'll attempt to ingest. This ensures that we exercise filesystem
1831-
// codepaths when determining the ingest target level.
1832-
require.NoError(t, d.Set([]byte("a"), nil, nil))
1833-
require.NoError(t, d.Set([]byte("d"), nil, nil))
1834-
require.NoError(t, d.Flush())
1835-
1836-
t.Run(fmt.Sprintf("index-%d", i), func(t *testing.T) {
1837-
defer func() {
1838-
if r := recover(); r != nil {
1839-
if e, ok := r.(error); ok && errors.Is(e, errorfs.ErrInjected) {
1840-
return
1841+
ii := errorfs.OnIndex(-1)
1842+
d, err := Open("", &Options{
1843+
FS: errorfs.Wrap(
1844+
mem, errorfs.ErrInjected.If(linkAndRemovePredicate{
1845+
isComplement: !linkAndRemove, InjectIndex: ii})),
1846+
Logger: panicLogger{},
1847+
L0CompactionThreshold: 8,
1848+
})
1849+
require.NoError(t, err)
1850+
// Force the creation of an L0 sstable that overlaps with the tables
1851+
// we'll attempt to ingest. This ensures that we exercise filesystem
1852+
// codepaths when determining the ingest target level.
1853+
require.NoError(t, d.Set([]byte("a"), nil, nil))
1854+
require.NoError(t, d.Set([]byte("d"), []byte("d0"), nil))
1855+
require.NoError(t, d.Flush())
1856+
1857+
checkVal := func(t *testing.T, expected string) {
1858+
ii.Store(math.MaxInt32)
1859+
val, closer, err := d.Get([]byte("d"))
1860+
require.NoError(t, err)
1861+
defer closer.Close()
1862+
require.Equal(t, expected, string(val))
1863+
}
1864+
observedPanic := false
1865+
var errWasInjected bool
1866+
t.Run(fmt.Sprintf("link-and-remove=%t,index=%d", linkAndRemove, i), func(t *testing.T) {
1867+
defer func() {
1868+
if r := recover(); r != nil {
1869+
// Error injected on link or remove should be completely hidden,
1870+
// let alone cause a panic.
1871+
require.False(t, linkAndRemove)
1872+
observedPanic = true
1873+
errWasInjected = ii.Load() < 0
1874+
t.Logf("error with panic")
1875+
if e, ok := r.(error); ok && errors.Is(e, errorfs.ErrInjected) {
1876+
return
1877+
}
1878+
// d.opts.Logger.Fatalf won't propagate ErrInjected
1879+
// itself, but should contain the error message.
1880+
if strings.HasSuffix(fmt.Sprint(r), errorfs.ErrInjected.Error()) {
1881+
return
1882+
}
1883+
t.Fatal(r)
18411884
}
1842-
// d.opts.Logger.Fatalf won't propagate ErrInjected
1843-
// itself, but should contain the error message.
1844-
if strings.HasSuffix(fmt.Sprint(r), errorfs.ErrInjected.Error()) {
1845-
return
1885+
}()
1886+
1887+
ii.Store(i)
1888+
err1 := d.Ingest(context.Background(), []string{"ext0"})
1889+
err2 := d.Ingest(context.Background(), []string{"ext1"})
1890+
errWasInjected = ii.Load() < 0
1891+
// Check that the value of "d" is as expected, given the observed
1892+
// errors, if any. We only do this if the error did not cause a panic,
1893+
// since the DB should be usable.
1894+
expectedVal := "d2"
1895+
if err2 != nil {
1896+
if err1 != nil {
1897+
expectedVal = "d0"
1898+
} else {
1899+
expectedVal = "d1"
18461900
}
1847-
t.Fatal(r)
18481901
}
1849-
}()
18501902

1851-
ii.Store(i)
1852-
err1 := d.Ingest(context.Background(), []string{"ext0"})
1853-
err2 := d.Ingest(context.Background(), []string{"ext1"})
1854-
err := firstError(err1, err2)
1855-
if err != nil && !errors.Is(err, errorfs.ErrInjected) {
1856-
t.Fatal(err)
1857-
}
1858-
})
1903+
checkVal(t, expectedVal)
1904+
err := firstError(err1, err2)
1905+
if err != nil {
1906+
// Error injected on link or remove should be completely hidden.
1907+
require.False(t, linkAndRemove)
1908+
if !errors.Is(err, errorfs.ErrInjected) {
1909+
t.Fatal(err)
1910+
}
1911+
if !errWasInjected {
1912+
t.Fatalf("error observed but not injected")
1913+
}
1914+
t.Logf("error without panic")
1915+
// Exercise some more operations on the DB, which must succeed. NB: we
1916+
// have disabled error injection by calling checkVal above.
1917+
require.NoError(t, d.Set([]byte("d"), []byte("d3"), nil))
1918+
checkVal(t, "d3")
1919+
require.NoError(t, d.Flush())
1920+
} else {
1921+
if errWasInjected && !linkAndRemove {
1922+
t.Fatalf("error injected but not observed")
1923+
}
1924+
}
1925+
})
18591926

1860-
// d.Close may error if we failed to flush the manifest.
1861-
_ = d.Close()
1927+
// d.Close may error if we failed to flush the manifest and panicked.
1928+
ii.Store(math.MaxInt32)
1929+
err = d.Close()
1930+
if !observedPanic {
1931+
require.NoError(t, err)
1932+
}
18621933

1863-
// If the injector's index is non-negative, the i-th filesystem
1864-
// operation was never executed.
1865-
if ii.Load() >= 0 {
1866-
break
1934+
// If !errWasInjected, the i-th filesystem operation was never executed.
1935+
if !errWasInjected {
1936+
break
1937+
}
18671938
}
18681939
}
18691940
}

0 commit comments

Comments
 (0)