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