diff --git a/src/core/Akka.Streams/ActorMaterializer.cs b/src/core/Akka.Streams/ActorMaterializer.cs index b71e1f05938..e4212faa6e8 100644 --- a/src/core/Akka.Streams/ActorMaterializer.cs +++ b/src/core/Akka.Streams/ActorMaterializer.cs @@ -6,7 +6,9 @@ //----------------------------------------------------------------------- using System; +using System.Collections.Concurrent; using System.Runtime.Serialization; +using System.Threading.Tasks; using Akka.Actor; using Akka.Configuration; using Akka.Dispatch; @@ -43,6 +45,18 @@ public static Config DefaultConfig() #region static + /// + /// Injecting the top-level Materializer HOCON configuration over and over again is expensive, so we want to avoid + /// doing it each time a materializer is instantiated. This flag will be set to true once the configuration has been + /// injected the first time. + /// + private static readonly ConcurrentDictionary InjectedConfig = new(); + + /// + /// Cache the default materializer settings so we don't constantly parse them + /// + private static readonly ConcurrentDictionary DefaultSettings = new(); + /// /// /// Creates a ActorMaterializer which will execute every step of a transformation @@ -75,9 +89,28 @@ public static ActorMaterializer Create(IActorRefFactory context, ActorMaterializ var haveShutDown = new AtomicBoolean(); var system = ActorSystemOf(context); - system.Settings.InjectTopLevelFallback(DefaultConfig()); + if(!InjectedConfig.TryGetValue(system, out _) && InjectedConfig.TryAdd(system, true)) + { + // Inject the top-level fallback config for the Materializer once, and only once. + // This is a performance optimization to avoid having to do this on every materialization. + system.Settings.InjectTopLevelFallback(DefaultConfig()); - settings = settings ?? ActorMaterializerSettings.Create(system); + static async Task CleanUp(ActorSystem sys) + { + // remove ActorSystem from cache when it terminates so we don't leak memory + await sys.WhenTerminated.ConfigureAwait(false); + InjectedConfig.TryRemove(sys, out _); + DefaultSettings.TryRemove(sys, out _); + } + +#pragma warning disable CS4014 + CleanUp(system); +#pragma warning restore CS4014 + + } + + // use the default settings if none have been passed in + settings ??= DefaultSettings.GetOrAdd(system, ActorMaterializerSettings.Create); return new ActorMaterializerImpl( system: system, @@ -90,14 +123,14 @@ public static ActorMaterializer Create(IActorRefFactory context, ActorMaterializ private static ActorSystem ActorSystemOf(IActorRefFactory context) { - if (context is ExtendedActorSystem) - return (ActorSystem)context; - if (context is IActorContext) - return ((IActorContext)context).System; - if (context == null) - throw new ArgumentNullException(nameof(context), "IActorRefFactory must be defined"); - - throw new ArgumentException($"ActorRefFactory context must be a ActorSystem or ActorContext, got [{context.GetType()}]"); + return context switch + { + ExtendedActorSystem system => system, + IActorContext actorContext => actorContext.System, + null => throw new ArgumentNullException(nameof(context), "IActorRefFactory must be defined"), + _ => throw new ArgumentException( + $"ActorRefFactory context must be a ActorSystem or ActorContext, got [{context.GetType()}]") + }; } #endregion