This commit is contained in:
Aiden
2026-05-11 19:58:14 +10:00
parent 205c90e52e
commit 1629dbc77a
8 changed files with 204 additions and 7 deletions

View File

@@ -18,12 +18,20 @@ PersistenceWriter::~PersistenceWriter()
StopAndFlush();
}
bool PersistenceWriter::WriteSnapshot(const PersistenceSnapshot& snapshot, std::string& error) const
void PersistenceWriter::SetResultCallback(ResultCallback callback)
{
std::lock_guard<std::mutex> lock(mMutex);
mResultCallback = std::move(callback);
}
bool PersistenceWriter::WriteSnapshot(const PersistenceSnapshot& snapshot, std::string& error)
{
if (!ValidateSnapshot(snapshot, error))
return false;
return WriteSnapshotThroughSink(snapshot, error);
const bool succeeded = WriteSnapshotThroughSink(snapshot, error);
PublishWriteResult(snapshot, succeeded, error, false);
return succeeded;
}
bool PersistenceWriter::EnqueueSnapshot(const PersistenceSnapshot& snapshot, std::string& error)
@@ -137,6 +145,27 @@ bool PersistenceWriter::WriteSnapshotThroughSink(const PersistenceSnapshot& snap
return true;
}
void PersistenceWriter::PublishWriteResult(const PersistenceSnapshot& snapshot, bool succeeded, const std::string& errorMessage, bool newerRequestPending)
{
ResultCallback callback;
{
std::lock_guard<std::mutex> lock(mMutex);
callback = mResultCallback;
}
if (!callback)
return;
PersistenceWriteResult result;
result.targetKind = snapshot.targetKind;
result.targetPath = snapshot.targetPath.string();
result.reason = snapshot.reason;
result.succeeded = succeeded;
result.errorMessage = errorMessage;
result.newerRequestPending = newerRequestPending;
callback(result);
}
void PersistenceWriter::StartWorkerLocked()
{
if (mWorkerRunning)
@@ -201,13 +230,16 @@ void PersistenceWriter::WorkerMain()
std::string error;
const bool succeeded = WriteSnapshotThroughSink(snapshot, error);
bool newerRequestPending = false;
{
std::lock_guard<std::mutex> lock(mMutex);
if (succeeded)
++mWrittenCount;
else
++mFailedCount;
newerRequestPending = PendingCountLocked() > 0;
}
PublishWriteResult(snapshot, succeeded, error, newerRequestPending);
}
}

View File

@@ -21,17 +21,29 @@ struct PersistenceWriterMetrics
uint64_t failedCount = 0;
};
struct PersistenceWriteResult
{
PersistenceTargetKind targetKind = PersistenceTargetKind::RuntimeState;
std::string targetPath;
std::string reason;
bool succeeded = false;
std::string errorMessage;
bool newerRequestPending = false;
};
class PersistenceWriter
{
public:
using SnapshotSink = std::function<bool(const PersistenceSnapshot&, std::string&)>;
using ResultCallback = std::function<void(const PersistenceWriteResult&)>;
explicit PersistenceWriter(
std::chrono::milliseconds debounceDelay = std::chrono::milliseconds(50),
SnapshotSink sink = SnapshotSink());
~PersistenceWriter();
bool WriteSnapshot(const PersistenceSnapshot& snapshot, std::string& error) const;
void SetResultCallback(ResultCallback callback);
bool WriteSnapshot(const PersistenceSnapshot& snapshot, std::string& error);
bool EnqueueSnapshot(const PersistenceSnapshot& snapshot, std::string& error);
void StopAndFlush();
PersistenceWriterMetrics GetMetrics() const;
@@ -45,12 +57,14 @@ private:
bool ValidateSnapshot(const PersistenceSnapshot& snapshot, std::string& error) const;
bool WriteSnapshotThroughSink(const PersistenceSnapshot& snapshot, std::string& error) const;
void PublishWriteResult(const PersistenceSnapshot& snapshot, bool succeeded, const std::string& errorMessage, bool newerRequestPending);
void StartWorkerLocked();
void WorkerMain();
std::size_t PendingCountLocked() const;
std::chrono::milliseconds mDebounceDelay;
SnapshotSink mSink;
ResultCallback mResultCallback;
mutable std::mutex mMutex;
std::condition_variable mCondition;
std::thread mWorker;

View File

@@ -24,6 +24,21 @@ double GenerateStartupRandom()
return distribution(randomDevice);
}
std::string PersistenceTargetKindName(PersistenceTargetKind targetKind)
{
switch (targetKind)
{
case PersistenceTargetKind::RuntimeState:
return "runtime-state";
case PersistenceTargetKind::StackPreset:
return "stack-preset";
case PersistenceTargetKind::RuntimeConfig:
return "runtime-config";
default:
return "unknown";
}
}
}
RuntimeStore::RuntimeStore() :
@@ -37,6 +52,15 @@ RuntimeStore::RuntimeStore() :
mStartTime(std::chrono::steady_clock::now()),
mLastScanTime((std::chrono::steady_clock::time_point::min)())
{
mPersistenceWriter.SetResultCallback([this](const PersistenceWriteResult& result) {
mHealthTelemetry.RecordPersistenceWriteResult(
result.succeeded,
PersistenceTargetKindName(result.targetKind),
result.targetPath,
result.reason,
result.errorMessage,
result.newerRequestPending);
});
}
HealthTelemetry& RuntimeStore::GetHealthTelemetry()

View File

@@ -169,6 +169,44 @@ bool HealthTelemetry::TryRecordRuntimeEventDispatchStats(std::size_t dispatchedE
return true;
}
void HealthTelemetry::RecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath,
const std::string& reason, const std::string& errorMessage, bool newerRequestPending)
{
std::lock_guard<std::mutex> lock(mMutex);
if (succeeded)
++mPersistence.writeSuccessCount;
else
++mPersistence.writeFailureCount;
mPersistence.lastWriteSucceeded = succeeded;
mPersistence.unsavedChanges = !succeeded || newerRequestPending;
mPersistence.newerRequestPending = newerRequestPending;
mPersistence.lastTargetKind = targetKind;
mPersistence.lastTargetPath = targetPath;
mPersistence.lastReason = reason;
mPersistence.lastErrorMessage = errorMessage;
}
bool HealthTelemetry::TryRecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath,
const std::string& reason, const std::string& errorMessage, bool newerRequestPending)
{
std::unique_lock<std::mutex> lock(mMutex, std::try_to_lock);
if (!lock.owns_lock())
return false;
if (succeeded)
++mPersistence.writeSuccessCount;
else
++mPersistence.writeFailureCount;
mPersistence.lastWriteSucceeded = succeeded;
mPersistence.unsavedChanges = !succeeded || newerRequestPending;
mPersistence.newerRequestPending = newerRequestPending;
mPersistence.lastTargetKind = targetKind;
mPersistence.lastTargetPath = targetPath;
mPersistence.lastReason = reason;
mPersistence.lastErrorMessage = errorMessage;
return true;
}
HealthTelemetry::SignalStatusSnapshot HealthTelemetry::GetSignalStatusSnapshot() const
{
std::lock_guard<std::mutex> lock(mMutex);
@@ -193,6 +231,12 @@ HealthTelemetry::RuntimeEventMetricsSnapshot HealthTelemetry::GetRuntimeEventMet
return mRuntimeEvents;
}
HealthTelemetry::PersistenceSnapshot HealthTelemetry::GetPersistenceSnapshot() const
{
std::lock_guard<std::mutex> lock(mMutex);
return mPersistence;
}
HealthTelemetry::Snapshot HealthTelemetry::GetSnapshot() const
{
std::lock_guard<std::mutex> lock(mMutex);
@@ -202,5 +246,6 @@ HealthTelemetry::Snapshot HealthTelemetry::GetSnapshot() const
snapshot.videoIO = mVideoIOStatus;
snapshot.performance = mPerformance;
snapshot.runtimeEvents = mRuntimeEvents;
snapshot.persistence = mPersistence;
return snapshot;
}

View File

@@ -69,12 +69,26 @@ public:
RuntimeEventDispatchSnapshot dispatch;
};
struct PersistenceSnapshot
{
uint64_t writeSuccessCount = 0;
uint64_t writeFailureCount = 0;
bool lastWriteSucceeded = true;
bool unsavedChanges = false;
bool newerRequestPending = false;
std::string lastTargetKind;
std::string lastTargetPath;
std::string lastReason;
std::string lastErrorMessage;
};
struct Snapshot
{
SignalStatusSnapshot signal;
VideoIOStatusSnapshot videoIO;
PerformanceSnapshot performance;
RuntimeEventMetricsSnapshot runtimeEvents;
PersistenceSnapshot persistence;
};
HealthTelemetry() = default;
@@ -107,10 +121,16 @@ public:
bool TryRecordRuntimeEventDispatchStats(std::size_t dispatchedEvents, std::size_t handlerInvocations,
std::size_t handlerFailures, double dispatchDurationMilliseconds);
void RecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath,
const std::string& reason, const std::string& errorMessage, bool newerRequestPending);
bool TryRecordPersistenceWriteResult(bool succeeded, const std::string& targetKind, const std::string& targetPath,
const std::string& reason, const std::string& errorMessage, bool newerRequestPending);
SignalStatusSnapshot GetSignalStatusSnapshot() const;
VideoIOStatusSnapshot GetVideoIOStatusSnapshot() const;
PerformanceSnapshot GetPerformanceSnapshot() const;
RuntimeEventMetricsSnapshot GetRuntimeEventMetricsSnapshot() const;
PersistenceSnapshot GetPersistenceSnapshot() const;
Snapshot GetSnapshot() const;
private:
@@ -119,4 +139,5 @@ private:
VideoIOStatusSnapshot mVideoIOStatus;
PerformanceSnapshot mPerformance;
RuntimeEventMetricsSnapshot mRuntimeEvents;
PersistenceSnapshot mPersistence;
};