Skip to content

canvod-airflow

Airflow DAG definitions for canvodpy GNSS-T pipelines.

canvod-airflow wraps canvodpy's stateless task functions (canvodpy.workflows.tasks) into Airflow TaskFlow DAGs, generated dynamically — one set per site configured in canvodpy's sites.yaml. No pipeline logic lives in this package: every task is a thin call into canvodpy.workflows.tasks, which stays in the core canvodpy package and has no Airflow dependency of its own.

DAGs

graph TD
    subgraph sbf["canvod_{site}_sbf — @daily"]
        A1["validate_dirs"] --> A2["check_sbf"] --> A3["process_sbf"]
        A3 --> A4["validate_ingest"] --> A5["calculate_vod"] --> A6["cleanup"]
    end
graph TD
    subgraph rinex["canvod_{site}_rinex — @daily"]
        B1["validate_dirs"] --> B2["wait_for_rinex"]
        B1 --> B3["wait_for_sp3"] --> B4["fetch_aux_data"]
        B2 --> B5["process_rinex"]
        B4 --> B5
        B5 --> B6["validate_ingest"] --> B7["calculate_vod"] --> B8["cleanup"]
    end
graph TD
    subgraph agency["canvod_{site}_sbf_agency — @daily"]
        C1["validate_dirs"] --> C2["check_sbf"]
        C1 --> C3["wait_for_sp3"] --> C4["fetch_aux_data"]
        C2 --> C5["process_sbf"]
        C4 --> C5
        C5 --> C6["validate_ingest"] --> C7["calculate_vod"] --> C8["cleanup"]
    end
DAG Ephemeris Data availability Chain
canvod_{site}_sbf Broadcast (embedded in SBF) Same-day validate_dirs → check_sbf → process_sbf → validate_ingest → calculate_vod → cleanup
canvod_{site}_rinex Agency SP3/CLK (final products) ~12-18 day lag validate_dirs → {wait_for_rinex, wait_for_sp3} → fetch_aux_data → process_rinex → validate_ingest → calculate_vod → cleanup
canvod_{site}_sbf_agency Agency SP3/CLK, SBF observables ~12-18 day lag validate_dirs → {check_sbf, wait_for_sp3} → fetch_aux_data → process_sbf → validate_ingest → calculate_vod → cleanup
canvod_backfill Whichever branch is selected at trigger time Manual resolve_dates → process_day (dynamically mapped, one task per date in the range)

Choose sbf for the fastest same-day results at broadcast-ephemeris precision, rinex/sbf_agency for the highest geometric quality once agency orbit/clock products are published.

Retry-driven scheduling

wait_for_rinex and wait_for_sp3 are sensors (mode="reschedule", 6-hour poke interval, 21-day timeout) — they wait for RINEX files and SP3/CLK products to become available rather than failing outright. This handles the natural lag between same-day SBF data and delayed agency products without any manual re-triggering.

Concurrency safety

Icechunk stores require serialized commits per branch. Every task that writes to the store (calculate_vod, process_sbf, process_rinex, and backfill's process_day) uses a shared canvod_store_write pool with one slot, serializing writes across all DAGs and backfill runs simultaneously. Create the pool once:

airflow pools set canvod_store_write 1 "Serialise Icechunk commits"

Backfill additionally sets max_active_tis_per_dagrun=1 on its dynamically mapped process_day task, so dates within a single backfill run process one at a time.

Manual backfill

canvod_backfill reprocesses a historical date range for one site and branch. Each date becomes an independent mapped task instance — a failure on one date retries only that date, the rest of the batch continues.

airflow dags trigger canvod_backfill --conf '{
    "site": "Rosalia",
    "branch": "sbf",
    "start_date": "2025-001",
    "end_date": "2025-010"
}'

branch selects which ephemeris/reader combination to use for the range: sbf (broadcast, same-day), rinex (agency SP3/CLK), or sbf_agency (SBF observables + agency geometry).

Task functions

All tasks call into canvodpy.workflows.tasks, which accepts only primitives (str, dict, list, None) and returns JSON-serializable dicts suitable for Airflow XCom:

from canvodpy.workflows.tasks import (
    validate_data_dirs,
    check_sbf,
    check_rinex,
    check_sp3_availability,
    fetch_aux_data,
    process_sbf,
    process_rinex,
    validate_ingest,
    calculate_vod,
    cleanup,
)

These functions have no Airflow dependency of their own — use them directly for scripting or debugging without running Airflow at all.

Installation

uv add "canvod-airflow[airflow] @ git+https://github.com/nfb2021/canvodpy-extensions.git@v0.1.0#subdirectory=packages/canvod-airflow"

apache-airflow is an optional extra, not a hard dependency — install it alongside whatever Airflow version your deployment environment provides.

Deployment

Point your Airflow dags_folder at a shim module that imports both DAG modules (the words "airflow" and "dag" must appear in the shim file for Airflow's DagBag keyword pre-filter):

# <dags_folder>/canvod_dags.py
from canvod.airflow.daily_processing import *  # noqa: F403  (airflow dag)
from canvod.airflow.backfill import *  # noqa: F403

Ensure canvodpy's config (canvod-settings.yaml) is accessible from the Airflow worker — DAGs are generated per configured site at parse time via canvod.config.load_config().

See the API Reference for the full public API.