Skip to content

Photon

Photon is the self-hosted geocoder that backs Tomoda's place-search autocomplete. It runs as a single in-cluster Deployment in the data namespace, serves the backend on photon.data.svc.cluster.local:2322, and pulls its index from a GCS bucket maintained by the Photon Indexer.

The Argo CD Application at k8s/apps/photon/application.yaml points at k8s/apps/photon/manifests.yaml and syncs into namespace: data.

Image and replication

image: rtuszik/photon-docker:2.2.0

The tag is pinned deliberately. Photon's index file format is sensitive to the binary version: a mismatch between the running pod and the index in the GCS bucket will fail to load. When bumping this tag also bump the matching tag in k8s/apps/photon-indexer/Dockerfile (PHOTON_RUNTIME_IMAGE) and in the developer-facing docker-compose.dev.yml over in the application repo. rtuszik/photon-docker:2.2.0 ships Photon 1.1.0.

Deployment is single-replica with strategy.type: Recreate. There is no rolling update — when a new pod comes up the old one must already be gone, because both would want to mount the same ReadWriteOnce PVC. Multi-replica scale-out is not supported by this layout; Photon itself can be replicated but would need a per-pod copy of the index (or RWX storage, which we don't run).

Storage

A single PersistentVolumeClaim named photon-data-pvc requests 250 Gi on the standard-rwo StorageClass:

accessModes: [ReadWriteOnce]
storageClassName: standard-rwo
resources:
  requests:
    storage: 250Gi

The size is set for UPDATE_STRATEGY=PARALLEL (see below) — during an atomic swap both the current index and the incoming index sit on disk side-by-side, so peak usage is roughly 2× the index size.

Resources and probes

requests: { memory: 2Gi,  cpu: 250m  }
limits:   { memory: 6Gi,  cpu: 2000m }

JAVA_OPTS=-Xmx4g caps the heap inside the limit. The cold-start path (load index from disk, warm Lucene) is slow, so probes are tuned long:

Probe Path Initial delay Period Notes
Readiness /status 120s 30s Pod stays out of the Service until Photon answers
Liveness /status 300s 60s failureThreshold: 10 to tolerate transient stalls during the atomic swap

Index updates

Photon is configured to auto-update from a static GCS URL:

env:
  - name: UPDATE_STRATEGY
    value: "PARALLEL"     # atomic swap; no downtime
  - name: UPDATE_INTERVAL
    value: "24h"          # poll the MD5 once a day
  - name: FILE_URL
    value: "https://storage.googleapis.com/development-485000-photon-index/planet/photon-db-planet-multilang-latest.tar.bz2"
  - name: MD5_URL
    value: "https://storage.googleapis.com/development-485000-photon-index/planet/photon-db-planet-multilang-latest.tar.bz2.md5"

The bucket is ${project_id}-photon-index. Photon polls the .md5 once every 24h; if it has changed it downloads the new tar alongside the running index, verifies the checksum, and atomically flips to the new index. This is the only update path — bumping the bucket contents is what rolls Photon forward.

Multilingual index

The index served from the bucket today is the 28-language multilingual build covering: en, ja, ko, zh, zh-Hans, zh-Hant, ar, he, hi, vi, th, id, tr, es, fr, de, it, pt, nl, pl, ru, sv, no, da, fi, el, cs, uk. This list mirrors services.SupportedLocalizationLanguages in the backend.

The multilingual rollout is in progress — see the Photon Multilang Rollout runbook for current status, per-region cutover plan, and how to verify the running pod is serving the new index.

Service

kind: Service
metadata:
  name: photon
  namespace: data
spec:
  type: ClusterIP
  ports:
    - port: 2322
      targetPort: 2322

Backend pods reach Photon at http://photon.data.svc.cluster.local:2322 — this exact URL is wired into the Tomoda backend Deployment as PHOTON_URL. The Service is not exposed via Ingress; there is no public Photon endpoint.

Operations

  • New index — build it via Photon Indexer (or the local script), upload to GCS, update the *-latest aliases. The running pod picks it up within 24h.
  • Force an update — restart the pod; on boot Photon re-checks the MD5 and downloads if the local copy is stale.
  • Storage pressure — if the PVC fills (large planet rebuild), expand it via kubectl edit pvc photon-data-pvc on a CSI driver that supports online expansion. Then bounce the pod.
  • Version bump — coordinate the runtime image and the indexer image in the same PR; cross-version index loads will fail readiness.