MCPcopy
hub / github.com/benbjohnson/litestream / TestDB_releaseReadLock_DoubleRollback

Function TestDB_releaseReadLock_DoubleRollback

db_internal_test.go:866–923  ·  view source on GitHub ↗

TestDB_releaseReadLock_DoubleRollback verifies that calling releaseReadLock() after the read transaction has already been rolled back does not return an error. This can happen during shutdown when concurrent checkpoint and close operations both attempt to release the read lock. Regression test for i

(t *testing.T)

Source from the content-addressed store, hash-verified

864// both attempt to release the read lock.
865// Regression test for issue #934.
866func TestDB_releaseReadLock_DoubleRollback(t *testing.T) {
867 dir := t.TempDir()
868 dbPath := filepath.Join(dir, "db")
869
870 db := NewDB(dbPath)
871 db.MonitorInterval = 0
872 db.Replica = NewReplica(db)
873 db.Replica.Client = &testReplicaClient{dir: t.TempDir()}
874 db.Replica.MonitorEnabled = false
875 if err := db.Open(); err != nil {
876 t.Fatal(err)
877 }
878
879 // Open SQL connection to create a WAL database
880 sqldb, err := sql.Open("sqlite", dbPath)
881 if err != nil {
882 t.Fatal(err)
883 }
884 defer sqldb.Close()
885
886 if _, err := sqldb.Exec(`PRAGMA journal_mode = wal;`); err != nil {
887 t.Fatal(err)
888 }
889
890 if _, err := sqldb.Exec(`CREATE TABLE t (id INT)`); err != nil {
891 t.Fatal(err)
892 }
893
894 // Sync to initialize the database and acquire read lock
895 if err := db.Sync(context.Background()); err != nil {
896 t.Fatal(err)
897 }
898
899 // Verify read transaction exists
900 if db.rtx == nil {
901 t.Fatal("expected read transaction to exist after Sync")
902 }
903
904 // First rollback - simulates what happens in execCheckpoint()
905 if err := db.rtx.Rollback(); err != nil {
906 t.Fatalf("first rollback failed: %v", err)
907 }
908
909 // Second call to releaseReadLock() - simulates what happens in Close()
910 // This should NOT return an error even though the transaction is already rolled back.
911 // Before the fix, this would return "sql: transaction has already been committed or rolled back"
912 if err := db.releaseReadLock(); err != nil {
913 t.Fatalf("releaseReadLock() returned error after double rollback: %v", err)
914 }
915
916 // Clean up - set rtx to nil since we manually rolled it back
917 db.rtx = nil
918
919 // Close should work without error
920 if err := db.Close(context.Background()); err != nil {
921 t.Fatalf("Close() failed: %v", err)
922 }
923}

Callers

nothing calls this directly

Calls 6

OpenMethod · 0.95
CloseMethod · 0.95
SyncMethod · 0.95
releaseReadLockMethod · 0.95
NewReplicaFunction · 0.85
NewDBFunction · 0.70

Tested by

no test coverage detected