/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2009, 2015 Oracle and/or its affiliates. All rights reserved. * */ using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading; using System.Xml; using NUnit.Framework; using BerkeleyDB; namespace CsharpAPITest { [TestFixture] public class TransactionTest : CSharpTestFixture { private DatabaseEnvironment deadLockEnv; [TestFixtureSetUp] public void SetUpTestFixture() { testFixtureName = "TransactionTest"; base.SetUpTestfixture(); DatabaseEnvironment.Remove(testFixtureHome); } [Test, ExpectedException(typeof(ExpectedTestException))] public void TestAbort() { testName = "TestAbort"; SetUpTest(true); DatabaseEnvironment env; Transaction txn; BTreeDatabase db; /* * Open an environment and begin a transaction. Open * a db and write a record the db within this transaction. */ PutRecordWithTxn(out env, testHome, testName, out txn); // Abort the transaction. txn.Abort(); /* * Undo all operations in the transaction so the * database couldn't be reopened. */ try { OpenBtreeDBInEnv(testName + ".db", env, out db, false, null); } catch (DatabaseException) { throw new ExpectedTestException(); } finally { env.Close(); } } [Test] public void TestCommit() { testName = "TestCommit"; SetUpTest(true); DatabaseEnvironment env; Transaction txn; BTreeDatabase db; /* * Open an environment and begin a transaction. Open * a db and write a record the db within this transaction. */ PutRecordWithTxn(out env, testHome, testName, out txn); // Commit the transaction. txn.Commit(); // Reopen the database. OpenBtreeDBInEnv(testName + ".db", env, out db, false, null); /* * Confirm that the record("key", "data") exists in the * database. */ try { db.GetBoth(new DatabaseEntry( ASCIIEncoding.ASCII.GetBytes("key")), new DatabaseEntry( ASCIIEncoding.ASCII.GetBytes("data"))); } catch (DatabaseException) { throw new TestException(); } finally { db.Close(); env.Close(); } } [Test] public void TestDiscard() { DatabaseEnvironment env; byte[] gid; testName = "TestDiscard"; SetUpTest(true); /* * Open an environment and begin a transaction * called "transaction". Within the transacion, open a * database, write a record and close it. Then prepare * the transaction and panic the environment. */ PanicPreparedTxn(testHome, testName, out env, out gid); /* * Recover the environment. Log and db files are not * destoyed so run normal recovery. Recovery should * use DB_CREATE and DB_INIT_TXN flags when * opening the environment. */ DatabaseEnvironmentConfig envConfig = new DatabaseEnvironmentConfig(); envConfig.RunRecovery = true; envConfig.Create = true; envConfig.UseTxns = true; envConfig.UseMPool = true; env = DatabaseEnvironment.Open(testHome, envConfig); PreparedTransaction[] preparedTxns = new PreparedTransaction[10]; preparedTxns = env.Recover(10, true); Assert.AreEqual(gid, preparedTxns[0].GlobalID); preparedTxns[0].Txn.Discard(); try { preparedTxns[0].Txn.Commit(); throw new TestException(); } catch (AccessViolationException) { } finally { env.Close(); } } [Test] public void TestPrepare() { testName = "TestPrepare"; SetUpTest(true); DatabaseEnvironment env; byte[] gid; /* * Open an environment and begin a transaction * called "transaction". Within the transacion, open a * database, write a record and close it. Then prepare * the transaction and panic the environment. */ PanicPreparedTxn(testHome, testName, out env, out gid); /* * Recover the environment. Log and db files are not * destoyed so run normal recovery. Recovery should * use DB_CREATE and DB_INIT_TXN flags when * opening the environment. */ DatabaseEnvironmentConfig envConfig = new DatabaseEnvironmentConfig(); envConfig.RunRecovery = true; envConfig.Create = true; envConfig.UseTxns = true; envConfig.UseMPool = true; env = DatabaseEnvironment.Open(testHome, envConfig); // Reopen the database. BTreeDatabase db; OpenBtreeDBInEnv(testName + ".db", env, out db, false, null); /* * Confirm that record("key", "data") exists in the * database. */ DatabaseEntry key, data; key = new DatabaseEntry( ASCIIEncoding.ASCII.GetBytes("key")); data = new DatabaseEntry( ASCIIEncoding.ASCII.GetBytes("data")); try { db.GetBoth(key, data); } catch (DatabaseException) { throw new TestException(); } finally { db.Close(); env.Close(); } } public void PanicPreparedTxn(string home, string dbName, out DatabaseEnvironment env, out byte[] globalID) { Transaction txn; // Put record into database within transaction. PutRecordWithTxn(out env, home, dbName, out txn); /* * Generate global ID for the transaction. Copy * transaction ID to the first 4 tyes in global ID. */ globalID = new byte[Transaction.GlobalIdLength]; byte[] txnID = new byte[4]; txnID = BitConverter.GetBytes(txn.Id); for (int i = 0; i < txnID.Length; i++) globalID[i] = txnID[i]; // Prepare the transaction. txn.Prepare(globalID); // Panic the environment. env.Panic(); } [Test] public void TestTxnName() { DatabaseEnvironment env; Transaction txn; testName = "TestTxnName"; SetUpTest(true); SetUpTransactionalEnv(testHome, out env); txn = env.BeginTransaction(); txn.Name = testName; Assert.AreEqual(testName, txn.Name); txn.Commit(); env.Close(); } [Test] public void TestTxnPriority() { DatabaseEnvironment env; Transaction txn; testName = "TestTxnPriority"; SetUpTest(true); SetUpTransactionalEnv(testHome, out env); txn = env.BeginTransaction(); txn.Priority = 555; Assert.AreEqual(555, txn.Priority); txn.Commit(); env.Close(); } [Test] public void TestSetLockTimeout() { testName = "TestSetLockTimeout"; SetUpTest(true); // Set lock timeout. TestTimeOut(true); } [Test] public void TestSetTxnTimeout() { testName = "TestSetTxnTimeout"; SetUpTest(true); // Set transaction time out. TestTimeOut(false); } /* * ifSetLock is used to indicate which timeout function * is used, SetLockTimeout or SetTxnTimeout. */ public void TestTimeOut(bool ifSetLock) { // Open environment and begin transaction. Transaction txn; deadLockEnv = null; SetUpEnvWithTxnAndLocking(testHome, out deadLockEnv, out txn, 0, 0, 0, 0); // Define deadlock detection and resolve policy. deadLockEnv.DeadlockResolution = DeadlockPolicy.YOUNGEST; if (ifSetLock == true) txn.SetLockTimeout(10); else txn.SetTxnTimeout(10); txn.Commit(); deadLockEnv.Close(); } public static void SetUpEnvWithTxnAndLocking(string envHome, out DatabaseEnvironment env, out Transaction txn, uint maxLock, uint maxLocker, uint maxObject, uint partition) { // Configure env and locking subsystem. LockingConfig lkConfig = new LockingConfig(); /* * If the maximum number of locks/lockers/objects * is given, then the LockingConfig is set. Unless, * it is not set to any value. */ if (maxLock != 0) lkConfig.MaxLocks = maxLock; if (maxLocker != 0) lkConfig.MaxLockers = maxLocker; if (maxObject != 0) lkConfig.MaxObjects = maxObject; if (partition != 0) lkConfig.Partitions = partition; DatabaseEnvironmentConfig envConfig = new DatabaseEnvironmentConfig(); envConfig.Create = true; envConfig.UseTxns = true; envConfig.UseMPool = true; envConfig.LockSystemCfg = lkConfig; envConfig.UseLocking = true; envConfig.NoLocking = false; env = DatabaseEnvironment.Open(envHome, envConfig); txn = env.BeginTransaction(); } public void PutRecordWithTxn(out DatabaseEnvironment env, string home, string dbName, out Transaction txn) { BTreeDatabase db; // Open a new environment and begin a transaction. SetUpTransactionalEnv(home, out env); TransactionConfig txnConfig = new TransactionConfig(); txnConfig.Name = "Transaction"; txn = env.BeginTransaction(txnConfig); Assert.AreEqual("Transaction", txn.Name); // Open a new database within the transaction. OpenBtreeDBInEnv(dbName + ".db", env, out db, true, txn); // Write to the database within the transaction. WriteOneIntoBtreeDBWithTxn(db, txn); // Close the database. db.Close(); } public void SetUpTransactionalEnv(string home, out DatabaseEnvironment env) { DatabaseEnvironmentConfig envConfig = new DatabaseEnvironmentConfig(); envConfig.Create = true; envConfig.UseLogging = true; envConfig.UseLocking = true; envConfig.UseMPool = true; envConfig.UseTxns = true; env = DatabaseEnvironment.Open( home, envConfig); } public void OpenBtreeDBInEnv(string dbName, DatabaseEnvironment env, out BTreeDatabase db, bool create, Transaction txn) { BTreeDatabaseConfig btreeDBConfig = new BTreeDatabaseConfig(); btreeDBConfig.Env = env; if (create == true) btreeDBConfig.Creation = CreatePolicy.IF_NEEDED; else btreeDBConfig.Creation = CreatePolicy.NEVER; if (txn == null) db = BTreeDatabase.Open(dbName, btreeDBConfig); else db = BTreeDatabase.Open(dbName, btreeDBConfig, txn); } public void WriteOneIntoBtreeDBWithTxn(BTreeDatabase db, Transaction txn) { DatabaseEntry key, data; key = new DatabaseEntry( ASCIIEncoding.ASCII.GetBytes("key")); data = new DatabaseEntry( ASCIIEncoding.ASCII.GetBytes("data")); db.Put(key, data, txn); } } }