# When does locale-dependent string sort produce different orders across hosts? **Date of Record:** 2026 November 2 **Scope:** Applies to string comparisons and sorts that use locale-dependent collation (ICU default, `strcoll`, database default collation) across hosts that may differ in locale, ICU version, or collation table. Excludes binary/byte-wise comparison and sorts pinned to a named, versioned collation. ## Core Distinction **Predicate.** Two string sorts produce the same order if and only if they use the same collation identifier *and* the same version of the collation table. Locale name alone is insufficient; ICU and glibc produce different orders for the same locale name across versions. **Failure.** A search index is sorted on one host with ICU 70 and queried on another with ICU 72. Names like "O'Brien" and "OConnor" that were adjacent become separated by an entry that moved between versions. Range queries miss rows. Pagination returns inconsistent pages. **Asymmetry.** Each host's sort is correct for its own locale. Only the *relationship* between two correct sorts is broken, and only for characters whose collation changed between versions. ## What Was Observed **Signature.** Pagination or range scans returning different rows on different replicas. Search results ordered differently across regions. Rows appearing in one host's results and not another's for the same range query. **Trace.** A `BETWEEN 'a' AND 'm'` query relies on collation order. Under ICU 70, "æ" sorts after "a"; under ICU 72, it sorts after "z". A row whose name begins with "æ" is included on one replica and excluded on another. No index corruption is present. **Why monitoring stays green.** Each host reports its own sort as consistent with its own state. Cross-host queries see a union that appears complete. ## Condition of Validity Cross-host sort consistency holds only while at least one applies: 1. **Binary collation.** Comparison is byte-wise, so order is a function of the encoding only. 2. **Pinned collation with version.** A named, versioned collation is used and its version is fixed across hosts and upgrades. 3. **Client-side sort.** Sort happens at a single layer after all data is gathered, so only one collation is in play. Condition 1 is the safest for keys used in equality and range filters. ## Decision Boundary - Binary collation on both hosts: identical order. - Versioned pinned collation: identical as long as upgrades are coordinated. - Locale name only, no version: divergence is possible on any library upgrade. - Client-side single-collation sort: identical by construction, at the cost of data movement. ## Operational Effect The defect grows slowly as libraries upgrade on independent schedules, then appears all at once when two hosts are compared. Remediation is binary collation for keys used in filters, or version-pinned collation with coordinated upgrades. The trap is to declare a locale like "en_US" and assume it is stable. It is not; it is a name that maps to a changing implementation. ```python from dataclasses import dataclass class LocaleSortError(RuntimeError): """Raised when string order is not reproducible across hosts.""" @dataclass(frozen=True) class CollationGate: collation: str # "binary", "versioned", "locale-name" pinned_version: str = "" def verify(self) -> bool: if self.collation == "binary": return True if self.collation == "versioned" and self.pinned_version: return True if self.collation == "locale-name": raise LocaleSortError( "locale-name collation without pinned version: order varies across library upgrades" ) raise LocaleSortError(f"collation {self.collation!r} is not reproducible") def dispatch_collation(gate: CollationGate) -> str: try: gate.verify() except LocaleSortError: return "binary" return gate.collation ```