Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

Per-hub Autofac child scopes cost ~55 KB per hub: replace Autofac with the standard container plus a per-hub overlay

Chiusa
#7,145 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

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
Tipo di issue
Refactoring
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
csharp
Ambito
backend

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

sev:H

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 M MiddlewareDeclaration, 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's IMessageHub).
    • 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 an IRegistrationSource), ModuleOwnedRootSource, ModuleContexts / ModuleServiceForwarders (take an ILifetimeScope caller), ModuleServices (reads IComponentRegistry, 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 for ILifetimeScope / Autofac before any public surface goes.

Plan

  1. 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.
  2. Port the module machinery off ILifetimeScope / IComponentRegistry onto the overlay's own hooks.
  3. 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 the Pairs-with: / Implementers: declarations the gates ask for.

Not established

  • How much of production's ~120 wrappers per hub was the AutoActivate waste that #7131 removes. The next dump answers it: compare the ExternalComponentRegistration count 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

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di Systemorph/MeshWeaver

Tutte le issue di Systemorph/MeshWeaver

Issue simili

Altre issue su C#

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.