invalidate_all_caches() clears module-level caches:
- rbac: UserPermissions.ur_caches + rp_caches
- pricing: PricingProgram.pricing_data
- uapi: UAPIData.apidata + org_users
- llmage: _uapi_cache + _uapiio_cache
GET /__hot_reload__ endpoint (registered only when hot_reload enabled):
- Manual trigger for cache flush during development
- Returns JSON with list of cleared caches
Automatic trigger:
- config.json change → invalidate_all_caches() called automatically
Each module cache is cleared independently with try/except so one
module's import failure doesn't block others.
New module: ahserver/hotreload.py
- FileWatcher: mtime-based file change detection
- HotReloader: clears JsonConfig and MiniI18N singletons on change
- Background task runs independently per process (no cross-process coord needed)
- Each process detects changes via stat() on shared filesystem
Config (conf/config.json):
"hot_reload": true — enable with default 2s interval
"hot_reload": {"enabled": true, "interval": 5} — custom interval
omit or false — disabled (production default)
What gets hot-reloaded:
- config.json → JsonConfig singleton cleared, next getConfig() reloads
- i18n/*/msg.txt → MiniI18N singleton + ServerEnv.myi18n cleared
Already hot-reloads without this feature:
- .dspy files (read from disk every request)
- .md files (read from disk every request)
- .tmpl/.ui files (Jinja2 auto_reload checks mtime)
Multi-process (reuse_port=True): each worker process runs its own
HotReloader. Since all share the filesystem, mtime-based detection
works independently without Redis/signals/pub-sub.
- ahserver/cache_sync.py: Redis Pub/Sub triggered + local process cache
- Each process maintains local cache (zero-latency reads)
- Cache invalidation via Redis Pub/Sub broadcast
- TTL fallback to prevent stale cache on missed messages
- Global singleton via get_cache_sync()
- Callback registration for auto-reload on invalidation
- Already depends on redis.asyncio (used by auth_api.py)
- Add fast path in processorResource._handle() for static assets (.js/.css/.png/.jpg/.gif/.ico/.svg/.woff etc)
Static files skip auth closures, i18n, url2processor, isHtml overhead
- Remove info() call in checkAuth() middleware that logged on EVERY request
(was causing disk flush on every request, blocking the async event loop)
- Log output now only uses debug() for timecost, exception() for errors
Changed from reading full file content to reading first 512 bytes in
binary mode, significantly improving static file serving performance
for large files like echarts.min.js (1MB+).
Before: async with aiofiles.open(fn,'r',encoding='utf-8') as f: b = await f.read()
After: async with aiofiles.open(fn,'rb') as f: b = await f.read(512)