Maintain Parquet Datasets¶
This guide covers physical dataset maintenance: compaction, deduplication,
repartitioning, and optimization. Since 0.25.0 these operations run through a
coordinator that produces an immutable, typed plan and executes it with
explicit safety guarantees, returning a typed MaintenanceResult.
Two workflows are available on every fsspec filesystem (registered when you
import fsspeckit):
- One-call - plan and execute in a single step
(
fs.compact_parquet_dataset(...)). - Plan-then-execute - create a plan, inspect it, then execute
(
fs.plan_parquet_compaction(...)+fs.execute_maintenance_plan(plan)). Planning never mutates the dataset; it replaces the removeddry_run=Truemode of the pre-0.25 helpers.
Migrating from the dictionary-returning helpers? See Coordinator-backed Maintenance.
Maintenance uses the PyArrow backend by default and requires no extra beyond the base install (PyArrow is a core dependency).
Compact small files¶
Fragmented datasets (many small parquet files) slow down reads. Compaction combines them:
max_rows_per_file is a hard upper bound on output rows per file;
target_mb_per_file is advisory. Output defaults to Snappy compression unless
you pass compression=.
Plan first, then execute¶
Planning is lock-free and touches nothing. The plan captures an immutable snapshot of the source dataset, so you can review exactly what will happen:
Use this workflow in production pipelines: log or persist the plan, require approval, then execute.
Choose the right operation¶
| Goal | One-call method | Planning method |
|---|---|---|
| Combine small files | compact_parquet_dataset |
plan_parquet_compaction |
| Remove duplicate rows, keep partitions | deduplicate_parquet_dataset |
plan_parquet_partition_local_deduplication |
| Remove duplicates and change partitioning | deduplicate_and_repartition_parquet_dataset |
plan_parquet_global_repartition_deduplication |
| Deduplicate + compact in one pass | optimize_parquet_dataset |
plan_parquet_optimization |
Partition-local deduplication is the default: it only rewrites files within
each physical partition, never moving rows across partition boundaries.
Global repartitioning is deliberately explicit - it rewrites the whole dataset
into a new partition layout, which is why it requires partition_columns.
Deduplicate by key¶
Key semantics:
- One row survives per key combination. With
dedup_order_by, rows are ordered ascending and the first row per key wins (ties fall back to physical order). Without it, physical order(partition path, file path, row offset)decides. - Omit
key_columnsto remove rows that are identical across all columns (exact duplicates). - Null and
NaNkey components compare equal; strings compare by exact stored value. - The legacy
-columndescending prefix from the pre-0.25 helpers is not supported. Ordering is always ascending and the first row per key wins, so to keep the latest record, ensure it sorts first (for example with an inverted ordering column), or omitdedup_order_byand rely on ingest order placing the preferred row first.
Repartition globally¶
This reads the entire dataset, deduplicates globally, and writes it out under the given hive-style partition columns. Expect a full rewrite.
Optimize (dedup + compaction)¶
optimize_parquet_dataset runs optional key-based deduplication followed by
compaction in one coordinated pass:
Pass no deduplicate_key_columns to get pure compaction with the same
planning and validation machinery.
Scope work with a partition filter¶
All planning and one-call methods accept partition_filter, a list of
partition-path prefixes. Only matching files are considered:
Understand the safety guarantees¶
The coordinator classifies every plan into a guarantee level, recorded on both
plan.guarantee_level and result.guarantee_level:
atomic_local(native local/POSIX filesystems): bounded advisory locks for cooperating fsspeckit readers/writers, staged writes in a sibling workspace, validation, atomic rename publication, and rollback before publication. Concurrent readers see either the old or the new dataset.best_effort_object_store(S3, GCS, Azure, and every other fsspec filesystem): no distributed lock, no atomic visibility, no automatic rollback. The coordinator writes and validates the full staged rewrite, copies outputs to their live keys, revalidates them, then revalidates every source object before deleting anything. If a source drifted mid-operation, it deletes no inputs and preserves staging and partial outputs as recovery artifacts on the result.
Practical guidance: on object stores, run maintenance against quiescent
datasets (no concurrent writers), and check result.succeeded plus
result.recovery before assuming the operation published.
Interpret the result¶
Key fields:
succeeded/error- overall outcome and failure summary.actual_metrics- published output row/file/byte counts (only on success;Noneotherwise).phase_outcomes- ordered per-phase detail (stage, write, validate, lock, drift_check, publish, cleanup).validation/publication- staged-output validation and atomic-rename outcomes.recovery- workspace and backup locations retained after a failure.plan- the executed plan, including the source snapshot (use it for before/after comparisons).
Use the coordinator directly¶
The filesystem facade always uses the PyArrow backend. To pin DuckDB (or hold a coordinator across operations), construct one yourself:
The planning methods mirror the facade:
plan_compaction, plan_partition_local_deduplication,
plan_global_repartition_deduplication, plan_coordinated_optimization.
Working examples¶
Runnable, validated examples live in the repository:
examples/datasets/getting_started/06_pyarrow_maintenance.py- stats, compaction planning, execution, and one-call optimization.examples/maintenance/dataset_deduplication_example.py- key-based dedup, exact-duplicate removal, optimization, and multi-column keys.
Related documentation¶
- Coordinator-backed Maintenance migration - replacement table for the removed dictionary-returning helpers.
- Dataset Handlers - the read/write/merge interface (maintenance is no longer part of it).
- Optimize Performance - caching, parallel reads, and type optimization.
- Generated API: fsspeckit.core.maintenance - full plan/result type reference.