Flow: Search Query
The most frequent customer operation. A search request enters via the Cloudflare search proxy worker and is routed to Marqo.
Request Path
graph TD
A["Customer App"]
subgraph cf["Cloudflare Worker (search_proxy: {env}-ecom-api)"]
subgraph auth["Auth & Settings middleware"]
B["Redirect check (DynamoDB)"]
C["Marqo search (MARQO_WORKER service binding)"]
end
D["Response assembly"]
E["Metrics enqueue (SQS)"]
end
F["Customer App"]
A --> B
A --> C
B --> D
C --> D
D --> F
D -.->|background| E
Step-by-Step
1. Worker Entry
Where: components/search_proxy/src/worker.ts → app.ts
The Hono app receives POST /api/v1/indexes/:index/search.
Logs:
{"message": "request_start", "method": "POST", "path": "/api/v1/indexes/{index}/search"}
Inspect: Tail the worker — see Cloudflare Workers.
npx wrangler tail {env}-ecom-api
2. Authentication
Where: components/search_proxy/src/middleware.ts → direct/platform.ts
Two auth methods (checked in order):
- Index ID header:
x-marqo-index-id: {system_account_id}-{index_name}(read-only operations) - API key:
Authorization: Bearer {api_key}
Failure: 401 with "Missing API key or index ID header".
Logs (DEBUG level; only logged at info if total >250ms):
{"message": "direct_auth_settings_readonly_timing", "auth_ms": 1.5, "settings_ms": 8.2}
3. Settings Retrieval from KV
Where: components/search_proxy/src/direct/platform.ts
- Resolve shop ID from API key (calls admin server) or extract from
x-marqo-index-idheader env.KV.get(shopId)→ JSON settings with index endpoint, API key, search config, feature flags
Failure: 404 "Not configured" if shop ID not in KV.
Inspect: Check KV contents — see Cloudflare Workers.
Logs:
{"message": "index_settings_timing", "shop_id": "...", "has_value": true, "timings": {"index_settings_kv_get_ms": 6.0}}
If settings missing: Check whether the Settings Sync flow completed successfully.
4. Read Alias Resolution (Optional)
Where: components/search_proxy/src/middleware.ts
If read_alias or index_aliases.doc_reads is configured, traffic is split across index targets using deterministic bucketing (hashed userId/sessionId).
Logs:
{"message": "read_alias_redirect", "original_index": "...", "aliased_index": "..."}
5. Query Config Override Lookup (Optional)
Where: components/search_proxy/src/query_overrides.ts
If feature_flags.qcfg_overrides is enabled:
- Normalize query → compute shard hash
- Check shard cache (10 min TTL) → KV_QCFG namespace
- Apply overrides: query replacement, config deep-merge, lexical expansion, quote mode
Timeout protection: 70ms max. If exceeded, proceeds with base settings.
Logs:
{"message": "qcfg_shard_cache_lookup", "cache_hit": true}
{"message": "qcfg took 3.21ms"}
6. Search Request Building
Where: components/search_proxy/src/search.ts
Transforms the customer request into a Marqo API request:
- Merges search config defaults (limit, offset, facets, sort, filter)
- Applies query prefix, collapse fields, score modifiers
- Handles image queries and multi-term queries (switch to tensor-only hybrid)
- Applies lexical expansion and quoting
7. Redirect Check (Parallel)
Where: components/search_proxy/src/redirects/redirects.ts
Runs in parallel with the Marqo search:
- Check Cloudflare Cache API (configurable TTL, default 10 min)
- On miss: query
SearchRedirectTablein DynamoDB (pk={accountId}#INDEX#{indexName},sk=QUERY#{normalizedQuery}) - Background cache update via
ctx.waitUntil()
Inspect: Check redirects in DynamoDB — see DynamoDB.
If a redirect is found, it takes priority over search results.
8. Marqo Search Execution (Parallel)
Where: components/search_proxy/src/search.ts
POST {settings.marqo_index_url}/indexes/{index_name}/search
Headers: x-api-key: {marqo_api_key}
Sent via MARQO_WORKER service binding (not direct HTTP).
Logs:
{"message": "marqo_search_response", "hitsCount": 12, "processingTimeMs": 45, "cacheHit": true, "totalHits": 150}
9. Response Assembly
If redirect found → return { redirect: { url, query } }.
Otherwise → return { hits, totalHits, query, limit, offset, facets, processingTimeMs }.
Documents are transformed: internal fields (_merch_*, _pixel_*) removed, flattened maps unflattened.
10. Metrics Enqueue (Background)
Where: components/search_proxy/src/metrics.ts
Non-blocking ctx.waitUntil() sends a metric event to SQS:
- Endpoint, status, latency, cache hit, geolocation (from Cloudflare headers)
- Queue:
{env}-EcomMetricsQueue
Logs:
{"message": "request_end", "status": 200, "reqStartMs": ..., "reqEndMs": ...}
Inspect: Check queue depth — see SQS.
What to Look For
| Symptom | Where to Check |
|---|---|
| 401 errors | Auth headers missing or malformed. Tail worker logs. |
| 404 "Not configured" | Settings not in KV. Check Settings Sync flow. |
| Slow searches (>500ms) | Check settings_ms in timing log (DEBUG level unless >250ms). KV cache miss? Check processingTimeMs for Marqo latency. |
| Wrong results | Check query overrides (qcfg_* logs). Check read alias bucketing. Check filter applied. |
| Stale results | KV cache TTL. Check settings exporter ran — see Settings Sync. |
| Empty results | Check filter expression in marqo_search_request log. Check index has documents. |
| Redirect not working | Check SearchRedirectTable in DynamoDB. Check redirect cache. |
| Metrics missing | Check SQS queue health. Check EcomMetricsWorker Lambda — see Lambda. |
Related Docs
- Search Proxy — worker bindings, KV namespaces, worker names
- Cloudflare Workers — how to tail, inspect KV
- DynamoDB — query redirect and settings tables
- Settings Sync — how settings reach KV
- Ecommerce — DDB table schemas