feat: wire SQLite replication between site nodes and fix ConfigurationDatabase tests
Add SiteReplicationActor (runs on every site node) to replicate deployed configs and store-and-forward buffer operations to the standby peer via cluster member discovery and fire-and-forget Tell. Wire ReplicationService handler and pass replication actor to DeploymentManagerActor singleton. Fix 5 pre-existing ConfigurationDatabase test failures: RowVersion NOT NULL on SQLite, stale migration name assertion, and seed data count mismatch.
This commit is contained in:
@@ -11,15 +11,11 @@
|
||||
@inject ITemplateEngineRepository TemplateEngineRepository
|
||||
@inject ISiteRepository SiteRepository
|
||||
@inject CommunicationService CommunicationService
|
||||
@inject IJSRuntime JS
|
||||
@implements IDisposable
|
||||
|
||||
<div class="container-fluid mt-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h4 class="mb-0">Debug View</h4>
|
||||
<div class="alert alert-info py-1 px-2 mb-0 small">
|
||||
Debug view streams are lost on failover. Re-open if connection drops.
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="mb-3">Debug View</h4>
|
||||
|
||||
<ToastNotification @ref="_toast" />
|
||||
|
||||
@@ -182,6 +178,24 @@
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (!firstRender) return;
|
||||
|
||||
var storedSiteId = await JS.InvokeAsync<string>("localStorage.getItem", "debugView.siteId");
|
||||
var storedInstanceName = await JS.InvokeAsync<string>("localStorage.getItem", "debugView.instanceName");
|
||||
|
||||
if (!string.IsNullOrEmpty(storedSiteId) && int.TryParse(storedSiteId, out var siteId)
|
||||
&& !string.IsNullOrEmpty(storedInstanceName))
|
||||
{
|
||||
_selectedSiteId = siteId;
|
||||
await LoadInstancesForSite();
|
||||
_selectedInstanceName = storedInstanceName;
|
||||
StateHasChanged();
|
||||
await Connect();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadInstancesForSite()
|
||||
{
|
||||
_siteInstances.Clear();
|
||||
@@ -224,6 +238,12 @@
|
||||
}
|
||||
|
||||
_connected = true;
|
||||
|
||||
// Persist selection to localStorage for auto-reconnect on refresh
|
||||
await JS.InvokeVoidAsync("localStorage.setItem", "debugView.siteId", _selectedSiteId.ToString());
|
||||
await JS.InvokeVoidAsync("localStorage.setItem", "debugView.instanceName", _selectedInstanceName);
|
||||
await JS.InvokeVoidAsync("localStorage.setItem", "debugView.siteIdentifier", site.SiteIdentifier);
|
||||
|
||||
_toast.ShowSuccess($"Connected to {_selectedInstanceName}");
|
||||
|
||||
// Periodic refresh (simulating SignalR push by re-subscribing)
|
||||
@@ -253,7 +273,7 @@
|
||||
_connecting = false;
|
||||
}
|
||||
|
||||
private void Disconnect()
|
||||
private async Task Disconnect()
|
||||
{
|
||||
_refreshTimer?.Dispose();
|
||||
_refreshTimer = null;
|
||||
@@ -268,6 +288,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Clear persisted selection — user explicitly disconnected
|
||||
await JS.InvokeVoidAsync("localStorage.removeItem", "debugView.siteId");
|
||||
await JS.InvokeVoidAsync("localStorage.removeItem", "debugView.instanceName");
|
||||
await JS.InvokeVoidAsync("localStorage.removeItem", "debugView.siteIdentifier");
|
||||
|
||||
_connected = false;
|
||||
_snapshot = null;
|
||||
_attributeValues.Clear();
|
||||
|
||||
Reference in New Issue
Block a user