Repository navigation
Python Maintain a Collection
Robin Kooyman edited this page Nov 10, 2026
·
1 revision
This recipe keeps a collection of NetCDF files in one table. New files arrive each day, and some files get corrections.
- Add the new files with one writer. Give each dataset a
source_fileattribute. - Replace the corrected files: delete their datasets, then add the new datasets.
- Compact the table.
- Free the disk space of old versions, with a margin for readers.
import logging
from datetime import timedelta
from pathlib import Path
import xarray as xr
import lance_nd
URI = "profiles.lance"
def load(path):
with xr.open_dataset(path) as ds:
ds = ds.load()
ds.attrs["source_file"] = path.name
return ds
def quoted(values):
return ", ".join("'" + v.replace("'", "''") + "'" for v in values)
def add(paths):
"""Add the datasets of `paths` in one commit. Skip files that fail."""
with lance_nd.append(URI) as writer:
for path in paths:
try:
writer.write(load(path))
except (OSError, lance_nd.LanceNdError) as error:
logging.warning("skipped %s: %s", path, error)
new_files = sorted(Path("incoming/new").glob("*.nc"))
corrected = sorted(Path("incoming/corrected").glob("*.nc"))
# 1. New files.
add(new_files)
# 2. Corrected files: delete the old datasets, then add the new datasets.
table = lance_nd.open(URI)
if corrected:
table.delete(f"__global_attrs__.source_file IN ({quoted(p.name for p in corrected)})")
add(corrected)
# 3. Compact.
table = lance_nd.open(URI)
table.compact(target_fragment_bytes=512 << 20)
# 4. Free disk space. Keep one day of versions for readers.
table.cleanup_old_versions(older_than=timedelta(days=1))-
One commit for many files.
add()uses one writer for all files of the day, so the table gets one version and full fragments. - Failed files. A file that cannot be opened or that does not fit the table is logged and skipped. The other files are committed.
- Many files at once. For tens of thousands of files, commit each few thousand files: open a new writer for each batch. A failed batch then loses less work.
-
Fast reads with workers. Opening NetCDF files is often slower than
the write. Read the files in a
concurrent.futures.ProcessPoolExecutor, and write the datasets in the main process. -
Stats. Create the table with
stats=True, so that region and value queries can skip datasets. - Margin. A reader that opened an older version fails when its files are removed. One day is enough for most readers.
with lance_nd.create(URI, stats=True, column_defaults={"compression": "zstd"}) as writer:
for path in sorted(Path("archive").glob("*.nc")):
writer.write(load(path))See Create a table for the options.
Concepts
Rust
Python