perf(config): read_raw_config serves cache hits without _CONFIG_LOCK

`_read_raw_config_impl` had the same lock-on-hit shape #117440 removed
from `_load_config_impl`: a microsecond cache hit queued behind
`_CONFIG_LOCK`, which `save_config()` holds across an atomic YAML write,
so one background config write stalled every raw read (per-turn policy
checks on the gateway). `_RAW_CONFIG_CACHE` already publishes each entry
as one `(*sig, data)` tuple replaced wholesale, so the lock never
protected the read; check the signature first without it and fall
through to the locked re-parse on a miss.
This commit is contained in:
kshitijk4poor
2026-09-22 14:24:18 +05:30
committed by kshitij
parent f68f9c4424
commit aeafa28342

View File

@@ -1962,6 +1962,20 @@ def cfg_get(cfg: Optional[Dict[str, Any]], *keys: str, default: Any = None) -> A
def _read_raw_config_impl(*, want_deepcopy: bool) -> Dict[str, Any]:
# Lock-free fast path for cache hits — same shape as `_load_config_impl`. `_RAW_CONFIG_CACHE`
# publishes each entry as ONE `(*sig, data)` tuple replaced wholesale, so a reader sees either
# the complete old entry or the complete new one; `_CONFIG_LOCK` only serializes the re-parse
# and the writers (`save_config()` holds it across an atomic YAML write, which used to stall
# every cached read for the duration). A lost race just falls through to the locked re-check.
try:
config_path = get_config_path()
cache_key = file_signature(config_path.stat())
cached = _RAW_CONFIG_CACHE.get(str(config_path))
if cached is not None and cached[:len(cache_key)] == cache_key:
return copy.deepcopy(cached[len(cache_key)]) if want_deepcopy else cached[len(cache_key)]
except Exception:
pass
with _CONFIG_LOCK:
try:
config_path = get_config_path()