The same query, the same index, and a different execution plan depending on which customer it is run for.
-- for a typical customer: 400 orders
-> Index lookup on orders using idx_customer
(actual rows=412 loops=1)
-- for the outlier: 200,000 orders
-> Table scan on orders
(actual rows=2,104,882 loops=1)
-- the optimiser estimates that 200k of 2.1M rows is
-- most of the table and a scan is cheaper than 200k
-- index lookups. it is not wrong.
The optimiser switching strategies at a selectivity threshold is correct behaviour and produces a discontinuity that is invisible in any aggregate measurement. A histogram on customer_id improved the estimate and did not change the decision — the actual fix was a covering index so the scan never happens, and the general lesson is that one query can have two plans.