feat(grpc): Phase 1B — central→site command plane over gRPC (default Akka) #27
Reference in New Issue
Block a user
Delete Branch "feat/grpc-sitecommand"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 1B of the ClusterClient→gRPC migration: the central→site command plane now has a gRPC implementation behind a flag. Default is still
Akka— nothing moves off ClusterClient by default. Rollback = flipScadaBridge:Communication:SiteTransportback. Rebased onto 1A (PR #26).What changed
site_command.proto(scadabridge.sitecommand.v1, 6 oneof RPCs / 28 commands) +SiteCommandDtoMapperwith round-trip goldens for all 28 commands + 22 replies + 18 nested types, guarded by reflection so a new command with no golden fails the build. Checked-in codegen (no active<Protobuf>item).SiteCommandDispatcher— one routing table shared by the AkkaSiteCommunicationActorand the new gRPCSiteCommandGrpcService, so the two transports can't drift. Parked commands stay node-local (replicated store);TriggerSiteFailoveracks before it leaves (dry-run resolve + deferredCommitLeave), pinned by two ordering tests.SiteCommandServiceadded to the shared interceptor'sDefaultGatedPrefixes(no second constructor — the Phase-0 defect stays fixed).ISiteCommandTransportinCentralCommunicationActor—AkkaSiteTransport(today's per-site ClusterClient path, extracted) andGrpcSiteTransport(new:SitePairChannelProviderwith sticky failover/failback, per-site PSK viaISitePskProvider, per-command deadlines matched to today's Ask timeouts, no cross-node retry onDeadlineExceeded). Rides the existing DB-refresh loop for gRPC channels — one poll loop, not two.CommunicationServiceandSiteCallAuditActoruntouched.Live gate — PASS (proportionate)
docs/plans/2026-07-22-clusterclient-to-grpc-live-gate.md. With central flipped toSiteTransport=Grpc: central→site rides authenticated gRPCSiteCommandService(ExecuteQuery,ExecuteParked→ 200) for all three sites, each under its ownSB-GRPC-PSK-{site}, 0 auth failures; central's:5000+:8083both intact (1A's Kestrel fix carried through).Plan-vs-code findings (recorded)
SiteTransportis a central-wide flag, so "flip for site-a only" isn't achievable and command-plane per-site coexistence can't be shown (the site→central plane in 1A is per-site). The gate instead proves all three sites over gRPC with distinct keys.DeploymentStateQueryandTriggerSiteFailoveruseQueryTimeout, notLifecycleTimeout— matched to the real Ask sites.SiteEnvelopeproducer exists (DebugStreamBridgeActor), so the reply plumbing routes to a real actor sender, not only Ask temp actors.Deferred (honest)
Tag commands / lifecycle / standby parked-retry (no deployed instance) and
TriggerSiteFailover(destructive, UI-only) — unit-proven, live-exercised in Phase 3's full UI command matrix.Verification
Rebased onto 1A-merged main; conflicts (both tracks restructured
SiteCommunicationActor,AkkaHostedService,CommunicationOptions,Program.cs) resolved keeping both seams.dotnet build0/0. Communication.Tests 662, Host.Tests 420 — full runs, green.Merged into
mainby fast-forward at2fa5e93c(branch was rebased on current main, so a clean ff — no merge commit). Merged tree builds 0/0; Communication.Tests 662 + Host.Tests 420 green. Closing.Pull request closed