diff --git a/docker-dev/seed/seed-clusters.sql b/docker-dev/seed/seed-clusters.sql index 0b7a1e5..d47d40e 100644 --- a/docker-dev/seed/seed-clusters.sql +++ b/docker-dev/seed/seed-clusters.sql @@ -55,45 +55,50 @@ IF NOT EXISTS (SELECT 1 FROM dbo.ServerCluster WHERE ClusterId = 'SITE-B') ------------------------------------------------------------------------------ -- ClusterNode — main cluster OPC UA publishers +-- +-- NodeId is ":4053" so it matches what ClusterRoleInfo + +-- ConfigPublishCoordinator derive from Akka.Cluster.Get(system).State.Members +-- (member.Address.Host:Port). NodeDeploymentState.NodeId is FK-bound to +-- ClusterNode.NodeId; mismatched values cause FK 547 on deploy. ------------------------------------------------------------------------------ -IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'driver-a') +IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'driver-a:4053') INSERT INTO dbo.ClusterNode (NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy) - VALUES ('driver-a', 'MAIN', 'driver-a', 4840, 8081, 'urn:OtOpcUa:driver-a', 200, 1, 'docker-dev-seed'); + VALUES ('driver-a:4053', 'MAIN', 'driver-a', 4840, 8081, 'urn:OtOpcUa:driver-a', 200, 1, 'docker-dev-seed'); -IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'driver-b') +IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'driver-b:4053') INSERT INTO dbo.ClusterNode (NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy) - VALUES ('driver-b', 'MAIN', 'driver-b', 4840, 8081, 'urn:OtOpcUa:driver-b', 150, 1, 'docker-dev-seed'); + VALUES ('driver-b:4053', 'MAIN', 'driver-b', 4840, 8081, 'urn:OtOpcUa:driver-b', 150, 1, 'docker-dev-seed'); ------------------------------------------------------------------------------ -- ClusterNode — site A ------------------------------------------------------------------------------ -IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-a-1') +IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-a-1:4053') INSERT INTO dbo.ClusterNode (NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy) - VALUES ('site-a-1', 'SITE-A', 'site-a-1', 4840, 8081, 'urn:OtOpcUa:site-a-1', 200, 1, 'docker-dev-seed'); + VALUES ('site-a-1:4053', 'SITE-A', 'site-a-1', 4840, 8081, 'urn:OtOpcUa:site-a-1', 200, 1, 'docker-dev-seed'); -IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-a-2') +IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-a-2:4053') INSERT INTO dbo.ClusterNode (NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy) - VALUES ('site-a-2', 'SITE-A', 'site-a-2', 4840, 8081, 'urn:OtOpcUa:site-a-2', 150, 1, 'docker-dev-seed'); + VALUES ('site-a-2:4053', 'SITE-A', 'site-a-2', 4840, 8081, 'urn:OtOpcUa:site-a-2', 150, 1, 'docker-dev-seed'); ------------------------------------------------------------------------------ -- ClusterNode — site B ------------------------------------------------------------------------------ -IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-b-1') +IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-b-1:4053') INSERT INTO dbo.ClusterNode (NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy) - VALUES ('site-b-1', 'SITE-B', 'site-b-1', 4840, 8081, 'urn:OtOpcUa:site-b-1', 200, 1, 'docker-dev-seed'); + VALUES ('site-b-1:4053', 'SITE-B', 'site-b-1', 4840, 8081, 'urn:OtOpcUa:site-b-1', 200, 1, 'docker-dev-seed'); -IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-b-2') +IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-b-2:4053') INSERT INTO dbo.ClusterNode (NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy) - VALUES ('site-b-2', 'SITE-B', 'site-b-2', 4840, 8081, 'urn:OtOpcUa:site-b-2', 150, 1, 'docker-dev-seed'); + VALUES ('site-b-2:4053', 'SITE-B', 'site-b-2', 4840, 8081, 'urn:OtOpcUa:site-b-2', 150, 1, 'docker-dev-seed'); ------------------------------------------------------------------------------ -- Galaxy MxAccess gateway — MAIN cluster diff --git a/docker-dev/traefik-dynamic.yml b/docker-dev/traefik-dynamic.yml index 610d0d8..e54429f 100644 --- a/docker-dev/traefik-dynamic.yml +++ b/docker-dev/traefik-dynamic.yml @@ -28,6 +28,14 @@ http: services: otopcua-admin: loadBalancer: + # Blazor Server uses SignalR; the WebSocket upgrade must hit the same + # backend that owns the circuit ID. Sticky cookie keeps each session + # pinned to one node so the post-handshake WebSocket doesn't 404. + sticky: + cookie: + name: otopcua_lb + httpOnly: true + sameSite: lax servers: - url: "http://admin-a:9000" - url: "http://admin-b:9000" @@ -38,6 +46,14 @@ http: otopcua-site-a: loadBalancer: + # Blazor Server uses SignalR; the WebSocket upgrade must hit the same + # backend that owns the circuit ID. Sticky cookie keeps each session + # pinned to one node so the post-handshake WebSocket doesn't 404. + sticky: + cookie: + name: otopcua_lb + httpOnly: true + sameSite: lax servers: - url: "http://site-a-1:9000" - url: "http://site-a-2:9000" @@ -48,6 +64,14 @@ http: otopcua-site-b: loadBalancer: + # Blazor Server uses SignalR; the WebSocket upgrade must hit the same + # backend that owns the circuit ID. Sticky cookie keeps each session + # pinned to one node so the post-handshake WebSocket doesn't 404. + sticky: + cookie: + name: otopcua_lb + httpOnly: true + sameSite: lax servers: - url: "http://site-b-1:9000" - url: "http://site-b-2:9000"