Skip to main content

Smart Spark Optimizer: Skew Rebalancer + Key Detector + DRTree

Project description

HexaDruid ๐Ÿง โšก

PyPI version Python Version License: MIT

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

To upgrade to the latest version?

pip install --upgrade 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 ๐Ÿ’ฅ


๐Ÿ› ๏ธ Main Classes & API Reference

Here are the key classes exposed by the HexaDruid package:

Class Description
HexaDruid Main entrypoint; handles smart salting, key detection, and schema optimization
SkewFeatureDetector Detects skewed numeric columns for rebalancing
KeyFeatureDetector Detects primary/composite keys (unique columns/combinations)
DRTree Decision Rule Tree for logical sharding
AutoParameterAdvisor Recommends optimal columns for skew balancing and groupBy
AdaptiveShuffleTuner Tunes shuffle partition count dynamically
Branch, Root (Advanced) Internal tree structure helpers


๐Ÿ“‘ API Reference

All core APIs are PySpark DataFrame-native. Below are the main classes and methods:


HexaDruid

Method Description
HexaDruid(df, salt_count=10, output_dir="hexa_druid_outputs") Initialize with a Spark DataFrame. Optional: set default salt bucket count and output directory.
apply_smart_salting(col_name, visualize=True, fine_tune=True, auto_tune=True, target_rows=1_000_000) Balance skew in col_name by bucketizing (salting), visualizing, tuning salt count, and auto-tuning partitions. Returns a salted DataFrame with new columns: salt, salted_key.
detect_keys(dr_tree=None, composite_max_size=3, composite_threshold=0.99, verbose=True) Detects the best candidate for a primary key or a composite key. Uses shard-aware logic if a dr_tree is passed. Returns a dict: {type, columns, confidence} or None.
repartition_on_salt(num_partitions=10) Repartitions the salted DataFrame evenly on the salted_key column. Returns a repartitioned DataFrame.
show_partition_sizes(df, label="") Prints the record count of each partition in the DataFrame, labeled for diagnostics.
build_shard_tree(detector, max_depth=3, min_samples=500) Recursively build a logical sharding tree (DRTree) by splitting on skewed columns. Detector must implement .detect().
analyze_distribution(col_name) Returns distribution stats (p95, p05, mean, std) for a given column.
schemaVisor(df, sample_fraction=0.2, max_depth=3, min_samples=500, skew_thresh=0.1, skew_top_n=3) (static) Infers schema types, casts columns, and builds a DRTree for sharding. Returns a tuple: (typed_df, StructType, DRTree).
infer_numeric_columns(df) (static) Returns a list of numeric columns (int, float, double, long, bigint).
detect_low_cardinality_categorical(df) (static) Finds the first string column with โ‰ค20 unique values (good for groupBy). Raises ValueError if none found.
timeit(func, label="") (static) Times the execution of a function and logs the result.
_plot_comparison(col_name, df2) (private) Generates and saves z-score barplots for original vs salted columns.

SkewFeatureDetector

Method Description
SkewFeatureDetector(threshold=0.1, top_n=3) Initialize detector with skew threshold and number of columns to return.
detect(df) Returns the top-N most skewed numeric columns by quartile-based score.

KeyFeatureDetector

Method Description
KeyFeatureDetector(verbose=False) Initialize detector for verbose logging.
detect(df, dr_tree=None) Returns best candidate key columns (single/composite), optionally shard-aware. List[str].
detectPrimaryKey(df, dr_tree=None, confidence_threshold=0.99, verbose=False) (static) Detects primary key with confidence score. Returns dict or None.
detectCompositeKey(df, dr_tree=None, max_combination_size=3, confidence_threshold=0.99, verbose=False) (static) Detects composite keys by testing combinations of 2-3 columns. Returns dict or None.

AutoParameterAdvisor

Method Description
AutoParameterAdvisor(df, skew_top_n=3, cat_top_n=3) Initialize with DataFrame and how many top columns to suggest.
recommend() Returns (skew candidates, groupBy candidates, metrics DataFrame).
advise() Interactive prompt: pick skew/groupBy columns (returns 2 strings).

DRTree

Method Description
DRTree() Create a new, empty Decision Rule Tree.
add_root(root) Add a Root node to the DRTree.
to_dict() Returns a JSON-serializable dictionary of the tree.

Branch, Root, DecisionNode, LeafNode

  • Branch: Simple data holder for a leaf/shard predicate and its name.
  • Root: Logical tree root node; holds one or more branches.
  • DecisionNode: Internal tree node representing a split on a numeric column.
  • LeafNode: Terminal node; represents a filtered logical subset ("shard") of your data.

balance_skew

Function Description
balance_skew(df, output_dir="hexa_druid_outputs", partitions=10, verbose=False) Runs full salting pipeline interactively: prompts for skew/groupBy columns, applies salting, shows before/after partition diagnostics. Returns a new DataFrame.

AdaptiveShuffleTuner

Method Description
tune(spark, df, target_rows=1_000_000) (static) Auto-tunes shuffle partitions based on target rows per partition. Returns repartitioned DataFrame.

Tip:

  • See the tests/ directory for working code samples and usage patterns.
  • All methods are intended for use with Spark DataFrames (PySpark >= 3.5.1).
  • Advanced users can directly use the KeyFeatureDetector, SkewFeatureDetector, and AutoParameterAdvisor in custom pipelines.

โš–๏ธ 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:

  1. Detect Skew

    • If z_score range is too large or
    • IQR shows asymmetry (Q3 - Q2 โ‰ซ Q2 - Q1)
  2. Split by Percentiles

percentiles = percentile_approx("amount", [0.0, 0.1, ..., 1.0])
  1. Salt Bucketing Logic
salt = when(col >= p0 & col < p1, 0) \
     .when(col >= p1 & col < p2, 1) ...
  1. Create Salted Key
salted_key = concat_ws("_", col("amount"), col("salt"))
df = df.withColumn("salted_key", salted_key).repartition("salted_key")
  1. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hexadruid-0.1.8.tar.gz (8.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hexadruid-0.1.8-py3-none-any.whl (7.5 kB view details)

Uploaded Python 3

File details

Details for the file hexadruid-0.1.8.tar.gz.

File metadata

  • Download URL: hexadruid-0.1.8.tar.gz
  • Upload date:
  • Size: 8.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.9

File hashes

Hashes for hexadruid-0.1.8.tar.gz
Algorithm Hash digest
SHA256 c126f52971ecd33d6be692e65120524d1026f2e71fca2289642c8ae1b2f33cb2
MD5 b4899975b1bdb7e942201e6b0f4eac86
BLAKE2b-256 bee790dace9b67bafed05c9f785dd58fe2aa6f6160df64b6e1a5ed9600c99373

See more details on using hashes here.

File details

Details for the file hexadruid-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: hexadruid-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 7.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.9

File hashes

Hashes for hexadruid-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 ba3fdf443cfad9299ba03b53b6d9c33bb5ed0374e8013766a9035a4a848597a9
MD5 63977e0e060e66859e8a89a82392feb9
BLAKE2b-256 84c6b7974b6d89579907cf04dc108d12348ae14f649f4aed6029597951a0b360

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page