netra-validator 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- netra_validator-0.1.0/.github/workflows/ci.yaml +47 -0
- netra_validator-0.1.0/.gitignore +34 -0
- netra_validator-0.1.0/LICENSE +21 -0
- netra_validator-0.1.0/PKG-INFO +334 -0
- netra_validator-0.1.0/README.md +312 -0
- netra_validator-0.1.0/benchmarks/README.md +94 -0
- netra_validator-0.1.0/benchmarks/__init__.py +0 -0
- netra_validator-0.1.0/benchmarks/configs/lofar_telemetry_odcs.yaml +173 -0
- netra_validator-0.1.0/benchmarks/generators/base.py +74 -0
- netra_validator-0.1.0/benchmarks/generators/lofar_telemetry.py +274 -0
- netra_validator-0.1.0/benchmarks/harness/collector.py +146 -0
- netra_validator-0.1.0/benchmarks/harness/system.py +70 -0
- netra_validator-0.1.0/benchmarks/results/ec2/lofar_1B.json +121 -0
- netra_validator-0.1.0/benchmarks/results/ec2/lofar_1B_spill.json +87 -0
- netra_validator-0.1.0/benchmarks/results/ec2/lofar_2B_spill.json +87 -0
- netra_validator-0.1.0/benchmarks/results/ec2/lofar_3B_spill.json +87 -0
- netra_validator-0.1.0/benchmarks/results/latest.json +121 -0
- netra_validator-0.1.0/benchmarks/results/local/lofar_100M.json +121 -0
- netra_validator-0.1.0/benchmarks/results/local/lofar_10M.json +121 -0
- netra_validator-0.1.0/benchmarks/results/local/lofar_1B_spill.json +87 -0
- netra_validator-0.1.0/benchmarks/results/local/lofar_200M.json +121 -0
- netra_validator-0.1.0/benchmarks/results/local/lofar_500M.json +121 -0
- netra_validator-0.1.0/benchmarks/results/local/lofar_500M_spill.json +87 -0
- netra_validator-0.1.0/benchmarks/results/local/lofar_50M.json +121 -0
- netra_validator-0.1.0/benchmarks/results/local/lofar_800M_spill.json +87 -0
- netra_validator-0.1.0/benchmarks/run.py +350 -0
- netra_validator-0.1.0/benchmarks/targets/base.py +70 -0
- netra_validator-0.1.0/benchmarks/targets/netra.py +148 -0
- netra_validator-0.1.0/docs/assets/cli_showcase.png +0 -0
- netra_validator-0.1.0/docs/assets/error_state.png +0 -0
- netra_validator-0.1.0/docs/assets/risky_execution_state.png +0 -0
- netra_validator-0.1.0/docs/assets/warning_prompt.png +0 -0
- netra_validator-0.1.0/pyproject.toml +94 -0
- netra_validator-0.1.0/src/netra_validator/__init__.py +13 -0
- netra_validator-0.1.0/src/netra_validator/cli/__init.py__ +0 -0
- netra_validator-0.1.0/src/netra_validator/cli/auditor.py +201 -0
- netra_validator-0.1.0/src/netra_validator/cli/components.py +932 -0
- netra_validator-0.1.0/src/netra_validator/cli/console.py +197 -0
- netra_validator-0.1.0/src/netra_validator/cli/main.py +283 -0
- netra_validator-0.1.0/src/netra_validator/cli/models.py +33 -0
- netra_validator-0.1.0/src/netra_validator/cli/theme.py +16 -0
- netra_validator-0.1.0/src/netra_validator/compiler/__init.py__ +0 -0
- netra_validator-0.1.0/src/netra_validator/compiler/duckdb_builder.py +112 -0
- netra_validator-0.1.0/src/netra_validator/compiler/polars_builder.py +81 -0
- netra_validator-0.1.0/src/netra_validator/config.py +111 -0
- netra_validator-0.1.0/src/netra_validator/engine/__init.py__ +0 -0
- netra_validator-0.1.0/src/netra_validator/engine/duckdb_runner.py +307 -0
- netra_validator-0.1.0/src/netra_validator/engine/polars_runner.py +402 -0
- netra_validator-0.1.0/src/netra_validator/engine/validator.py +363 -0
- netra_validator-0.1.0/src/netra_validator/exceptions.py +89 -0
- netra_validator-0.1.0/src/netra_validator/parser/__init.py__ +0 -0
- netra_validator-0.1.0/src/netra_validator/parser/models.py +221 -0
- netra_validator-0.1.0/src/netra_validator/parser/reader.py +81 -0
- netra_validator-0.1.0/src/netra_validator/report/__init.py__ +0 -0
- netra_validator-0.1.0/src/netra_validator/report/generator.py +205 -0
- netra_validator-0.1.0/src/netra_validator/types.py +101 -0
- netra_validator-0.1.0/tests/__init__.py +0 -0
- netra_validator-0.1.0/tests/conftest.py +238 -0
- netra_validator-0.1.0/tests/integration/test_duckdb_runner.py +248 -0
- netra_validator-0.1.0/tests/integration/test_polars_runner.py +250 -0
- netra_validator-0.1.0/tests/integration/test_validator_e2e.py +311 -0
- netra_validator-0.1.0/tests/unit/__init__.py +0 -0
- netra_validator-0.1.0/tests/unit/test_compiler.py +140 -0
- netra_validator-0.1.0/tests/unit/test_config.py +152 -0
- netra_validator-0.1.0/tests/unit/test_parser.py +145 -0
- netra_validator-0.1.0/tests/unit/test_report.py +205 -0
- netra_validator-0.1.0/uv.lock +1352 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
# 1. Triggers
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
|
|
10
|
+
# 2. Concurrency
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
quality-gate:
|
|
17
|
+
name: Code Quality & Tests
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout code
|
|
22
|
+
uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v5
|
|
26
|
+
with:
|
|
27
|
+
enable-cache: true
|
|
28
|
+
cache-dependency-glob: "uv.lock"
|
|
29
|
+
|
|
30
|
+
- name: Set up Python 3.10
|
|
31
|
+
run: uv python install 3.10
|
|
32
|
+
|
|
33
|
+
- name: Install dependencies
|
|
34
|
+
run: uv sync --all-extras --dev
|
|
35
|
+
|
|
36
|
+
# E. Scoped Quality Checks
|
|
37
|
+
- name: Check Formatting
|
|
38
|
+
run: uv run ruff format --check src/ tests/
|
|
39
|
+
|
|
40
|
+
- name: Lint Code
|
|
41
|
+
run: uv run ruff check src/ tests/
|
|
42
|
+
|
|
43
|
+
- name: Static Type Check
|
|
44
|
+
run: uv run mypy src/ tests/
|
|
45
|
+
|
|
46
|
+
- name: Run Tests
|
|
47
|
+
run: uv run pytest -v
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.so
|
|
5
|
+
.venv/
|
|
6
|
+
.env
|
|
7
|
+
|
|
8
|
+
# Distribution
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
|
|
13
|
+
# Tools & Caches
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.hypothesis/
|
|
18
|
+
.coverage
|
|
19
|
+
htmlcov/
|
|
20
|
+
|
|
21
|
+
# Generated data
|
|
22
|
+
benchmarks/data/*
|
|
23
|
+
tests/data/*
|
|
24
|
+
|
|
25
|
+
# Editors & IDEs
|
|
26
|
+
.zed/
|
|
27
|
+
.idea/
|
|
28
|
+
.vscode/
|
|
29
|
+
*.swp
|
|
30
|
+
.DS_Store
|
|
31
|
+
|
|
32
|
+
# User specific source setups
|
|
33
|
+
# You can put anything here that needs to be done to prepare your env.
|
|
34
|
+
.user_env.*
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nitin Paul
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: netra-validator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: High-performance out-of-core data validation tool built with Polars and DuckDB.
|
|
5
|
+
Project-URL: Homepage, https://github.com/netrabase/netra-validator
|
|
6
|
+
Project-URL: Repository, https://github.com/netrabase/netra-validator
|
|
7
|
+
Project-URL: Issues, https://github.com/netrabase/netra-validator/issues
|
|
8
|
+
Author-email: Nitin Paul <nitinpaul.dev@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Requires-Dist: duckdb>=1.5.4
|
|
13
|
+
Requires-Dist: netra-core>=0.1.0
|
|
14
|
+
Requires-Dist: polars>=1.0.0
|
|
15
|
+
Requires-Dist: psutil>=7.2.2; sys_platform == 'win32'
|
|
16
|
+
Requires-Dist: pyarrow>=14.0.0
|
|
17
|
+
Requires-Dist: pydantic>=2.7.0
|
|
18
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
19
|
+
Requires-Dist: rich>=14.3.1
|
|
20
|
+
Requires-Dist: typer>=0.24.0
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# Netra Validator
|
|
24
|
+
|
|
25
|
+
[](https://pypi.org/project/netra-validator/)
|
|
26
|
+
[](https://github.com/netrabase/netra-validator/actions)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### High-performance data validator built with Polars and DuckDB
|
|
30
|
+
|
|
31
|
+
Netra Validator is a high-performance, out-of-core data validation engine and pipeline firewall built with [Polars](https://github.com/pola-rs/polars) and [DuckDB](https://github.com/duckdb/duckdb). It validates datasets against **Open Data Contract Standard (ODCS v3.1.0)** YAML contracts (authored manually or auto-generated via [Netra Profiler](https://github.com/netrabase/netra-profiler)), separates clean and quarantined records, and produces a strictly typed audit report.
|
|
32
|
+
|
|
33
|
+
Traditional validation frameworks either load entire datasets into memory (crashing on large files) or require distributed clusters like Apache Spark. Netra Validator maximizes your [Data Quality Envelope](#data-quality-envelope) by combining Polars' multi-threaded streaming engine with DuckDB's out-of-core SQL capability, allowing you to validate large-scale datasets (e.g., 16.5 GB Parquet, 12 columns, 1 billion rows) locally on a single node such as a 32 GB laptop (see [Performance Benchmarks](#performance-benchmarks) below).
|
|
34
|
+
|
|
35
|
+
## Data Quality Envelope
|
|
36
|
+
|
|
37
|
+
The **Data Quality Envelope** is the maximum size and complexity of data your pipeline can profile, validate and quarantine within your hardware limits and compute budget.
|
|
38
|
+
|
|
39
|
+
As data volumes grow, teams are typically forced to either water down their quality checks (relying on rough samples or skipping expensive operations) or migrate to costly cloud instances or spin up multi-node distributed clusters just to run routine data quality checks. Netra Validator is designed to be a **value multiplier** for your existing hardware, allowing you to:
|
|
40
|
+
|
|
41
|
+
- **Stay Local Longer:** Run comprehensive quality checks on larger-than-memory datasets directly on your laptop or on-premise workstation without migrating to an HPC or cloud platform.
|
|
42
|
+
- **Scale Vertically, Not Horizontally:** Fully saturate the CPU cores and RAM of a single compute node, eliminating the operational and network shuffle overhead of distributed frameworks.
|
|
43
|
+
- **Control Compute & Energy Costs:** Avoid public cloud elasticity markups and reduce cluster energy consumption for predictable, recurring data health checks.
|
|
44
|
+
|
|
45
|
+
## Performance Benchmarks
|
|
46
|
+
|
|
47
|
+
> *Note: All scripts used to generate the synthetic datasets, run the benchmarks, and collect the results can be found in the [`benchmarks/`](https://github.com/netrabase/netra-validator/tree/main/benchmarks) directory.*
|
|
48
|
+
|
|
49
|
+
### Methodology
|
|
50
|
+
|
|
51
|
+
To evaluate Netra Validator under realistic scientific and industrial workloads, we designed a synthetic **LOFAR Radio Telescope Station Telemetry** dataset based on the system architecture and antenna anomaly taxonomy described in [*"Real-time station monitor and stationtest pipelines for LOFAR 2.0, Wang et al. (2025)"*](https://arxiv.org/abs/2503.02425):
|
|
52
|
+
|
|
53
|
+
- **Schema (12 Columns):** Models station receiver unit (RCU) telemetry combining 64-bit timestamps (`timestamp_utc`), categorical identifiers and state flags (`station_id`, `antenna_type`, `hardware_status`, `gps_sync_status`, `firmware_version`), 64-bit sensor floats (`frequency_mhz`, `signal_power_db`, `ambient_temp_c`, `packet_drop_rate`), 32-bit integers (`rcu_id`), and boolean flags (`rfi_flag`).
|
|
54
|
+
- **Contract Complexity:** Every benchmark run executes the [`lofar_telemetry_odcs.yaml`](https://github.com/netrabase/netra-validator/blob/main/benchmarks/configs/lofar_telemetry_odcs.yaml) contract end-to-end:
|
|
55
|
+
1. **16 Row-Level Constraints:** Completeness (`required`) checks on all columns, numeric range bounds, categorical set membership (`invalidValues`), and **two regular expression checks** (`station_id` and `firmware_version`) evaluated across every row.
|
|
56
|
+
2. **Physical Data Partitioning:** Compresses and writes **~99.15% of the dataset** to disk as clean records (Snappy Parquet) and **~0.85%** to disk as quarantined records.
|
|
57
|
+
3. **4 Global SQL Rules:** Three cross-column anomaly checks (automatically fused into a single table scan) plus a **3-column composite Primary Key uniqueness check** (`GROUP BY timestamp_utc, station_id, rcu_id`) across the entire dataset.
|
|
58
|
+
- **Measurement Protocol:** In-memory scaling benchmarks execute **1 warmup run followed by 3 timed iterations**, reporting the **median** and run-to-run standard deviation (`± std`). Out-of-core (`spill_to_disk`) envelope runs execute a **single cold-start pass** (`--warmup 0 --iterations 1`) to measure maximum single-node capacity without cumulative thermal throttling or SSD write-cache saturation.
|
|
59
|
+
- **On-Disk vs. In-Memory Footprint:** All dataset sizes listed below reflect compressed Snappy Parquet files on disk. Because Parquet applies dictionary encoding (`RLE_DICTIONARY`) and Snappy compression to repeated categorical strings ([Apache Parquet Encodings](https://parquet.apache.org/docs/file-format/data-pages/encodings/)), the 16.5 GB, 1-billion-row file expands to **~85.8 GB in uncompressed Apache Arrow memory (~5.2x larger)** when materialized ([Apache Arrow Columnar Format](https://arrow.apache.org/docs/format/Columnar.html)).
|
|
60
|
+
|
|
61
|
+
### A. Single-Node Execution (10 Million to 1 Billion Rows)
|
|
62
|
+
|
|
63
|
+
All local benchmarks were executed on a standard consumer laptop:
|
|
64
|
+
|
|
65
|
+
- **CPU:** Intel(R) Core(TM) i7-10750H @ 2.60GHz (6 Physical / 12 Logical Cores)
|
|
66
|
+
- **RAM:** 31.16 GB (32 GB Physical)
|
|
67
|
+
- **OS:** Ubuntu 24.04.4 LTS (Linux 7.0)
|
|
68
|
+
- **Storage:** 512 GB NVMe SSD
|
|
69
|
+
|
|
70
|
+
#### 1. In-Memory Execution (10 Million to 500 Million Rows)
|
|
71
|
+
|
|
72
|
+
In default (in-memory) mode, all 12 logical CPU threads are utilized:
|
|
73
|
+
|
|
74
|
+
| Scale | Compressed Size (In-Memory Size) | Execution Time | Peak RAM |
|
|
75
|
+
| :--- | :--- | :--- | :--- |
|
|
76
|
+
| **10M** | 0.16 GB (~0.8 GB) | **3.49s** (±0.14s) | 2.19 GB |
|
|
77
|
+
| **50M** | 0.82 GB (~4.2 GB) | **18.43s** (±1.40s) | 6.91 GB |
|
|
78
|
+
| **100M** | 1.65 GB (~8.6 GB) | **32.11s** (±1.20s) | 10.59 GB |
|
|
79
|
+
| **200M** | 3.30 GB (~17.2 GB) | **65.44s (~1.1m)** (±3.21s) | 13.29 GB |
|
|
80
|
+
| **500M** | 8.25 GB (~42.9 GB) | **196.66s (~3.3m)** (±10.56s) | **27.58 GB** |
|
|
81
|
+
|
|
82
|
+
**Key Takeaways:**
|
|
83
|
+
- **Zero Startup Overhead:** At 10 million rows, Netra validates 16 column rules, writes partitioned Parquet files, and evaluates 4 global SQL rules in **3.49 seconds**, finishing before a distributed cluster can initialize.
|
|
84
|
+
- **Sub-Linear Memory Growth:** Increasing the dataset size by **50x** (from 10M to 500M rows) increases peak memory usage by only **12.6x** (`2.19 GB` to `27.58 GB`).
|
|
85
|
+
- **Validating Larger-Than-Memory Datasets Locally:** At 500 million rows, the dataset represents **~42.9 GB of uncompressed data**, exceeding the laptop's 32 GB of physical RAM. Netra validates and partitions the entire file in **3.3 minutes** within `27.58 GB` of RAM.
|
|
86
|
+
|
|
87
|
+
#### 2. Out-of-Core Envelope Expansion (500 Million to 1 Billion Rows)
|
|
88
|
+
|
|
89
|
+
On a 32 GB laptop, in-memory mode reaches its practical ceiling at 500 million rows (`27.58 GB` RAM), and larger workloads (`800M` and `1B` rows) exceed available system memory. Enabling `spill_to_disk` bounds memory consumption by processing data in smaller chunks (`50,000` rows) and spilling intermediate state to disk (`16 GB` engine memory limit, `4` active threads):
|
|
90
|
+
|
|
91
|
+
| Scale | Compressed Size (In-Memory Size) | Execution Time | Peak RAM |
|
|
92
|
+
| :--- | :--- | :--- | :--- |
|
|
93
|
+
| **500M** | 8.25 GB (~42.9 GB) | **229.91s (~3.8m)** | **16.91 GB** |
|
|
94
|
+
| **800M** | 13.20 GB (~68.6 GB) | **378.65s (~6.3m)** | **21.14 GB** |
|
|
95
|
+
| **1B** | 16.50 GB (~85.8 GB) | **506.21s (~8.4m)** | **28.79 GB** |
|
|
96
|
+
|
|
97
|
+
**Key Takeaways:**
|
|
98
|
+
- **10.67 GB RAM Reduction at `500M` Rows:** Compared to in-memory mode (`27.58 GB` in `196.66s`), enabling `spill_to_disk` at 500M rows cuts peak memory down to **`16.91 GB` (-38.7%)** for a modest `+33.25s` (+16.9%) trade-off in execution time, keeping roughly half of the laptop's RAM free for the OS and other applications.
|
|
99
|
+
- **2x Local Data Quality Envelope:** Out-of-core execution doubles the maximum dataset size the 32 GB laptop can handle, validating **1 Billion rows (~85.8 GB of uncompressed data)** and writing 991.5 million clean rows and 8.5 million quarantined rows to disk in **8.4 minutes**.
|
|
100
|
+
|
|
101
|
+
### B. Cloud Scale-Up (1 Billion to 3 Billion Rows)
|
|
102
|
+
|
|
103
|
+
In LOFAR 2.0, each station's Local Control Unit (LCU) is equipped with a **16-core / 32-thread processor and 64 GB of RAM** ([Wang et al., 2025, Sec. 5](https://arxiv.org/abs/2503.02425)). To benchmark Netra Validator on a similar hardware profile, we tested 1-billion to 3-billion-row datasets on an equivalent AWS EC2 compute node:
|
|
104
|
+
|
|
105
|
+
- **Instance:** AWS EC2 `c8id.8xlarge`
|
|
106
|
+
- **CPU:** Intel(R) Xeon(R) 6975P-C (16 Physical / 32 Logical vCPUs)
|
|
107
|
+
- **RAM:** 61.77 GB (64 GB Instance Class)
|
|
108
|
+
- **OS:** Ubuntu 24.04.1 LTS (Linux 7.0 AWS)
|
|
109
|
+
- **Storage:** Attached Nitro NVMe SSD
|
|
110
|
+
|
|
111
|
+
We evaluated both **In-Memory Mode** (`32` Polars threads / `32` DuckDB threads) and **Out-of-Core Mode** (`32` Polars threads / `8` DuckDB threads, `250,000`-row Polars batches, `32 GB` engine memory limit):
|
|
112
|
+
|
|
113
|
+
| Scale | Compressed Size (In-Memory Size) | In-Memory (Time / RAM) | Out-of-Core (Time / RAM<sup>†</sup>) |
|
|
114
|
+
| :--- | :--- | :--- | :--- |
|
|
115
|
+
| **1B** | 16.50 GB (~85.8 GB) | **198.47s (~3.3m)** / 56.05 GB | **278.64s (~4.6m)** / **34.01 GB** |
|
|
116
|
+
| **2B** | 33.00 GB (~171.6 GB) | *Exceeds 64 GB RAM* | **575.74s (~9.6m)** / **35.16 GB** |
|
|
117
|
+
| **3B** | 49.50 GB (~257.4 GB) | *Exceeds 64 GB RAM* | **858.14s (~14.3m)** / **61.41 GB** |
|
|
118
|
+
|
|
119
|
+
<sup>†</sup> *Note on Peak RAM (`peak_rss_mb`): `memory_limit_mb` (`16 GB` on Laptop, `32 GB` on EC2) caps the SQL engine's working buffer during disk spilling. Total process RAM reported by the OS (`getrusage`) can rise above this cap on massive datasets due to OS file caching and the index overhead required to track hundreds of millions of spilled records on disk.*
|
|
120
|
+
|
|
121
|
+
**Key Takeaways:**
|
|
122
|
+
|
|
123
|
+
- **From Disk Spilling to In-Memory (`1B` Rows):** A 1-Billion-row workload that requires out-of-core disk spilling on a 32 GB laptop (`506.21s`) runs **entirely in memory in 3.3 minutes (`198.47s`)** on a 64 GB node.
|
|
124
|
+
- **3x Server Envelope Expansion (`1B` to `3B` Rows):** On a 64 GB machine, in-memory mode reaches its limit at 1 billion rows (`56.05 GB` RAM). Enabling `spill_to_disk` expands the single-node **Data Quality Envelope by 3x**, validating **2 billion rows** in **9.6 minutes** and **3 billion rows** (49.50 GB Parquet / ~257.4 GB uncompressed) in **14.3 minutes** without requiring a multi-node cluster.
|
|
125
|
+
- **Memory Behavior Across Scale (`1B` vs. `3B`):** At `1B` and `2B` rows, out-of-core mode holds total RAM steady at **`34.01 GB` – `35.16 GB`** (saving `22.04 GB` of RAM at `1B` compared to in-memory mode). At `3B` rows, the index overhead required to track 3 billion unique keys across temporary disk files pushes total process memory to **`61.41 GB`**, reaching the practical ceiling of a 64 GB node.
|
|
126
|
+
|
|
127
|
+
### C. Throughput & Execution Stability
|
|
128
|
+
|
|
129
|
+
As datasets grow from millions to billions of rows, Netra maintains steady processing speed in both execution modes without hitting a performance cliff:
|
|
130
|
+
|
|
131
|
+
#### 1. In-Memory Mode (3 Timed Iterations + 1 Warmup)
|
|
132
|
+
|
|
133
|
+
| Environment | Scale Range | Row Velocity (Median) | Disk Throughput (Median) | Run-to-Run Std Dev (`± std`) |
|
|
134
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
135
|
+
| **Laptop (32 GB)** | 10M – 500M | **2.54M – 3.11M rows/s** | 40.0 – 49.0 MB/s | ±0.14s (10M) to ±10.56s (500M) |
|
|
136
|
+
| **EC2 `c8id.8xlarge` (64 GB)** | 1B | **5.04M rows/s** | **79.3 MB/s** | **±2.13s** |
|
|
137
|
+
|
|
138
|
+
#### 2. Out-of-Core Mode (Single Cold-Start Pass)
|
|
139
|
+
|
|
140
|
+
| Environment | Scale Range | Row Velocity | Disk Throughput | Execution Time Scaling |
|
|
141
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
142
|
+
| **Laptop (32 GB)** | 500M – 1B | **1.98M – 2.17M rows/s** | 31.1 – 34.2 MB/s | `229.91s` (500M) ➔ `506.21s` (1B) |
|
|
143
|
+
| **EC2 `c8id.8xlarge` (64 GB)** | 1B – 3B | **3.47M – 3.59M rows/s** | **54.7 – 56.5 MB/s** | `278.64s` (1B) ➔ `858.14s` (3B) |
|
|
144
|
+
|
|
145
|
+
**Key Takeaways:**
|
|
146
|
+
- **Linear O(N) Scaling at Multi-Billion-Row Scale:** On the 32-vCPU server, doubling the out-of-core workload from **1B to 2B rows** increases execution time by **2.07x** (`278.64s` to `575.74s`), and tripling it to **3B rows** increases execution time by **3.08x** (`858.14s`).
|
|
147
|
+
- **No Throughput Degradation:** Processing velocity varies by less than **2.5%** across a 3x volume increase on the server (`3.59M rows/s` at 1B vs. `3.50M rows/s` at 3B) and less than **9%** across a 2x volume increase on the laptop (`2.17M rows/s` at 500M vs. `1.98M rows/s` at 1B).
|
|
148
|
+
|
|
149
|
+
## Features
|
|
150
|
+
|
|
151
|
+
- **Native ODCS v3.1.0 Support:** Validates datasets against any compliant [Open Data Contract Standard (v3.1.0)](https://bitol-io.github.io/open-data-contract-standard/v3.1.0/) YAML file—whether from an existing data catalog, written by hand, or generated automatically with [`netra-profiler`](https://github.com/netrabase/netra-profiler).
|
|
152
|
+
- **Automated Data Quarantine:** Automatically splits your input dataset into clean (`validated`) and corrupted (`quarantined`) Parquet files, ensuring bad data never enters downstream tables.
|
|
153
|
+
- **Single-Pass SQL Rule Batching:** Automatically combines multiple `SELECT COUNT(*) FROM {object} WHERE ...` contract rules into a single DuckDB table scan, with automatic per-rule fallback if a query contains a syntax error.
|
|
154
|
+
- **Configurable Global Target (`raw` vs. `validated`):** Choose whether Pass 2 Global/SQL rules evaluate the original raw dataset or only the clean rows that survived Pass 1 (`global_target`).
|
|
155
|
+
- **Out-of-Core Execution:** Process larger-than-memory datasets under strict RAM budgets using configurable disk spilling, micro-batch sizing, pre-flight free-space checks, and automatic thread clamping.
|
|
156
|
+
- **Container-Aware Thread Budgeting:** Automatically detects CPU limits via environment variables (`NETRA_POLARS_THREADS`, `NETRA_DUCKDB_THREADS`), Linux `cgroup` container quotas (Docker/Kubernetes), or physical core counts.
|
|
157
|
+
- **CI/CD Quality Gates:** Enforce strict pipeline governance with `--fail-on-critical` and `--fail-on-warnings` flags for GitHub Actions, GitLab CI, Airflow, and Dagster.
|
|
158
|
+
- **Rich CLI & Headless JSON Mode:** View validation summaries and rule diagnostics in the terminal, or pass `--json` for strictly typed JSON output in automated pipelines.
|
|
159
|
+
|
|
160
|
+
### Supported Validation Capabilities
|
|
161
|
+
|
|
162
|
+
| Validation Layer | Rule / Constraint | ODCS v3.1.0 Property |
|
|
163
|
+
| :--- | :--- | :--- |
|
|
164
|
+
| **Column / Row-Level** | Null / Completeness Check | `required: true` |
|
|
165
|
+
| | Numeric Lower & Upper Bounds | `logicalTypeOptions.minimum` / `maximum` |
|
|
166
|
+
| | String Length Bounds | `logicalTypeOptions.minLength` / `maxLength` |
|
|
167
|
+
| | Regular Expression Matching | `logicalTypeOptions.pattern` |
|
|
168
|
+
| | Allowed Categorical Values | `quality.metric: invalidValues` (`validValues`) |
|
|
169
|
+
| **Table / Global** | Primary Key Uniqueness | `primaryKey: true` |
|
|
170
|
+
| | Custom Column SQL Rules | `quality.type: sql` (`{object}`, `{property}`) |
|
|
171
|
+
| | Custom Table & Cross-Column SQL | `schema.quality.type: sql` (`{object}`) |
|
|
172
|
+
| | Mathematical SQL Boundaries | `mustBeEqual`, `mustBe`, `mustBeLessThan`, `mustBeGreaterThan` |
|
|
173
|
+
|
|
174
|
+
## Installation
|
|
175
|
+
|
|
176
|
+
We recommend installing Netra Validator with [uv](https://github.com/astral-sh/uv):
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
uv pip install netra-validator
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Or with standard `pip`:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
pip install netra-validator
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Quickstart
|
|
189
|
+
|
|
190
|
+
### 1. Command Line
|
|
191
|
+
|
|
192
|
+
Validate any dataset against an ODCS v3.1.0 YAML contract directly from your terminal:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# Optional: Auto-generate a baseline ODCS v3.1.0 contract with netra-profiler
|
|
196
|
+
netra profile dataset.parquet --json > profile.json
|
|
197
|
+
netra contract profile.json > contract.yaml
|
|
198
|
+
|
|
199
|
+
# Validate the dataset against the contract
|
|
200
|
+
netra validate dataset.parquet contract.yaml
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+

|
|
204
|
+
|
|
205
|
+
<details>
|
|
206
|
+
<summary><b>More Screenshots</b></summary>
|
|
207
|
+
<br>
|
|
208
|
+
|
|
209
|
+
**Out-Of-Memory Risk Warning:**
|
|
210
|
+

|
|
211
|
+
|
|
212
|
+
**"Risky Execution" State:**
|
|
213
|
+

|
|
214
|
+
|
|
215
|
+
**Data Contract Error:**
|
|
216
|
+

|
|
217
|
+
|
|
218
|
+
</details>
|
|
219
|
+
|
|
220
|
+
#### CLI Options & Quality Gates
|
|
221
|
+
|
|
222
|
+
- `-o, --valid <path>`: Custom output path for rows that pass all row-level checks (defaults to `validated_<dataset>`).
|
|
223
|
+
- `-q, --quarantine <path>`: Custom output path for rows that fail one or more row-level checks (defaults to `quarantined_<dataset>`).
|
|
224
|
+
- `--global-target [raw|validated]`: Choose whether Pass 2 DuckDB SQL rules run against the `raw` input dataset (default) or the `validated` clean output from Pass 1.
|
|
225
|
+
- `-c, --config <path>`: Path to a custom `netra_config.yaml` file.
|
|
226
|
+
- `--fail-on-critical`: Enable the CI/CD Quality Gate and exit with code `1` if any `CRITICAL` anomalies (row quarantines or critical SQL failures) are detected.
|
|
227
|
+
- `--fail-on-warnings`: Enable the strictest CI/CD Quality Gate and exit with code `1` if **any** anomalies (`WARNING` or `CRITICAL`) are detected.
|
|
228
|
+
- `-y, --force`: Bypass interactive confirmation prompts when pre-flight hardware warnings are triggered.
|
|
229
|
+
- `--json`: Disable the interactive terminal UI and print the validation report as formatted JSON to `stdout`.
|
|
230
|
+
- `-V, --version`: Show the netra-validator version and exit.
|
|
231
|
+
|
|
232
|
+
### 2. Python API
|
|
233
|
+
|
|
234
|
+
Netra Validator provides a fully typed Python API for integration into Airflow, Dagster, Prefect, or custom data engineering services:
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
from netra_validator.config import ValidatorConfig
|
|
238
|
+
from netra_validator.engine.validator import Validator
|
|
239
|
+
|
|
240
|
+
# 1. Configure quality gates and validator execution settings
|
|
241
|
+
# (Or load directly from disk via `ValidatorConfig.from_yaml("netra_config.yaml")`)
|
|
242
|
+
config = ValidatorConfig(
|
|
243
|
+
{
|
|
244
|
+
"pipeline": {
|
|
245
|
+
"fail_on_critical": True,
|
|
246
|
+
"fail_on_warnings": False,
|
|
247
|
+
},
|
|
248
|
+
"validator": {
|
|
249
|
+
"global_target": "validated", # Run Pass 2 SQL checks on clean Pass 1 output
|
|
250
|
+
"spill_to_disk": {
|
|
251
|
+
"enabled": True,
|
|
252
|
+
"memory_limit_mb": 16384, # 16 GB RAM ceiling for DuckDB Pass 2
|
|
253
|
+
"chunk_size": 50_000,
|
|
254
|
+
"temp_directory": "/tmp/netra_spill",
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
}
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# 2. Initialize and run the Validator
|
|
261
|
+
validator = Validator(
|
|
262
|
+
contract_path="contracts/lofar_telemetry_odcs.yaml",
|
|
263
|
+
dataset_path="data/lofar_telemetry.parquet",
|
|
264
|
+
output_path="output/validated_telemetry.parquet",
|
|
265
|
+
quarantine_path="output/quarantined_telemetry.parquet",
|
|
266
|
+
config=config,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
report = validator.run()
|
|
270
|
+
|
|
271
|
+
# 3. Inspect the strictly typed ValidationReport
|
|
272
|
+
summary = report["dataset"]
|
|
273
|
+
print(f"Total Evaluated: {summary['total_rows_evaluated']:,}")
|
|
274
|
+
print(f"Valid Rows: {summary['valid_rows']:,}")
|
|
275
|
+
print(f"Quarantined Rows: {summary['quarantined_rows']:,}")
|
|
276
|
+
print(f"Pipeline Status: {report['pipeline_status']}")
|
|
277
|
+
|
|
278
|
+
# 4. Enforce CI/CD Quality Gate programmatically
|
|
279
|
+
pipeline_ctx = report["_meta"]["pipeline_context"]
|
|
280
|
+
if pipeline_ctx and pipeline_ctx["exit_code"] != 0:
|
|
281
|
+
raise RuntimeError(
|
|
282
|
+
f"Data contract validation failed: {pipeline_ctx['reason']}"
|
|
283
|
+
)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### 3. Universal Configuration (`netra_config.yaml`)
|
|
287
|
+
|
|
288
|
+
Netra uses a centralized `netra_config.yaml` file to manage CI/CD quality gates and engine execution settings across the Netra suite. Global `pipeline` rules apply by default and can be overridden specifically for the validator under the `validator` block:
|
|
289
|
+
|
|
290
|
+
```yaml
|
|
291
|
+
# ---------------------------------------------------------
|
|
292
|
+
# PIPELINE: Global CI/CD Quality Gate Defaults
|
|
293
|
+
# ---------------------------------------------------------
|
|
294
|
+
pipeline:
|
|
295
|
+
fail_on_critical: true # Exit with code 1 if CRITICAL anomalies are detected
|
|
296
|
+
fail_on_warnings: false # Exit with code 1 if ANY anomalies (Warning or Critical) are detected
|
|
297
|
+
|
|
298
|
+
# ---------------------------------------------------------
|
|
299
|
+
# VALIDATOR: Contract Execution & Out-of-Core Settings
|
|
300
|
+
# ---------------------------------------------------------
|
|
301
|
+
validator:
|
|
302
|
+
global_target: "raw" # Evaluate Pass 2 SQL rules on "raw" input or "validated" output
|
|
303
|
+
|
|
304
|
+
# Optional: Override global `pipeline` gates specifically for `netra validate`
|
|
305
|
+
pipeline:
|
|
306
|
+
fail_on_critical: true
|
|
307
|
+
fail_on_warnings: true
|
|
308
|
+
|
|
309
|
+
spill_to_disk:
|
|
310
|
+
enabled: false # Enable micro-batching and DuckDB disk spilling
|
|
311
|
+
memory_limit_mb: 16384 # Maximum RAM ceiling for DuckDB Pass 2 (16 GB default)
|
|
312
|
+
chunk_size: 50000 # Polars Pass 1 micro-batch row count when spilling is enabled
|
|
313
|
+
temp_directory: "/tmp/netra_spill"
|
|
314
|
+
min_spill_space_gb: 10 # Pre-flight disk free space requirement before starting a run
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Netra Validator automatically looks for `netra_config.yaml` in your current working directory. You can also pass it explicitly via CLI flag or environment variable:
|
|
318
|
+
|
|
319
|
+
##### Using Command Line Flag
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
netra validate dataset.parquet contract.yaml --config path/to/custom_config.yaml
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
#### Environment Variable Overrides
|
|
326
|
+
|
|
327
|
+
To control CPU core allocation in shared or containerized environments, set the following environment variables before running Netra Validator:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
export NETRA_CONFIG="/etc/netra/production_config.yaml"
|
|
331
|
+
export NETRA_POLARS_THREADS=8
|
|
332
|
+
export NETRA_DUCKDB_THREADS=8
|
|
333
|
+
netra validate dataset.parquet contract.yaml
|
|
334
|
+
```
|