Cardinality is what makes a metric expensive

A metric with a label is one time series per distinct label value, and a label with unbounded values is an unbounded number of series.

// 4 series. fine.
$counter->inc(['route' => 'checkout', 'status' => '200']);

// one series PER ORDER. 400,000 of them. this kills the server.
$counter->inc(['order_id' => $order->id]);

// the rule: a label value must come from a SMALL, KNOWN set.
//   route, status class, region, version — yes
//   id, email, url with a query string, error message — no

The failure is not gradual: a metrics backend handles ten thousand series comfortably and falls over at a million, and the transition takes about a day once the bad label ships. The url case is the subtle one, because a route template is bounded and a raw path with ids in it is not. Anything genuinely high-cardinality belongs in a log or a trace, which are designed for it.