1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2009, 2013 Oracle and/or its affiliates. All rights reserved. 5 * 6 */ 7 using System; 8 using System.Collections; 9 using System.Collections.Generic; 10 using System.IO; 11 using System.Text; 12 using System.Threading; 13 using System.Xml; 14 using NUnit.Framework; 15 using BerkeleyDB; 16 17 namespace CsharpAPITest 18 { 19 [TestFixture] 20 public class TransactionTest : CSharpTestFixture 21 { 22 23 private DatabaseEnvironment deadLockEnv; 24 25 [TestFixtureSetUp] SetUpTestFixture()26 public void SetUpTestFixture() 27 { 28 testFixtureName = "TransactionTest"; 29 base.SetUpTestfixture(); 30 31 DatabaseEnvironment.Remove(testFixtureHome); 32 } 33 34 [Test, ExpectedException(typeof(ExpectedTestException))] TestAbort()35 public void TestAbort() 36 { 37 testName = "TestAbort"; 38 SetUpTest(true); 39 40 DatabaseEnvironment env; 41 Transaction txn; 42 BTreeDatabase db; 43 44 /* 45 * Open an environment and begin a transaction. Open 46 * a db and write a record the db within this transaction. 47 */ 48 PutRecordWithTxn(out env, testHome, testName, out txn); 49 50 // Abort the transaction. 51 txn.Abort(); 52 53 /* 54 * Undo all operations in the transaction so the 55 * database couldn't be reopened. 56 */ 57 try 58 { 59 OpenBtreeDBInEnv(testName + ".db", env, 60 out db, false, null); 61 } 62 catch (DatabaseException) 63 { 64 throw new ExpectedTestException(); 65 } 66 finally 67 { 68 env.Close(); 69 } 70 } 71 72 [Test] TestCommit()73 public void TestCommit() 74 { 75 testName = "TestCommit"; 76 SetUpTest(true); 77 78 DatabaseEnvironment env; 79 Transaction txn; 80 BTreeDatabase db; 81 82 /* 83 * Open an environment and begin a transaction. Open 84 * a db and write a record the db within this transaction. 85 */ 86 PutRecordWithTxn(out env, testHome, testName, out txn); 87 88 // Commit the transaction. 89 txn.Commit(); 90 91 // Reopen the database. 92 OpenBtreeDBInEnv(testName + ".db", env, 93 out db, false, null); 94 95 /* 96 * Confirm that the record("key", "data") exists in the 97 * database. 98 */ 99 try 100 { 101 db.GetBoth(new DatabaseEntry( 102 ASCIIEncoding.ASCII.GetBytes("key")), 103 new DatabaseEntry( 104 ASCIIEncoding.ASCII.GetBytes("data"))); 105 } 106 catch (DatabaseException) 107 { 108 throw new TestException(); 109 } 110 finally 111 { 112 db.Close(); 113 env.Close(); 114 } 115 } 116 117 [Test] TestDiscard()118 public void TestDiscard() 119 { 120 DatabaseEnvironment env; 121 byte[] gid; 122 123 testName = "TestDiscard"; 124 SetUpTest(true); 125 126 /* 127 * Open an environment and begin a transaction 128 * called "transaction". Within the transacion, open a 129 * database, write a record and close it. Then prepare 130 * the transaction and panic the environment. 131 */ 132 PanicPreparedTxn(testHome, testName, out env, out gid); 133 134 /* 135 * Recover the environment. Log and db files are not 136 * destoyed so run normal recovery. Recovery should 137 * use DB_CREATE and DB_INIT_TXN flags when 138 * opening the environment. 139 */ 140 DatabaseEnvironmentConfig envConfig 141 = new DatabaseEnvironmentConfig(); 142 envConfig.RunRecovery = true; 143 envConfig.Create = true; 144 envConfig.UseTxns = true; 145 envConfig.UseMPool = true; 146 env = DatabaseEnvironment.Open(testHome, envConfig); 147 148 PreparedTransaction[] preparedTxns 149 = new PreparedTransaction[10]; 150 preparedTxns = env.Recover(10, true); 151 152 Assert.AreEqual(gid, preparedTxns[0].GlobalID); 153 preparedTxns[0].Txn.Discard(); 154 try { 155 preparedTxns[0].Txn.Commit(); 156 throw new TestException(); 157 } catch (AccessViolationException) { 158 } finally { 159 env.Close(); 160 } 161 } 162 163 [Test] TestPrepare()164 public void TestPrepare() 165 { 166 testName = "TestPrepare"; 167 SetUpTest(true); 168 169 DatabaseEnvironment env; 170 byte[] gid; 171 172 /* 173 * Open an environment and begin a transaction 174 * called "transaction". Within the transacion, open a 175 * database, write a record and close it. Then prepare 176 * the transaction and panic the environment. 177 */ 178 PanicPreparedTxn(testHome, testName, out env, out gid); 179 180 /* 181 * Recover the environment. Log and db files are not 182 * destoyed so run normal recovery. Recovery should 183 * use DB_CREATE and DB_INIT_TXN flags when 184 * opening the environment. 185 */ 186 DatabaseEnvironmentConfig envConfig 187 = new DatabaseEnvironmentConfig(); 188 envConfig.RunRecovery = true; 189 envConfig.Create = true; 190 envConfig.UseTxns = true; 191 envConfig.UseMPool = true; 192 env = DatabaseEnvironment.Open(testHome, envConfig); 193 194 // Reopen the database. 195 BTreeDatabase db; 196 OpenBtreeDBInEnv(testName + ".db", env, out db, 197 false, null); 198 199 /* 200 * Confirm that record("key", "data") exists in the 201 * database. 202 */ 203 DatabaseEntry key, data; 204 key = new DatabaseEntry( 205 ASCIIEncoding.ASCII.GetBytes("key")); 206 data = new DatabaseEntry( 207 ASCIIEncoding.ASCII.GetBytes("data")); 208 try 209 { 210 db.GetBoth(key, data); 211 } 212 catch (DatabaseException) 213 { 214 throw new TestException(); 215 } 216 finally 217 { 218 db.Close(); 219 env.Close(); 220 } 221 222 } 223 PanicPreparedTxn(string home, string dbName, out DatabaseEnvironment env, out byte[] globalID)224 public void PanicPreparedTxn(string home, string dbName, 225 out DatabaseEnvironment env, out byte[] globalID) 226 { 227 Transaction txn; 228 229 // Put record into database within transaction. 230 PutRecordWithTxn(out env, home, dbName, out txn); 231 232 /* 233 * Generate global ID for the transaction. Copy 234 * transaction ID to the first 4 tyes in global ID. 235 */ 236 globalID = new byte[Transaction.GlobalIdLength]; 237 byte[] txnID = new byte[4]; 238 txnID = BitConverter.GetBytes(txn.Id); 239 for (int i = 0; i < txnID.Length; i++) 240 globalID[i] = txnID[i]; 241 242 // Prepare the transaction. 243 txn.Prepare(globalID); 244 245 // Panic the environment. 246 env.Panic(); 247 248 } 249 250 [Test] TestTxnName()251 public void TestTxnName() 252 { 253 DatabaseEnvironment env; 254 Transaction txn; 255 256 testName = "TestTxnName"; 257 SetUpTest(true); 258 259 SetUpTransactionalEnv(testHome, out env); 260 txn = env.BeginTransaction(); 261 txn.Name = testName; 262 Assert.AreEqual(testName, txn.Name); 263 txn.Commit(); 264 env.Close(); 265 } 266 267 [Test] TestTxnPriority()268 public void TestTxnPriority() { 269 DatabaseEnvironment env; 270 Transaction txn; 271 272 testName = "TestTxnPriority"; 273 SetUpTest(true); 274 275 SetUpTransactionalEnv(testHome, out env); 276 txn = env.BeginTransaction(); 277 txn.Priority = 555; 278 Assert.AreEqual(555, txn.Priority); 279 txn.Commit(); 280 env.Close(); 281 } 282 283 [Test] TestSetLockTimeout()284 public void TestSetLockTimeout() 285 { 286 testName = "TestSetLockTimeout"; 287 SetUpTest(true); 288 289 // Set lock timeout. 290 TestTimeOut(true); 291 292 } 293 294 [Test] TestSetTxnTimeout()295 public void TestSetTxnTimeout() 296 { 297 testName = "TestSetTxnTimeout"; 298 SetUpTest(true); 299 300 // Set transaction time out. 301 TestTimeOut(false); 302 303 } 304 305 /* 306 * ifSetLock is used to indicate which timeout function 307 * is used, SetLockTimeout or SetTxnTimeout. 308 */ TestTimeOut(bool ifSetLock)309 public void TestTimeOut(bool ifSetLock) 310 { 311 // Open environment and begin transaction. 312 Transaction txn; 313 deadLockEnv = null; 314 SetUpEnvWithTxnAndLocking(testHome, 315 out deadLockEnv, out txn, 0, 0, 0, 0); 316 317 // Define deadlock detection and resolve policy. 318 deadLockEnv.DeadlockResolution = 319 DeadlockPolicy.YOUNGEST; 320 if (ifSetLock == true) 321 txn.SetLockTimeout(10); 322 else 323 txn.SetTxnTimeout(10); 324 325 txn.Commit(); 326 deadLockEnv.Close(); 327 } 328 SetUpEnvWithTxnAndLocking(string envHome, out DatabaseEnvironment env, out Transaction txn, uint maxLock, uint maxLocker, uint maxObject, uint partition)329 public static void SetUpEnvWithTxnAndLocking(string envHome, 330 out DatabaseEnvironment env, out Transaction txn, 331 uint maxLock, uint maxLocker, uint maxObject, uint partition) 332 { 333 // Configure env and locking subsystem. 334 LockingConfig lkConfig = new LockingConfig(); 335 336 /* 337 * If the maximum number of locks/lockers/objects 338 * is given, then the LockingConfig is set. Unless, 339 * it is not set to any value. 340 */ 341 if (maxLock != 0) 342 lkConfig.MaxLocks = maxLock; 343 if (maxLocker != 0) 344 lkConfig.MaxLockers = maxLocker; 345 if (maxObject != 0) 346 lkConfig.MaxObjects = maxObject; 347 if (partition != 0) 348 lkConfig.Partitions = partition; 349 350 DatabaseEnvironmentConfig envConfig = 351 new DatabaseEnvironmentConfig(); 352 envConfig.Create = true; 353 envConfig.UseTxns = true; 354 envConfig.UseMPool = true; 355 envConfig.LockSystemCfg = lkConfig; 356 envConfig.UseLocking = true; 357 envConfig.NoLocking = false; 358 env = DatabaseEnvironment.Open(envHome, envConfig); 359 txn = env.BeginTransaction(); 360 } 361 PutRecordWithTxn(out DatabaseEnvironment env, string home, string dbName, out Transaction txn)362 public void PutRecordWithTxn(out DatabaseEnvironment env, 363 string home, string dbName, out Transaction txn) 364 { 365 BTreeDatabase db; 366 367 // Open a new environment and begin a transaction. 368 SetUpTransactionalEnv(home, out env); 369 TransactionConfig txnConfig = new TransactionConfig(); 370 txnConfig.Name = "Transaction"; 371 txn = env.BeginTransaction(txnConfig); 372 Assert.AreEqual("Transaction", txn.Name); 373 374 // Open a new database within the transaction. 375 OpenBtreeDBInEnv(dbName + ".db", env, out db, true, txn); 376 377 // Write to the database within the transaction. 378 WriteOneIntoBtreeDBWithTxn(db, txn); 379 380 // Close the database. 381 db.Close(); 382 } 383 SetUpTransactionalEnv(string home, out DatabaseEnvironment env)384 public void SetUpTransactionalEnv(string home, 385 out DatabaseEnvironment env) 386 { 387 DatabaseEnvironmentConfig envConfig = 388 new DatabaseEnvironmentConfig(); 389 envConfig.Create = true; 390 envConfig.UseLogging = true; 391 envConfig.UseLocking = true; 392 envConfig.UseMPool = true; 393 envConfig.UseTxns = true; 394 env = DatabaseEnvironment.Open( 395 home, envConfig); 396 } 397 OpenBtreeDBInEnv(string dbName, DatabaseEnvironment env, out BTreeDatabase db, bool create, Transaction txn)398 public void OpenBtreeDBInEnv(string dbName, 399 DatabaseEnvironment env, out BTreeDatabase db, 400 bool create, Transaction txn) 401 { 402 BTreeDatabaseConfig btreeDBConfig = 403 new BTreeDatabaseConfig(); 404 btreeDBConfig.Env = env; 405 if (create == true) 406 btreeDBConfig.Creation = CreatePolicy.IF_NEEDED; 407 else 408 btreeDBConfig.Creation = CreatePolicy.NEVER; 409 if (txn == null) 410 db = BTreeDatabase.Open(dbName, 411 btreeDBConfig); 412 else 413 db = BTreeDatabase.Open(dbName, 414 btreeDBConfig, txn); 415 } 416 WriteOneIntoBtreeDBWithTxn(BTreeDatabase db, Transaction txn)417 public void WriteOneIntoBtreeDBWithTxn(BTreeDatabase db, 418 Transaction txn) 419 { 420 DatabaseEntry key, data; 421 422 key = new DatabaseEntry( 423 ASCIIEncoding.ASCII.GetBytes("key")); 424 data = new DatabaseEntry( 425 ASCIIEncoding.ASCII.GetBytes("data")); 426 db.Put(key, data, txn); 427 } 428 } 429 } 430