public void PendingTransactionTest() { if (!EnvironmentSupportsTransactions) { Assert.Inconclusive("Cannot test in this environment"); return; } Assume.That(AmbientTransaction == null, "Leftovers are not expected here"); // thus not allowing to use ambient storage transaction Repository.Settings.StorageTransactionSettings = StorageTransactionSettings.DisallowJoiningAmbientManaged | StorageTransactionSettings.AlwaysStartNew; using (var standaloneTransaction = GetStandaloneTransaction()) { Assume.That(standaloneTransaction != null && standaloneTransaction.IsActive); using (var scope = StorageTransactionScope.Create(Repository, standaloneTransaction)) { Assert.IsFalse(scope.IsNullScope); Assert.IsFalse(scope.IsTransactionOwner); Assert.IsFalse(scope.NoTransaction); Assert.AreSame(standaloneTransaction, scope.UnderlyingTransaction); } Assert.IsTrue(standaloneTransaction.IsActive); Assert.IsNull(AmbientTransaction); } }
public void DetachTransactionTest() { if (!EnvironmentSupportsTransactions) { Assert.Inconclusive("Cannot test transactions in this environment"); return; } Repository.Settings.StorageTransactionSettings = StorageTransactionSettings.RequireTransactions | StorageTransactionSettings.DisallowJoiningAmbientManaged; Assume.That(AmbientTransaction == null); IFileSystemTransaction transaction; using (var scope = StorageTransactionScope.Create(Repository)) { var underlyingTRansaction = scope.UnderlyingTransaction; Assert.IsNotNull(underlyingTRansaction); Assert.AreSame(AmbientTransaction, underlyingTRansaction, "Underlying transaction must be installed into the context"); transaction = scope.DetachTransaction(); Assert.AreSame(AmbientTransaction, transaction, "Detaching transaction must not change context"); } Assert.IsNull(AmbientTransaction, "Detached scope must still restore context when disposed"); Assert.IsTrue(transaction.IsActive, "Detached context must not end transaction when disposed"); transaction.Dispose(); }
/// <summary> /// Get transaction scope reusing pending transaction if already pending. /// </summary> /// <param name="lazy"> /// Whether the scope needs to be lazy or eager. /// </param> /// <returns> /// New scope. /// </returns> /// <remarks> /// Eager scope is for complete units of work such as <see cref="IRepositoryWriter.Flush()"/>. /// Lazy scope is for the methods which need to take part in external [long] transactions but do not represent units of /// work themselves, such as <see cref="IRepositoryWriter.Write(IDataItem)"/> /// Pending transaction is registered and subscribed to only first time when returned scope is not owning the transaction /// </remarks> private StorageTransactionScope GetTransactionScope(bool lazy) { CheckNotDisposed(); StorageTransactionScope scope; if (lazy) { scope = StorageTransactionScope.CreateLazy(Repository, PendingTransaction); } else { scope = StorageTransactionScope.Create(Repository, PendingTransaction); } if (!scope.IsNullScope && !scope.IsTransactionOwner && PendingTransaction == null) { PendingTransaction = scope.UnderlyingTransaction; if (_subscriber != null) { PendingTransaction.Subscribe(_subscriber); } PendingTransaction.Subscribe((ITransactionNotification)this); Check.Ensure(!scope.ToBeDisposed, "Pending transaction must not be disposed - we need to keep the single instance of slave pending transaction"); } return(scope); }
public void TestAmbientKtmTransaction() { if (!bfs.Repository.Util.SystemInfo.IsAnyWindows || !EnvironmentSupportsTransactions) { Assert.Inconclusive("Cannot test in this environment"); return; } using (var scope = CreateStandaloneKtmTransactionScope(true)) { // thus allowing to use ambient storage transaction Repository.Settings.StorageTransactionSettings = StorageTransactionSettings.DisallowJoiningAmbientManaged; KtmTransaction ambientTransaction = (KtmTransaction)AmbientTransaction; // eager scope using (StorageTransactionScope target = StorageTransactionScope.Create(Repository)) { Assert.IsFalse(target.IsNullScope, "Eager scope must not produce null scope"); Assert.IsFalse(target.IsTransactionOwner, "There's external transaction to be used"); Assert.IsFalse(target.HasChangedContext); Assert.IsFalse(target.NoTransaction); Assert.IsFalse(target.ToBeDisposed); Assert.AreSame(ambientTransaction, AmbientTransaction); Assert.AreSame(ambientTransaction, target.UnderlyingTransaction); Assert.AreSame(ambientTransaction, target.PreviousTransaction); } Assert.IsNotNull(AmbientTransaction); Assert.AreSame(ambientTransaction, AmbientTransaction); // lazy scope using (StorageTransactionScope target = StorageTransactionScope.CreateLazy(Repository, null)) { Assert.IsFalse(target.IsNullScope, "Because the \"always start new\" option is OFF"); Assert.IsFalse(target.IsTransactionOwner, "Lazy scope must never be transaction owner"); Assert.IsFalse(target.HasChangedContext, "Lazy scope can only change context to NULL and only when ambient transaction is present but its usage prohibited"); Assert.IsFalse(target.NoTransaction, "Lazy scope can result in no ambient transaction when ambient transaction is present but its usage prohibited or there's " + "no ambient transaction at all"); Assert.IsFalse(target.ToBeDisposed, "Lazy scope must not own or dispose underlying transaction"); Assert.AreSame(ambientTransaction, AmbientTransaction); Assert.AreSame(ambientTransaction, target.UnderlyingTransaction); Assert.AreSame(ambientTransaction, target.PreviousTransaction); } Assert.IsNotNull(AmbientTransaction); Assert.AreSame(ambientTransaction, AmbientTransaction); } Assert.IsNull(AmbientTransaction); }
public void TestLoopingAndNesting() { if (!EnvironmentSupportsTransactions) { Assert.Inconclusive("Cannot test in this environment"); return; } Assume.That(AmbientTransaction == null, "Leftovers are not expected here"); // thus allowing to use ambient storage transaction Repository.Settings.StorageTransactionSettings = StorageTransactionSettings.DisallowJoiningAmbientManaged; using (var standaloneTransaction = GetStandaloneTransaction()) { for (int n = 0; n < 100; ++n) { using (var scope = StorageTransactionScope.CreateLazy(Repository, standaloneTransaction)) { Assert.AreSame(standaloneTransaction, scope.UnderlyingTransaction); Assert.IsFalse(scope.ToBeDisposed); Assert.AreSame(standaloneTransaction, AmbientTransaction); using (var nestedScope = StorageTransactionScope.Create(Repository, standaloneTransaction)) { Assert.IsFalse(nestedScope.HasChangedContext); Assert.AreSame(standaloneTransaction, scope.UnderlyingTransaction); Assert.IsFalse(scope.ToBeDisposed); Assert.AreSame(standaloneTransaction, AmbientTransaction); using (var anotherTransaction = GetStandaloneTransaction()) { Assert.AreSame(standaloneTransaction, AmbientTransaction); Assert.Throws( Is.InstanceOf <InvalidOperationException>() , () => StorageTransactionScope.Create(Repository, anotherTransaction)); } nestedScope.Complete(); } } Assert.IsNull(AmbientTransaction); Assert.IsTrue(standaloneTransaction.IsActive); } } }
public void TestEagerWithAmbientKtmTransactionProhibited() { if (!bfs.Repository.Util.SystemInfo.IsAnyWindows || !EnvironmentSupportsTransactions) { Assert.Inconclusive("Cannot test in this environment"); return; } Assume.That(AmbientTransaction == null, "Leftovers are not expected here"); // thus not allowing to use ambient storage transaction Repository.Settings.StorageTransactionSettings = StorageTransactionSettings.DisallowJoiningAmbientManaged | StorageTransactionSettings.AlwaysStartNew; using (var scope_ = CreateStandaloneKtmTransactionScope(true)) { KtmTransaction ambientTransaction = (KtmTransaction)AmbientTransaction; // eager scope using (StorageTransactionScope target = StorageTransactionScope.Create(Repository)) { Assert.IsFalse(target.IsNullScope, "Eager scope must not produce null scope"); Assert.IsTrue(target.IsTransactionOwner, "The use of external transaction is prohibited by Repository.Settings.StorageTransactionSettings"); Assert.IsTrue(target.HasChangedContext); Assert.IsFalse(target.NoTransaction); Assert.IsTrue(target.ToBeDisposed); Assert.AreNotSame(ambientTransaction, AmbientTransaction); Assert.AreNotSame(ambientTransaction, target.UnderlyingTransaction); Assert.AreSame(ambientTransaction, target.PreviousTransaction); } Assert.IsNotNull(AmbientTransaction); Assert.AreSame(ambientTransaction, AmbientTransaction); } Assert.IsNull(AmbientTransaction); }
public void TestAmbientManagedFirstEntry() { if (!EnvironmentSupportsTransactions) { Assert.Inconclusive("Cannot test transactions in this environment"); return; } Repository.Settings.StorageTransactionSettings = StorageTransactionSettings.RequireTransactions; TransactionSubscriber subscriber = new TransactionSubscriber(); IFileSystemTransaction transaction; using (TransactionScope masterScope = new TransactionScope()) { using (var scope = StorageTransactionScope.Create(Repository)) { Assert.AreEqual(1, StorageTransactionScope.ScopeNestLevel); Assert.IsTrue(scope.HasChangedContext, "Scope must have changed context"); Assert.IsFalse(scope.IsTransactionOwner, "Transaction must be slave under managed"); Assert.IsFalse(scope.ToBeDisposed, "Scope having created slave transaction must not be disposing"); Assert.IsNotNull(AmbientTransaction, "Context not set"); Assert.AreSame(AmbientTransaction, scope.UnderlyingTransaction, "Underlying transaction is not in sync with context"); Assert.IsTrue(scope.UnderlyingTransaction.IsActive, "Transaction must be active in this scope"); Assert.IsFalse(scope.UnderlyingTransaction.Unsubscribe(subscriber), "Unsubscribing not yet subscribed returned true"); scope.UnderlyingTransaction.Subscribe(subscriber); transaction = scope.UnderlyingTransaction; Assert.Throws( Is.InstanceOf <InvalidOperationException>() , () => transaction.Commit(), "Committing slave transaction must throw InvalidOperationException"); Assert.IsFalse(scope.ToBeDisposed, "Scope turned disposing after transaction detached"); Assert.IsFalse(scope.IsTransactionOwner); } Assert.AreEqual(0, StorageTransactionScope.ScopeNestLevel); Assert.IsTrue(transaction.IsActive, "Detached transaction must remain active after disposing scope"); Assert.AreEqual(0, subscriber.TimesNotified, "Disposing detached scope must not result in notification"); Assert.IsNull(KtmTransaction.Current, "The scope was disposed and must have restored context"); using (var scope = StorageTransactionScope.Create(Repository, transaction)) { Assert.AreEqual(1, StorageTransactionScope.ScopeNestLevel); Assert.AreSame(transaction, scope.UnderlyingTransaction, "Underlying transaction is not what was explicitly assigned"); Assert.IsTrue(scope.HasChangedContext, "Scope must have changed context"); Assert.IsFalse(scope.IsTransactionOwner, "Transaction must be slave under managed"); Assert.IsFalse(scope.ToBeDisposed, "Scope must not be disposing as explicitly specified when creating"); Assert.IsNotNull(AmbientTransaction, "Context not set"); Assert.AreSame(AmbientTransaction, scope.UnderlyingTransaction, "Underlying transaction is not in sync with context"); Assert.IsTrue(scope.UnderlyingTransaction.IsActive, "Transaction must be active in this scope"); scope.UnderlyingTransaction.Subscribe(subscriber); } int level = StorageTransactionScope.ScopeNestLevel; Assert.AreEqual(0, StorageTransactionScope.ScopeNestLevel); Assert.IsTrue(transaction.IsActive, "Detached transaction must remain active after disposing scope"); Assert.AreEqual(0, subscriber.TimesNotified, "Scope was not to dispose or commit transaction"); Assert.IsNull(AmbientTransaction, "The scope was disposed and must have restored context"); masterScope.Complete(); Assume.That(transaction.IsActive && subscriber.TimesNotified == 0); } System.Threading.Thread.Sleep(50); Assert.IsFalse(transaction.IsActive, "Master transaction was committed, KTM must be committed too"); Assert.AreEqual(1, subscriber.TimesNotified, "Master transaction was committed, must have recieved 1 notification"); Assert.IsTrue(subscriber.Committed, "Transaction must have beed reported committed."); Assert.Throws( Is.InstanceOf <InvalidOperationException>() , () => transaction.Subscribe(subscriber), "Subscribing to inactive transaction must throw exception"); }