Photon Geocoding Service¶
Photon is a self-hosted, OpenStreetMap-based geocoder. Tomoda runs its own Photon instance both locally and in production as a complement to Google Places.
Why self-host¶
Google Places is excellent but has two practical downsides:
- Cost. Per-request pricing on autocomplete and place details adds up fast at scale, especially for low-value queries (typeahead while a user is still thinking).
- Rate limits and outages. A hard external dependency on a third-party API for every search is fragile.
Photon gives us:
- A free local fallback / cache layer for forward geocoding.
- Predictable latency (in-cluster, no internet round-trip).
- The ability to pre-load multilingual indexes for the markets we care about (English, Japanese, Simplified and Traditional Chinese).
How the backend uses it¶
The Go service is at backend/internal/services/photon_service.go. The backend calls it for forward geocoding (text → coordinates + place metadata) with a 3-second timeout so that a misbehaving Photon Pod can never wedge a request handler.
The full search flow in the LocationsService (see ../backend/services/locations.md) layers providers: Google Places is the primary, Photon is the fallback when Google fails or returns nothing useful. The two have different strengths — Google for fresh business data, Photon for OSM-curated POIs and address coverage.
Local¶
The local Photon container is defined in docker-compose.dev.yml:
photon:
image: rtuszik/photon-docker:2.2.0 # Photon 1.1.0 inside
ports: ["2322:2322"]
volumes: [photon_data:/photon/data]
environment:
- JAVA_OPTS=-Xmx2g
- REGION=${PHOTON_REGION:-canada}
The image is pinned to rtuszik/photon-docker:2.2.0 — :latest is dangerous because the on-disk index format is version-sensitive. When bumping you also have to bump the Photon images in the devops repo. See DevOps → Photon Deployment and DevOps → Photon Indexer.
On first start the container downloads the index for REGION (default canada — the smallest, fastest to download) from the community mirror at r2.koalasec.org. This takes several minutes; the Compose healthcheck has a 300s start_period to wait it out.
Using the multilingual GCS index locally¶
The community mirror only ships English / German / French / Italian. To test Japanese or Chinese queries against the same index used in production, set the FILE_URL and MD5_URL env vars to point at the GCS-hosted multilingual bundle, then recreate the container:
# Canada multilingual (smallest)
export PHOTON_FILE_URL="https://storage.googleapis.com/development-485000-photon-index/canada/photon-db-canada-multilang-latest.tar.bz2"
export PHOTON_MD5_URL="https://storage.googleapis.com/development-485000-photon-index/canada/photon-db-canada-multilang-latest.tar.bz2.md5"
docker compose -f docker-compose.dev.yml down -v
docker compose -f docker-compose.dev.yml up -d photon
(The compose file documents how to wire those env vars in.)
Production¶
In the cluster, Photon runs as a Pod in the data namespace. The backend reaches it via the cluster-internal DNS name:
PHOTON_URL=http://photon.platform.svc.cluster.local:2322
(set in k8s/apps/tomoda/base/backend-deployment.yaml in the devops repo.)
The production index lives in gs://development-485000-photon-index/, organized by region (canada/, asia/, …) with multilang variants covering en, ja, zh, zh-Hans, and zh-Hant — matching the four supported frontend locales (en-US, ja-JP, zh-CN, zh-TW).
The index is rebuilt periodically by a photon-indexer CronJob (currently suspended awaiting Nominatim setup — indexes are built manually via scripts/photon-index-local.sh in the devops repo). See DevOps → Photon Multilang Rollout for the rollout history and DevOps → Photon Indexer for the cluster side.
When something's wrong¶
task devhangs for several minutes on first run. Almost always the Photon container downloading its index. Taildocker logs tomoda-photonto watch progress.- Backend logs say
photon: timeout. The 3s timeout fired. Check that the Photon Pod isReady; if not, look at its logs (it may be re-downloading an index after a volume wipe). - Geocoding results are missing non-English text. You're probably on the default-region community index, not the multilingual GCS one. See above.
See also¶
../backend/services/locations.md— how the LocationsService composes Google Places + Photondocker-compose.dev.yml— the local Photon service definitionbackend/internal/services/photon_service.go— the Go client