Potential for missing or spurious rows with multikey lookups larger than 64 bytes

5 June 2026
Product Affected Versions Related Issues Fixed In
YSQL v2025.2.0.0 to v2025.2.3.0 #31688 v2025.2.3.1,
Upcoming releases: v2025.2.4.0, v2026.1.0.0

Description

On affected versions, a query that performs a batched lookup of multiple key values on the leading column of an index may return an incorrect result set: rows that exist on disk can be silently omitted, and rows that were deleted (or older versions of updated rows) can silently reappear.

This issue is a highly specific edge case involving memory optimization and pointer tracking during batched multi-key lookups at query time. Triggering this issue requires a particular sequence and combination of key lengths, as described in the details.

Mitigation

Upgrade to a release that contains the fix (see the above table).

If an immediate upgrade is not possible, disable the variable bloom filter optimization by setting the following YB-TServer flags and restarting the YB-TServer processes:

--enable_scan_choices_variable_bloom_filter=false
--disable_last_seen_ht_rollback=true

Disabling these flags avoids the incorrect-results path. Scans that previously benefited from the optimization may perform additional SST reads, but correctness is restored.

Details

The bug was introduced by the variable bloom filter support added in #28439, first released in v2025.2.0.0.

YugabyteDB uses bloom filters to avoid unnecessary SST file reads during key lookups. Each SST file has an associated bloom filter; before reading a file to check whether it contains a target key, RocksDB consults the filter. If the filter reports the key is definitely absent, the file is skipped entirely.

YugabyteDB optimizes these scans by pointing directly to an internal memory buffer rather than repeatedly copying key data by value. The issue manifests as a highly specific edge case under a precise sequence of events:

  1. A query is performing a batched multi-key lookup, in which there are several discrete target keys for the same scan. The common ways a query produces one are:
  2. The scan begins processing a batch of small keys, which safely fit into a fast, default inline memory buffer (under 64 bytes).
  3. A subsequent key within the same query batch exceeds 64 bytes, forcing the database to dynamically reallocate and move the buffer to a larger space in memory.
  4. Because the logical data has not changed, optimization paths intentionally skip refreshing the underlying iterator. However, because the physical buffer was moved, the iterator is left tracking a stale pointer to an old, deallocated memory address.

This memory mismatch changes the bloom filter evaluations for the remainder of the scan, resulting in missing or spurious rows in the query output.

This issue does not cause any direct change to the data on disk: data in the SST files remains intact. However, a DML query affected by this issue may make unintended changes.