mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-03 08:06:01 +08:00
* feat(db): add per-user CachedSettingsStore decorator SettingsStore methods hit the database on every call. The v2 engine path (effect_adapter) and the dispatcher's per-turn tool permission loading both called get_all_settings() without caching, adding unnecessary DB round-trips on every agentic loop iteration. Add a write-through CachedSettingsStore decorator that caches get_all_settings() results per user_id. Write operations (set_setting, delete_setting, set_all_settings) delegate to the inner store then invalidate that user's cache entry. The write lock is held across DB loads to prevent stale-data races from concurrent invalidations. Wire the cache into TenantScope via a new settings_store field on AgentDeps, so all settings reads in the agent loop go through the cache. Remove the per-turn cached_tool_permissions Mutex hack from ChatDelegate that was working around the missing cache layer. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review feedback - Store Arc<HashMap> in cache instead of bare HashMap to avoid cloning the full settings map on every cache hit. get_setting/has_settings now only clone the single requested value or check emptiness through the Arc. - Route get_setting_with_admin_fallback() through self.settings() instead of self.inner so both the per-user and admin lookups go through the cache. - Update settings section comment to accurately describe which methods delegate through settings(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address all PR review feedback - Use `crate::db::` imports instead of `super::` (convention fix) - Add `wrap()` factory fn to CachedSettingsStore, simplify app.rs construction - Store `Arc<HashMap>` in cache to avoid full map clones on hits - Expose `invalidate_user()` and `flush()` public methods - Wire `flush()` into SIGHUP handler via concrete `settings_cache` on AppComponents - Wire `settings_store` into GatewayState and route all settings handlers through it so web UI writes invalidate the cache (critical fix) - Route `get_setting_with_admin_fallback()` through `self.settings()` - Add error-path test (FailingStore mock, cache not poisoned on error) - Add concurrent-access test (8 concurrent readers, inner store hit once) - Add TenantScope caller-level test (read/write through cache wiring) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: reuse resolve_settings_store() in settings_tools_set_handler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address all review feedback on CachedSettingsStore - Add TTL (300s) and max-entries cap (1000) to bound cache staleness and memory growth. Entries expire after TTL; cache clears when cap exceeded. - Route admin tool_policy GET/PUT through resolve_settings_store() so writes invalidate the __admin__ cache entry. - Route settings_export_handler and settings_tools_list_handler through resolve_settings_store() (were bypassing cache on reads). - Wire invalidate_user() into users_delete_handler and users_suspend_handler so deleted/suspended users' settings are evicted. - Replace GatewayState.settings_store (trait object) with settings_cache (concrete CachedSettingsStore) — single field for both trait dispatch and cache management, no desync risk. - Add settings_override to ExtensionManager with with_settings_store() builder. All settings reads/writes in ExtensionManager now route through the cached store when available. - Make ExtensionManager::settings_store() pub(crate); update AuthManager to call it instead of database(), closing the auth descriptor cache bypass. - Remove unused wrap() method; merge redundant invalidate/invalidate_user. - Add tracing::debug on SIGHUP cache flush. - Expand module docs with design assumptions, known bypass paths, TTL and eviction semantics. - Add tests: expired_entry_triggers_reload, fresh_entry_does_not_reload, max_entries_cap_triggers_eviction. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: collapse nested if into filter to satisfy clippy Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>