Skip to content

Instantly share code, notes, and snippets.

@thefringeninja
Created August 15, 2013 17:50
Show Gist options
  • Save thefringeninja/6242976 to your computer and use it in GitHub Desktop.
Save thefringeninja/6242976 to your computer and use it in GitHub Desktop.
Profiling RavenDB with Nancy
public class RavenProfilerModule : NancyModule
{
private readonly IEnumerable<IDocumentStore> documentStores;
public RavenProfilerModule(IEnumerable<IDocumentStore> documentStores)
: base("/_raven-profiler")
{
this.documentStores = documentStores;
Get["/"] = p =>
{
var sessions = this.Bind<RequestedSessions>();
var viewModel = GetProfilingInformation(sessions.SessionId);
return Negotiate.WithModel(viewModel);
};
}
private RavenProfiledSessionViewModel GetProfilingInformation(IEnumerable<Guid> sessionIds)
{
var profilingInformations = from documentStore in documentStores.OfType<DocumentStore>()
from sessionId in sessionIds
select documentStore.GetProfilingInformationFor(sessionId);
return new RavenProfiledSessionViewModel(profilingInformations);
}
}
public class RequestedSessions
{
public Guid[] SessionId { get; set; }
}
public static class RavenProfiling
{
private const string ContextualSessionStorageKey = "###RavenProfilerSessions###";
private static readonly
ConcurrentDictionary
<NancyContext, Tuple<Action<InMemoryDocumentSessionOperations>, Action<ProfilingInformation>>>
Operations =
new ConcurrentDictionary
<NancyContext, Tuple<Action<InMemoryDocumentSessionOperations>, Action<ProfilingInformation>>>();
public static void InitializeFor(IDocumentStore documentStore, IPipelines pipelines)
{
var store = documentStore as DocumentStore;
if (store == null)
throw new ArgumentException("Must be a Raven.Client.DocumentStore", "documentStore");
InitializeFor(store, pipelines);
}
private static void InitializeFor(DocumentStore documentStore, IPipelines pipelines)
{
documentStore.Conventions.DisableProfiling = false;
documentStore.InitializeProfiling();
pipelines.BeforeRequest.AddItemToStartOfPipeline(
context =>
{
if (documentStore.WasDisposed)
return null;
Action<InMemoryDocumentSessionOperations> onSessionCreated =
operation => SessionCreated(operation, context);
Action<ProfilingInformation> onInformationCreated =
information => InformationCreated(information, context);
if (false == Operations.TryAdd(context, Tuple.Create(onSessionCreated, onInformationCreated)))
return null;
documentStore.SessionCreatedInternal += onSessionCreated;
return null;
});
pipelines.AfterRequest.AddItemToEndOfPipeline(
context =>
{
if (documentStore.WasDisposed)
return;
//do something here to get the session ids into the rendered view.
// I use cassette so I will add an inline script with a hook.
//var bundesHelper = context.Items[BundlesHelper.BUNDLES_HELPER] as IBundlesHelper;
// if (bundesHelper != null)
// TrackSessionsInBundles(bundesHelper, context);
Tuple<Action<InMemoryDocumentSessionOperations>, Action<ProfilingInformation>> operation;
if (false == Operations.TryGetValue(context, out operation))
return;
documentStore.SessionCreatedInternal -= operation.Item1;
ProfilingInformation.OnContextCreated -= operation.Item2;
});
}
private static void InformationCreated(ProfilingInformation information, NancyContext context)
{
// add additional context
}
private static void SessionCreated(InMemoryDocumentSessionOperations operations, NancyContext context)
{
GetContextualSessionStorage(context).Add(operations.Id);
}
private static List<Guid> GetContextualSessionStorage(NancyContext context)
{
object trackedSessionsThing;
List<Guid> trackedSessions;
if (context.Items.TryGetValue(ContextualSessionStorageKey, out trackedSessionsThing)
&& (trackedSessions = trackedSessionsThing as List<Guid>) != null)
return trackedSessions;
trackedSessions = new List<Guid>();
context.Items[ContextualSessionStorageKey] = trackedSessions;
return trackedSessions;
}
/*
public static void TrackSessionsInBundles(IBundlesHelper bundles, NancyContext context)
{
const string script = @"
(function(document, window){{
var profiler = new window.RavenProfiler('{0}', document, window);
profiler.forSession({1});
}})(document, window);
";
var trackedSessions = GetContextualSessionStorage(context);
if (false == trackedSessions.Any())
return;
var sessionIdsInJson = "[" + String.Join(
",",
trackedSessions.Select(sessionId => "'" + sessionId + "'")) + "]";
bundles.AddInlineScript(
String.Format(script, context.ToFullPath("~/_raven-profiler"), sessionIdsInJson),
references: new[] {"~/scripts/features/raven-profiler"});
}*/
}
public class RavenRemoteCallViewModel
{
private readonly string method;
private readonly Uri url;
public RavenRemoteCallViewModel(RequestResultArgs model)
{
At = model.At;
Duration = model.DurationMilliseconds + " ms";
method = model.Method;
Status = model.HttpResult + " " + (((HttpStatusCode) model.HttpResult).ToString().Titleize());
url = new Uri("http://ravendb" + model.Url);
}
public string Status { get; private set; }
public string Request
{
get { return method + " " + Endpoint; }
}
public string Endpoint
{
get
{
// should get rid of /databases/YourDatabaseName
return "/" + String.Join("/", url.AbsolutePath.Split('/').Skip(3));
}
}
public IEnumerable<string> Query
{
get
{
return url.Query.Length < 1
? new string[0]
: url.Query.Substring(1).Split('&')
.Select(HttpUtility.UrlDecode)
.Select(HttpUtility.UrlDecode);
}
}
public DateTime At { get; private set; }
public string Duration { get; private set; }
}
public class RavenProfiledSessionViewModel : IEnumerable<RavenRemoteCallViewModel>
{
private readonly IEnumerable<RavenRemoteCallViewModel> items;
public RavenProfiledSessionViewModel(IEnumerable<ProfilingInformation> profilingInformations)
{
items = from profilingInformation in profilingInformations
where profilingInformation != null
from result in profilingInformation.Requests
select new RavenRemoteCallViewModel(result);
}
#region IEnumerable<RavenRemoteCallViewModel> Members
public IEnumerator<RavenRemoteCallViewModel> GetEnumerator()
{
return items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment