Skip to content

Conversation

@beastoin
Copy link
Collaborator

@beastoin beastoin commented Feb 9, 2026

Summary

Cache Google Maps geocoding API results in Redis to reduce API costs ($1.1K/mo).

Changes:

  • Add Redis cache with ~100m precision (.3f coordinate rounding) and 48h TTL
  • Defensive place.get('place_id') access (prevents KeyError on malformed responses)
  • Log cache read/write failures with logging.warning() (no silent swallowing)
  • Use model_dump() for Pydantic v2 serialization

Closes #4653

Test plan

  • 16 unit tests covering: cache precision, hits, misses, Redis failures, API edge cases (status != OK, missing/null place_id, empty/missing types), corrupt cache (invalid JSON, schema mismatch)
  • Live dev test against real Redis + Google Maps API:
    • First call: 0.408s (API hit), cache written with 48h TTL
    • Second call: 0.071s (cache hit) — 6x faster, zero API cost
    • Cache key precision verified at 100m (~3 decimal places)
  • All tests pass locally

Risks/edge cases

  • 100m rounding means slightly different addresses could be returned for nearby but distinct locations — acceptable tradeoff for cost savings
  • Redis downtime: falls through to API call with logged warning (no user impact)

by AI for @beastoin

Rounds coordinates to ~1km precision (2 decimal places) for cache key.
Same location lookups across sessions and instances now hit Redis
instead of making fresh Google Maps API calls. Geocoding API is not
covered by GCP credits — this is a direct cost reduction.

Resolves the existing TODO comment in the code.

Fixes #4653

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces Redis caching for geocoding API results, which is a great improvement for performance and cost-saving. The implementation is straightforward. My review includes a few suggestions to improve robustness and maintainability, mainly around error handling for Redis operations and simplifying data serialization using Pydantic's built-in methods. These changes will make the caching mechanism more resilient and easier to maintain.

Comment on lines 24 to 25
except Exception:
pass
Copy link
Contributor

Choose a reason for hiding this comment

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

high

Silently catching all exceptions with except Exception: pass is risky as it can hide critical issues with Redis connectivity or data corruption. It's better to log the exception. This allows for monitoring and debugging of the caching layer while still allowing the function to gracefully fall back to the API call. Please also add import logging at the top of the file.

Suggested change
except Exception:
pass
except Exception as e:
logging.warning('Redis cache get failed for key %s: %s', cache_key, e)

Comment on lines 47 to 54
cache_data = {
'google_place_id': geo.google_place_id,
'latitude': geo.latitude,
'longitude': geo.longitude,
'address': geo.address,
'location_type': geo.location_type,
}
r.set(cache_key, json.dumps(cache_data), ex=172800)
Copy link
Contributor

Choose a reason for hiding this comment

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

high

Instead of manually constructing the cache_data dictionary, you can use the Pydantic model's built-in .dict() method to serialize the geo object. This is more concise and robust, as it will automatically adapt if the Geolocation model is updated in the future.

        r.set(cache_key, json.dumps(geo.dict()), ex=172800)

Comment on lines 55 to 56
except Exception:
pass
Copy link
Contributor

Choose a reason for hiding this comment

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

high

Similar to the cache read, silently ignoring exceptions on cache write is not ideal. Logging the error will provide visibility into any issues with writing to Redis, without breaking the request flow. Please ensure import logging is present at the top of the file.

    except Exception as e:
        logging.warning('Failed to cache geocode for key %s: %s', cache_key, e)

@beastoin
Copy link
Collaborator Author

We need to be more precise about the local cache. Lets go with 100 m instead of 1 km. Should we?

- Change cache key rounding from .2f (~1km) to .3f (~100m) per review
- Replace silent except-pass with logging.warning for cache failures
- Use model_dump() instead of manual dict construction (Pydantic v2)
- Add 9 unit tests covering cache hits, misses, precision, and Redis failures

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@beastoin
Copy link
Collaborator Author

Done — pushed with 100m precision (.3f rounding). Also addressed Gemini's review feedback:

Changes:

  • Cache key: .2f.3f (~1km → ~100m)
  • Silent except: passlogging.warning() for both read/write failures
  • Manual dict → model_dump() (Pydantic v2)
  • Added 9 unit tests (precision, cache hit/miss, Redis failure resilience)

All tests pass.

beastoin and others added 3 commits February 11, 2026 02:42
Address tester gaps:
- API status != OK returns None
- Missing/empty place_id returns None
- Empty/missing types gives location_type=None
- Invalid JSON in cache falls through to API with warning
- Schema mismatch in cache falls through to API with warning

15 tests total, all pass.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Use place.get('place_id') instead of place['place_id'] to handle
  missing key gracefully (returns None instead of KeyError)
- Add test for missing place_id key in API response

16 tests total, all pass.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Tests against real Redis + Google Maps API:
- First call hits API, caches with 48h TTL
- Second call serves from cache (faster)
- Cache key uses 3 decimal precision (~100m)
- Distant coords produce different keys
- Auto-cleanup of test cache keys

Run: cd backend && source env && pytest tests/integration/test_geocoding_cache_live.py -v -s

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@beastoin
Copy link
Collaborator Author

lgtm

@beastoin beastoin merged commit 2c9e584 into main Feb 11, 2026
1 check passed
@beastoin beastoin deleted the perf/cache-geocoding-api-4653 branch February 11, 2026 02:50
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.

perf: cache geocoding API results to avoid repeated lookups

1 participant