Per-hub Autofac child scopes cost ~55 KB per hub: replace Autofac with the standard container plus a per-hub overlay
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 20/100
Direzione di ricerca
This is a deep DI-architecture rewrite replacing Autofac child lifetime scopes with a custom per-hub overlay over Microsoft.Extensions.DependencyInjection. Start by reading src/MeshWeaver.ServiceProvider (ServiceProviderExtensions, ModuleServiceProvider) and the Autofac registration sources in src/MeshWeaver.Mesh.Contract (ModuleOwnedRootSource, ModuleServices), plus the referenced issue #7082 and prior work #7131. Done means a per-hub overlay with documented lookup order, thread safety and disposal semantics, all gated suites (HubScopeDeclaresOnlyItsOwnServicesTest, StreamReleasesItsHubTest) passing, and Autofac removed from core, Plugins and tests. Not suitable for a first contribution; the maintainer-marked plan is staged and has open design points.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Defect
Every hub owns an Autofac child lifetime scope with its own registrations (ServiceProviderExtensions.SetupModules → BeginLifetimeScope(c => Populate(...))). A scope with local registrations answers every service it resolves from its parents by wrapping each parent registration in an ExternalComponentRegistration, with its own resolve-pipeline builder and middleware declarations, kept for the life of the hub. That per-hub constant, multiplied by the hub population, is the largest block of the portal's out-of-memory heap (Refs #7082):
- memex portal OOM dump (2026-10-23, 14.6 GB core): 12,060,761
ExternalComponentRegistration, 50.8 MMiddlewareDeclaration, 5.78 GiB of Autofac rows in 12.3 GB of managed heap. - The dump's own counters size the population at ~100,000 hubs, so ~120 wrappers ≈ 58 KB of Autofac per hub.
- Measured in a test mesh after #7131 (99 node hubs plus hosted hubs, 516 hubs): Autofac is still 55 KB per hub. #7131 removed only the wasted part (the startable pass wrapping every ancestor instance registration) and the duplicated type definitions. What remains is the services a hub genuinely resolves. It cannot be removed while a hub is an Autofac child scope with registrations of its own, because the wrapper is how Autofac gives a parent-scoped service the hub's own dependencies.
Direction (maintainer)
One single DI container, the standard .NET one (Microsoft.Extensions.DependencyInjection), and Autofac removed entirely. The only reason for Autofac was to extend the container at each hub level. That extension becomes a thin, thread-safe layer of our own over the standard container:
- Root:
services.BuildServiceProvider()holds every root registration and owns every root singleton. - Per hub: an overlay provider holding the hub's own descriptors (frozen into an immutable lookup when the hub is built) plus a per-hub cache of the instances it actually resolved. Nothing is copied from the parent.
- Lookup order: the hub's own registrations, then each parent hub's in turn, then the root.
- Root singletons are served by the root container, shared by everyone.
- Root scoped/transient registrations resolved through a hub are activated by the overlay, with dependencies taken from the hub, so per-hub overrides keep winning (e.g. root
AddScoped<INodeValidator, RlsNodeValidator>()built with the hub'sIMessageHub). IEnumerable<T>is parent registrations first, then the hub's own.
- Thread safety: immutable registration lookups; at-most-once singleton construction per registration (synchronous, never awaited); recursion detected per resolve chain.
- Ownership: the layer disposes only what it built, in reverse order, when the hub ends. Externally owned forwarders (
NotOwnedServiceDescriptor) are never disposed. - Live module update: the last step of a lookup is a hook for services nobody registered, replacing
ModuleOwnedRootSource/ModuleFallbackSource(Autofac registration sources). Open design point: a root singleton built by the standard container cannot receive a module type by constructor injection; each case gets a forwarder registration or a lazy resolve.
Autofac surface to retire (measured in core src/)
src/MeshWeaver.ServiceProvider:ServiceProviderExtensions,ModuleServiceProvider,ModuleScopeOwnership(pipeline middleware),NotOwnedServiceDescriptor,HubScopeStartables,ReflectionCacheEviction.src/MeshWeaver.Mesh.Contract:MeshBuilder(registers anIRegistrationSource),ModuleOwnedRootSource,ModuleContexts/ModuleServiceForwarders(take anILifetimeScopecaller),ModuleServices(readsIComponentRegistry,IsRegisteredWithKey,TypedService/KeyedService).- 13 test files in core and 3 files in MeshWeaver.Plugins reference
ILifetimeScope/ Autofac. In-mesh NodeType sources compile only at runtime: sweep the live mesh forILifetimeScope/Autofacbefore any public surface goes.
Plan
- Build the hub overlay behind
SetupModules, with the root still on Autofac.- Gate: the full Messaging.Hub, Data, Graph, Hosting and Compiler.Pipeline suites, including
HubScopeDeclaresOnlyItsOwnServicesTest(override and startable negative controls),StreamReleasesItsHubTest(a disposed hub must be collectable) and the module-ownership diagnostics tests. - Yardstick: zero wrapper registrations per hub, and per-hub retained bytes measured against #7131's 2.04 MB per node-hub activation.
- Gate: the full Messaging.Hub, Data, Graph, Hosting and Compiler.Pipeline suites, including
- Port the module machinery off
ILifetimeScope/IComponentRegistryonto the overlay's own hooks. - Switch the root to
BuildServiceProvider(), remove the Autofac packages, and update the Plugins and test call sites. This is a paired change: core lands first, with thePairs-with:/Implementers:declarations the gates ask for.
Not established
- How much of production's ~120 wrappers per hub was the
AutoActivatewaste that #7131 removes. The next dump answers it: compare theExternalComponentRegistrationcount with the hub count. - Whether every module-owned type a root singleton depends on can move to a forwarder or a lazy resolve.
- Lingua principale
- C#
- Stelle
- 12
- Fork
- 5
- Merge medio
- 6h 30m
- PR unite (30g)
- 960
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Nessuna guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di Systemorph/MeshWeaver
-
bug ci-failure sev:H
Difficoltà 4/5 3-5 giorni Idoneità per principianti 42/100
Systemorph/MeshWeaver#7549 ·
I maintainer di solito rispondono entro 1 giorno
-
bug sev:M
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
Systemorph/MeshWeaver#7548 ·
I maintainer di solito rispondono entro 1 giorno
-
bug sev:M
Difficoltà 4/5 3-5 giorni Idoneità per principianti 35/100
Systemorph/MeshWeaver#7542 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
area:hosting enhancement feature:registry-retention sev:M
Difficoltà 5/5 Più di una settimana Idoneità per principianti 20/100
Systemorph/MeshWeaver#7530 ·
I maintainer di solito rispondono entro 1 giorno
-
bug sev:L
Difficoltà 4/5 3-5 giorni Idoneità per principianti 35/100
Systemorph/MeshWeaver#7497 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di Systemorph/MeshWeaver
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno
-
[Bug] Dashboard renders attachment links that resolve to 404 when the FILES binding is absentApertabug 優先: 低
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 92/100
-
.NET area: storage bug good first issue
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
Larson-Logan/medal-sync#61 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
I maintainer di solito rispondono entro 1 giorno