Summary
The finder lets visitors find dive shops and courses by place, and powers the website search box. locator-api is the public finder API: it builds cards, prices and map pins. search-api owns every OpenSearch query and also answers the website search box directly. A Lambda pipeline keeps a PostGIS database and the search indexes in step with PADI Travel within 10 minutes.
| Component | Technology | Role |
|---|---|---|
| locator-api | Go, port 8080 | Shops, shop pages, nearby, courses, map, suggest, filters. |
| search-api | Go, port 8081 | All OpenSearch queries for the finder (private) and website search suggest and results (public). Adds Google places to finder search-box suggestions (800 ms, best effort, not used for mainland China, Hong Kong or Macau). |
| Pipeline Lambdas | Go | ingest (PADI Travel changes), indexer (builds documents), rebuild (new index generations, rollback), members (member file), translate, crowdinhook and crowdinrelay (Crowdin translation and webhooks) |
| FinderDB | Aurora PostgreSQL + PostGIS | 40 PADI Travel tables, finder-owned tables, document store |
| OpenSearch | Amazon OpenSearch Service | Finder indexes and the Drupal content index |
Timeouts nest so the caller always answers: search-api 2 s, locator-api to search-api 2.5 s, locator-api request 3 s.
Data sources
| Feed | From | Path | Cadence |
|---|---|---|---|
| Shops, courses, prices, places | PADI Travel database | AWS DMS, then Kinesis, then the ingest Lambda | Continuous |
| Shop membership | PADI member file | S3 event, EventBridge, SQS, members Lambda | On each new file |
| Finder text translations | Crowdin | translate Lambda, Crowdin AI, webhook | On change, hourly safety net |
| Place enrichment | Google Places | search-api, per request | Live |
A shop appears only when both the member file and PADI Travel's own rules agree it should.
Components (C4 level 3)
Change data capture
A PADI Travel change reaches the live index within 10 minutes. Ingest applies a change only when it is newer than the last one stored for that row, so replays are safe. A full rebuild creates a new generation of all four indexes and switches them in one step; rollback points them back.
Liveness of the feed is judged from stream age, hop-by-hop lag and a daily comparison of the index with FinderDB. A heartbeat through the stream is not possible because DMS does not replicate its own heartbeat table; a scheduled row in a replicated PADI Travel table would provide one.
Finder data model
FinderDB holds three layers: the PADI Travel tables copied by DMS (with 58 personal, banking and legal columns excluded), finder-owned tables, and a document store that the indexer turns into search documents. Finder translations exist only in FinderDB and cannot be rebuilt from PADI Travel, which is why the database has 14-day point-in-time recovery.
| Index | One document per | Count (production, November 2026) |
|---|---|---|
| Shops | shop, with its bookable experiences | about 5,700 |
| Courses | shop and course pair | about 130,000 |
| Places | place or shop, for lookup | about 2,900 places and 5,700 shops |
| Reference | exchange rates, filters | 2 |
| Website content (Drupal) | published translation of a page | about 34,500 |
Prices are stored in their own currency and converted at query time.
Finder query
When part of a query fails, the response names the failed section; the API returns 503 only when nothing could be searched.
Website search
The website search box queries finder indexes and Drupal content in one call. Drupal owns its content documents; the search service owns the index template, analysers and aliases. Each language has its own fields, and visitors see only their language.
Indexing website content
Environments and sizing
| Development | Staging | Production | |
|---|---|---|---|
| Compute | 1 host per service | 1 host per service | Autoscaling groups behind load balancers: search 1 to 4, locator 1 to 3 hosts. One of each today; at least 2 across zones before launch. |
| Datastores | Containers on the host | Containers on the host | OpenSearch Service: 1 node (m7g.large, 50 GB) today, 3 nodes with 1 replica before launch. Aurora Serverless v2, 1 to 8 capacity units: writer today, reader added before launch. |
| Data | PADI Travel staging | PADI Travel production | PADI Travel production, dedicated stream |
| Origin protection | Per-hostname client certificates from Cloudflare | Same | Load balancer mutual TLS, Cloudflare address ranges only |