Skip to content

Conversation

@ingoratsdorf
Copy link
Contributor

@ingoratsdorf ingoratsdorf commented Sep 2, 2025

Caching get_setting_value independent of what backend is used.

image

Summary by CodeRabbit

  • Performance

    • Introduced a two-tier in-memory caching layer for settings, improving repeated-lookup speed and reducing latency.
    • Optimized cache usage to accelerate settings retrieval across the app.
  • Bug Fixes

    • More reliable cache refresh when settings change, preventing stale or partially parsed values.
    • Safer settings loading with explicit error handling to avoid propagating invalid data.
    • Ensures settings timestamps update only after successful parsing for consistent behavior.

Caching get_setting_value independent from what backend is used.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 2, 2025

Walkthrough

Introduces a secondary in-memory cache for settings and updates get_setting and get_setting_value to use a two-tier caching strategy. Enhances file-change invalidation to clear both caches. Refactors JSON loading with with-open and explicit error handling, updating the last cache date only after successful parsing.

Changes

Cohort / File(s) Summary
Settings caching and JSON load handling
server/helper.py
Added SETTINGS_SECONDARYCACHE dict; updated get_setting to clear both caches on file change and to set last cache date post-successful parse; implemented two-tier lookup in get_setting_value using secondary cache, conf.mySettings, then get_setting; refactored JSON loading with with-open and explicit JSON decode/value error handling.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant Helper as helper.get_setting_value
  participant SecCache as Secondary Cache
  participant Conf as conf.mySettings
  participant Prim as helper.get_setting
  participant FS as table_settings.json

  Caller->>Helper: get_setting_value(key)
  Helper->>SecCache: Lookup key
  alt Hit in secondary cache
    SecCache-->>Helper: value
    Helper-->>Caller: value
  else Miss in secondary cache
    Helper->>Conf: Read mySettings[key]
    alt Found in conf
      Conf-->>Helper: value
      Helper->>SecCache: Store key=value
      Helper-->>Caller: value
    else Not in conf
      Helper->>Prim: get_setting(key)
      Prim->>FS: Check mtime / read JSON
      alt File changed
        Prim->>SecCache: Clear all
        Prim->>Prim: Clear primary cache
      end
      Prim->>FS: Parse JSON (with-open)
      alt Parse success
        Prim->>Prim: Update primary cache + last cache date
        Prim-->>Helper: value or None
        Helper->>SecCache: Store key=value
        Helper-->>Caller: value
      else Parse error
        Prim-->>Helper: None
        Helper-->>Caller: None
      end
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

A rabbit caches, swift and sly,
Two hops ahead, it nimbly tries—
First the quick burrow, then the deep,
JSON woods where settings sleep.
When files shift, it clears the trail,
Nose to wind—no lookup stale. 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5695f4f and 00c7bb6.

📒 Files selected for processing (1)
  • server/helper.py (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/helper.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: docker_dev
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ingoratsdorf ingoratsdorf mentioned this pull request Sep 2, 2025
4 tasks
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
server/helper.py (1)

331-349: Do not preemptively wipe caches; use transactional reload + in-place update.

Reassigning to {} drops existing objects (breaking external refs) and clearing before a successful parse causes avoidable outages on transient JSON errors. Load into a temporary map, then replace contents atomically; fall back to old cache on errors.

Apply:

-    # invalidate CACHE
-    SETTINGS_CACHE = {}
-    SETTINGS_SECONDARYCACHE={}
-
-    # Load JSON and populate cache
-    try:
-        with open(settingsFile, 'r') as json_file:
-            data = json.load(json_file)
-            SETTINGS_CACHE = {item["setKey"]: item for item in data.get("data", [])}
-    except json.JSONDecodeError:
-        mylog('none', [f'[Settings] ⚠ JSON decode error in file {settingsFile}'])
-        return None
-    except ValueError as e:
-        mylog('none', [f'[Settings] ⚠ Value error: {e} in file {settingsFile}'])
-        return None
-
-    # Only update file date when we successfully parsed the file
-    SETTINGS_LASTCACHEDATE = fileModifiedTime
+    # Load JSON and populate cache (transactional)
+    try:
+        with open(settingsFile, 'r') as json_file:
+            data = json.load(json_file)
+        new_cache = {item["setKey"]: item for item in data.get("data", [])}
+    except json.JSONDecodeError:
+        mylog('none', [f'[Settings] ⚠ JSON decode error in file {settingsFile}'])
+        # Keep serving from the previous cache instead of dropping values
+        return SETTINGS_CACHE.get(key)
+    except ValueError as e:
+        mylog('none', [f'[Settings] ⚠ Value error: {e} in file {settingsFile}'])
+        return SETTINGS_CACHE.get(key)
+
+    # Only update global caches after successful parse (in-place to preserve references)
+    SETTINGS_CACHE.clear()
+    SETTINGS_CACHE.update(new_cache)
+    SETTINGS_SECONDARYCACHE.clear()
+    SETTINGS_LASTCACHEDATE = fileModifiedTime

Optional concurrency hardening (outside this hunk): protect this critical section with a module-level RLock if the app runs multi-threaded.

🧹 Nitpick comments (2)
server/helper.py (2)

293-294: Good addition: secondary in-memory cache introduced.

Adds a fast path for get_setting_value; sensible for hot settings. Consider documenting expected key cardinality and lifetime to make it clear this is bounded by number of settings.


397-398: Typo in comment.

“retrive” → “retrieve”.

-    # Otherwise fall back to retrive from json
+    # Otherwise fall back to retrieve from json
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a9d7ca8 and 5695f4f.

📒 Files selected for processing (1)
  • server/helper.py (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: docker_dev
🔇 Additional comments (4)
server/helper.py (4)

347-349: Nice: timestamp updated only after successful parse.

This fixes an edge case where a bad read could pin an invalid cache state.


394-395: Good: populate secondary cache from conf.mySettings path.

Ensures hot lookups bypass tuple scanning on subsequent calls.


409-412: Good: also cache JSON-derived values.

Keeps secondary cache coherent across both sources.


311-313: Cache stability concern appears unfounded: no external references to SETTINGS_CACHE/SETTINGS_SECONDARYCACHE found
Ran rg -nP -C2 '\bSETTINGS_(SECONDARY)?CACHE\b' -g '!server/helper.py' — no matches. These globals aren’t imported elsewhere, so reassigning them in server/helper.py won’t break external code. Please manually confirm there are no dynamic imports before proceeding.

@jokob-sk
Copy link
Collaborator

jokob-sk commented Sep 2, 2025

@ingoratsdorf amazing! Seems like we've managed to lower the CPU usage by 40-50% (14% -> 7%) which is amazing. Let me know if this is ready to be merged

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@ingoratsdorf
Copy link
Contributor Author

Merged suggestion by CodeRabbit.
Should be all good now.

@jokob-sk jokob-sk merged commit b46bdb9 into netalertx:main Sep 2, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants