Repository navigation
refactor(di): replace Autofac with MeshWeaver's own container, MeshServiceProvider (#7145) - #7147
Conversation
…rviceProvider (#7145) One container class for the mesh root, every hub's level, every module generation's container and every plain scope, over standard ServiceDescriptors. A hub level holds only its own descriptors, frozen, and the instances it built; a parent's scoped or transient service resolved in a hub is built with the hub's own dependencies; module-declared types are answered by an IServiceRegistrationSource at the last step of a lookup. Per-instance locks (no scope-wide lock), faults never cached, cycles refused by name, a scope disposes only what it built, no process-static cache. Module machinery (ModuleServiceProvider, ModuleFallbackSource, ScopedDependencyWalk, caller-owned module scopes, ownership diagnostics, ModuleOwnedRootSource, ModuleContexts/ModuleServices callers) moved off Autofac types; CallerSharedInstanceLock, HubScopeStartables, ReflectionCacheEviction and PopulateMeshWeaver(ContainerBuilder) are gone. The disposed-container classification lives in HubDisposingException only. Autofac and Autofac.Extensions.DependencyInjection are removed from Directory.Packages.props and every project. Measured (same harness both sides, 608 hubs): live heap 150.2 -> 119.0 MB, DI-container rows 44.5 -> 2.7 KB/hub, wrapper registrations 18,146 -> 0, retained per node-hub activation 2.31 -> 1.77 MB. Refs #7145 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Foundational DI, live-module, and teardown changes require human review; the two recorded findings are documentation nits.
1 open issue
What changed in this PR
Replaces Autofac with MeshServiceProvider across root, hub, module, and ordinary scopes, preserving hierarchical resolution and live-module integration while reducing container overhead.
Changes:
- Adds registration-source and activation-observer contracts and migrates module caller plumbing.
- Removes Autofac dependencies and cache-eviction hooks; centralizes disposed-container classification.
- Adapts tests, architecture documentation, and teardown guidance to the replacement provider.
Review notes:
- Nit —
src/MeshWeaver.ServiceProvider/IServiceRegistrationSource.cs:12(1 vote): Qualify the uncached-lookup guarantee. Source-provided singleton/scoped instances still follow their declared lifetime and may retain collectible types or delay generation changes; the stronger guarantee applies to transient module stand-ins. - Nit —
test/MeshWeaver.Messaging.Hub.Test/HubDisposesItsServiceProviderTest.cs:33(3 votes): Distinguish historical Autofac retention from current behavior: parents neither track nor dispose child scopes, so each hub scope must be closed explicitly.
| File | Description |
|---|---|
test/MeshWeaver.Messaging.Hub.Test/TeardownHubCreationFreezeTest.cs |
Updates scope terminology in comments. |
test/MeshWeaver.Messaging.Hub.Test/ScopeDisposedDuringInitializationTest.cs |
Updates disposal comments. |
test/MeshWeaver.Messaging.Hub.Test/HubDisposesItsServiceProviderTest.cs |
Updates ownership commentary; child-scope documentation nit noted above. |
test/MeshWeaver.Messaging.Hub.Test/HubCreationDuringTeardownIsNotAFaultTest.cs |
Updates container descriptions. |
test/MeshWeaver.Messaging.Hub.Test/HandlerFaultPastDisposeHostedHubsTest.cs |
Uses replacement-provider disposal constants. |
test/MeshWeaver.Messaging.Hub.Test/DisposalRegistrantFaultIsolationTest.cs |
Uses replacement-provider disposal constants. |
test/MeshWeaver.Messaging.Hub.Test/AHubAtShutDownIsNotHandedOutTest.cs |
Updates scope terminology in comments. |
test/MeshWeaver.Layout.Test/SubscriberTeardownReleasesTheOwnerSyncHubTest.cs |
Updates retained-scope commentary. |
test/MeshWeaver.Layout.Test/RenderFaultAcrossScopeTeardownTest.cs |
Updates diagnostic and teardown comments. |
test/MeshWeaver.Hosting.Test/RecycledHubRefusalTest.cs |
Migrates disposal fixtures and reason text. |
test/MeshWeaver.Hosting.Test/QueryConnectionsReleasedOnTeardownTest.cs |
Updates disposed-scope commentary. |
test/MeshWeaver.Hosting.Test/OwnerAnswerRecognitionGuard.cs |
Uses replacement-provider disposal constants. |
test/MeshWeaver.Hosting.Test/MeshTeardownRunsAfterEveryOtherHostedServiceTest.cs |
Updates shutdown comments and assertion wording. |
test/MeshWeaver.Hosting.Test/MeshHostBuilderTeardownOrderingTest.cs |
Renames the test and updates scope wording. |
test/MeshWeaver.Hosting.Test/IoPoolTest.cs |
Updates pooled-work teardown comments. |
test/MeshWeaver.Hosting.Test/HubScopeClosesAfterTeardownDrainsTest.cs |
Updates explicit scope-closure commentary. |
test/MeshWeaver.Hosting.Orleans.TestBase/OrleansShutdownRaceSuppressor.cs |
Uses centralized disposal classification. |
test/MeshWeaver.Hosting.Orleans.TestBase/OrleansClusterDisposal.cs |
Updates container comments. |
test/MeshWeaver.Hosting.Orleans.Test/RoutingQuiescenceTest.cs |
Updates container-ordering terminology. |
test/MeshWeaver.Hosting.Orleans.Test/RoutingAfterScopeTeardownTest.cs |
Migrates the disposed-container fixture. |
test/MeshWeaver.Hosting.Orleans.Test/PodHubDeactivatedActivationClassificationTest.cs |
Generalizes the synthetic disposed-object name. |
test/MeshWeaver.Hosting.Orleans.Test/IoPoolSiloTeardownTest.cs |
Updates disposal comments. |
test/MeshWeaver.Hosting.Orleans.Test/HubConstructionOutcomeReportingTest.cs |
Uses replacement-provider disposal constants. |
test/MeshWeaver.Hosting.Orleans.Test/ClusterDisposalRetentionTest.cs |
Updates retained-container commentary. |
test/MeshWeaver.Graph.Test/TeardownWaitsForCollectibleUnloadsTest.cs |
Updates retention and singleton-release comments. |
test/MeshWeaver.Graph.Test/RecursiveDeleteSurvivesARecycledLeafTest.cs |
Updates disposal commentary. |
test/MeshWeaver.Graph.Test/MeshWeaver.Graph.Test.csproj |
Removes the Autofac dependency. |
test/MeshWeaver.Graph.Test/MeshNavigationRegisteredTwiceDedupesTest.cs |
Inspects registrations through MeshServiceProvider. |
test/MeshWeaver.Graph.Test/EveryHubShutsDownBeforeItsContainerTest.cs |
Updates shutdown-order comments. |
test/MeshWeaver.Graph.Test/ARecycleCascadeEnumeratesFromASurvivorTest.cs |
Updates diagnostic comments and assertion text. |
test/MeshWeaver.Graph.Test/ActivityConflictSeverityTest.cs |
Migrates the disposed-container fixture. |
test/MeshWeaver.Fixture/XUnitFileOutputHelper.cs |
Updates teardown-logging commentary. |
test/MeshWeaver.Fixture/TestTeardownExceptionSuppressor.cs |
Uses centralized disposal classification. |
test/MeshWeaver.Fixture/TestBase.cs |
Updates provider-lifetime commentary. |
test/MeshWeaver.Fixture/ServiceSetup.cs |
Updates provider-disposal comments. |
test/MeshWeaver.Documentation.Test/RootedRxConnectionRatchetGuard.cs |
Updates historical teardown terminology. |
test/MeshWeaver.Documentation.Test/HubDisposalJoinRatchetGuard.cs |
Updates scope-disposal terminology. |
test/MeshWeaver.Data.Test/StreamReleasesItsHubTest.cs |
Updates retained-provider descriptions. |
test/MeshWeaver.Data.Test/ReadPathStreamMintingTest.cs |
Updates per-hub container terminology. |
test/MeshWeaver.Data.Test/DeadStreamSafetyTest.cs |
Updates disposal comments. |
test/MeshWeaver.Data.Test/BuildMustNotConstructHubsTest.cs |
Updates nested-container commentary. |
test/MeshWeaver.Data.Test/AutocompleteSettlesOnConvergenceTest.cs |
Updates disposed-scope commentary. |
test/MeshWeaver.ContentCollections.Test/ReferenceAsCacheKeyTest.cs |
Updates synchronization-scope terminology. |
test/MeshWeaver.Compiler.Pipeline.Test/PlatformReferences.cs |
Updates historical leak descriptions. |
test/MeshWeaver.Compiler.Pipeline.Test/ModuleRootServicesSwapTest.cs |
Moves the retirement trigger to RegistryRead. |
test/MeshWeaver.Compiler.Pipeline.Test/ModuleLiveUpdateGuardTest.cs |
Removes the Autofac-specific cache guard. |
test/MeshWeaver.Compiler.Pipeline.Test/MeshWeaver.Compiler.Pipeline.Test.csproj |
Removes the Autofac dependency. |
test/MeshWeaver.Compiler.Pipeline.Test/AWithdrawnModuleIsAbsentFromTheSweepTest.cs |
Renames the host test and updates comments. |
test/MeshWeaver.Compiler.Pipeline.Test/AnotherModulesScopedServiceIsTheCallersTest.cs |
Resolves callers as MeshServiceProvider. |
test/MeshWeaver.Compiler.Pipeline.Test/AModuleOverrideHidesTheRootRegistrationTest.cs |
Adapts the type/key registration predicate. |
test/MeshWeaver.Compiler.Pipeline.Test/AModuleNodeValidatorIsNeverBuiltForNoCallerTest.cs |
Migrates caller-scope introspection. |
test/MeshWeaver.Compiler.Pipeline.Test/AModuleForwarderOwnsNothingItReturnsTest.cs |
Updates provider identity assertions. |
test/MeshWeaver.Compiler.Pipeline.Test/AModuleConstructionFaultStaysLocalTest.cs |
Creates caller scopes through BeginScope. |
test/MeshWeaver.Compiler.Pipeline.Test/ACreateNeverWaitsOnAModuleContainerUnderConstructionTest.cs |
Marks scope-wide locking as historical. |
test/MeshWeaver.Compiler.Pipeline.Test/ACallersModuleScopeEndsWithTheGenerationTest.cs |
Migrates caller-scope extraction. |
test/Memex.Portal.Shared.Test/PlatformReferences.cs |
Updates historical leak descriptions. |
test/HubDisposalJoinSites.allow |
Updates explanatory scope terminology. |
src/MeshWeaver.ServiceProvider/README.md |
Documents the replacement container and design reference. |
src/MeshWeaver.ServiceProvider/MeshWeaver.ServiceProvider.csproj |
Removes Autofac packages and exposes internals to tests. |
src/MeshWeaver.ServiceProvider/IServiceRegistrationSource.cs |
Adds source and activation-observer contracts; caching documentation nit noted above. |
src/MeshWeaver.Messaging.Hub/Serialization/TypeRegistry.cs |
Generalizes collectible-cache comments. |
src/MeshWeaver.Messaging.Hub/ScopeTeardown.cs |
Updates disposed-scope probe comments. |
src/MeshWeaver.Messaging.Hub/OwnedConnectionExtensions.cs |
Updates scope-teardown terminology. |
src/MeshWeaver.Messaging.Hub/MessageService.cs |
Updates disposal and retention comments. |
src/MeshWeaver.Messaging.Hub/MessageHubConfiguration.cs |
Updates provider and ownership comments. |
src/MeshWeaver.Messaging.Hub/MessageHub.cs |
Updates construction and scope-disposal commentary. |
src/MeshWeaver.Messaging.Hub/HubDisposalJoin.cs |
Updates teardown terminology. |
src/MeshWeaver.Messaging.Contract/IReleasesRemoteState.cs |
Updates retained-scope documentation. |
src/MeshWeaver.Messaging.Contract/IPooledSubscribeScheduler.cs |
Updates scope-disposal documentation. |
src/MeshWeaver.Messaging.Contract/IHubScopeDisposalSequencer.cs |
Revises scope-ownership commentary. |
src/MeshWeaver.Mesh.Contract/Threading/TeardownOrderedScopeDisposal.cs |
Documents untracked children and explicit disposal. |
src/MeshWeaver.Mesh.Contract/Threading/JsonMemberAccessorCacheEviction.cs |
Distinguishes JSON caching from replacement DI. |
src/MeshWeaver.Mesh.Contract/Threading/IoPoolRegistry.cs |
Updates scope-disposal commentary. |
src/MeshWeaver.Mesh.Contract/Threading/IoPoolOptions.cs |
Updates pooled-work teardown comments. |
src/MeshWeaver.Mesh.Contract/Threading/IoPool.cs |
Updates hub-scope construction comments. |
src/MeshWeaver.Mesh.Contract/Threading/IIoPool.cs |
Updates scope-creation documentation. |
src/MeshWeaver.Mesh.Contract/Security/PermissionEvaluator.cs |
Updates disposed-container commentary. |
src/MeshWeaver.Mesh.Contract/ModuleServiceForwarders.cs |
Migrates proxy and options-forwarder caller types. |
src/MeshWeaver.Mesh.Contract/ModuleLoadContext.cs |
Removes Autofac reflection-cache eviction. |
src/MeshWeaver.Mesh.Contract/MeshTeardownExtensions.cs |
Updates teardown documentation. |
src/MeshWeaver.Mesh.Contract/MeshBuilder.cs |
Registers the replacement module lookup source. |
src/MeshWeaver.Mesh.Contract/IsolatedModuleHostedService.cs |
Updates container-resolution commentary. |
src/MeshWeaver.Mesh.Contract/InstalledModules.cs |
Updates host/container compatibility comments. |
src/MeshWeaver.Mesh.Contract/ActivityTracker.cs |
Updates pooled scope-construction comments. |
src/MeshWeaver.Layout/Composition/LayoutDefinition.cs |
Updates localization-scope commentary. |
src/MeshWeaver.Layout/Composition/LayoutAreaHost.cs |
Updates render-teardown comments. |
src/MeshWeaver.Layout/AreaErrorClassifier.cs |
Updates disposal-classification comments. |
src/MeshWeaver.Hosting/Security/AccessControlPipeline.cs |
Centralizes disposed-container classification. |
src/MeshWeaver.Hosting/Persistence/Query/StorageAdapterMeshQueryProvider.cs |
Updates dependency-cycle terminology. |
src/MeshWeaver.Hosting/Persistence/Query/MeshQuery.cs |
Updates pooled-subscription commentary. |
src/MeshWeaver.Hosting/Persistence/PersistenceExtensions.cs |
Updates hierarchical-resolution documentation. |
src/MeshWeaver.Hosting/MeshTeardownHostedService.cs |
Updates root-container commentary. |
src/MeshWeaver.Hosting/MeshNodeStreamCache.cs |
Updates scope-teardown commentary. |
src/MeshWeaver.Hosting/MeshHostApplicationBuilder.cs |
Updates host-shutdown comments. |
src/MeshWeaver.Hosting.Orleans/RoutingQuiescence.cs |
Updates root-container terminology. |
src/MeshWeaver.Hosting.Orleans/RoutingGrain.cs |
Updates disposed-container diagnostic comments. |
src/MeshWeaver.Hosting.Orleans/OrleansServerRegistryExtensions.cs |
Updates routing-teardown comments. |
src/MeshWeaver.Hosting.Orleans/MessageHubGrain.cs |
Updates disposal-classification commentary. |
src/MeshWeaver.Graph/MeshDataSource.cs |
Updates scope-retention and disposal comments. |
src/MeshWeaver.Graph/Configuration/NodeTypeRecycleCascade.cs |
Updates disposed-provider diagnostic comments. |
src/MeshWeaver.Graph.Contract/SyncedQueryMeshNodes.cs |
Updates dependency-cycle terminology. |
src/MeshWeaver.Graph.Contract/SyncedQueryDataSourceExtensions.cs |
Updates deferred-resolution commentary. |
src/MeshWeaver.Documentation/Data/WhatsNew/2026-09-04-a-pod-shutting-down-is-no-longer-an-error.md |
Updates the existing entry’s container terminology. |
src/MeshWeaver.Documentation/Data/Architecture/WriteVerdictTotality.md |
Updates disposal and historical locking descriptions. |
src/MeshWeaver.Documentation/Data/Architecture/WorkspaceReferences.md |
Updates scope-cost terminology. |
src/MeshWeaver.Documentation/Data/Architecture/TestStateIsolation.md |
Updates teardown terminology. |
src/MeshWeaver.Documentation/Data/Architecture/SyncHubPopulation.md |
Distinguishes historical container measurements. |
src/MeshWeaver.Documentation/Data/Architecture/ReadPathStreamMinting.md |
Updates retained-scope terminology. |
src/MeshWeaver.Documentation/Data/Architecture/ReadingTheMemoryWatchdog.md |
Annotates historical container diagnostics. |
src/MeshWeaver.Documentation/Data/Architecture/ReadingASiloEviction.md |
Marks scope-wide locking as historical. |
src/MeshWeaver.Documentation/Data/Architecture/ReadingARecurrenceReopen.md |
Updates disposed-scope terminology. |
src/MeshWeaver.Documentation/Data/Architecture/OrleansTaskScheduler.md |
Updates scope-teardown terminology. |
src/MeshWeaver.Documentation/Data/Architecture/NodeTypeCompilation.md |
Updates straggler-filter terminology. |
src/MeshWeaver.Documentation/Data/Architecture/MeshLifecycle.md |
Names the replacement provider in lifecycle guidance. |
src/MeshWeaver.Documentation/Data/Architecture/LogWatchTriage.md |
Updates the container-disposal incident description. |
src/MeshWeaver.Documentation/Data/Architecture/HubDisposalModel.md |
Updates scope-disposal terminology. |
src/MeshWeaver.Documentation/Data/Architecture/EnumeratingFromASurvivor.md |
Updates closed-scope diagnostic descriptions. |
src/MeshWeaver.Documentation/Data/Architecture/DocumentParts.md |
Annotates historical allocation evidence. |
src/MeshWeaver.Documentation/Data/Architecture/DisposedScopeAndDyingHubs.md |
Adds a replacement-container history note. |
src/MeshWeaver.Documentation/Data/Architecture/DisposalStallVerdicts.md |
Annotates historical container allocation evidence. |
src/MeshWeaver.Documentation/Data/Architecture/DebuggingNativeCrashes.md |
Adds a container-replacement history note. |
src/MeshWeaver.Documentation/Data/Architecture/ControlledIoPooling.md |
Updates scope-teardown terminology. |
src/MeshWeaver.Documentation/Data/Architecture/AReleaseRefusedAtTheSource.md |
Updates scope-retention descriptions. |
src/MeshWeaver.Documentation/Data/Architecture/AReferenceThatCannotBeAKey.md |
Updates scope-cost terminology. |
src/MeshWeaver.Documentation/Data/Architecture/ActivityMirrorReleaseLifetime.md |
Updates disposed-container terminology. |
src/MeshWeaver.Data/Workspace.cs |
Updates synchronization-scope commentary. |
src/MeshWeaver.Data/Serialization/JsonSynchronizationStream.cs |
Updates disposal-diagnostic comments. |
src/MeshWeaver.Data/ISynchronizationStream.cs |
Updates scope-cost documentation. |
src/MeshWeaver.Data/GenericUnpartitionedDataSource.cs |
Updates nested-container commentary. |
src/MeshWeaver.Data/DataExtensions.cs |
Updates teardown and read-failure comments. |
src/MeshWeaver.Data.Contract/Messages.cs |
Updates subscription-retention documentation. |
src/MeshWeaver.ContentCollections/ContentCollectionReference.cs |
Updates synchronization-scope terminology. |
src/MeshWeaver.Connection.Orleans/IoPoolSiloTeardown.cs |
Updates startup-abort disposal comments. |
src/MeshWeaver.Compiler.Pipeline/NodeTypeCompilationHelpers.cs |
Updates scope terminology. |
src/MeshWeaver.Compiler.Pipeline/MeshNodeCompilationService.cs |
Updates disposed-container commentary. |
src/MeshWeaver.Compiler.Pipeline/CompilationCacheService.cs |
Removes Autofac eviction hooks and updates documentation. |
Directory.Packages.props |
Removes central Autofac package versions. |
.github/workflows/dotnet-test.yml |
Generalizes a memory-diagnostic comment. |
.claude/skills/sigsegv/SKILL.md |
Updates historical container and cache guidance. |
Review effort: Balanced
| /// <item>An unclosed child scope <b>keeps everything it built</b> until the parent | ||
| /// itself is disposed. A portal that activates and recycles hubs for hours therefore accumulates | ||
| /// one undisposed scope per hub, with everything each one holds — which is why a disposable-mesh | ||
| /// e2e runner reaches ~7 GB of 7 GB and starts taking 20-second GC pauses.</item> |
There was a problem hiding this comment.
Correct, and thanks. Item 2 describes the Autofac container this PR removes, where a parent lifetime scope tracked every child it began and so kept each unclosed hub scope alive until the parent itself was disposed. MeshServiceProvider does not track children (its own ownership paragraph says so: a scope nobody closes is garbage once nobody holds it), so under the replacement an unclosed hub scope is not retained by the parent and is not cleaned up by it either: its disposables are simply never disposed, finalizer-only.
The requirement this test pins is unchanged by that difference: each hub scope must be closed explicitly when its hub is disposed (HostedHubsCollection.CloseScopeWhenDisposed), because nothing else will dispose what it built. The assertion is exactly that, and it holds on both containers.
This is a wording correction in a test's XML comment and changes no behaviour, so per the round-5 rule I am not starting another review round for it alone. It will be rewritten as "historically (Autofac) retained until the parent was disposed; now never disposed at all unless closed" in the next push this PR needs for a real defect.
Test Results 31 files ± 0 31 suites ±0 1h 7m 46s ⏱️ - 1m 55s Results for commit 4a55e79. ± Comparison against base commit 0bc587e. This pull request removes 81 and adds 55 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
#7147) NotOwnedServiceDescriptorExtensions (its PopulateMeshWeaver takes an Autofac ContainerBuilder) and ReflectionCacheEviction (it purged Autofac's reflection caches) are deleted with Autofac, not moved. No forwarder can keep them without keeping Autofac, and no fleet repository names them, so they are recorded as transitional allow entries naming this pull request. check-type-forwards.py passes locally against merge base 19ac9a0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The disposed-container match now lives once, in FaultClassifier (#7117), keyed on MeshServiceProvider's object name and phrase; HubDisposingException forwards to it. Main's new FaultClassifier tests are ported from the Autofac exception shape to the container's own. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… forwarder onto FaultClassifier Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ister, not that it ran on another thread id (#7164) TheWaitedType_IsStillDelivered_OffTheRegisteringThread compared the delivery's managed thread id to the test method's. The delivery is a pool work item and the test's thread is a pool thread: once the test awaits, that thread returns to the pool and can run the delivery, which KEEPS the contract and failed the assertion (seen on #7147's local Hub.Test run: 730/731). The test now marks the window in which Register is on the stack and asks whether the delivery ran inside it. Negative control: dropping the registry's ObserveOn hop (inline delivery) turns it red. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nt (#7163) * docs(heap): rank the next per-hub costs after the container replacement Same harness as #7147 with a dump before and after 99 activations, ownership by a ClrMD nearest-owner walk: 145 KB per hub, of which type registry 44.5 KB, messaging 37.7 KB, serialization 34.2 KB. Cross-cutting: ConcurrentDictionary skeletons are 45.3 KB per hub (29.6 per hub, 60% empty, per-processor locks). Ranks six candidates with fix shapes and estimates, corrects the reading of AsyncDelivery (a handler delegate, not a queued message), and explains the two allocation-churn sources (change-feed observer-array copies, PE header reads). Refs #7082 Refs #7145 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs(heap): constraints on the key-cell replacement, non-additive estimates, opt-in exact-path feed channel (review of #7163) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Refs #7145 — replaces Autofac with MeshWeaver's own container,
MeshServiceProvider, at every level (the mesh root, each hub's level, each module generation's container, every plain scope), and removes the Autofac packages from core. Scope per the owner: the whole replacement in one change, then measure.Why our own container, and not
services.BuildServiceProvider()at the rootThe plan in #7145 put the standard provider at the root and our layer only on the hubs. Building it showed the root cannot be the standard provider without losing a feature the portal runs on:
IServiceScopeFactory(it cannot be overridden), so every request and Blazor circuit scope is a plain MS DI scope.DialogService) reach a circuit only through a lookup hook on the root (ModuleOwnedRootSource). A plain MS DI scope has no hook, so those types would stop resolving in circuits and requests.So one class serves every level. It implements the standard
Microsoft.Extensions.DependencyInjectioncontracts overServiceDescriptors. The standard specification suite (Microsoft.Extensions.DependencyInjection.Specification.Tests10.0.0, run locally against the container directly, through an empty level, and with every registration on a level) passes except for deliberate differences: hierarchical scopes (a hub's child scopes must see the hub's registrations), a level does not own the root's singletons, and one AnyKey enumerable-caching detail. Design:Doc/Architecture/HubServiceLayers(new).What changed
MeshServiceProvider(src/MeshWeaver.ServiceProvider):IServiceRegistrationSource(the "last step" hook).INodeValidatorresolved in a hub gets that hub's ownIMessageHub. A root open generic closed over a collectible type is held by the asking level, never by the root.Lazy. A cycle is refused by name, including one that runs through a factory.NotOwnedServiceDescriptorresult or a source stand-in. A parent does not track its children.Lazy<T>,Func<T>, andIEnumerable<Lazy<T>>as one deferred element per registration ofT.ReflectionCacheEvictionis gone.ModuleServiceProvider,ModuleFallbackSource,ScopedDependencyWalk, the caller-owned module scopes and the ownership diagnostics (now activation observers) run on the container's own API.ModuleOwnedRootSourceis anIServiceRegistrationSource.ModuleContextsandModuleServicesisMeshServiceProvider.CallerSharedInstanceLock(which set Autofac's private_synchRootby reflection),HubScopeStartablesandPopulateMeshWeaver(ContainerBuilder)are deleted.HubDisposingException.IsDisposedContainer/IsDisposedContainerReason, keyed on the container's object nameMeshWeaver.ServiceScope. The access pipeline, the test suppressors and the Orleans race suppressor use it. A disposed scope still answers an UNREGISTERED service with null, as before: teardown code probes optional services on dying hubs.The requested service '<key> (<type>)' has not been registered., so the module-lease alarm regex still matches.AutofacandAutofac.Extensions.DependencyInjectionare removed fromDirectory.Packages.props,MeshWeaver.ServiceProvider.csprojand the two test projects. Comments and docs no longer name Autofac except as a history note that says it was removed.Proof
dotnet build -c Release -warnaserroris clean for every touched src project and these test projects: Messaging.Hub, Compiler.Pipeline, Data, Layout, Hosting, Graph, Hosting.Orleans, Documentation, Memex.Portal.Shared.FollowedSubscriptionOwnerIdentityTest.AFollowedDirectSubscriber_NeverBecomesTheOwnerWatchersIdentity, which fails identically on an unmodified worktree at the merge base4287711857(pre-existing, filed to triage asrbuergi/Feedback/20261025-followed-subscription-owner-identity-red-on-main). After merging origin/main the touched projects rebuild clean and the targeted suites (new main tests in Memex.Portal.Shared and Deployment.Contract, Documentation, the container tests) stay green. MeshWeaver.Plugins against this core: 10 suites, 8,453 tests green (see the paired PR); the core and Plugins operator test scripts pass their dump-analysis fixtures.IEnumerable<Lazy<INodeValidator>>resolved EMPTY. It was read as "registrations ofLazy<T>", so every node validator, row-level security included, was skipped. Three read-access tests in Hosting went red (AutocompleteHonoursReadAccessTest×2,SystemOwnedSyncConfigIsVisibleToPlatformAdminsTest), and so didAModuleNodeValidatorIsNeverBuiltForNoCallerTest. All four are green after the fix, andMeshServiceProviderTestpins the behaviour.hub.ServiceProviderwas not the hub's level. This turnedHubDisposesItsServiceProviderTest×2 andScopeDisposedDuringInitializationTestred. Factories now get the scope itself, and cycles through factories are caught on slot re-entry.ScopeTeardownRenderTest).RootAndModuleSingletons…,AScopedRootService…,CallerAndModuleScoped…) asserted a DEADLOCK on main, on Autofac's scope-wide lock. They now force the same interleaving, assert that both constructions were inside their builds at once, and complete.ACollectibleTypeIsNeverHeldByTheRootTest: its negative control resolves the same closure at the root and sees the load context retained.HubScopeDeclaresOnlyItsOwnServicesTest: a hub-local override still wins (directly, through a parent-scoped service, and in enumeration order), and a root-scoped service in a hosted hub gets that hub'sIMessageHub.rbuergi/Script/sweep-retired-direads every Code and NodeType node per partition. It covered 127 partitions and 1,483 nodes onsystemorph, and 17 partitions and 296 nodes oncontrol, with 0 failed reads. The positive controlPrebuiltAssemblySeederwas found on both.ILifetimeScope,ModuleServiceProvider,ReflectionCacheEviction,NotOwnedServiceDescriptor,CallerOfRootScope,IRegistrationSourceand the others had 0 callers.CreateMeshWeaverServiceProvideris named by 2 scratch scripts and is kept. "Autofac" appears once, in the text of a doc comment inHosting/InstanceAction/Source/ObservationQueries(the paired Plugins PR rewords it).search_chunksreturnedsearched:true,count:0.Footprint (one harness on both sides,
dumpheap -stat -live)The harness: 99 Markdown node hubs activated through
GetMeshNodeStreamin one Monolith test mesh, subscriptions held, then a--type Heapdump. "Before" is core at the merge base (Autofac, after #7131); everything else is identical. This harness holds 608MessageHubs on both sides. #7131 measured 516 with a different harness, so compare within the table.MeshServiceProvider)ExternalComponentRegistration)GC.GetTotalMemory)#7131's own table, for reference: 133.9 MB live heap and 2.04 MB per node hub, of which 55 KB per hub was Autofac and 23 KB per hub was TypeRegistry.
HubScopeDeclaresOnlyItsOwnServicesTestnow asserts that a hub level holds zero registrations of its parents. Recorded inDoc/Architecture/PortalHeapIsHubs→ Per-hub DI and type metadata.Post-roll step (owed, not triggered here)
After this rolls to memex: if a fresh dump exists, file a governed
AnalyzeDump(heap stats) on the control instance and compare it with the #7082 OOM reading. Check that noExternalComponentRegistrationis present, read the per-hub container rows at production's hub population, and compare the live heap against the 12.3 GB OOM reading. Nothing on the cluster was triggered by this PR.Order and declarations
The paired MeshWeaver.Plugins PR is compatible with BOTH containers, because it classifies through core's
HubDisposingException.IsDisposedContainerand builds real containers throughCreateMeshWeaverServiceProvider. It therefore lands FIRST, so Plugins main never goes red when this platform set seals. Memex drops its own Autofac pins in a separate PR.Pairs-with: Systemorph/MeshWeaver.Plugins#4170
Satellite-pins: Autofac, Autofac.Extensions.DependencyInjection —
git grep -i autofacover MeshWeaver.Plugins origin/main: noPackageReferenceto either (the only uses wereusing AutofacinMeshWeaver.AI.Test×2 andMeshWeaver.Import/TableToTypeMapping.cs, all removed by the paired PR); the other satellites have 0 mentions; Memex pins its own versions in its ownDirectory.Packages.props(removed in Systemorph/Memex#821).No address needs a recycle. This is framework code and is picked up when replicas roll. Every hub is rebuilt on its new process.
🤖 Generated with Claude Code