Skip to content

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.tsapp.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.tsdirect/platform.ts

Two auth methods (checked in order):

  1. Index ID header: x-marqo-index-id: {system_account_id}-{index_name} (read-only operations)
  2. 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

  1. Resolve shop ID from API key (calls admin server) or extract from x-marqo-index-id header
  2. 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:

  1. Normalize query → compute shard hash
  2. Check shard cache (10 min TTL) → KV_QCFG namespace
  3. 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:

  1. Check Cloudflare Cache API (configurable TTL, default 10 min)
  2. On miss: query SearchRedirectTable in DynamoDB (pk={accountId}#INDEX#{indexName}, sk=QUERY#{normalizedQuery})
  3. 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.