blob: 514f1b64ce0b885fe698c2b96e0d9a8aa0f6a4b3 [file]
.. Licensed to the Apache Software Foundation (ASF) under one
.. or more contributor license agreements. See the NOTICE file
.. distributed with this work for additional information
.. regarding copyright ownership. The ASF licenses this file
.. to you under the Apache License, Version 2.0 (the
.. "License"); you may not use this file except in compliance
.. with the License. You may obtain a copy of the License at
.. http://www.apache.org/licenses/LICENSE-2.0
.. Unless required by applicable law or agreed to in writing,
.. software distributed under the License is distributed on an
.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
.. KIND, either express or implied. See the License for the
.. specific language governing permissions and limitations
.. under the License.
Parquet Metadata Cache
======================
Overview
--------
Paimon C++ can cache serialized Parquet metadata footer bytes for Parquet data files.
The cache is used by ``ParquetReaderBuilder`` before opening the Arrow Parquet
reader. On a cache miss, Paimon C++ loads the Parquet file metadata, serializes
it as a complete metadata footer, and stores those bytes in the public
``Cache`` abstraction. On a cache hit, Paimon C++ parses the cached footer bytes into
``parquet::FileMetaData`` and passes the metadata to the Parquet reader.
The cache stores serialized metadata footer bytes instead of caching a
``parquet::FileMetaData`` instance. This keeps the cache value compact and
similar to manifest cache values: the cache weight follows the actual cached
bytes, while the Parquet library still owns metadata parsing and validation.
This optimization is useful when the same Parquet files are opened repeatedly
in the same process, for example repeated ``get`` or ``scan`` requests over the
same snapshot. On a cache hit, the read path avoids reading the Parquet footer
bytes from the filesystem again. Paimon C++ still parses the cached footer bytes
into ``parquet::FileMetaData`` for each reader open. ColumnIndex and OffsetIndex
bytes also use ``CacheKind::DATA_FILE_FOOTER`` with their actual positions and
lengths. Data pages and column chunks are not stored in this metadata cache.
Configuration
-------------
Parquet metadata caching is disabled by default. Embedding applications that
need it can provide a custom ``Cache`` implementation and inject it through
``ScanContextBuilder`` or ``ReadContextBuilder``. Parquet reader builders
receive the cache from the read context and create cache keys with
``CacheKind::DATA_FILE_FOOTER`` internally.
The cache key represents the file footer and is created from the file URI with
position ``-1`` and length ``-1``. Callers do not need to construct this key
directly; they only need to route ``CacheKind::DATA_FILE_FOOTER`` entries to an
appropriate cache backend.
Example:
.. code-block:: cpp
class RoutingCache : public paimon::Cache {
public:
RoutingCache(std::shared_ptr<paimon::Cache> default_cache,
std::shared_ptr<paimon::Cache> parquet_metadata_cache)
: default_cache_(std::move(default_cache)),
parquet_metadata_cache_(std::move(parquet_metadata_cache)) {}
paimon::Result<std::shared_ptr<paimon::CacheValue>> Get(
const std::shared_ptr<paimon::CacheKey>& key,
std::function<paimon::Result<std::shared_ptr<paimon::CacheValue>>(
const std::shared_ptr<paimon::CacheKey>&)> supplier) override {
return Select(key)->Get(key, std::move(supplier));
}
// Put(), Invalidate(), InvalidateAll(), and Size() route in the same way.
private:
std::shared_ptr<paimon::Cache> Select(
const std::shared_ptr<paimon::CacheKey>& key) const {
return key && key->GetKind() == paimon::CacheKind::DATA_FILE_FOOTER
? parquet_metadata_cache_
: default_cache_;
}
std::shared_ptr<paimon::Cache> default_cache_;
std::shared_ptr<paimon::Cache> parquet_metadata_cache_;
};
auto cache = std::make_shared<RoutingCache>(
std::make_shared<MyDefaultCache>(),
std::make_shared<MyParquetMetadataCache>());
paimon::ScanContextBuilder scan_builder(table_path);
scan_builder.WithCache(cache);
paimon::ReadContextBuilder read_builder(table_path);
read_builder.WithCache(cache);
Passing ``nullptr`` or omitting ``WithCache()`` leaves Parquet metadata caching
disabled. If a file URI cannot be obtained, Paimon C++ also bypasses the cache
and opens the Parquet file normally.
Reader-Local Page Indexes
-------------------------
Set ``parquet.read.enable-offset-index-cache=true`` to reuse parsed OffsetIndex
objects during bitmap trimming, page-range planning and filtered decoding within
one file reader. The option defaults to ``false``; it does not require
``WithCache()``. Enable it only after measuring the CPU/memory trade-off for the
workload, especially when projecting many columns.
These objects are not stored in the caller's shared ``Cache``. They are released
with their owning row-group index reader; the existing limit of 1,024 retained
row-group readers is a count limit, not a parsed-index byte budget. Restricted
predicate index readers remain separate so their column hints do not restrict
later projected-column reads.
Parsed page locations require approximately
``retained row groups x accessed columns x pages per column x sizeof(PageLocation)``
bytes, plus vector/map/object overhead, in addition to serialized index buffers.
For example, with a 24-byte ``PageLocation``, 1,024 retained row groups, 10 accessed
columns and 1,000 pages per column require about 234 MiB for page locations alone.
There is no fixed byte upper bound; memory scales with the file's index sizes.
This is independent of ``ReadAheadCache`` and its per-file ``FileBlockCache``,
which reuse bytes rather than parsed objects. That block cache survives resets
of the prefetch plan, but does not provide reuse across independent file-cache
lifetimes. This optimization introduces no data-cache option or shared data cache.
Filesystem or read-ahead byte caching does not replace this optimization: Arrow
deserializes the cached index bytes again on every ``GetOffsetIndex`` call.
With the option disabled, Paimon retains the existing byte-buffer reuse without
retaining parsed OffsetIndex objects.
Future Optimizations
--------------------
- Add hit, miss, bypass, and eviction metrics for Parquet metadata cache.
- Add single-flight loading for high-concurrency misses on the same Parquet
file.
- Evaluate sharing cached metadata footer bytes with page-index prefetch logic when
those read paths can use the same cache abstraction.