From fe7fb27c319e498d1c9f9e69ce7d06f2343ff42a Mon Sep 17 00:00:00 2001 From: ttu Date: Fri, 21 Aug 2026 18:55:06 +0300 Subject: [PATCH] fix: run the commit handler on a dedicated background thread --- CHANGELOG.md | 1 + JsonFlatFileDataStore/DataStore.cs | 44 ++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f80f0ad..d85c710 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * FIXED: Retry JSON parse on read to tolerate concurrent partial-file writes * FIXED: A failing commit action no longer hangs other callers in the same batch * FIXED: Collection key in file not matching configured case is now matched case-insensitively, instead of reading empty and duplicating the key on save +* FIXED: Run DataStore's commit handler on its own background thread, so writes are not delayed when the application's thread pool is busy ### [2.4.2] - 2023-06-25 * FIXED: Duplicate collection data to JSON on save when configured case was not used with collection name diff --git a/JsonFlatFileDataStore/DataStore.cs b/JsonFlatFileDataStore/DataStore.cs index ad68f27..de4bf17 100644 --- a/JsonFlatFileDataStore/DataStore.cs +++ b/JsonFlatFileDataStore/DataStore.cs @@ -72,23 +72,39 @@ public DataStore(string path, bool useLowerCamelCase = true, string keyProperty _jsonData = GetJsonObjectFromFile(); - // Run updates on a background thread and use BlockingCollection to prevent multiple updates to run simultaneously - Task.Run(() => + // Run updates on a dedicated background thread and use BlockingCollection to prevent multiple updates to run simultaneously. + // The handler blocks on Take() for the whole lifetime of the store, so it must not sit on a thread pool thread: a process + // holding several stores would then permanently occupy that many pool threads and starve every other continuation. + var commitHandlerThread = new Thread(() => { - CommitActionHandler.HandleStoreCommitActions(_cts.Token, - _updates, - executionState => _executingJsonUpdate = executionState, - jsonText => - { - lock (_jsonData) + try + { + CommitActionHandler.HandleStoreCommitActions(_cts.Token, + _updates, + executionState => _executingJsonUpdate = executionState, + jsonText => { - _jsonData = JObject.Parse(jsonText); - } + lock (_jsonData) + { + _jsonData = JObject.Parse(jsonText); + } + + return FileAccess.WriteJsonToFile(_filePath, _encryptJson, jsonText); + }, + GetJsonTextFromFile); + } + catch + { + // Keeps the previous Task.Run semantics, where an exception escaping the handler + // ended the loop as an unobserved task exception instead of tearing down the process. + } + }) + { + IsBackground = true, + Name = "JsonFlatFileDataStore commit handler" + }; - return FileAccess.WriteJsonToFile(_filePath, _encryptJson, jsonText); - }, - GetJsonTextFromFile); - }); + commitHandlerThread.Start(); } public void Dispose()