|
|
@@ -9,6 +9,8 @@ package db
|
|
|
import (
|
|
|
"bytes"
|
|
|
"context"
|
|
|
+ "fmt"
|
|
|
+ "os"
|
|
|
"testing"
|
|
|
|
|
|
"github.com/syncthing/syncthing/lib/db/backend"
|
|
|
@@ -795,6 +797,52 @@ func TestUpdateTo14(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+func TestFlushRecursion(t *testing.T) {
|
|
|
+ // Verify that a commit hook can write to the transaction without
|
|
|
+ // causing another flush and thus recursion.
|
|
|
+
|
|
|
+ // Badger doesn't work like this.
|
|
|
+ if os.Getenv("USE_BADGER") != "" {
|
|
|
+ t.Skip("Not supported on Badger")
|
|
|
+ }
|
|
|
+
|
|
|
+ db := NewLowlevel(backend.OpenMemory())
|
|
|
+ defer db.Close()
|
|
|
+
|
|
|
+ // A commit hook that writes a small piece of data to the transaction.
|
|
|
+ hookFired := 0
|
|
|
+ hook := func(tx backend.WriteTransaction) error {
|
|
|
+ err := tx.Put([]byte(fmt.Sprintf("hook-key-%d", hookFired)), []byte(fmt.Sprintf("hook-value-%d", hookFired)))
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+ hookFired++
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // A transaction.
|
|
|
+ tx, err := db.NewWriteTransaction(hook)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+ defer tx.Release()
|
|
|
+
|
|
|
+ // Write stuff until the transaction flushes, thus firing the hook.
|
|
|
+ i := 0
|
|
|
+ for hookFired == 0 {
|
|
|
+ err := tx.Put([]byte(fmt.Sprintf("key-%d", i)), []byte(fmt.Sprintf("value-%d", i)))
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal(err)
|
|
|
+ }
|
|
|
+ i++
|
|
|
+ }
|
|
|
+
|
|
|
+ // The hook should have fired precisely once.
|
|
|
+ if hookFired != 1 {
|
|
|
+ t.Error("expect one hook fire, not", hookFired)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func numBlockLists(db *Lowlevel) (int, error) {
|
|
|
it, err := db.Backend.NewPrefixIterator([]byte{KeyTypeBlockList})
|
|
|
if err != nil {
|