Skip to main content

Platform-agnostic job routing framework for Spark ETL pipelines

Project description

SparkRouter

PyPI version Python License

Platform-agnostic job routing framework for Spark ETL pipelines.

Write your ETL logic once, run it on Databricks, AWS Glue, EMR, or Docker containers.

Why SparkRouter?

  • Write Once, Run Anywhere: Same job code runs on multiple Spark platforms
  • Clean Architecture: Factory + Template Method patterns keep code testable
  • Configuration-Driven: JSON config via CLI, no code changes between environments

Installation

uv add sparkrouter
# or
pip install sparkrouter

Quick Start

How It Works

flowchart LR
    A["Airflow DAG"] --> B["Entry Script"] --> C["Your Job Factory"] --> D["Your Job"]
  1. Airflow (or any orchestrator) triggers a Spark platform job
  2. The entry script routes to your code via --module_name
  3. Your factory creates the job with dependencies
  4. Your job runs the business logic

Step 1: Write Your Job

# my_etl_job.py
from sparkrouter import AbstractJob

class MyETLJob(AbstractJob):
    def execute_job(self, input_path: str, output_path: str) -> dict:
        # Your business logic here
        print(f"Processing {input_path} -> {output_path}")
        return {"records_processed": 1000}

    def on_success(self, results):
        print(f"Done: {results['records_processed']} records")

    def on_failure(self, error_message):
        print(f"Failed: {error_message}")

Step 2: Write Your Factory

# my_etl_job_factory.py
from sparkrouter import AbstractJobFactory
from my_etl_job import MyETLJob

class MyETLJobFactory(AbstractJobFactory):
    def create_job(self, **kwargs) -> MyETLJob:
        return MyETLJob()

def main(**kwargs):
    """Entry point called by SparkRouter."""
    factory = MyETLJobFactory()
    return factory.run(**kwargs)

Step 3: Run Locally

No entry script needed - run directly from the installed package:

python -m sparkrouter.entry_points.container \
    --module_name my_etl_job_factory \
    --input_path "/data/input" \
    --output_path "/data/output"

Platform Deployment

When deploying to Glue, Databricks, or EMR, those platforms require a script file at an S3/DBFS location. SparkRouter uses a single entry script per platform that routes to any of your jobs via --module_name. You create this script once, upload it once, and reuse it for all jobs.

AWS Glue

1. Create the entry script (one time):

# glue_entry.py
from sparkrouter.entry_points.glue import main

if __name__ == "__main__":
    main()

2. Upload to S3 (one time):

aws s3 cp glue_entry.py s3://my-bucket/scripts/glue_entry.py

3. Run any job by specifying --module_name:

GlueJobOperator(
    script_location='s3://my-bucket/scripts/glue_entry.py',
    script_args={
        '--module_name': 'mypackage.jobs.my_etl_job_factory',
        '--input_path': 's3://data/input/',
        '--output_path': 's3://data/output/',
    },
    default_arguments={
        '--additional-python-modules': 'sparkrouter,mypackage',
    },
)

Databricks

1. Create the entry script (one time):

# databricks_entry.py
from sparkrouter.entry_points.databricks import main

if __name__ == "__main__":
    main()

2. Upload to DBFS (one time):

databricks fs cp databricks_entry.py dbfs:/scripts/databricks_entry.py

3. Run any job by specifying --module_name:

DatabricksSubmitRunOperator(
    spark_python_task={
        'python_file': 'dbfs:/scripts/databricks_entry.py',
        'parameters': [
            '--module_name', 'mypackage.jobs.my_etl_job_factory',
            '--input_path', 's3://data/input/',
            '--output_path', 's3://data/output/',
        ],
    },
    libraries=[
        {'pypi': {'package': 'sparkrouter'}},
        {'pypi': {'package': 'mypackage'}},
    ],
)

EMR

1. Create the entry script (one time):

# emr_entry.py
import os
from sparkrouter.entry_points.container import ContainerEntryPoint

class EMREntryPoint(ContainerEntryPoint):
    @property
    def service_provider(self) -> str:
        return "EMR"

    def add_platform_context(self, args):
        args = super().add_platform_context(args)
        args['region'] = os.environ.get('AWS_REGION')
        return args

    def detect_spark(self) -> bool:
        return True  # EMR always has Spark

def main(argv=None):
    return EMREntryPoint().run(argv)

if __name__ == "__main__":
    main()

2. Upload to S3 (one time):

aws s3 cp emr_entry.py s3://my-bucket/scripts/emr_entry.py

3. Run any job via spark-submit:

spark-submit s3://my-bucket/scripts/emr_entry.py \
    --module_name mypackage.jobs.my_etl_job_factory \
    --input_path s3://data/input/ \
    --output_path s3://data/output/

Examples

See the examples directory:

License

Apache License 2.0

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

sparkrouter-0.2.1.tar.gz (20.4 kB view details)

Uploaded Source

Built Distribution

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

sparkrouter-0.2.1-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file sparkrouter-0.2.1.tar.gz.

File metadata

  • Download URL: sparkrouter-0.2.1.tar.gz
  • Upload date:
  • Size: 20.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for sparkrouter-0.2.1.tar.gz
Algorithm Hash digest
SHA256 9c2f29a08471b61ba08702edbc6736ad949a88ec5d0899a609f2294ec2e32c16
MD5 b03126275a42d5dea4630151636eedcc
BLAKE2b-256 0c38d8158bfddb9e5f5c10819afc196401689ce9597fedf858f8bc0eb39d8e97

See more details on using hashes here.

File details

Details for the file sparkrouter-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: sparkrouter-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 16.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for sparkrouter-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fa7038dc1585c9f0d03d08480cad4782b78106bf2a4a8068fc0155bda60294d0
MD5 1ca0d07290dd9ca9501e1c6875dbb974
BLAKE2b-256 5d3ce459dbd98d14cc3d7d73954489d4ac192ec11bcf978c53b7b006f8f934e4

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