Private per-customer similarity search over an API, where each tenant has private data, the criteria are user-defined, and the system has to stay fast and cheap over ordinary HTTP and JSON. Cross-tenant leakage is a security failure rather than a bug, and per-tenant isolation done naively multiplies cost until it hits a hard ceiling while the business is still small.
The stated ask is a comparison engine. The real problem is the layer under it: keeping each customer's data walled off, keeping the run cost below what the feature earns, and surviving the front-end integration limits. One of those limits was concrete and non-negotiable: a front end that auto-retries any API call past 150 seconds, which forces idempotency on every endpoint whether or not anyone planned for it.
The reflex, and the fix.
A separate database per tenant
It is the intuitive way to guarantee isolation, and it is easy to explain to a security reviewer.
It multiplies cost per tenant and hits a ceiling early, so the architecture that felt safest becomes the reason the product cannot grow.
One shared store with a filter enforced below the app layer
Enforcing the per-tenant filter on every read beneath the application, plus a rerank stage so user-defined criteria never force a re-embed.
Hard isolation, run cost held to a few dollars per tenant per month, and criteria that can change without recomputing anything.
Isolate below the application layer and put the variable criteria in rerank, because both costs compound with tenants.
Embed the catalog once
Precompute rather than embedding per query. This is cents to dollars, not the cost most people picture when they imagine the bill for a similarity feature.
Retrieve, then rerank
User-defined criteria live in the rerank stage, so a customer changing what they care about never forces a re-embed of the catalog.
Isolation enforced below the app layer
One shared store with a per-tenant filter applied on every read, beneath the application, so an application bug cannot produce a cross-tenant read.
Feedback without a model per tenant
A global base ranker plus per-tenant feature weighting, with graceful fallback until a tenant accumulates enough signal to be worth weighting.
Idempotent async endpoints
Because the front end auto-retries past 150 seconds, a non-idempotent endpoint would double-fire under ordinary load. Idempotency is a requirement here, not a refinement.
A single headline number hides where a system fails. This work was scored on the dimensions that actually decide whether it holds in production, measured on real, held-out cases rather than the demo path.
A per-tenant database that satisfies the security conversation, costs a multiple of what the feature earns, and hits a ceiling while the customer count is still small.
Private per-customer similarity over an API with precomputed catalog embeddings, a configurable rerank stage for user-defined criteria, hard per-tenant isolation enforced below the app layer, and idempotent endpoints. Shipped with zero cross-tenant leaks under adversarial testing and run cost held to a few dollars per tenant per month.
What it owns, and what it hands to a person.
Isolation is enforced at the read path, which is the right place, and that makes the read path a component with no acceptable failure rate. The per-tenant weighting needs accumulated signal, so new tenants run on the global ranker until they have earned their own weights.
When a feature is priced per customer, the architecture is a cost decision before it is a capability decision. Put the expensive work where it happens once, put the variable work where it can change for free, and enforce the security boundary below the layer most likely to have a bug.