LocalDb adoption Phase 1 + 2: consolidate the site database, delete the bespoke replicators #23
@@ -1,7 +1,9 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using ZB.MOM.WW.LocalDb;
|
using ZB.MOM.WW.LocalDb;
|
||||||
using ZB.MOM.WW.ScadaBridge.SiteEventLogging;
|
using ZB.MOM.WW.ScadaBridge.SiteEventLogging;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.SiteRuntime.Persistence;
|
||||||
using ZB.MOM.WW.ScadaBridge.SiteRuntime.Tracking;
|
using ZB.MOM.WW.ScadaBridge.SiteRuntime.Tracking;
|
||||||
|
using ZB.MOM.WW.ScadaBridge.StoreAndForward;
|
||||||
|
|
||||||
namespace ZB.MOM.WW.ScadaBridge.Host;
|
namespace ZB.MOM.WW.ScadaBridge.Host;
|
||||||
|
|
||||||
@@ -44,6 +46,12 @@ public static class SiteLocalDbSetup
|
|||||||
{
|
{
|
||||||
OperationTrackingSchema.Apply(connection);
|
OperationTrackingSchema.Apply(connection);
|
||||||
SiteEventLogSchema.Apply(connection);
|
SiteEventLogSchema.Apply(connection);
|
||||||
|
|
||||||
|
// Phase 2: the site's configuration tables and the store-and-forward buffer
|
||||||
|
// now live in this file too. Created here, deliberately NOT registered — see
|
||||||
|
// below.
|
||||||
|
SiteStorageSchema.Apply(connection);
|
||||||
|
StoreAndForwardSchema.Apply(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both tables qualify: each has an explicit primary key (RegisterReplicated
|
// Both tables qualify: each has an explicit primary key (RegisterReplicated
|
||||||
@@ -52,6 +60,13 @@ public static class SiteLocalDbSetup
|
|||||||
db.RegisterReplicated("OperationTracking");
|
db.RegisterReplicated("OperationTracking");
|
||||||
db.RegisterReplicated("site_events");
|
db.RegisterReplicated("site_events");
|
||||||
|
|
||||||
|
// The Phase 2 tables are created above but NOT registered yet. The bespoke
|
||||||
|
// SiteReplicationActor / StoreAndForward ReplicationService still own replicating
|
||||||
|
// them until the Task 14 cutover deletes both and registers these in one commit.
|
||||||
|
// Registering them now would mean two independent replicators writing the same
|
||||||
|
// rows — harmless in principle (both upsert) but it would mask a defect in either
|
||||||
|
// one, which is exactly what the cutover needs to be able to see.
|
||||||
|
|
||||||
// AFTER registration, so migrated rows enter the oplog and reach the peer like
|
// AFTER registration, so migrated rows enter the oplog and reach the peer like
|
||||||
// any other write. Before it, they would be invisible to replication forever.
|
// any other write. Before it, they would be invisible to replication forever.
|
||||||
SiteLocalDbLegacyMigrator.Migrate(db, config);
|
SiteLocalDbLegacyMigrator.Migrate(db, config);
|
||||||
|
|||||||
@@ -215,6 +215,62 @@ public class SiteLocalDbWiringTests : IDisposable
|
|||||||
Assert.Contains(LocalDbMetrics.MeterName, SiteServiceRegistration.ObservedMeters);
|
Assert.Contains(LocalDbMetrics.MeterName, SiteServiceRegistration.ObservedMeters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Site_LocalDb_CreatesThePhase2Tables_InTheConsolidatedFile()
|
||||||
|
{
|
||||||
|
// Phase 2 (Task 7). The nine configuration tables and the store-and-forward buffer
|
||||||
|
// now live in the consolidated file rather than in scadabridge.db and
|
||||||
|
// store-and-forward.db. Asserting through the REAL composition root is what proves
|
||||||
|
// OnReady applies both schemas — the stores themselves also call Apply, so a test
|
||||||
|
// that went through a store would pass even if OnReady never touched them.
|
||||||
|
var db = _host.Services.GetRequiredService<ILocalDb>();
|
||||||
|
|
||||||
|
var tables = TableNames(db);
|
||||||
|
|
||||||
|
// One from each Phase 1 schema, then every Phase 2 table.
|
||||||
|
Assert.Contains("OperationTracking", tables);
|
||||||
|
Assert.Contains("site_events", tables);
|
||||||
|
Assert.Contains("sf_messages", tables);
|
||||||
|
foreach (var table in new[]
|
||||||
|
{
|
||||||
|
"deployed_configurations", "static_attribute_overrides", "shared_scripts",
|
||||||
|
"external_systems", "database_connections", "notification_lists",
|
||||||
|
"data_connection_definitions", "smtp_configurations", "native_alarm_state",
|
||||||
|
})
|
||||||
|
{
|
||||||
|
Assert.Contains(table, tables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Site_LocalDb_DoesNotYetRegisterThePhase2Tables()
|
||||||
|
{
|
||||||
|
// The "not yet" is the assertion that matters, and it is why this is an EXACTLY
|
||||||
|
// check rather than a Contains. Until the Task 14 cutover, the bespoke
|
||||||
|
// SiteReplicationActor and the StoreAndForward ReplicationService still own these
|
||||||
|
// tables. Registering them early would run two replicators over the same rows and
|
||||||
|
// hide a defect in either one behind the other's writes.
|
||||||
|
//
|
||||||
|
// When Task 14 lands, this test does not get deleted — it gets inverted.
|
||||||
|
var db = _host.Services.GetRequiredService<ILocalDb>();
|
||||||
|
|
||||||
|
Assert.Equal(
|
||||||
|
["OperationTracking", "site_events"],
|
||||||
|
db.ReplicatedTables.Keys.OrderBy(k => k, StringComparer.Ordinal).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HashSet<string> TableNames(ILocalDb db)
|
||||||
|
{
|
||||||
|
using var connection = db.CreateConnection();
|
||||||
|
using var cmd = connection.CreateCommand();
|
||||||
|
cmd.CommandText = "SELECT name FROM sqlite_master WHERE type = 'table'";
|
||||||
|
using var reader = cmd.ExecuteReader();
|
||||||
|
|
||||||
|
var names = new HashSet<string>(StringComparer.Ordinal);
|
||||||
|
while (reader.Read()) names.Add(reader.GetString(0));
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Site_LocalDb_CreatesTheConfiguredFile()
|
public void Site_LocalDb_CreatesTheConfiguredFile()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user