Smart Spark Optimizer: Skew Rebalancer + Key Detector + DRTree
Project description
HexaDruid ๐ง โก
HexaDruid is an intelligent Spark optimizer designed to tackle data skew, ambiguous key detection, and schema bloat using smart salting, recursive shard-aware rule trees, and adaptive tuning. It enables better parallelism, safer memory layout, and intelligent insight into skewed datasets using PySparkโs native DataFrame API.
๐ Installation
pip install hexadruid
๐ Features
- ๐ Smart Salting using Z-score or IQR skew analysis + percentile bucketing
- ๐ฒ Recursive DRTree for shard-based logical filtering with SQL predicates
- ๐ Primary & Composite Key Detection (UUIDs, alphanumerics, hex โ optional)
- ๐ง Schema Inference with safe type coercion, length introspection & metadata tags
- โ๏ธ Auto-Parameter Advisor for optimal salt count and shuffle parallelism
- ๐ Z-Score Plots and partition size diagnostics for visibility
- โ Fully PySpark-native โ No RDDs, no CLI dependencies, no black-box wrappers
๐ง Quickstart
from hexadruid import HexaDruid
hd = HexaDruid(df)
# Step 1: Apply smart salting to balance skew
df_salted = hd.apply_smart_salting("sales_amount")
# Step 2 (Optional): Detect candidate primary or composite keys
key_info = hd.detect_keys()
# Step 3: Run schema optimizer + DRTree analyzer
typed_df, inferred_schema, dr_tree = HexaDruid.schemaVisor(df)
๐ What Does It Do?
Imagine a typical DataFrame:
| order_id (UUID) | amount |
|---|---|
| a12e... | 500.0 |
| b98c... | 5000.0 |
| ... | ... |
You're doing:
df.groupBy("amount").agg(...)
But most rows have the same amount, so Spark sends 99% of the work to 1 executor = skew ๐ฅ
โ๏ธ Smart Salting to the Rescue
df2 = hd.apply_smart_salting("amount")
What happens?
Step 1: Analyze column distribution via IQR or Z-score
Step 2: Generate N percentile buckets
Step 3: Assign salt ID per row using bucket bounds
Step 4: Create salted_key = amount_salt
Step 5: Repartition on salted_key for parallelism
๐ This rebalances the shuffle phase for joins, groupBy, and aggregates.
๐ง DRTree Explained Visually
The DRTree is a decision-rule tree, not a classifier.
It recursively splits data into shards by applying SQL-style predicates. Each leaf is a filtered logical subset of the DataFrame.
[Root: sales_amount]
|
โโโโโโโโโโโโโดโโโโโโโโโโโโโ
[amount <= 500] [amount > 500]
| |
โโโโโโโโโดโโโโโโโโ โโโโโโโโดโโโโโโโโ
[amount <= 100] [>100, โค500] [>500, โค1000] [>1000]
| | | |
[Leaf A] [Leaf B] [Leaf C] [Leaf D]
(shard_1) (shard_2) (shard_3) (shard_4)
Each leaf holds:
- Filtered subset of the DataFrame (as a Spark SQL query)
- Associated metadata like row count, min/max, schema drift
- Auto key detection can run within these shards
๐ฌ Leaf-Level Parallelization
DRTree enables parallel insight:
- Each leaf is autonomous (you can infer schema, key, and stats per leaf)
- Makes the system robust to changes over time (drift detection)
- Enables controlled analytics:
[DRTree Output]
Leaf A:
- rows: 30K
- key_confidence: 0.92
- type: Float(5,2)
Leaf D:
- rows: 300K (hotspot!)
- key_confidence: 0.12
- type: String(255)
๐ Key Detection (Optional & Shard-Aware)
key_info = hd.detect_keys()
You donโt need to force primary keys.
This is just analysis โ it evaluates uniqueness confidence for each column (or combination of columns):
- Primary Key:
score = (approx_count_distinct(col) / total_rows) - null_ratio
If score โฅ 0.99, itโs a good candidate.
- Composite Key:
combo_key = concat_ws("_", col1, col2, ...)
score = approx_count_distinct(combo_key) / total_rows - null_ratio
DRTree passes its shard filters into detect_keys() to evaluate keys per subgroup โ boosting accuracy.
๐ง Smart Salting Internals
๐งช Step-by-step:
-
Detect Skew
- If
z_scorerange is too large or - IQR shows asymmetry (Q3 - Q2 โซ Q2 - Q1)
- If
-
Split by Percentiles
percentiles = percentile_approx("amount", [0.0, 0.1, ..., 1.0])
- Salt Bucketing Logic
salt = when(col >= p0 & col < p1, 0) \
.when(col >= p1 & col < p2, 1) ...
- Create Salted Key
salted_key = concat_ws("_", col("amount"), col("salt"))
df = df.withColumn("salted_key", salted_key).repartition("salted_key")
- Auto-Tune Salt Count
- If distribution is dense, fewer buckets suffice
- Otherwise, more salting is applied dynamically
๐ Visualization Example
Output from schemaVisor():
Leaf Node A [shard_0]
- size: 102,391
- type: Float(8,2)
- confidence: 92%
Leaf Node B [shard_1]
- size: 489,128 (dense zone)
- skew detected!
- Recommended salt count: 10
You can visualize the Z-score distribution:
Before:
[โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ ]
After:
[โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ ]
๐งช Testing
pytest tests/
Mocked SparkSession with synthetic data is used to ensure full coverage.
๐งฑ Suggested Project Structure
hexadruid/
โโโ __init__.py
โโโ core.py # HexaDruid entry point
โโโ skew_balancer.py # Smart salting logic
โโโ drtree.py # DRTree shard splitting
โโโ key_detection.py # Unique key checker
โโโ schema_optimizer.py # Type inference
โโโ advisor.py # Parameter tuning
โโโ utils.py # Logging, plots, etc.
โโโ tests/ # Test suite
๐ง Roadmap
- CLI interface
- Delta Lake + Iceberg support
- JupyterLab extension
- DRTree JSON export for audits
- Cost metrics estimation
- Column statistics and visualization dashboard
๐ License
MIT License
๐ค Contributing
Pull requests, ideas, and contributions welcome!
We believe Spark shouldnโt be slow. Letโs make it smarter together.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hexadruid-0.1.6.tar.gz.
File metadata
- Download URL: hexadruid-0.1.6.tar.gz
- Upload date:
- Size: 25.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b96a95d82eeb5f7501385d4f56fabf48a32026931849fe0ffe4b98c1584ea75
|
|
| MD5 |
2164ee35cf0c11d4e7d6ed114e8306f3
|
|
| BLAKE2b-256 |
9861dbb9729577cb0c4dc42012a7563d197137fbafa47754570af7c8ffb98308
|
File details
Details for the file hexadruid-0.1.6-py3-none-any.whl.
File metadata
- Download URL: hexadruid-0.1.6-py3-none-any.whl
- Upload date:
- Size: 22.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1786a5d7d80c30a93214e64fe7fde5743eaf92de6bf90fa55d8cfaf274185a1
|
|
| MD5 |
d950e412c49a414e5cab6795e49efc99
|
|
| BLAKE2b-256 |
90231eef14642ccb5661496af80a4a4cc8350537eb887dd805df4c339c6585f8
|