Skip to content

Instantly share code, notes, and snippets.

@benerdin
Created March 14, 2013 13:27
Show Gist options
  • Select an option

  • Save benerdin/5161284 to your computer and use it in GitHub Desktop.

Select an option

Save benerdin/5161284 to your computer and use it in GitHub Desktop.
This class attempts to instantiate a RavenDB DocumentStore when the "DocumentStore" getter is called. The DocumentStore is a thread-safe singleton that will only block other threads for a maximum of 30 seconds when the store is unavailable.
using NLog;
using Raven.Client;
using Raven.Client.Document;
using Raven.Client.Exceptions;
using System;
using System.Threading.Tasks;
namespace Beyond.Services
{
/// <summary>
/// Provides the RavenDB DocumentStore as needed.
/// </summary>
public class RavenDocumentStoreProvider : IRavenDocumentStoreProvider
{
/// <summary>
/// Gets the RavenDB document store.
/// </summary>
public IDocumentStore DocumentStore
{
get
{
EnsureRavenConfiguration();
return _documentStore;
}
// For unit testing only.
internal set
{
_documentStore = value;
}
}
/// <summary>
/// Loads a document safely handling conflicts.
/// </summary>
/// <typeparam name="TDocument">Type of document.</typeparam>
/// <param name="id">ID to load.</param>
/// <param name="session">Optional session to use.</param>
/// <returns>A TDocument.</returns>
public TDocument SafeLoad<TDocument>(string id, IDocumentSession session)
where TDocument : class
{
try
{
if (session == null)
{
using (var newSession = DocumentStore.OpenSession())
{
return newSession.Load<TDocument>(id);
}
}
else
{
return session.Load<TDocument>(id);
}
}
catch (ConflictException ex)
{
_logger.ErrorException(ex.ToString(), ex);
return null;
}
}
/// <summary>
/// Makes sure that we have a valid raven doc store, or if we're in the middle
/// of setup, they will get an empty doc store.
/// </summary>
private static void EnsureRavenConfiguration()
{
// If we don't yet have a doc store, then we need to get it,
// but only enter this code if there is no task that is setting
// up Raven. Only 1 request is allow through at a time. We have not
// yet successfully set up Raven, we will fail fast if Raven is
// being requested by others.
if (_documentStore == null && !_documentStoreIsBeingConfigured)
{
// We get the global lock.
lock (syncLock)
{
// If the doc store is STILL not set, and our task
if (_documentStore == null && !_documentStoreIsBeingConfigured)
{
try
{
// Tell other requesters that we have it; they should
// keep going (with a null doc store)
_documentStoreIsBeingConfigured = true;
// Keep trying to create the store if it fails
_task = Task.Run(
_logger.TryOrLog(() =>
{
try
{
_documentStore = GetDocumentStore();
}
finally
{
_documentStoreIsBeingConfigured = false;
}
})
);
// don't let it go for more than 30 seconds.
_task.Wait(30000);
}
catch
{
// we're done, so the document is now either valid or
// we'll let the next requester try again.
_documentStoreIsBeingConfigured = false;
throw;
}
}
}
if (_documentStore == null)
throw new ApplicationException("RavenDB Document Store could not be initialized.");
}
}
/// <summary>
/// Gets the document store.
/// </summary>
/// <returns>The document store.</returns>
/// <remarks>
/// Do this only once per AppDomain load. It's very expensive.
/// </remarks>
private static IDocumentStore GetDocumentStore()
{
// Create the DocumentStore (expensive operation).
IDocumentStore documentStore = new DocumentStore
{
ConnectionStringName = "RavenDB",
Credentials = System.Net.CredentialCache.DefaultNetworkCredentials // For "trusted connections": see comments at http://ravendb.net/docs/client-api/connecting-to-a-ravendb-datastore
};
// No replicating at this time: read & write to primary server only.
documentStore.Conventions.FailoverBehavior = FailoverBehavior.FailImmediately;
// Initialize the store (must be done before creating indexes)
documentStore = documentStore.Initialize();
// Return document store
return documentStore;
}
private static IDocumentStore _documentStore;
private static bool _documentStoreIsBeingConfigured = false;
private static object syncLock = new object();
private static Task _task;
/// <summary>
/// Logger.
/// </summary>
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment