Class FooterByteCache
java.lang.Object
org.elasticsearch.xpack.esql.datasources.cache.FooterByteCache
JVM-wide cache for file footer bytes (Parquet footers, ORC postscripts) shared across columnar
format readers. Backed by the Elasticsearch
Cache utility which provides:
- LRU eviction with a configurable byte budget via
CacheBuilder.setMaximumWeight(long) - Thundering-herd protection via
Cache.computeIfAbsent(K, org.elasticsearch.common.cache.CacheLoader<K, V>)— concurrent callers for the same key coalesce into a single load; exactly one thread performs I/O while others wait for the result - Time-based expiration via
CacheBuilder.setExpireAfterAccess(org.elasticsearch.core.TimeValue)— entries expire after 30Ls of inactivity so that file modifications between queries are detected. Within a single query, concurrent splits keep the entry alive by resetting the access timer on every read.
Why a JVM-wide singleton instead of per-query / per-reader?
A per-query cache would avoid cross-query staleness entirely (no need for TTL) and is the cleaner design. However, the current architecture makes it impractical:- A single query fans out into ~N independent split readers (one per thread), each of which
creates its own
ParquetStorageObjectAdapterorOrcStorageObjectAdapter. These adapters share no common parent or query-scoped context — they are created deep in the format reader call chain (FormatReader.read(StorageObject, ...)) with no mechanism to pass shared state between them. - The thundering-herd benefit requires a single cache visible to all concurrent split readers. Without a shared object, each adapter would issue its own tail read — exactly the redundant I/O this cache exists to prevent.
Cache key design
The key is(path, fileLength). Adding lastModified was considered but rejected
because range splits are created via StorageProvider.newObject(path, length) with no
pre-populated modification time — calling lastModified() would trigger an extra HEAD
request per split, defeating the purpose of the cache. The access-based TTL provides bounded
staleness instead.
Cached byte[] entries must be treated as immutable by all callers.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordCache key identifying a file by its storage path and total length. -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final longDefault cache budget across the JVM (8 MiB).static final longDefault max single entry (2 MiB). -
Method Summary
Modifier and TypeMethodDescriptionbyte[]get(FooterByteCache.Key key) Returns cached footer bytes ornullif not present.static FooterByteCacheReturns the JVM-wide singleton instance.byte[]getOrLoad(FooterByteCache.Key key, CacheLoader<FooterByteCache.Key, byte[]> loader) Returns cached footer bytes for the given key, or loads them via the provided loader.voidRemoves all entries.longMaximum byte size for a single cache entry.voidput(FooterByteCache.Key key, byte[] bytes) Stores footer bytes directly, e.g.
-
Field Details
-
DEFAULT_MAX_BYTES
public static final long DEFAULT_MAX_BYTESDefault cache budget across the JVM (8 MiB).- See Also:
-
DEFAULT_MAX_ENTRY_BYTES
public static final long DEFAULT_MAX_ENTRY_BYTESDefault max single entry (2 MiB). Prevents caching unusually large footers.- See Also:
-
-
Method Details
-
getInstance
Returns the JVM-wide singleton instance. -
maxEntryBytes
public long maxEntryBytes()Maximum byte size for a single cache entry. -
invalidateAll
public void invalidateAll()Removes all entries. Intended for test isolation.
-