From 5e7108d1f51ba8e9f5e9149d7076a32aec4f362a Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Mon, 27 Apr 2026 18:45:08 -0500 Subject: [PATCH] Pick up JasperFx.Events 1.30.0 + pre-warm EventGraph._nameToType JasperFx.Events 1.30.0 (JasperFx/jasperfx#193) makes ProjectionGraph.DiscoverGeneratedEvolvers cheaper at cold start by filtering framework / library assemblies out before the GetCustomAttributes scan and by caching results across IDocumentStore instances in the same process. Bumps the pin so we benefit immediately. Companion Marten-side optimization in the same PR: pre-populate EventGraph._nameToType from registered event types during Initialize. TypeForDotNetName otherwise falls through Type.GetType(assemblyQualifiedName) on first read of each event type from the database, and that fallback is itself O(loaded-assemblies). Pre-warming both the AssemblyQualifiedName and FullName entries in a single ImHashMap.Swap means the first read of every known event type lands directly in the cache. New types (those discovered lazily via _events.OnMissing) still fall through the existing path. Continues the 8.x cold-start trim begun in #4295. See umbrella issue #4294. Co-Authored-By: Claude Opus 4.7 (1M context) --- Directory.Packages.props | 2 +- src/Marten/Events/EventGraph.cs | 27 ++++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index c3fc950111..3af4eabf2a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -14,7 +14,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Marten/Events/EventGraph.cs b/src/Marten/Events/EventGraph.cs index 42a08ab47a..a1e2c28573 100644 --- a/src/Marten/Events/EventGraph.cs +++ b/src/Marten/Events/EventGraph.cs @@ -606,10 +606,31 @@ internal void Initialize(DocumentStore store) _tombstones = new RetryBlock(executeTombstoneBlock, logger, _cancellation.Token); - foreach (var mapping in _events) + + // Pre-warm name->type so the first read of each event type from the database + // doesn't fall through Type.GetType(assemblyQualifiedName) in TypeForDotNetName, + // which is itself O(loaded-assemblies). Populate both AssemblyQualifiedName and + // FullName since both shapes appear in event metadata over the lifetime of a + // store. Done as a single Swap so we don't churn ImHashMap. + _nameToType.Swap(map => { - mapping.JsonTransformation(null); - } + foreach (var mapping in _events) + { + mapping.JsonTransformation(null); + + var docType = mapping.DocumentType; + if (docType.AssemblyQualifiedName is { } aqn) + { + map = map.AddOrUpdate(aqn, docType); + } + if (docType.FullName is { } fullName) + { + map = map.AddOrUpdate(fullName, docType); + } + } + + return map; + }); autoDiscoverTagTypesFromProjections(); }