Created
March 14, 2013 13:27
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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