feat(mesh): FetchAndCache apply path — fetch->cache->apply-from-bytes, null=apply-failure
Phase 3 Task 5. Under ConfigSource:Mode=FetchAndCache, a DispatchDeployment whose revision differs from _currentRevision kicks off a gRPC artifact fetch that PipeTo's its result back to Self as FetchedForApply (never a blocking await in a receive — that would freeze the mailbox for the fetch deadline). HandleFetchedForApply then applies synchronously on the actor thread, mirroring ApplyAndAck: null bytes = apply FAILURE (keep last-known-good, do not advance the revision, ack Failed — #485); non-null bytes reconcile drivers, rebuild the address space and push subscriptions FROM the bytes in hand (OpcUaPublishActor never reads central SQL), then cache them. A faulted fetch task maps to null bytes so it can't escape as an unhandled message. Extracted ReconcileDriversFromBlob from ReconcileDrivers so Direct-read, FetchAndCache and boot-from-cache share one reconcile body (the #485 empty guard moves into it); ApplyCachedArtifact now reuses it instead of duplicating the spawn-plan logic. DI: DriverHostActor.Props gains fetchAndCacheMode + artifactFetcher (LAST, per the positional-forwarding warning); Runtime resolves them from IOptions<ConfigSourceOptions> (fetcher only in FetchAndCache mode); Host registers GrpcDeploymentArtifactFetcher under hasDriver. Claude-Session: https://claude.ai/code/session_01GASWkNEi68FSCtvr6rLoEW
This commit is contained in:
@@ -282,6 +282,16 @@ public static class ServiceCollectionExtensions
|
||||
// harnesses) rather than given a null-object, so DriverHostActor skips caching outright
|
||||
// instead of pretending to cache into a sink that drops everything.
|
||||
var deploymentArtifactCache = resolver.GetService<IDeploymentArtifactCache>();
|
||||
// Per-cluster mesh Phase 3: ConfigSource:Mode selects where this driver reads config.
|
||||
// FetchAndCache pulls the artifact from central over gRPC and reads only the LocalDb cache;
|
||||
// Direct (default) reads central SQL as before. The fetcher is resolved only in FetchAndCache
|
||||
// mode (registered by the Host under hasDriver); its absence there is a misconfiguration the
|
||||
// actor fails the apply on rather than fetching nothing forever.
|
||||
var configSourceOptions =
|
||||
resolver.GetService<IOptions<ConfigSourceOptions>>()?.Value ?? new ConfigSourceOptions();
|
||||
var fetchAndCacheMode = string.Equals(
|
||||
configSourceOptions.Mode, ConfigSourceOptions.ModeFetchAndCache, StringComparison.OrdinalIgnoreCase);
|
||||
var artifactFetcher = fetchAndCacheMode ? resolver.GetService<IDeploymentArtifactFetcher>() : null;
|
||||
// Where this actor publishes its Primary-gate verdict for the alarm store-and-forward
|
||||
// drain, which runs on a timer and so cannot read RedundancyStateChanged itself.
|
||||
// Registered by AddAlarmHistorian; absent when no durable sink is configured, in which
|
||||
@@ -455,7 +465,9 @@ public static class ServiceCollectionExtensions
|
||||
invokerFactory: invokerFactory,
|
||||
deploymentArtifactCache: deploymentArtifactCache,
|
||||
redundancyRoleView: redundancyRoleView,
|
||||
replicationPeerHost: replicationPeerHost),
|
||||
replicationPeerHost: replicationPeerHost,
|
||||
fetchAndCacheMode: fetchAndCacheMode,
|
||||
artifactFetcher: artifactFetcher),
|
||||
DriverHostActorName);
|
||||
registry.Register<DriverHostActorKey>(driverHost);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user