step 5
This commit is contained in:
@@ -48,6 +48,9 @@ RuntimeUpdateController::RuntimeUpdateController(
|
||||
mRuntimeEventDispatcher.Subscribe(
|
||||
RuntimeEventType::RuntimeReloadRequested,
|
||||
[this](const RuntimeEvent& event) { HandleRuntimeReloadRequested(event); });
|
||||
mRuntimeEventDispatcher.Subscribe(
|
||||
RuntimeEventType::RuntimePersistenceRequested,
|
||||
[this](const RuntimeEvent& event) { HandleRuntimePersistenceRequested(event); });
|
||||
mRuntimeEventDispatcher.Subscribe(
|
||||
RuntimeEventType::ShaderBuildRequested,
|
||||
[this](const RuntimeEvent& event) { HandleShaderBuildRequested(event); });
|
||||
@@ -158,6 +161,16 @@ void RuntimeUpdateController::HandleRuntimeReloadRequested(const RuntimeEvent& e
|
||||
mRuntimeStore.ClearReloadRequest();
|
||||
}
|
||||
|
||||
void RuntimeUpdateController::HandleRuntimePersistenceRequested(const RuntimeEvent& event)
|
||||
{
|
||||
const RuntimePersistenceRequestedEvent* payload = std::get_if<RuntimePersistenceRequestedEvent>(&event.payload);
|
||||
if (!payload)
|
||||
return;
|
||||
|
||||
std::string error;
|
||||
mRuntimeStore.RequestPersistence(payload->request, error);
|
||||
}
|
||||
|
||||
void RuntimeUpdateController::HandleShaderBuildRequested(const RuntimeEvent& event)
|
||||
{
|
||||
const ShaderBuildEvent* payload = std::get_if<ShaderBuildEvent>(&event.payload);
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
private:
|
||||
void HandleRuntimeStateBroadcastRequested(const RuntimeEvent& event);
|
||||
void HandleRuntimeReloadRequested(const RuntimeEvent& event);
|
||||
void HandleRuntimePersistenceRequested(const RuntimeEvent& event);
|
||||
void HandleShaderBuildRequested(const RuntimeEvent& event);
|
||||
void HandleShaderBuildPrepared(const RuntimeEvent& event);
|
||||
void HandleShaderBuildFailed(const RuntimeEvent& event);
|
||||
|
||||
@@ -43,7 +43,6 @@ std::string PersistenceTargetKindName(PersistenceTargetKind targetKind)
|
||||
|
||||
RuntimeStore::RuntimeStore() :
|
||||
mRenderSnapshotBuilder(*this),
|
||||
mHealthTelemetry(),
|
||||
mReloadRequested(false),
|
||||
mCompileSucceeded(false),
|
||||
mStartupRandom(GenerateStartupRandom()),
|
||||
@@ -128,6 +127,35 @@ PersistenceSnapshot RuntimeStore::BuildRuntimeStatePersistenceSnapshot(const Per
|
||||
return BuildRuntimeStatePersistenceSnapshotLocked(request);
|
||||
}
|
||||
|
||||
bool RuntimeStore::RequestPersistence(const PersistenceRequest& request, std::string& error)
|
||||
{
|
||||
if (request.targetKind != PersistenceTargetKind::RuntimeState)
|
||||
{
|
||||
error = "Unsupported persistence request target: " + PersistenceTargetKindName(request.targetKind);
|
||||
mHealthTelemetry.RecordPersistenceWriteResult(
|
||||
false,
|
||||
PersistenceTargetKindName(request.targetKind),
|
||||
std::string(),
|
||||
request.reason,
|
||||
error,
|
||||
false);
|
||||
return false;
|
||||
}
|
||||
|
||||
const PersistenceSnapshot snapshot = BuildRuntimeStatePersistenceSnapshot(request);
|
||||
if (mPersistenceWriter.EnqueueSnapshot(snapshot, error))
|
||||
return true;
|
||||
|
||||
mHealthTelemetry.RecordPersistenceWriteResult(
|
||||
false,
|
||||
PersistenceTargetKindName(request.targetKind),
|
||||
snapshot.targetPath.string(),
|
||||
request.reason,
|
||||
error,
|
||||
false);
|
||||
return false;
|
||||
}
|
||||
|
||||
PersistenceSnapshot RuntimeStore::BuildRuntimeStatePersistenceSnapshotLocked(const PersistenceRequest& request) const
|
||||
{
|
||||
PersistenceSnapshot snapshot;
|
||||
@@ -211,7 +239,7 @@ bool RuntimeStore::CreateStoredLayer(const std::string& shaderId, std::string& e
|
||||
|
||||
mReloadRequested = true;
|
||||
MarkRenderStateDirtyLocked();
|
||||
return SavePersistentState(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeStore::DeleteStoredLayer(const std::string& layerId, std::string& error)
|
||||
@@ -222,7 +250,7 @@ bool RuntimeStore::DeleteStoredLayer(const std::string& layerId, std::string& er
|
||||
|
||||
mReloadRequested = true;
|
||||
MarkRenderStateDirtyLocked();
|
||||
return SavePersistentState(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeStore::MoveStoredLayer(const std::string& layerId, int direction, std::string& error)
|
||||
@@ -239,7 +267,7 @@ bool RuntimeStore::MoveStoredLayer(const std::string& layerId, int direction, st
|
||||
|
||||
mReloadRequested = true;
|
||||
MarkRenderStateDirtyLocked();
|
||||
return SavePersistentState(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeStore::MoveStoredLayerToIndex(const std::string& layerId, std::size_t targetIndex, std::string& error)
|
||||
@@ -256,7 +284,7 @@ bool RuntimeStore::MoveStoredLayerToIndex(const std::string& layerId, std::size_
|
||||
|
||||
mReloadRequested = true;
|
||||
MarkRenderStateDirtyLocked();
|
||||
return SavePersistentState(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeStore::SetStoredLayerBypassState(const std::string& layerId, bool bypassed, std::string& error)
|
||||
@@ -267,7 +295,7 @@ bool RuntimeStore::SetStoredLayerBypassState(const std::string& layerId, bool by
|
||||
|
||||
mReloadRequested = true;
|
||||
MarkParameterStateDirtyLocked();
|
||||
return SavePersistentState(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeStore::SetStoredLayerShaderSelection(const std::string& layerId, const std::string& shaderId, std::string& error)
|
||||
@@ -278,18 +306,19 @@ bool RuntimeStore::SetStoredLayerShaderSelection(const std::string& layerId, con
|
||||
|
||||
mReloadRequested = true;
|
||||
MarkRenderStateDirtyLocked();
|
||||
return SavePersistentState(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeStore::SetStoredParameterValue(const std::string& layerId, const std::string& parameterId, const ShaderParameterValue& value, bool persistState, std::string& error)
|
||||
{
|
||||
(void)persistState;
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
|
||||
if (!mCommittedLiveState.SetParameterValue(layerId, parameterId, value, error))
|
||||
return false;
|
||||
|
||||
MarkParameterStateDirtyLocked();
|
||||
return !persistState || SavePersistentState(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeStore::ResetStoredLayerParameterValues(const std::string& layerId, std::string& error)
|
||||
@@ -300,7 +329,7 @@ bool RuntimeStore::ResetStoredLayerParameterValues(const std::string& layerId, s
|
||||
return false;
|
||||
|
||||
MarkParameterStateDirtyLocked();
|
||||
return SavePersistentState(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeStore::SaveStackPresetSnapshot(const std::string& presetName, std::string& error) const
|
||||
@@ -340,7 +369,7 @@ bool RuntimeStore::LoadStackPresetSnapshot(const std::string& presetName, std::s
|
||||
|
||||
mReloadRequested = true;
|
||||
MarkRenderStateDirtyLocked();
|
||||
return SavePersistentState(error);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RuntimeStore::HasStoredLayer(const std::string& layerId) const
|
||||
@@ -503,11 +532,6 @@ bool RuntimeStore::LoadPersistentState(std::string& error)
|
||||
return mCommittedLiveState.LoadPersistentStateValue(root);
|
||||
}
|
||||
|
||||
bool RuntimeStore::SavePersistentState(std::string& error) const
|
||||
{
|
||||
return mPersistenceWriter.EnqueueSnapshot(BuildRuntimeStatePersistenceSnapshotLocked(PersistenceRequest::RuntimeStateRequest("SavePersistentState")), error);
|
||||
}
|
||||
|
||||
PersistenceSnapshot RuntimeStore::BuildStackPresetPersistenceSnapshot(const std::string& presetName) const
|
||||
{
|
||||
const std::string safeStem = LayerStackStore::MakeSafePresetFileStem(presetName);
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
bool InitializeStore(std::string& error);
|
||||
std::string BuildPersistentStateJson() const;
|
||||
PersistenceSnapshot BuildRuntimeStatePersistenceSnapshot(const PersistenceRequest& request) const;
|
||||
bool RequestPersistence(const PersistenceRequest& request, std::string& error);
|
||||
bool PollStoredFileChanges(bool& registryChanged, bool& reloadRequested, std::string& error);
|
||||
|
||||
bool CreateStoredLayer(const std::string& shaderId, std::string& error);
|
||||
@@ -83,7 +84,6 @@ public:
|
||||
|
||||
private:
|
||||
bool LoadPersistentState(std::string& error);
|
||||
bool SavePersistentState(std::string& error) const;
|
||||
PersistenceSnapshot BuildRuntimeStatePersistenceSnapshotLocked(const PersistenceRequest& request) const;
|
||||
PersistenceSnapshot BuildStackPresetPersistenceSnapshot(const std::string& presetName) const;
|
||||
bool ScanShaderPackages(std::string& error);
|
||||
@@ -93,11 +93,11 @@ private:
|
||||
void MarkParameterStateDirtyLocked();
|
||||
|
||||
RenderSnapshotBuilder mRenderSnapshotBuilder;
|
||||
mutable PersistenceWriter mPersistenceWriter;
|
||||
RuntimeConfigStore mConfigStore;
|
||||
ShaderPackageCatalog mShaderCatalog;
|
||||
CommittedLiveState mCommittedLiveState;
|
||||
HealthTelemetry mHealthTelemetry;
|
||||
mutable PersistenceWriter mPersistenceWriter;
|
||||
mutable std::mutex mMutex;
|
||||
bool mReloadRequested;
|
||||
bool mCompileSucceeded;
|
||||
|
||||
@@ -7,8 +7,8 @@ Phases 1-5 separate durable state, coordination policy, render-facing snapshots,
|
||||
## Status
|
||||
|
||||
- Phase 6 design package: proposed.
|
||||
- Phase 6 implementation: Step 4 complete.
|
||||
- Current alignment: `RuntimeStore` owns durable serialization, config, package metadata, preset IO, and persistence requests; `CommittedLiveState` owns the current committed/session layer state; and `RuntimeCoordinator` publishes typed persistence requests for persisted mutations. The remaining issue is that actual disk writes are still synchronous store work rather than queued, debounced, atomic background writes.
|
||||
- Phase 6 implementation: Step 5 complete.
|
||||
- Current alignment: `RuntimeStore` owns durable serialization, config, package metadata, preset IO, and persistence request execution; `CommittedLiveState` owns the current committed/session layer state; and `RuntimeCoordinator` publishes typed persistence requests for persisted mutations. Runtime-state persistence is now requested through the coordinator/event path and executed by the background writer.
|
||||
|
||||
Current persistence footholds:
|
||||
|
||||
@@ -252,9 +252,17 @@ Route `RuntimePersistenceRequested` or coordinator persistence outcomes into the
|
||||
|
||||
Initial target:
|
||||
|
||||
- accepted durable mutations request persistence
|
||||
- transient-only mutations do not
|
||||
- runtime reload/preset policies remain explicit
|
||||
- [x] accepted durable mutations request persistence
|
||||
- [x] transient-only mutations do not
|
||||
- [x] runtime reload/preset policies remain explicit
|
||||
|
||||
Current implementation:
|
||||
|
||||
- Store mutation methods update committed durable/session state and mark render state dirty, but no longer enqueue runtime-state writes directly.
|
||||
- `RuntimeCoordinator` remains the owner of the persistence decision and publishes `RuntimePersistenceRequested` only for accepted durable mutations.
|
||||
- `RuntimeUpdateController` handles `RuntimePersistenceRequested` and calls `RuntimeStore::RequestPersistence(...)`.
|
||||
- `RuntimeStore::RequestPersistence(...)` validates the request target, builds the runtime-state snapshot, enqueues it on `PersistenceWriter`, and records enqueue failures in `HealthTelemetry`.
|
||||
- Stack preset save remains a synchronous preset-file write; preset load updates state and relies on the coordinator persistence request for runtime-state persistence.
|
||||
|
||||
### Step 6. Define Shutdown Flush
|
||||
|
||||
@@ -307,14 +315,14 @@ Operator-triggered preset save often feels like it should complete before report
|
||||
|
||||
Phase 6 can be considered complete once the project can say:
|
||||
|
||||
- [ ] durable mutations enqueue persistence instead of directly writing from mutation paths
|
||||
- [ ] runtime-state writes are debounced/coalesced
|
||||
- [ ] writes use temp-file/replace or equivalent atomic policy
|
||||
- [ ] persistence failures are reported through structured health/events
|
||||
- [ ] transient/live-only mutations do not request persistence
|
||||
- [x] durable mutations enqueue persistence instead of directly writing from mutation paths
|
||||
- [x] runtime-state writes are debounced/coalesced
|
||||
- [x] writes use temp-file/replace or equivalent atomic policy
|
||||
- [x] persistence failures are reported through structured health/events
|
||||
- [x] transient/live-only mutations do not request persistence
|
||||
- [ ] shutdown flush behavior is explicit and tested
|
||||
- [ ] `RuntimeStore` remains durable-state/serialization owner, not worker policy owner
|
||||
- [ ] persistence behavior has focused non-render tests
|
||||
- [x] `RuntimeStore` remains durable-state/serialization owner, not worker policy owner
|
||||
- [x] persistence behavior has focused non-render tests
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
@@ -242,6 +242,12 @@ void TestRuntimeCoordinatorPersistenceEvents()
|
||||
Expect(snapshot.reason == "unit-test", "runtime-state persistence snapshot preserves request reason");
|
||||
Expect(snapshot.targetPath.filename().string() == "runtime_state.json", "runtime-state persistence snapshot targets the runtime state file");
|
||||
Expect(snapshot.contents.find("\"layers\"") != std::string::npos, "runtime-state persistence snapshot contains serialized layer state");
|
||||
Expect(store.RequestPersistence(PersistenceRequest::RuntimeStateRequest("unit-test-request"), error),
|
||||
"runtime store accepts runtime-state persistence requests");
|
||||
PersistenceRequest unsupportedRequest;
|
||||
unsupportedRequest.targetKind = PersistenceTargetKind::StackPreset;
|
||||
unsupportedRequest.reason = "unsupported-unit-test";
|
||||
Expect(!store.RequestPersistence(unsupportedRequest, error), "runtime store rejects unsupported persistence request targets");
|
||||
|
||||
RuntimeEventDispatcher dispatcher(64);
|
||||
std::vector<RuntimeEvent> seenEvents;
|
||||
|
||||
Reference in New Issue
Block a user