Preserve cached configuration when refresh fails

Build replacement appenders and thresholds before installing them and
advancing the local version. Release partial replacements and old caches
outside the configuration lock.  In order to make the `ensureFreshness`
version check as cheap as posible and only incur overhead when we
actually need to update the local cache, isolate the refresh logic in a
separate scope to prevent initialization and cleanup of temporary
variables when the version has not changed

Add regression coverage for repeated clone failures, cache preservation,
cleanup, and retry without another configuration change. Update
documentation to reflect the remaining freshness issue.
This commit is contained in:
jdb committed 2026-09-29 08:10:12 -05:00
1 parent d5e3b9c301
commit 14e71c72ca
4 files changed
+99 -32

No files matched your search

+12 -8
View File
@@ -314,9 +314,10 @@ loggers or configuring each worker separately.
> [!NOTE]
> Thread-safety work is still in progress. Custom state and captured
> formatters must follow the ownership rules above. Runtime configuration
> refresh and concurrent local-time timestamp construction still have known
> failures. Nim 2.2.10 also produces sanitizer reports in its default allocator
> formatters must follow the ownership rules above. Standalone `isEnabled()`
> checks do not yet notice changed namespace thresholds, and concurrent
> local-time timestamp construction still has a known failure.
> Nim 2.2.10 also produces sanitizer reports in its default allocator
> during some cross-thread allocation patterns. See
> [the regression tests](tests/README.md) for the remaining work.
@@ -375,9 +376,10 @@ let handle = localLogSvc.service
```
Configuration methods remain available on both types. Shared handles contain no
cache; `ensureFreshness(handle)` only validates initialization, and
`handle.appenders()` clones the current shared configuration. Local services
retain their per-thread caches and refresh them through `ensureFreshness(local)`.
cache; `handle.appenders()` clones the current shared configuration.
`ensureFreshness` applies only to *ThreadLocalLogService*, which maintains a
per-thread cache. Calls to `ensureFreshness(handle)` must be removed or changed
to refresh the relevant local service when migrating to version 3.0.0.
*ThreadLocalLogService* is the object that users are expected to interact with
during regular operation and support both the configuration functions of
@@ -450,8 +452,10 @@ proc ensureFreshness*(ls: ThreadLocalLogService) =
# Only acquire lock and copy if versions differ
withLock ls.shared.lock:
ls.configVersion = ls.shared.configVersion.load
# Sync state...
let version = ls.shared.configVersion.load()
# Build the complete replacement cache, then install it after success.
# If building raises, preserve the previous cache and version for retry.
ls.configVersion = version
```
Goals/Motivation: