بنقرة واحدة
spark-pipeline
Design and implement Apache Spark data pipelines — batch and streaming ETL
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Design and implement Apache Spark data pipelines — batch and streaming ETL
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Consult and write the ARAYA postoffice — the operational-directive channel. Advisory, never a gate: read it at cycle start, append your entry at cycle end, consider its directives, never be blocked by it. Governance acts never travel the postoffice.
Audit and enforce brand compliance across all projects and platforms — logo,
Design REST and GraphQL APIs following OpenAPI 3.1 standards. Produce complete
Connect frontend to backend — type-safe API clients, request/response handling,
Implement authentication and authorization middleware — JWT validation, role-based
Design scalable component architectures — design systems, component libraries,
| name | spark-pipeline |
| description | Design and implement Apache Spark data pipelines — batch and streaming ETL |
| governance | Constitution ENG-004: Engineering Excellence & Software Craftsmanship Standard |
Design and implement Apache Spark data pipelines — batch and streaming ETL using PySpark/SparkSQL — following medallion architecture patterns with validation, error handling, and production-grade reliability.
Data pipelines that "work on sample data" fail in production with null values, schema drift, and silent data loss. This skill produces Spark pipelines with schema enforcement, audit columns, error handling, and idempotent writes — reliable enough for production data engineering.
When building ETL/ELT pipelines for data lakes. When processing large datasets (> 1GB) that require distributed processing. When implementing medallion architecture (Bronze → Silver → Gold).
Data source details (CSV, JSON, Parquet, JDBC, Kafka), target schema, business transformation rules, partitioning requirements.
# spark_jobs/bronze_ingestion.py
from pyspark.sql import SparkSession, DataFrame
from pyspark.sql.types import StructType, StructField, StringType, DoubleType
from pyspark.sql.functions import input_file_name, current_timestamp, lit
from datetime import datetime
spark = SparkSession.builder \
.appName("Bronze Ingestion — Trades") \
.config("spark.sql.sources.partitionOverwriteMode", "dynamic") \
.getOrCreate()
# Define explicit schema (inferSchema=False — schema is explicit)
trade_schema = StructType([
StructField("trade_id", StringType(), False),
StructField("customer_id", StringType(), False),
StructField("coin_id", StringType(), False),
StructField("trade_type", StringType(), False),
StructField("quantity", DoubleType(), False),
StructField("price", DoubleType(), False),
StructField("trade_date", StringType(), False),
])
class BronzeIngestion:
"""Ingest raw data into Bronze layer — append-only, immutable."""
def __init__(self, source_path: str, bronze_path: str):
self.source_path = source_path
self.bronze_path = bronze_path
self.ingestion_ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def read_source(self) -> DataFrame:
"""Read source data with explicit schema."""
return (
spark.read
.option("header", "false") # CSV has no header
.option("delimiter", ";") # Semicolon-delimited
.schema(trade_schema) # Enforce schema
.csv(self.source_path)
)
def add_audit_columns(self, df: DataFrame) -> DataFrame:
"""Add audit columns required on every table."""
return df.withColumns({
"record_source": input_file_name(),
"ingestion_timestamp": current_timestamp(),
"source_file": input_file_name(),
})
def validate_row_counts(self, df: DataFrame, source_count: int) -> None:
"""Validate ingestion row count matches source."""
bronze_count = df.count()
if bronze_count != source_count:
raise ValueError(
f"Row count mismatch: source={source_count}, bronze={bronze_count}. "
f"Difference: {abs(source_count - bronze_count)} rows"
)
print(f"✅ Row count validated: {bronze_count:,} rows ingested")
def write_bronze(self, df: DataFrame) -> None:
"""Write to Bronze layer — append-only, partitioned by date."""
(
df.write
.mode("append") # Append-only — never overwrite Bronze
.format("parquet")
.partitionBy("ingestion_date")
.option("compression", "snappy")
.save(self.bronze_path)
)
def run(self) -> None:
"""Execute full Bronze ingestion pipeline."""
print(f"🔄 Starting Bronze ingestion: {self.ingestion_ts}")
# 1. Read source
raw_df = self.read_source()
source_count = raw_df.count()
print(f"📥 Source rows: {source_count:,}")
# 2. Add audit columns
enriched_df = self.add_audit_columns(raw_df)
# 3. Add ingestion date for partitioning
enriched_df = enriched_df.withColumn(
"ingestion_date", current_timestamp().cast("date")
)
# 4. Validate
self.validate_row_counts(enriched_df, source_count)
# 5. Write
self.write_bronze(enriched_df)
print(f"✅ Bronze ingestion complete: {self.bronze_path}")
# --- Usage ---
if __name__ == "__main__":
ingestion = BronzeIngestion(
source_path="./data/trades/",
bronze_path="./data/bronze/trades/"
)
ingestion.run()
record_source, ingestion_timestamp, source_fileinferSchema=False — schema must be explicit; inferred schemas cause production failuresrecord_source, ingestion_timestamp, source_file