Permalink
Browse files

Moving the way we are handling storage of the recovery information

Instead of storing and recovering on the server, we store / recover locally
  • Loading branch information...
1 parent 02e68bd commit f6a8d4cc91ec9852797eb2bfb4634eeb82adee0c @ayende ayende committed Oct 8, 2012
@@ -83,13 +83,6 @@ public void CanUseDtc_Promoted()
}
}
- [Fact]
- public void CanStoreRecoveryInfo()
- {
- store.DatabaseCommands.StoreRecoveryInformation(Guid.NewGuid(), Guid.NewGuid(),Guid.NewGuid().ToByteArray());
- }
-
-
public class Item
{
public string Name { get; set; }
@@ -7,9 +7,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using Raven.Bundles.Tests.Expiration;
-using Raven.Bundles.Tests.Replication;
-using Raven.Bundles.Tests.Replication.Bugs;
namespace Raven.Bundles.Tryouts
{
@@ -18,10 +15,7 @@ class Program
static void Main(string[] args)
{
Console.WriteLine("starting...");
- using (var x = new David())
- {
- x.Can_replicate_between_two_instances_create_delete_create_quickly();
- }
+
}
}
}
@@ -461,23 +461,6 @@ public byte[] PromoteTransaction(Guid fromTxId)
}
/// <summary>
- /// Stores the recovery information.
- /// </summary>
- /// <param name="resourceManagerId">The resource manager Id for this transaction</param>
- /// <param name="txId">The tx id.</param>
- /// <param name="recoveryInformation">The recovery information.</param>
- public void StoreRecoveryInformation(Guid resourceManagerId,Guid txId, byte[] recoveryInformation)
- {
- CurrentOperationContext.Headers.Value = OperationsHeaders;
- var jObject = new RavenJObject
- {
- {"Resource-Manager-Id", resourceManagerId.ToString()},
- {Constants.NotForReplication, true}
- };
- database.PutStatic("transactions/recoveryInformation/" + txId, null, new MemoryStream(recoveryInformation), jObject);
- }
-
- /// <summary>
/// Returns a new <see cref="IDatabaseCommands"/> using the specified credentials
/// </summary>
/// <param name="credentialsForSession">The credentials for session.</param>
@@ -201,14 +201,6 @@ public interface IDatabaseCommands : IHoldProfilingInformation
byte[] PromoteTransaction(Guid fromTxId);
/// <summary>
- /// Stores the recovery information
- /// </summary>
- /// <param name="resourceManagerId">The resource manager Id for this transaction</param>
- /// <param name="txId">The tx id.</param>
- /// <param name="recoveryInformation">The recovery information.</param>
- void StoreRecoveryInformation(Guid resourceManagerId, Guid txId, byte[] recoveryInformation);
-
- /// <summary>
/// Returns a new <see cref="IDatabaseCommands"/> using the specified credentials
/// </summary>
/// <param name="credentialsForSession">The credentials for session.</param>
@@ -972,32 +972,6 @@ public byte[] PromoteTransaction(Guid fromTxId)
return ExecuteWithReplication("PUT", u => DirectPromoteTransaction(fromTxId, u));
}
- /// <summary>
- /// Stores the recovery information.
- /// </summary>
- /// <param name="resourceManagerId">The resource manager Id for this transaction</param>
- /// <param name="txId">The tx id.</param>
- /// <param name="recoveryInformation">The recovery information.</param>
- public void StoreRecoveryInformation(Guid resourceManagerId, Guid txId, byte[] recoveryInformation)
- {
- ExecuteWithReplication<object>("PUT", u =>
- {
- var metadata = new RavenJObject
- {
- {"Resource-Manager-Id", resourceManagerId.ToString()},
- {Constants.NotForReplication, true}
- };
- var webRequest = jsonRequestFactory.CreateHttpJsonRequest(
- new CreateHttpJsonRequestParams(this, u + "/static/transactions/recoveryInformation/" + txId, "PUT", metadata, credentials, convention)
- .AddOperationHeaders(OperationsHeaders));
-
- webRequest.Write(new MemoryStream(recoveryInformation));
-
- webRequest.ExecuteRequest();
- return null;
- });
- }
-
private byte[] DirectPromoteTransaction(Guid fromTxId, string operationUrl)
{
var webRequest = jsonRequestFactory.CreateHttpJsonRequest(
@@ -0,0 +1,109 @@
+//-----------------------------------------------------------------------
+// <copyright file="PendingTransactionRecovery.cs" company="Hibernating Rhinos LTD">
+// Copyright (c) Hibernating Rhinos LTD. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.IO.IsolatedStorage;
+using System.Transactions;
+using NLog;
+using Raven.Client.Connection;
+using Raven.Abstractions.Extensions;
+
+namespace Raven.Client.Document.DTC
+{
+ public class PendingTransactionRecovery
+ {
+ private static readonly Logger logger = LogManager.GetCurrentClassLogger();
+
+ public void Execute(IDatabaseCommands commands)
+ {
+ var resourceManagersRequiringRecovery = new HashSet<Guid>();
+ using (var store = IsolatedStorageFile.GetMachineStoreForDomain())
+ {
+ foreach (var file in store.GetFileNames("*.recovery-information"))
+ {
+ var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
+ Debug.Assert(fileNameWithoutExtension != null);
+ var parts = fileNameWithoutExtension.Split(new[] { "-$$-" }, StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length != 2)
+ continue;
+
+ Guid resourceManagerId, txId;
+
+ if (Guid.TryParse(parts[0], out resourceManagerId) == false)
+ continue;
+ if (Guid.TryParse(parts[1], out txId) == false)
+ continue;
+
+ try
+ {
+ using (var fileStream = store.OpenFile(file, FileMode.Open, FileAccess.Read))
+ {
+ TransactionManager.Reenlist(resourceManagerId, fileStream.ReadData(), new InternalEnlistment(commands, txId));
+ resourceManagersRequiringRecovery.Add(resourceManagerId);
+ logger.Info("Recovered transaction {0}", txId);
+ }
+ }
+ catch (Exception e)
+ {
+ logger.WarnException("Could not re-enlist in DTC transaction for tx: " + txId, e);
+ }
+
+ foreach (var rm in resourceManagersRequiringRecovery)
+ {
+ try
+ {
+ TransactionManager.RecoveryComplete(rm);
+ }
+ catch (Exception e)
+ {
+ logger.WarnException("Could not properly complete recovery of resource manager: " + rm, e);
+ }
+ }
+
+ }
+ }
+ }
+
+ public class InternalEnlistment : IEnlistmentNotification
+ {
+ private readonly IDatabaseCommands database;
+ private readonly Guid txId;
+
+ public InternalEnlistment(IDatabaseCommands database, Guid txId)
+ {
+ this.database = database;
+ this.txId = txId;
+ }
+
+ public void Prepare(PreparingEnlistment preparingEnlistment)
+ {
+ // shouldn't be called, already
+ // prepared, otherwise we won't have this issue
+ preparingEnlistment.Prepared();
+ }
+
+ public void Commit(Enlistment enlistment)
+ {
+ database.Commit(txId);
+ enlistment.Done();
+ }
+
+ public void Rollback(Enlistment enlistment)
+ {
+ database.Rollback(txId);
+ enlistment.Done();
+ }
+
+ public void InDoubt(Enlistment enlistment)
+ {
+ database.Rollback(txId);
+ enlistment.Done();
+ }
+ }
+ }
+}
@@ -511,18 +511,6 @@ public override byte[] PromoteTransaction(Guid fromTxId)
}
/// <summary>
- /// Stores the recovery information for the specified transaction
- /// </summary>
- /// <param name="resourceManagerId">The resource manager Id for this transaction</param>
- /// <param name="txId">The tx id.</param>
- /// <param name="recoveryInformation">The recovery information.</param>
- public void StoreRecoveryInformation(Guid resourceManagerId, Guid txId, byte[] recoveryInformation)
- {
- IncrementRequestCount();
- DatabaseCommands.StoreRecoveryInformation(resourceManagerId, txId, recoveryInformation);
- }
-
- /// <summary>
/// Query RavenDB dynamically using LINQ
/// </summary>
/// <typeparam name="T">The result of the query</typeparam>
@@ -26,6 +26,7 @@
using Raven.Client.Silverlight.Connection;
using Raven.Client.Silverlight.Connection.Async;
#else
+using Raven.Client.Document.DTC;
using Raven.Client.Listeners;
#endif
@@ -376,25 +377,35 @@ public override IDocumentStore Initialize()
};
#endif
}
+
+ initialized = true;
+
+#if !SILVERLIGHT
+ RecoverPendingTransactions();
+
+ if (string.IsNullOrEmpty(DefaultDatabase) == false)
+ {
+ DatabaseCommands.ForDefaultDatabase().EnsureDatabaseExists(DefaultDatabase, ignoreFailures: true);
+ }
+#endif
+
}
catch (Exception)
{
Dispose();
throw;
}
- initialized = true;
-
-#if !SILVERLIGHT
- if (string.IsNullOrEmpty(DefaultDatabase) == false)
- {
- DatabaseCommands.ForDefaultDatabase().EnsureDatabaseExists(DefaultDatabase, ignoreFailures: true);
- }
-#endif
-
return this;
}
+#if !SILVERLIGHT
+ private void RecoverPendingTransactions()
+ {
+ var pendingTransactionRecovery = new PendingTransactionRecovery();
+ pendingTransactionRecovery.Execute(DatabaseCommands);
+ }
+#endif
private void InitializeSecurity()
{
if (Conventions.HandleUnauthorizedResponse != null)
@@ -5,6 +5,8 @@
// </copyright>
//-----------------------------------------------------------------------
using System;
+using System.IO;
+using System.IO.IsolatedStorage;
using System.Transactions;
using NLog;
@@ -41,8 +43,17 @@ public void Prepare(PreparingEnlistment preparingEnlistment)
onTxComplete();
try
{
- session.StoreRecoveryInformation(session.ResourceManagerId, PromotableRavenClientEnlistment.GetLocalOrDistributedTransactionId(transaction),
- preparingEnlistment.RecoveryInformation());
+ using (var machineStoreForApplication = IsolatedStorageFile.GetMachineStoreForDomain())
+ {
+ var name = GetTransactionRecoveryInformationFileName();
+ using (var file = machineStoreForApplication.CreateFile(name + ".temp"))
+ {
+ var recoveryInformation = preparingEnlistment.RecoveryInformation();
+ file.Write(recoveryInformation, 0, recoveryInformation.Length);
+ file.Flush(true);
+ }
+ machineStoreForApplication.MoveFile(name + ".temp", name);
+ }
}
catch (Exception e)
{
@@ -53,6 +64,11 @@ public void Prepare(PreparingEnlistment preparingEnlistment)
preparingEnlistment.Prepared();
}
+ private string GetTransactionRecoveryInformationFileName()
+ {
+ return session.ResourceManagerId.ToString() + "-$$-" + PromotableRavenClientEnlistment.GetLocalOrDistributedTransactionId(transaction) + ".recovery-information";
+ }
+
/// <summary>
/// Notifies an enlisted object that a transaction is being committed.
/// </summary>
@@ -63,13 +79,19 @@ public void Commit(Enlistment enlistment)
try
{
session.Commit(PromotableRavenClientEnlistment.GetLocalOrDistributedTransactionId(transaction));
+
+ using (var machineStoreForApplication = IsolatedStorageFile.GetMachineStoreForDomain())
+ {
+ machineStoreForApplication.DeleteFile(GetTransactionRecoveryInformationFileName());
+ }
}
catch (Exception e)
{
logger.ErrorException("Could not commit distributed transaction", e);
return; // nothing to do, DTC will mark tx as hang
}
enlistment.Done();
+
}
/// <summary>
@@ -82,6 +104,11 @@ public void Rollback(Enlistment enlistment)
try
{
session.Rollback(PromotableRavenClientEnlistment.GetLocalOrDistributedTransactionId(transaction));
+
+ using (var machineStoreForApplication = IsolatedStorageFile.GetMachineStoreForDomain())
+ {
+ machineStoreForApplication.DeleteFile(GetTransactionRecoveryInformationFileName());
+ }
}
catch (Exception e)
{
@@ -36,13 +36,5 @@ public interface ITransactionalDocumentSession
/// <param name="fromTxId">From tx id.</param>
/// <returns>The token representing the distributed transaction</returns>
byte[] PromoteTransaction(Guid fromTxId);
-
- /// <summary>
- /// Stores the recovery information for the specified transaction
- /// </summary>
- /// <param name="resourceManagerId">The resource manager Id for this transaction</param>
- /// <param name="txId">The tx id.</param>
- /// <param name="recoveryInformation">The recovery information.</param>
- void StoreRecoveryInformation(Guid resourceManagerId, Guid txId, byte[] recoveryInformation);
}
}
@@ -108,6 +108,7 @@
<Compile Include="Document\DocumentQuery.cs" />
<Compile Include="Document\DocumentSession.cs" />
<Compile Include="Document\DocumentStore.cs" />
+ <Compile Include="Document\DTC\PendingTransactionRecovery.cs" />
<Compile Include="Document\FailoverBehavior.cs" />
<Compile Include="Document\HiLoKeyGenerator.cs" />
<Compile Include="Document\HiLoKeyGeneratorBase.cs" />
Oops, something went wrong.

0 comments on commit f6a8d4c

Please sign in to comment.