Skip to content

⚡ Bolt: Optimize issue endpoints with response caching#475

Open
RohanExploit wants to merge 1 commit intomainfrom
bolt-issue-cache-optimization-10758030150171408291
Open

⚡ Bolt: Optimize issue endpoints with response caching#475
RohanExploit wants to merge 1 commit intomainfrom
bolt-issue-cache-optimization-10758030150171408291

Conversation

@RohanExploit
Copy link
Owner

@RohanExploit RohanExploit commented Feb 25, 2026

💡 What:
Optimized get_recent_issues and get_nearby_issues endpoints in backend/routers/issues.py to cache the serialized JSON response string instead of the raw Python objects (List of Dicts/Models).

🎯 Why:
Previously, cache hits still incurred the cost of FastAPI's JSONResponse serialization (iterating over lists, serializing Pydantic models/dicts to JSON). For high-traffic endpoints like the feed and nearby issues map, this redundant serialization consumed unnecessary CPU cycles.

📊 Impact:

  • Reduces CPU usage on cache hits by serving pre-serialized JSON bytes directly.
  • Skips Pydantic validation and jsonable_encoder overhead on cache hits.
  • Ensures consistent datetime serialization (ISO 8601) between cache hits and misses.
  • Safe rollout: Cache keys prefixed with v2_ to prevent data type conflicts with any existing cache entries.

🔬 Measurement:
Verified with a new test suite backend/tests/test_issues_cache_mocked.py which mocks the database and cache to ensure:

  1. Cache miss: Fetches from DB, serializes to JSON, stores in cache, returns correct JSON.
  2. Cache hit: Serves content directly from cache without hitting DB or re-serializing, returning identical JSON.

PR created automatically by Jules for task 10758030150171408291 started by @RohanExploit


Summary by cubic

Optimized the recent and nearby issue endpoints to cache pre-serialized JSON and serve it directly, cutting CPU use on cache hits and keeping date formats consistent. Cache keys are versioned with v2_ to avoid conflicts with older entries.

  • Performance
    • Cache JSON strings for get_recent_issues and get_nearby_issues.
    • Return Response(content=..., media_type="application/json") on hits to skip Pydantic/encoding work.
    • Added mocked tests to verify miss→hit flow and prevent DB work on cache hits.

Written for commit fb69615. Summary will update on new commits.

Summary by CodeRabbit

  • Refactor

    • Updated caching mechanism for nearby and recent issues endpoints to improve performance by reducing serialization overhead on subsequent requests and ensuring consistent response handling.
  • Tests

    • Added comprehensive test coverage for caching behavior, validating both cache miss and hit scenarios for recent and nearby issues endpoints to ensure correct isolation and response consistency.

- Implements JSON response caching for `get_recent_issues` and `get_nearby_issues`
- Bypasses Pydantic validation and serialization on cache hits
- Updates cache keys to `v2_` to avoid conflicts with legacy data
- Consistent ISO 8601 date serialization for cache hits and misses
- Verified with new mocked test `backend/tests/test_issues_cache_mocked.py`
@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copilot AI review requested due to automatic review settings February 25, 2026 14:18
@netlify
Copy link

netlify bot commented Feb 25, 2026

Deploy Preview for fixmybharat canceled.

Name Link
🔨 Latest commit fb69615
🔍 Latest deploy log https://app.netlify.com/projects/fixmybharat/deploys/699f04ab635afb00087690a8

@github-actions
Copy link

🙏 Thank you for your contribution, @RohanExploit!

PR Details:

Quality Checklist:
Please ensure your PR meets the following criteria:

  • Code follows the project's style guidelines
  • Self-review of code completed
  • Code is commented where necessary
  • Documentation updated (if applicable)
  • No new warnings generated
  • Tests added/updated (if applicable)
  • All tests passing locally
  • No breaking changes to existing functionality

Review Process:

  1. Automated checks will run on your code
  2. A maintainer will review your changes
  3. Address any requested changes promptly
  4. Once approved, your PR will be merged! 🎉

Note: The maintainers will monitor code quality and ensure the overall project flow isn't broken.

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

This PR introduces v2 caching for recent and nearby issues endpoints by storing pre-serialized JSON strings in cache instead of Python objects, reducing serialization overhead on subsequent cache hits and ensuring consistent response typing.

Changes

Cohort / File(s) Summary
Issue Caching V2 Implementation
backend/routers/issues.py
Introduces v2 caching strategy for nearby and recent issues endpoints by storing pre-serialized JSON strings in cache with v2-prefixed keys, returning HTTP responses with cached JSON payloads to eliminate serialization overhead on cache hits.
Caching Behavior Tests
backend/tests/test_issues_cache_mocked.py
New test module that verifies caching behavior for recent and nearby issues endpoints with fixtures for mocking database sessions and caches, including test cases for cache miss-then-hit scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

size/m

Poem

🐰 A cache so swift, with JSON strings galore,
No object trees to serialize anymore!
V2 awakens, efficiency springs,
Pre-serialized dreams on responsive wings! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main optimization: caching serialized JSON responses for issue endpoints to improve performance.
Description check ✅ Passed The pull request description comprehensively covers all required template sections with clear explanations of what, why, and impact.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bolt-issue-cache-optimization-10758030150171408291

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 2 files

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR optimizes the get_recent_issues and get_nearby_issues endpoints by caching pre-serialized JSON strings instead of Python objects. This reduces CPU usage on cache hits by eliminating redundant FastAPI JSONResponse serialization, Pydantic validation, and jsonable_encoder overhead.

Changes:

  • Modified /api/issues/recent and /api/issues/nearby endpoints to cache JSON strings and return raw Response objects
  • Added cache key versioning with v2_ prefix to avoid conflicts with legacy cached data
  • Added comprehensive test suite to verify cache miss/hit behavior with mocked database and cache

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
backend/routers/issues.py Updated get_nearby_issues and get_recent_issues to cache serialized JSON strings and return Response objects directly
backend/tests/test_issues_cache_mocked.py Added new test suite with mocked database and cache to verify caching behavior works correctly for both endpoints

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

recent_issues_cache.set(data, cache_key)
return data
# Optimize: Cache serialized JSON string to avoid serialization overhead on subsequent requests
json_content = json.dumps(data, default=str)
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

The default=str fallback in json.dumps is too broad and could mask serialization issues. Since created_at is already converted to ISO format string on line 700, the default=str won't be called for datetimes. However, for other potentially non-JSON-serializable types, this will silently convert them to strings using their str method, which may not produce valid JSON representations.

Consider being more explicit about which types need custom serialization, or verify that all data is already JSON-serializable before calling json.dumps. Pydantic's model_dump(mode='json') (used in get_nearby_issues) is a more robust approach as it handles all Pydantic types consistently.

Suggested change
json_content = json.dumps(data, default=str)
json_content = json.dumps(data)

Copilot uses AI. Check for mistakes.
import json
from datetime import datetime, timezone

from fastapi import Response
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

The Response import on line 15 can be consolidated with the other FastAPI imports on line 2 for better organization. This would make:
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, Query, Request, BackgroundTasks, status, Response

Copilot uses AI. Check for mistakes.
Comment on lines 288 to +306
@router.get("/api/issues/nearby", response_model=List[NearbyIssueResponse])
def get_nearby_issues(
latitude: float = Query(..., ge=-90, le=90, description="Latitude of the location"),
longitude: float = Query(..., ge=-180, le=180, description="Longitude of the location"),
radius: float = Query(50.0, ge=10, le=500, description="Search radius in meters"),
limit: int = Query(10, ge=1, le=50, description="Maximum number of results"),
db: Session = Depends(get_db)
):
"""
Get issues near a specific location for deduplication purposes.
Returns issues within the specified radius, sorted by distance.
"""
try:
# Check cache first
cache_key = f"{latitude:.5f}_{longitude:.5f}_{radius}_{limit}"
cached_data = nearby_issues_cache.get(cache_key)
if cached_data:
return cached_data
# v2: cache JSON string to avoid conflicts with v1 list data and ensure safe typing
cache_key = f"v2_nearby_{latitude:.5f}_{longitude:.5f}_{radius}_{limit}"
cached_json = nearby_issues_cache.get(cache_key)
if cached_json:
return Response(content=cached_json, media_type="application/json")
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

When returning a raw Response object with pre-serialized JSON content, FastAPI bypasses the response_model validation and serialization. The response_model decorator on line 288 will still generate the correct OpenAPI schema, but the actual response won't be validated against it.

This is intentional for caching optimization, but it means that if the manually constructed data structures (nearby_responses list at lines 335-348) don't match the NearbyIssueResponse schema, there will be no validation error. Consider adding a comment noting that the response structure must match NearbyIssueResponse schema, or periodically validate the structure in tests.

Copilot uses AI. Check for mistakes.
Comment on lines 662 to +672
@router.get("/api/issues/recent", response_model=List[IssueSummaryResponse])
def get_recent_issues(
limit: int = Query(10, ge=1, le=50, description="Number of issues to return"),
offset: int = Query(0, ge=0, description="Number of issues to skip"),
db: Session = Depends(get_db)
):
cache_key = f"recent_issues_{limit}_{offset}"
cached_data = recent_issues_cache.get(cache_key)
if cached_data:
return JSONResponse(content=cached_data)
# v2: cache JSON string to avoid conflicts with v1 list data and ensure safe typing
cache_key = f"v2_recent_issues_{limit}_{offset}"
cached_json = recent_issues_cache.get(cache_key)
if cached_json:
return Response(content=cached_json, media_type="application/json")
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

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

When returning a raw Response object with pre-serialized JSON content, FastAPI bypasses the response_model validation and serialization. The response_model decorator on line 662 will still generate the correct OpenAPI schema, but the actual response won't be validated against it.

This is intentional for caching optimization, but it means that if the manually constructed data dictionaries (lines 696-707) don't match the IssueSummaryResponse schema, there will be no validation error. Consider adding a comment noting that the response structure must match IssueSummaryResponse schema, or periodically validate the structure in tests.

Copilot uses AI. Check for mistakes.
Copy link

@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: 3

🧹 Nitpick comments (1)
backend/tests/test_issues_cache_mocked.py (1)

84-88: Strengthen cache assertions to lock in the v2 contract.

These tests already capture cached payloads; also assert key prefix and serialized payload type to prevent regressions in cache format/keying.

🧪 Suggested assertions
     assert mock_recent.set.called
     args, _ = mock_recent.set.call_args
     cached_content = args[0]
+    cache_key = args[1]
+    assert isinstance(cached_content, str)
+    assert cache_key.startswith("v2_recent_issues_")
         assert mock_nearby.set.called
         args, _ = mock_nearby.set.call_args
         cached_content = args[0]
+        cache_key = args[1]
+        assert isinstance(cached_content, str)
+        assert cache_key.startswith("v2_nearby_")

Also applies to: 128-132

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/tests/test_issues_cache_mocked.py` around lines 84 - 88, The test
captures the mocked cache set but doesn't assert the cache key format or payload
serialization; update the assertions around mock_recent.set.call_args (used in
tests in backend/tests/test_issues_cache_mocked.py) to unpack both key and value
(e.g., key, payload = mock_recent.set.call_args[0]), assert the key uses the
expected prefix (e.g., key.startswith("recent:") or the project's configured
prefix), and assert the payload is the serialized type expected by v2 (e.g.,
isinstance(payload, str) or bytes) and that it deserializes to the expected
structure (e.g., json.loads(payload) yields a dict/list). Apply the same
stronger assertions for the other occurrence around lines 128-132.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/routers/issues.py`:
- Around line 351-355: The nearby cache is being written as JSON
(nearby_issues_cache.set) but cache invalidation is only partial
(recent_issues_cache.clear is called in one path), leaving stale data after
mutations; update the mutating handlers (upvote_issue, verify_issue_endpoint,
update_issue_status and any other commit paths that change issues) to call the
shared _invalidate_issue_caches() immediately after the successful DB
commit/transaction so both nearby_issues_cache and recent_issues_cache are
cleared consistently; locate where nearby_issues_cache.set/json_content is used
and ensure the same _invalidate_issue_caches() is invoked in those write
handlers' success branches to keep reads consistent.

In `@backend/tests/test_issues_cache_mocked.py`:
- Line 28: The one-line class definition "class MockTensor: pass" violates the
Ruff E701 rule; replace it with a multi-line definition for MockTensor (e.g.,
define class MockTensor: followed by a newline with a pass statement indented)
so the class body is on its own line and conforms to the linter.
- Around line 9-31: The test file mutates process-global sys.modules entries
(e.g., assignments to sys.modules['magic'], sys.modules['telegram'],
mock_torch/simulated 'torch' module and MockTensor) at import time and never
restores originals; change this to use a test-scoped fixture or monkeypatch to
set and restore modules (for example, use pytest's monkeypatch.setitem for each
key or a fixture that saves originals and restores them in teardown) and ensure
the temporary mock_torch and MockTensor are injected via
monkeypatch.setitem('sys.modules', 'torch', mock_torch) (or equivalent) so no
global leakage occurs after tests complete.

---

Nitpick comments:
In `@backend/tests/test_issues_cache_mocked.py`:
- Around line 84-88: The test captures the mocked cache set but doesn't assert
the cache key format or payload serialization; update the assertions around
mock_recent.set.call_args (used in tests in
backend/tests/test_issues_cache_mocked.py) to unpack both key and value (e.g.,
key, payload = mock_recent.set.call_args[0]), assert the key uses the expected
prefix (e.g., key.startswith("recent:") or the project's configured prefix), and
assert the payload is the serialized type expected by v2 (e.g.,
isinstance(payload, str) or bytes) and that it deserializes to the expected
structure (e.g., json.loads(payload) yields a dict/list). Apply the same
stronger assertions for the other occurrence around lines 128-132.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 39d5fbb and fb69615.

📒 Files selected for processing (2)
  • backend/routers/issues.py
  • backend/tests/test_issues_cache_mocked.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants