perf(runtime): Direct-mode apply threads the in-hand artifact (1 SQL read per apply, not 3)
This commit is contained in:
@@ -8,6 +8,7 @@ using ZB.MOM.WW.OtOpcUa.Commons.Types;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
|
||||
using ZB.MOM.WW.OtOpcUa.Runtime.Drivers;
|
||||
using ZB.MOM.WW.OtOpcUa.Runtime.OpcUa;
|
||||
using ZB.MOM.WW.OtOpcUa.Runtime.ScriptedAlarms;
|
||||
using ZB.MOM.WW.OtOpcUa.Runtime.Tests.Harness;
|
||||
|
||||
@@ -199,6 +200,47 @@ public sealed class DriverHostActorTests : RuntimeActorTestBase
|
||||
plan.PredicateSource.ShouldContain("ctx.GetTag");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a Direct-mode apply hands the artifact bytes it has already read to the
|
||||
/// address-space rebuild instead of leaving the publish actor to re-read the same row from central SQL.</summary>
|
||||
/// <remarks>
|
||||
/// The Direct apply reads <c>Deployment.ArtifactBlob</c> once (<c>ReconcileDrivers</c> returns it as
|
||||
/// <c>appliedBlob</c>) and then used to Tell <c>RebuildAddressSpace</c> with NO bytes — so
|
||||
/// <c>OpcUaPublishActor.HandleRebuild</c> re-read the row, and <c>PushDesiredSubscriptions</c> read it
|
||||
/// a third time: three central-SQL reads of one immutable blob per apply. Carrying the in-hand bytes
|
||||
/// collapses that to one. The FetchAndCache path has always been shaped this way, because it has no
|
||||
/// ConfigDb to re-read from — this asserts Direct mode now matches it. Byte-equality (rather than
|
||||
/// merely non-null) is the assertion that matters: a rebuild fed the WRONG artifact would materialise
|
||||
/// a configuration nobody deployed.
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public void Direct_mode_apply_hands_the_already_read_artifact_to_the_rebuild()
|
||||
{
|
||||
var db = NewInMemoryDbFactory();
|
||||
var deploymentId = V3ArtifactSeeder.SeedV3Deployment(db, RevA,
|
||||
rawTags: new[] { (Driver: "drv-1", DriverName: "Modbus", Device: "dev1", Tag: "speed", DataType: "Double", Writable: false) },
|
||||
unsRefs: new[] { (Area: "filling", Line: "line1", Equip: "station1", BackingTag: "speed", Effective: (string?)null) });
|
||||
|
||||
byte[] seeded;
|
||||
using (var seedCtx = db.CreateDbContext())
|
||||
seeded = seedCtx.Deployments.Single(d => d.DeploymentId == deploymentId.Value).ArtifactBlob!;
|
||||
seeded.Length.ShouldBeGreaterThan(0);
|
||||
|
||||
var coordinator = CreateTestProbe();
|
||||
var publish = CreateTestProbe();
|
||||
var actor = Sys.ActorOf(DriverHostActor.Props(
|
||||
db, TestNode, coordinator.Ref,
|
||||
localRoles: new HashSet<string> { "driver" },
|
||||
opcUaPublishActor: publish.Ref));
|
||||
|
||||
actor.Tell(new DispatchDeployment(deploymentId, RevA, CorrelationId.NewId()));
|
||||
coordinator.ExpectMsg<ApplyAck>(PresenceBudget).Outcome.ShouldBe(ApplyAckOutcome.Applied);
|
||||
|
||||
var rebuild = publish.ExpectMsg<OpcUaPublishActor.RebuildAddressSpace>(PresenceBudget);
|
||||
rebuild.DeploymentId.ShouldBe(deploymentId);
|
||||
rebuild.Artifact.ShouldNotBeNull();
|
||||
rebuild.Artifact.ShouldBe(seeded);
|
||||
}
|
||||
|
||||
private static DeploymentId SeedDeploymentWithScriptedAlarm(
|
||||
IDbContextFactory<OtOpcUaConfigDbContext> db, RevisionHash rev)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user