Skip to content

Blooms: case-insensitive regex literals prune matching structured metadata #24459

Description

@hundredwatt

Describe the bug

When Bloom Gateway filtering is enabled, a pipeline label filter with a case-insensitive regex literal can return no lines even though the structured-metadata value matches.

{app="a"} | device_id=~"(?i)a66347031de50be5"

The same filter works without blooms, and these variants also work with blooms:

{app="a"} | device_id=~"a66347031de50be5"          # case-sensitive literal
{app="a"} | device_id=~"(?i)a66347031de50be5.*"    # extra wildcard
{app="a"} | device_id=~"(?i)a66347031de50be5$"     # explicit anchor
{app="a"} | device_id=~"^(?i)a66347031de50be5"     # explicit anchor

To Reproduce

  1. Loki with bloom creation and Bloom Gateway filtering enabled for the tenant.
  2. Ingest a log with structured metadata device_id="a66347031de50be5" (lowercase). Wait until a bloom is built for that chunk.
  3. Query:
{app="a"} | device_id=~"(?i)a66347031de50be5"
  1. Observed: 0 lines.
  2. Repeat with | device_id=~"a66347031de50be5" or | device_id=~"(?i)a66347031de50be5.*". Those return the line.

Without blooms, pkg/logql / logqltest already treats (?i)a66347031de50be5 as an equality match (with case folding) and returns both a66347031de50be5 and A66347031DE50BE5.

Expected behavior

| device_id=~"(?i)a66347031de50be5" should return every line whose device_id value equals that hex string, ignoring case. Bloom filtering should not drop a chunk that contains a case-folded match.

Cause

ExtractTestableLabelMatchers in pkg/storage/bloom/v1/ast_extractor.go simplifies (?i)a66347031de50be5 to a KeyValueMatcher whose Value is the regex engine's canonical literal:

case regexsyn.OpLiteral:
    return KeyValueMatcher{
        Key:   key,
        Value: string(reg.Rune),
    }

regexp/syntax with FoldCase stores that literal in uppercase (A66347031DE50BE5). Blooms are built from the stored metadata as-is (a66347031de50be5), so the bloom test misses and the gateway prunes the chunk.

Patterns that do not simplify to a bare OpLiteral (.*, ^, $, character classes) become UnsupportedLabelMatcher, for which bloom tests always pass. That is why adding a wildcard or anchor "fixes" the query.

Confirmed with:

{app="a"} | device_id=~"a66347031de50be5"           → KeyValueMatcher{Key:"device_id", Value:"a66347031de50be5"}
{app="a"} | device_id=~"(?i)a66347031de50be5"       → KeyValueMatcher{Key:"device_id", Value:"A66347031DE50BE5"}
{app="a"} | device_id=~"(?i)a66347031de50be5.*"     → UnsupportedLabelMatcher{}
{app="a"} | device_id=~"(?i)a66347031de50be5$"      → UnsupportedLabelMatcher{}
{app="a"} | device_id=~"^(?i)a66347031de50be5"      → UnsupportedLabelMatcher{}

Workaround

Disable bloom_gateway_enable_filtering for the tenant, or rewrite the filter so the extractor cannot turn it into a case-folded equality ((?i)….*, ^, $).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions