Learning Platform
Глоссарий Troubleshooting
Урок 17.03 · 40 мин
Продвинутый
F3CMUSIGMODIOUnitEncUnitFlatBuffersColumn SkipDecoupled DictionaryResearch PrototypeFuture-proof

F3 Architecture

F3 (Future-proof File Format) — исследовательский формат, разработанный CMU Database Group. Авторы: Andrew Pavlo, Jignesh Patel, Wes McKinney (создатель Apache Arrow и Pandas), Huanchen Zhang. Опубликован на SIGMOD 2025 (сентябрь 2025) — одной из двух top-tier конференций по базам данных.

DANGER

F3 — исследовательский прототип, а не production-ready формат. GitHub: future-file-format/F3 (Rust). Нет PyPI пакета, нет Docker image, нет production deployments. Ценность для инженера — понимание архитектурных идей, которые могут появиться в будущих форматах (включая Parquet 3.0).

F3 назван «Future-proof» потому что решает фундаментальную проблему: как обновлять кодировки данных без обновления библиотек на всех серверах. Текущие форматы (Parquet, ORC) требуют, чтобы reader знал все кодировки файла. Неизвестная кодировка = ошибка чтения. F3 решает это через embedded WebAssembly decoders (подробнее в уроке 04).

NOTE

F3 является частью broader research программы CMU по форматам хранения: «An Empirical Evaluation of Columnar Storage Formats» (VLDB 2023), «NULLS!» (DaMoN 2024), «Towards Functional Decomposition of Storage Formats» (CIDR 2025). Wes McKinney, один из авторов F3, — создатель Apache Arrow и Pandas.

Три принципа F3

F3 строится на трёх принципах, каждый из которых — ответ на конкретное ограничение Parquet:

Три принципа F3

Interoperability

Interoperability: любой reader с Wasm runtime может прочитать любой F3 файл, даже с custom encodings. Нет зависимости от конкретной библиотеки. Contrast: Nimble (один reader), Parquet (reader должен знать все encodings).
РешениеEmbedded Wasm decoders: каждый файл содержит WebAssembly код для декодирования своих данных. Reader: загрузить Wasm → выполнить в sandbox → получить decoded data. Не нужно знать encoding заранее.

Extensibility

Extensibility: новые кодировки deploy'ятся мгновенно — embed Wasm decoder в файл. Нет необходимости обновлять reader libraries на тысячах серверов. Writer решает, какую кодировку использовать.
РешениеDeploy новой кодировки: скомпилировать Rust/C → Wasm → embed в файлы. Старые readers (с Wasmtime) читают новые файлы без обновлений. Нет coordination overhead между writer и reader.

Efficiency

Efficiency: несмотря на Wasm overhead (10-30% vs native), F3 компенсирует за счёт storage layout improvements: IOUnit tuning, decoupled dictionaries, column-level I/O skip.
РешениеLayout optimizations: IOUnit (I/O unit) size tuned per storage medium. Dictionary scope independent from row group. Column-level metadata skip. Wasm overhead offset by layout gains.

Структура файла F3

F3 вводит два ключевых понятия: IOUnit (единица I/O) и EncUnit (единица кодирования). В Parquet оба привязаны к row group/page — F3 развязывает их:

Структура файла F3
F3 FileF3 файл: header + data sections + footer. Логически организован как row groups (привычная абстракция), но физическая организация — IOUnits, каждый из которых содержит EncUnits. Footer — FlatBuffers metadata.

IOUnit: развязка I/O и логики

Ключевая инновация F3 — IOUnit decoupled from row group. В Parquet размер I/O определяется row group (обычно ~128MB). F3 позволяет writer’у настраивать размер I/O-единицы независимо:

IOUnit: настраиваемый размер I/O

Parquet: I/O = Row Group

Parquet: I/O unit = row group или column chunk. Размер фиксирован writer'ом при создании файла. Обычно ~128MB. Оптимально для HDFS (block size 128MB), но не для NVMe или S3.
ПроблемаNVMe SSD: optimal read unit = 4KB-4MB. 128MB row group = over-read. S3: optimal range request = 16-64MB. Parquet: один размер для всех — compromise. I/O amplification на обеих крайностях.

F3: IOUnit (tunable)

F3: IOUnit = отдельная абстракция. Writer настраивает размер под target storage. Один и тот же logical row group может быть разбит на IOUnits разного размера для разных storage media.
НастройкаWriter API: set_iounit_size(4MB) для NVMe, set_iounit_size(64MB) для S3. Row group logical scope (10K строк) и IOUnit physical size (N MB) — независимые параметры. Один row group → M IOUnits.
TIP

IOUnit decoupling — практически значимая идея. Современные storage системы: NVMe SSD (4KB pages, 4MB optimal reads), cloud object storage (S3 с optimal 8-64MB GET requests), persistent memory (64B cache lines). Один формат файла должен работать эффективно на всех. Parquet’s fixed row group = compromise. F3’s tunable IOUnit = per-medium optimization.

EncUnit: минимальная единица кодирования

Внутри IOUnit данные организованы в EncUnits — минимальные единицы кодирования/декодирования:

EncUnit — минимальная единица декодирования

IOUnit

IOUnit содержит EncUnits для одной или нескольких колонок. Каждый EncUnit — самодостаточная единица: содержит encoded данные + ссылку на Wasm decoder. Можно декодировать один EncUnit без остальных.
EncUnit: col_A, rows 0-999EncUnit для колонки A, первые 1000 строк. Содержит: encoded payload + encoding descriptor (ссылка на Wasm module ID). Decoder: загрузить Wasm module → вызвать decode(payload) → получить decoded values.
EncUnit: col_B, rows 0-999EncUnit для колонки B, те же строки. Другая кодировка (может быть). Другой Wasm decoder (если кодировка отличается). Каждый EncUnit — независимый. Можно декодировать col_A без col_B.
EncUnit: col_C, rows 0-999EncUnit для колонки C. Может использовать ту же кодировку (и тот же Wasm module), что и col_A. Wasm modules дедуплицируются: один module → ссылки из множества EncUnits.

В Parquet аналог EncUnit — page (data page внутри column chunk). Но page в Parquet привязана к column chunk, который привязан к row group. F3 развязывает эту иерархию:

Parquet Page vs F3 EncUnit

Decoupled Dictionary Scope

В Parquet dictionary привязан к column chunk (= row group). Это создаёт проблему для высококардинальных колонок:

Dictionary Scope: Parquet vs F3

Parquet: Dict = Row Group scope

Parquet: dictionary = один на column chunk (= row group). Если row group = 1M строк, dictionary содержит все уникальные значения для 1M строк. High cardinality (100K уникальных) → dictionary > value column → fallback на PLAIN.
ПроблемаRow group 1M строк, колонка city (50K уникальных городов). Dictionary: 50K entries × avg 20 bytes = 1MB. Если row group ещё больше → dictionary растёт → в какой-то момент Parquet падает на PLAIN encoding. Нет возможности сужить scope.

F3: Dict = Configurable scope

F3: dictionary scope = configurable. Writer выбирает: per-EncUnit (узкий), per-IOUnit, per-row-group, per-file (широкий). Оптимальный scope зависит от кардинальности и query patterns.
ГибкостьLow cardinality (status: 5 values): file-wide dictionary — 5 entries, max reuse. Medium cardinality (city: 50K): per-IOUnit dictionary — limited size, reasonable compression. High cardinality (email): per-EncUnit dictionary — tiny scope, still usable.

FlatBuffer Metadata с Column-Level I/O Skip

F3 использует FlatBuffers (как Nimble и Vortex) для метаданных с дополнительной оптимизацией — column-level I/O skip:

Column-Level I/O Skip в F3
Query: 3 columns из 1000Запрос: SELECT col_A, col_C FROM table WHERE col_B > 100. Таблица: 1000 колонок. Нужно: metadata для col_A, col_B, col_C. Не нужно: metadata для остальных 997 колонок.
F3 Footer (FlatBuffers)FlatBuffers footer F3 организован так, что metadata каждой колонки — по отдельному offset. Reader может прыгнуть к metadata col_A → прочитать offset её EncUnits → прыгнуть к col_B → прочитать → прыгнуть к col_C. 997 колонок = zero bytes read.
Optimized I/O PlanИз metadata 3 колонок: reader знает exact offsets и sizes нужных EncUnits. Строит I/O plan: coalesced reads только для нужных EncUnits. 997 колонок × N IOUnits = zero I/O.
NOTE

Column-level I/O skip — не уникальная идея F3. Parquet тоже может пропускать колонки (column projection). Разница в metadata efficiency: Parquet footer содержит Thrift для ВСЕХ колонок (нужно десериализовать всё), F3 footer — FlatBuffers с per-column offsets (читаем только нужные). При 1000+ колонок разница ощутима.

General-Purpose Decoding API

F3 определяет стандартный API, который должен реализовать каждый Wasm decoder:

F3 Decoding API
Wasm Decoder InterfaceКаждый Wasm module реализует стандартный интерфейс: decode(encoded_bytes) → decoded_bytes. Плюс metadata functions: output_type(), output_size(input_size), name(). Reader знает, как вызвать любой decoder — стандартный ABI.
Композиция decoder'овEncUnit может использовать цепочку decoder'ов: ZSTD → Delta → Varint. F3 metadata описывает pipeline: Wasm module IDs в порядке применения. Reader применяет decoder'ы последовательно: decode1(decode2(decode3(raw_bytes))).

Сравнение с Parquet Page Structure

Иерархия данных: Parquet vs F3

Parquet Hierarchy

Parquet: жёсткая иерархия. File → Row Groups → Column Chunks → Pages. Каждый уровень привязан к предыдущему. Row group определяет scope dictionary, column chunk определяет scope pages.
Linked HierarchyRow Group (logical + physical) → Column Chunk (per column, per row group, dictionary scope) → Data Page (encoding unit, ~64KB). Все уровни связаны. Нельзя изменить dictionary scope без изменения row group.

F3 Hierarchy

F3: развязанная иерархия. Logical Row Group (строки), IOUnit (bytes, physical), EncUnit (encoding). Три независимые размерности: сколько строк, сколько bytes per I/O, какой encoding scope.
Decoupled HierarchyRow Group (logical only — количество строк). IOUnit (physical only — количество bytes, tunable per storage). EncUnit (encoding only — minimal decode unit). Dictionary scope: independent (per EncUnit/IOUnit/RG/file).

Развязка позволяет оптимизировать каждую размерность независимо:

Независимые размерности F3

Wasm Decoder Registry

Footer F3 файла содержит registry embedded Wasm decoders:

Wasm Decoder Registry в Footer
F3 FooterFooter F3 содержит: schema, row group descriptors, column metadata, и Wasm decoder registry. Registry — массив Wasm modules, каждый с ID. EncUnit descriptor ссылается на ID decoder'а.
TIP

Wasm modules дедуплицируются: один файл с 100 integer колонками ссылается на один delta-varint decoder (5KB), а не 100 копий. Registry overhead: количество уникальных encodings × средний module size. Для типичного файла (3-5 encodings): 15-100KB — меньше одного Parquet page.

Полный Read Path

Как F3 reader обрабатывает запрос:

F3 Read Path
Query: SELECT col_A, col_C WHERE col_B > 100Запрос: SELECT col_A, col_C WHERE col_B > 100. F3 reader начинает с footer.
Step 1: Read FooterSeek to end → read footer size → read FlatBuffers footer. Загрузить Wasm decoder registry в Wasmtime runtime. Pre-compile Wasm modules (one-time cost per file open).
Step 2: Column SkipFlatBuffers: jump to col_A metadata, col_B metadata, col_C metadata. Прочитать statistics (min/max) для col_B → skip IOUnits where max(col_B) <= 100. Определить нужные EncUnits.
Step 3: Read EncUnitsCoalesced reads: собрать adjacent EncUnits в batch I/O requests. Read нужные EncUnits с диска. Каждый EncUnit: encoded payload + encoding descriptor (Wasm module ID).
Step 4: Wasm DecodeДля каждого EncUnit: lookup Wasm module по ID → call decode(payload) → получить decoded values. Wasm execution в sandbox (memory isolation). Pipeline decoders если несколько уровней кодирования.

Decoded Columns → Apply Filter → Result

Decoded column data: col_A values, col_B values (для filter), col_C values. Стандартный columnar output — Arrow-compatible. Filter: apply col_B > 100 на decoded values.

Сравнение с форматами курса

F3 в контексте нового поколения

Итоги

F3 — это архитектурный blueprint, а не production формат. Ключевые инновации:

  1. Decoupled hierarchy. Три независимые размерности: logical (row group), physical (IOUnit), encoding (EncUnit). В Parquet все три связаны через row group.

  2. Tunable IOUnit. Writer настраивает I/O unit size под storage medium: 4MB для NVMe, 64MB для S3. Нет компромиссов.

  3. Decoupled dictionary scope. Dictionary scope — независимый параметр, не привязан к row group. Оптимальный выбор per column cardinality.

  4. FlatBuffer metadata с column-level skip. Reader deseriализует metadata только нужных колонок — O(K) вместо O(N) для K из N колонок.

  5. Embedded Wasm decoders. Каждый файл самодостаточен — содержит decoder’ы для своих данных. Подробнее в следующем уроке.

Следующий урок — F3 Wasm-декодеры: как именно работает embedded Wasm, какой overhead, и как это решает проблему fleet-wide encoding deployment.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 4. F3 разделяет IOUnit (physical I/O) и Row Group (logical scope). Data engineer пишет F3 файл, который будет храниться на NVMe SSD (optimal read: 4MB) и реплицирован на S3 (optimal GET: 64MB). Как он настроит IOUnit для каждого?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5