func TestPurgeOldLeases(t *testing.T) { defer leaktest.AfterTest(t)() // We're going to block gossip so it doesn't come randomly and clear up the // leases we're artificially setting up. gossipSem := make(chan struct{}, 1) serverParams := testingshim.TestServerParams{ Knobs: base.TestingKnobs{ SQLLeaseManager: &LeaseManagerTestingKnobs{ GossipUpdateEvent: func(cfg config.SystemConfig) { gossipSem <- struct{}{} <-gossipSem }, }, }, } s, db, kvDB, cleanup := sqlutils.SetupServerWithParams(t, serverParams) defer cleanup() leaseManager := s.LeaseManager().(*LeaseManager) // Block gossip. gossipSem <- struct{}{} defer func() { // Unblock gossip. <-gossipSem }() if _, err := db.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k CHAR PRIMARY KEY, v CHAR); `); err != nil { t.Fatal(err) } tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "test") var leases []*LeaseState err := kvDB.Txn(func(txn *client.Txn) error { for i := 0; i < 3; i++ { lease, err := leaseManager.acquireFreshestFromStore(txn, tableDesc.ID) if err != nil { t.Fatal(err) } leases = append(leases, lease) if err := leaseManager.Release(lease); err != nil { t.Fatal(err) } } return nil }) if err != nil { t.Fatal(err) } ts := leaseManager.findTableState(tableDesc.ID, false, nil) if numLeases := getNumLeases(ts); numLeases != 3 { t.Fatalf("found %d leases instead of 3", numLeases) } if err := ts.purgeOldLeases( kvDB, false, 1 /* minVersion */, leaseManager.LeaseStore); err != nil { t.Fatal(err) } if numLeases := getNumLeases(ts); numLeases != 1 { t.Fatalf("found %d leases instead of 1", numLeases) } ts.mu.Lock() correctLease := ts.active.data[0] == leases[2] ts.mu.Unlock() if !correctLease { t.Fatalf("wrong lease survived purge") } }
// Test that a SQL txn that resolved a name can keep resolving that name during // its lifetime even after the table has been renamed. // Also tests that the name of a renamed table cannot be reused until everybody // has stopped using it. Otherwise, we'd have different transactions in the // systems using a single name for different tables. // Also tests that the old name cannot be used by node that doesn't have a lease // on the old version even while the name mapping still exists. func TestTxnCanStillResolveOldName(t *testing.T) { defer leaktest.AfterTest(t)() var lmKnobs LeaseManagerTestingKnobs // renameUnblocked is used to block the rename schema change until the test // doesn't need the old name->id mapping to exist anymore. renameUnblocked := make(chan interface{}) serverParams := testingshim.TestServerParams{ Knobs: base.TestingKnobs{ SQLExecutor: &ExecutorTestingKnobs{ SyncSchemaChangersRenameOldNameNotInUseNotification: func() { <-renameUnblocked }, }, SQLLeaseManager: &lmKnobs, }} var mu sync.Mutex var waitTableID sqlbase.ID // renamed is used to block until the node cannot get leases with the original // table name. It will be signaled once the table has been renamed and the update // about the new name has been processed. Moreover, not only does an update to // the name needs to have been received, but the version of the descriptor needs to // have also been incremented in order to guarantee that the node cannot get // leases using the old name (an update with the new name but the original // version is ignored by the leasing refresh mechanism). renamed := make(chan interface{}) lmKnobs.TestingLeasesRefreshedEvent = func(cfg config.SystemConfig) { mu.Lock() defer mu.Unlock() if waitTableID != 0 { if isRenamed(waitTableID, "t2", 2, cfg) { close(renamed) waitTableID = 0 } } } s, db, kvClient, cleanup := sqlutils.SetupServerWithParams(t, serverParams) defer cleanup() sql := ` CREATE DATABASE test; CREATE TABLE test.t (a INT PRIMARY KEY); ` _, err := db.Exec(sql) if err != nil { t.Fatal(err) } tableDesc := sqlbase.GetTableDescriptor(kvClient, "test", "t") mu.Lock() waitTableID = tableDesc.ID mu.Unlock() txn, err := db.Begin() if err != nil { t.Fatal(err) } // Run a command to make the transaction resolves the table name. if _, err := txn.Exec("SELECT * FROM test.t"); err != nil { t.Fatal(err) } // Concurrently, rename the table. threadDone := make(chan interface{}) go func() { // The ALTER will commit and signal the main thread through `renamed`, but // the schema changer will remain blocked by the lease on the "t" version // held by the txn started above. if _, err := db.Exec("ALTER TABLE test.t RENAME TO test.t2"); err != nil { panic(err) } close(threadDone) }() // Block until the LeaseManager has processed the gossip update. <-renamed // Run another command in the transaction and make sure that we can still // resolve the table name. if _, err := txn.Exec("SELECT * FROM test.t"); err != nil { t.Fatal(err) } // Check that the name cannot be reused while somebody still has a lease on // the old one (the mechanism for ensuring this is that the entry for the old // name is not deleted from the database until the async schema changer checks // that there's no more leases on the old version). if _, err := db.Exec("CREATE TABLE test.t (a INT PRIMARY KEY)"); !testutils.IsError( err, `table "t" already exists`) { t.Fatal(err) } if err := txn.Commit(); err != nil { t.Fatal(err) } // Check that the old name is not usable outside of the transaction now // that the the node doesn't have a lease on it anymore (committing the txn // should have released the lease on the version of the descriptor with the // old name), even thoudh the name mapping still exists. lease := s.LeaseManager().(*LeaseManager).tableNames.get(tableDesc.ID, "t", s.Clock()) if lease != nil { t.Fatalf(`still have lease on "t"`) } if _, err := db.Exec("SELECT * FROM test.t"); !testutils.IsError( err, `table "test.t" does not exist`) { t.Fatal(err) } close(renameUnblocked) // Block until the thread doing the rename has finished, so the test can clean // up. It needed to wait for the transaction to release its lease. <-threadDone }