Skip to content

RegexCache and GlobCache grow unbounded for the lifetime of the process #103

Description

@matt-edmondson

What's wrong

TextFilter.cs:84-85 declares two process-lifetime static caches:

private static readonly ConcurrentDictionary<string, Regex> RegexCache = new();
private static readonly ConcurrentDictionary<string, Glob> GlobCache = new();

They're populated in ResolveGlob (lines 376-386) and DoesMatchRegex (lines 409-427), keyed by the raw filter/token text plus a 2-character case-sensitivity prefix (CacheKey). There is no eviction, expiry, size cap, or Clear/TryRemove call anywhere in the file — every distinct pattern ever passed in stays cached for the lifetime of the process.

Concrete failure scenario

The library's own commit history describes its intended use case as a live, keystroke-driven filter box (e.g. commit b249339, "a list that throws mid-keystroke on a pathological pattern"). In that use case, each keystroke of an incremental search — "h", "he", "hel", "hell", "hello", … — produces a distinct cache key that's never evicted. Adding the case-sensitivity feature (CacheKey's "i:"/"s:" prefix) further doubled the key space for the same pattern text. In a long-lived desktop/UI process — this library is consumed by ktsu's ImGui-based tools, which run indefinitely — cache size grows directly with how much text a user has typed into filter boxes over the app's entire lifetime, with no upper bound. This is unbounded memory growth, not bounded by the size of the data being filtered.

Suggested fix

Bound the caches — e.g. a max-entry cap with simple eviction (LRU, or clear-and-restart once a threshold is hit), or a time-based expiry window. Even a generously-sized fixed cap (a few thousand entries) would close this without materially affecting the hot-path hit cost.

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

Metadata

Metadata

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions