func makePostgres(t testing.TB) (graph.QuadStore, graph.Options, func()) { var conf dock.Config conf.Image = "postgres:9.5" conf.OpenStdin = true conf.Tty = true conf.Env = []string{`POSTGRES_PASSWORD=postgres`} addr, closer := dock.RunAndWait(t, conf, func(addr string) bool { conn, err := pq.Open(`postgres://postgres:postgres@` + addr + `/postgres?sslmode=disable`) if err != nil { return false } conn.Close() return true }) addr = `postgres://postgres:postgres@` + addr + `/postgres?sslmode=disable` if err := createSQLTables(addr, nil); err != nil { closer() t.Fatal(err) } qs, err := newQuadStore(addr, nil) if err != nil { closer() t.Fatal(err) } return qs, nil, func() { qs.Close() closer() } }
// Test that abruptly closing a pgwire connection releases all leases held by // that session. func TestPGWireConnectionCloseReleasesLeases(t *testing.T) { defer leaktest.AfterTest(t)() s, _, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) defer s.Stopper().Stop() url, cleanupConn := sqlutils.PGUrl(t, s.ServingAddr(), "SetupServer", url.User(security.RootUser)) defer cleanupConn() conn, err := pq.Open(url.String()) if err != nil { t.Fatal(err) } ex := conn.(driver.Execer) if _, err := ex.Exec("CREATE DATABASE test", nil); err != nil { t.Fatal(err) } if _, err := ex.Exec("CREATE TABLE test.t (i INT PRIMARY KEY)", nil); err != nil { t.Fatal(err) } // Start a txn so leases are accumulated by queries. if _, err := ex.Exec("BEGIN", nil); err != nil { t.Fatal(err) } // Get a table lease. if _, err := ex.Exec("SELECT * FROM test.t", nil); err != nil { t.Fatal(err) } // Abruptly close the connection. if err := conn.Close(); err != nil { t.Fatal(err) } // Verify that there are no leases held. tableDesc := sqlbase.GetTableDescriptor(kvDB, "test", "t") lm := s.LeaseManager().(*LeaseManager) // Looking for a table state validates that there used to be a lease on the // table. ts := lm.findTableState(tableDesc.ID, false /* create */) if ts == nil { t.Fatal("table state not found") } ts.mu.Lock() leases := ts.active.data ts.mu.Unlock() if len(leases) != 1 { t.Fatalf("expected one lease, found: %d", len(leases)) } // Wait for the lease to be released. util.SucceedsSoon(t, func() error { ts.mu.Lock() refcount := ts.active.data[0].refcount ts.mu.Unlock() if refcount != 0 { return errors.Errorf( "expected lease to be unused, found refcount: %d", refcount) } return nil }) }
// Test that a connection closed abruptly while a SQL txn is in progress results // in that txn being rolled back. func TestSessionFinishRollsBackTxn(t *testing.T) { defer leaktest.AfterTest(t)() aborter := NewTxnAborter() defer aborter.Close(t) params, _ := createTestServerParams() params.Knobs.SQLExecutor = aborter.executorKnobs() s, mainDB, _ := serverutils.StartServer(t, params) defer s.Stopper().Stop() { pgURL, cleanup := sqlutils.PGUrl( t, s.ServingAddr(), "TestSessionFinishRollsBackTxn", url.User(security.RootUser)) defer cleanup() if err := aborter.Init(pgURL); err != nil { t.Fatal(err) } } if _, err := mainDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k INT PRIMARY KEY, v TEXT); `); err != nil { t.Fatal(err) } // We're going to test the rollback of transactions left in various states // when the connection closes abruptly. // For the state CommitWait, there's no actual rollback we can test for (since // the kv-level transaction has already been committed). But we still // exercise this state to check that the server doesn't crash (which used to // happen - #9879). tests := []sql.TxnStateEnum{sql.Open, sql.RestartWait, sql.CommitWait} for _, state := range tests { t.Run(state.String(), func(t *testing.T) { // Create a low-level lib/pq connection so we can close it at will. pgURL, cleanupDB := sqlutils.PGUrl( t, s.ServingAddr(), state.String(), url.User(security.RootUser)) defer cleanupDB() conn, err := pq.Open(pgURL.String()) if err != nil { t.Fatal(err) } connClosed := false defer func() { if connClosed { return } if err := conn.Close(); err != nil { t.Fatal(err) } }() txn, err := conn.Begin() if err != nil { t.Fatal(err) } tx := txn.(driver.Execer) if _, err := tx.Exec("SET TRANSACTION PRIORITY NORMAL", nil); err != nil { t.Fatal(err) } if state == sql.RestartWait || state == sql.CommitWait { if _, err := tx.Exec("SAVEPOINT cockroach_restart", nil); err != nil { t.Fatal(err) } } insertStmt := "INSERT INTO t.test(k, v) VALUES (1, 'a')" if state == sql.RestartWait { // To get a txn in RestartWait, we'll use an aborter. if err := aborter.QueueStmtForAbortion( insertStmt, 1 /* restartCount */, false /* willBeRetriedIbid */); err != nil { t.Fatal(err) } } if _, err := tx.Exec(insertStmt, nil); err != nil { t.Fatal(err) } if err := aborter.VerifyAndClear(); err != nil { t.Fatal(err) } if state == sql.RestartWait || state == sql.CommitWait { _, err := tx.Exec("RELEASE SAVEPOINT cockroach_restart", nil) if state == sql.CommitWait { if err != nil { t.Fatal(err) } } else if !testutils.IsError(err, "pq: restart transaction:.*") { t.Fatal(err) } } // Abruptly close the connection. connClosed = true if err := conn.Close(); err != nil { t.Fatal(err) } // Check that the txn we had above was rolled back. We do this by reading // after the preceding txn and checking that we don't get an error and // that we haven't been blocked by intents (we can't exactly test that we // haven't been blocked but we assert that the query didn't take too // long). // We do the read in an explicit txn so that automatic retries don't hide // any errors. // TODO(andrei): Figure out a better way to test for non-blocking. // Use a trace when the client-side tracing story gets good enough. txCheck, err := mainDB.Begin() if err != nil { t.Fatal(err) } // Run check at low priority so we don't push the previous transaction and // fool ourselves into thinking it had been rolled back. if _, err := txCheck.Exec("SET TRANSACTION PRIORITY LOW"); err != nil { t.Fatal(err) } ts := timeutil.Now() var count int if err := txCheck.QueryRow("SELECT COUNT(1) FROM t.test").Scan(&count); err != nil { t.Fatal(err) } // CommitWait actually committed, so we'll need to clean up. if state != sql.CommitWait { if count != 0 { t.Fatalf("expected no rows, got: %d", count) } } else { if _, err := txCheck.Exec("DELETE FROM t.test"); err != nil { t.Fatal(err) } } if err := txCheck.Commit(); err != nil { t.Fatal(err) } if d := timeutil.Since(ts); d > time.Second { t.Fatalf("Looks like the checking tx was unexpectedly blocked. "+ "It took %s to commit.", d) } }) } }