Skip to content

Delegate GeoIP lookups to hebcal-geoip2 microservice#81

Merged
mjradwin merged 1 commit into
mainfrom
claude/geoip-memory-optimization-hmievd
Jun 30, 2026
Merged

Delegate GeoIP lookups to hebcal-geoip2 microservice#81
mjradwin merged 1 commit into
mainfrom
claude/geoip-memory-optimization-hmievd

Conversation

@mjradwin

Copy link
Copy Markdown
Member

Summary

Replaces in-process MaxMind GeoIP database lookups with calls to the standalone hebcal-geoip2 Go microservice. This reduces heap memory usage by ~63 MB (the size of GeoLite2-City.mmdb) while maintaining the same geolocation functionality.

Key Changes

  • New src/geoipClient.js: HTTP client for the hebcal-geoip2 microservice

    • Supports both Unix domain sockets (default) and TCP connections
    • Implements aggressive timeouts (20 ms connect, 250 ms request) for fast failure
    • Gracefully falls back to null if the service is unreachable or slow
    • Reuses keep-alive connections across lookups
    • Includes optional pino-style logger for debug diagnostics
  • Refactored src/geoip.js: Renamed openGeoIpDbs()setupGeoIp()

    • Initializes the geoip client instead of loading the in-process database
    • Reads socket path and optional TCP host/port from hebcal-dot-com.ini
    • Logs connection details at startup
  • Updated src/location.js: Made getLocationFromGeoIp() and getLocationFromQueryOrGeoIp() async

    • Now calls ctx.geoipClient.lookup(ip) instead of ctx.geoipCity.get(ip)
    • Returns {geo: 'none'} if the client is absent or the lookup fails
  • Updated callers: Made async where needed

    • src/defaultLangTz.js: setDefautLangTz() is now async
    • src/shabbat.js: geoIpRedirect() is now async
    • src/fridge.js: fridgeIndex() is now async
    • src/homepage.js, src/parshaIndex.js, src/converter.js: await setDefautLangTz()
  • Removed dependency: maxmind package no longer needed

  • Updated tests: test/location.test.js now mocks geoipClient.lookup() instead of geoipCity.get()

  • New test: test/geoipClient.test.js validates the client with a mock HTTP server

  • Documentation: Updated CLAUDE.md to explain the new architecture

Implementation Details

The geoip client uses a promise-based API that never rejects—all errors (connection timeouts, service errors, malformed responses) resolve to null. This ensures GeoIP is truly best-effort: if the microservice is slow or down, the request handler falls back gracefully without blocking the response.

The client manages a single http.Agent with keep-alive enabled (maxSockets: 64, maxFreeSockets: 16) to efficiently reuse connections across multiple lookups.

https://claude.ai/code/session_01JdsEBnay9JZScamV9AodLM

Previously we opened GeoLite2-City.mmdb in-process via the `maxmind`
package, which held the entire ~63 MB database in the heap for the life
of the process even though fewer than ~1 in 500 requests need a GeoIP
lookup (homepage, bare /shabbat, etc.).

Delegate the lookup to the standalone hebcal-geoip2 Go microservice,
which mmaps the database (a few MB resident, shared via the page cache)
and answers GET /lookup?ip= over a Unix domain socket. The web process
now holds no GeoIP database; the trade-off is a ~0.2-0.5 ms loopback
round-trip on the few routes that need it.

- Add src/geoipClient.js: keep-alive HTTP client over the unix socket
  with a 20 ms connect timeout. Any error/timeout/non-200 resolves to
  null so getLocationFromGeoIp() falls back to {geo:'none'} when the
  service is unreachable.
- Rewrite src/geoip.js: setupGeoIp() configures the client (socket path
  from hebcal.geoip.socket, or TCP via hebcal.geoip.host/port).
- getLocationFromGeoIp() / getLocationFromQueryOrGeoIp() /
  setDefautLangTz() become async; await them in shabbat, converter,
  fridge, homepage, and parshaIndex.
- Drop the now-unused `maxmind` dependency.
- Tests for the client (success, 204, service-down fallback, keep-alive)
  and updated location tests.
- CLAUDE.md: note the full `npm run build` is required before tests, and
  document the new GeoIP architecture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdsEBnay9JZScamV9AodLM
@sonarqubecloud

Copy link
Copy Markdown

@mjradwin mjradwin merged commit da9e8fa into main Jun 30, 2026
6 checks passed
@mjradwin mjradwin deleted the claude/geoip-memory-optimization-hmievd branch June 30, 2026 17:25
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