Battle-tested emergency runbooks, triage procedures, and performance knobs for Apache Spark, dbt, Airflow, and Lakehouse platforms. Structured as Problem Scenario → Root Cause Triage → Hardened Solution.
Showing 69 of 69 runbooks
69 / 69 runbooks
Spark Catalyst Optimization Lifecycle
Interactive Flow
How declarative queries transform from raw syntax into hardware-accelerated Tungsten physical execution.
Stage 5 of 6
AQE Runtime Loop — Mid-Flight Feedback
Adaptive Query Execution intercepts data after shuffle map stages, inspects actual partition size metrics, and dynamically re-optimizes the running physical plan.
Problem / Challenge:Need to alternate between declarative SQL and programmatic DataFrame pipelines without performance penalties or redundant re-evaluations.
Production Solution: Use createOrReplaceTempView() to query DataFrames seamlessly via SQL, and selectExpr() to inject SQL expressions directly inside DataFrame API chains. Both compile down to identical Catalyst ASTs.
spark-interface-switching.pyPython
1# 1. Register DataFrame as Catalyst session-scoped view
4# 2. Seamlessly blend SQL into programmatic DataFrame pipeline
5df_summary = spark.sql("""
6 SELECT customer_id, SUM(amount) AS total_spend,
7 DENSE_RANK() OVER (ORDER BY SUM(amount) DESC) as rank
8 FROM v_orders
9 GROUP BY customer_id
10""")
11
12# 3. Use selectExpr for zero-overhead SQL expressions in DataFrame API
13final_df = df_summary.selectExpr(
14"customer_id",
15"total_spend",
16"CASE WHEN rank <= 10 THEN 'VIP' ELSE 'STANDARD' END AS segment"
17)
Critical• Medium Effort
Arrow-Vectorized Pandas UDFs
10x-100x speedup over row-by-row Python UDFs
Problem / Challenge:Standard Python UDFs run row-by-row inside an isolated Python daemon process, incurring staggering Py4J IPC socket serialization overhead (pickle) and disabling Whole-Stage CodeGen.
Production Solution: Always replace standard @udf with vectorized @pandas_udf (backed by Apache Arrow columnar memory). Arrow shares memory zero-copy between the JVM and Python worker.
spark-vectorized-pandas-udfs.pyPython
1import pandas as pd
2from pyspark.sql.functions import pandas_udf
3from pyspark.sql.types import DoubleType
4
5# Apache Arrow vectorized scalar UDF: batches Series -> Series
Problem / Challenge:Branching DataFrames that feed multiple downstream targets re-evaluate the entire upstream DAG from source storage each time an action is called, burning duplicate compute.
Production Solution: Use .persist(StorageLevel.MEMORY_AND_DISK_SER) with Kryo serialization. Avoid plain .cache() if data exceeds available RAM to prevent GC thrashing. Always call .unpersist() in a finally block.
spark-persistence-strategy.pyPython
1from pyspark import StorageLevel
2
3# Filter & heavy join branch used by both aggregates and ML pipeline
Problem / Challenge:Filtering a dimension table (e.g. date_dim.year = 2026) still scans all physical partition folders across a 100TB fact table because partition values cannot be statically inferred before join execution.
Production Solution: Verify spark.sql.optimizer.dynamicPartitionPruning.enabled=true. Spark dynamically evaluates the dimension filter, creates a broadcast subquery, and prunes irrelevant fact partition directories prior to scanning.
spark-dynamic-partition-pruning.pyPython
1# 1. Ensure DPP and CBO are active in Spark session
5# 2. Schema requirement: Fact table MUST be partitioned by join key
6# fact_sales partitioned by (order_date)
7# dim_date non-partitioned
8
9# 3. Query with dimension filter automatically activates DPP
10df_joined = spark.sql("""
11 SELECT f.order_id, f.amount, d.fiscal_quarter
12 FROM fact_sales f
13 JOIN dim_date d ON f.order_date = d.calendar_date
14 WHERE d.fiscal_year = 2026
15""")
16
17# Verify in explain plan:
18# Look for: "PartitionFilters: [dynamicpruningexpression(f.order_date#12 IN ...)]"
Critical• Medium Effort
Lineage Truncation via checkpoint()
Eliminates driver StackOverflowError; drops driver JVM memory by 80%
Problem / Challenge:Recursive graphs, iterative ML algorithms, or deep ETL chains (50+ loops) crash the Spark Driver with java.lang.StackOverflowError during Catalyst plan optimization.
Production Solution: Use df.checkpoint() or df.localCheckpoint() to physically truncate RDD lineage. Unlike .cache(), checkpointing severs the DAG tree entirely and writes materialized partitions to disk.
15x-30x faster execution than Python UDFs; 0 JVM-Python serialization
Problem / Challenge:Developers use Python UDFs to iterate, filter, and transform complex nested array and struct fields, causing heavy Py4J IPC socket serialization and disabling Tungsten CodeGen.
Production Solution: Replace UDFs with Spark native Higher-Order Functions: transform(), filter(), aggregate(), array_compact(), and zip_with(). They execute 100% inside Tungsten C++ / JVM engine.
spark-udf-native-replacement.pyPython
1from pyspark.sql.functions import expr
2
3# ANTI-PATTERN: Row-by-row Python UDF deserializing array of structs
4# @udf(ArrayType(DoubleType()))
5# def apply_discount_udf(items): return [x['price'] * 0.9 for x in items if x['in_stock']]
6
7# PRODUCTION CHEAT CODE: Native Catalyst higher-order expressions
8# 1. transform: map every element in array
9# 2. filter: filter elements matching predicate
10optimized_df = df.withColumn(
11"discounted_in_stock_prices",
12 expr("""
13 transform(
14 filter(line_items, x -> x.in_stock = true),
15 x -> round(x.price * 0.90, 2)
16 )
17 """)
18)
19
20# 3. aggregate: fold / reduce array elements to a single scalar
40%-60% job runtime reduction on audit-heavy pipelines
Problem / Challenge:Auditing data quality by running multiple df.filter().count() passes throughout a pipeline forces repeated full table scans, doubling cluster execution costs.
Production Solution: Use Spark AccumulatorV2 to count corrupt, null, or out-of-bounds records inline during single-pass transformations, reporting metrics to the driver with zero extra shuffle passes.
28# Driver reads aggregated telemetry with ZERO extra compute passes
29print("Audit Telemetry:", audit_acc.value)
Critical• Low Effort
Off-Heap Memory & Tungsten Allocations
90% reduction in JVM GC pause durations on 1TB+ shuffles
Problem / Challenge:Multi-gigabyte shuffle stages trigger massive JVM Garbage Collection (GC) pauses (stop-the-world for 30s+), causing executor heartbeat timeouts and cluster instability.
Production Solution: Enable off-heap memory via spark.memory.offHeap.enabled=true and allocate off-heap sizing. Tungsten manages memory using sun.misc.Unsafe raw byte allocations, completely bypassing the JVM GC collector.
spark-offheap-tungsten-memory.pyPython
1# Cluster / SparkSession configuration for Off-Heap memory
Problem / Challenge:Default Java serialization is bulky, slow, and encapsulates extensive metadata per object, inflating shuffle payloads and causing prolonged Garbage Collection (GC) pauses.
Production Solution: Switch to Kryo serialization in Spark conf, increase buffer sizing, and favor primitive arrays/StructTypes over complex nested Scala/Java collection instances.
spark-kryo-memory-tuning.pyPython
1# Spark session configuration for production Kryo serializer
Problem / Challenge:Wide-dependency transformations (joins, aggregations) fail with OutOfMemoryError in reduce tasks, or run sluggishly due to partition starvation.
Production Solution: Target 2-3 tasks per available executor CPU core and keep partition sizes between 100MB and 200MB. Replace groupByKey with reduceByKey/aggregateByKey to perform map-side combines.
Zero network shuffle on bucketed joins, 5x-10x query speedup
Problem / Challenge:Large-scale Sort-Merge Joins trigger massive network exchanges, local disk shuffle spills, and straggler tasks caused by data skew.
Production Solution: Force Broadcast Hash Join (BHJ) for lookup tables using BROADCAST hint. Pre-bucket persistent tables on join keys to bypass shuffles entirely. Enable Adaptive Query Execution (AQE).
Problem / Challenge:Static query execution plans can neither adapt to post-filter partition sizes nor re-balance skewed partition keys discovered at runtime.
Production Solution: Enable Spark 3.x+ AQE flags to automatically coalesce small shuffle partitions, dynamically convert Sort-Merge Joins to Broadcast Joins, and split skewed partitions into sub-tasks.
Eliminates straggler tasks; 8x-15x faster completion on skewed joins
Problem / Challenge:Severe key skew (e.g. 80% of rows have null or default customer_id = 0) causes 199 tasks to finish in 10s while 1 single task runs for 3 hours and aborts with ExecutorLostFailure / OOM.
Production Solution: Salt the skewed key on the large table with a pseudo-random integer (0 to N-1). Explode the dimension table with an array of all salts [0..N-1] and join on composite salted key.
Problem / Challenge:A query runtime spikes from 3 minutes to 6 hours because Catalyst fell back to BroadcastNestedLoopJoin (BNLJ) due to non-equi join conditions (e.g. df1.start <= df2.end), creating an O(M * N) Cartesian cross.
Production Solution: Rewrite non-equi joins by creating synthetic interval/bucket keys for equi-joining, then applying the inequality filter post-join. Verify in explain plan that BNLJ is eliminated.
100% elimination of disk spill; 4x-8x stage speedup
Problem / Challenge:Spark UI shows high 'Spill (Memory)' and 'Spill (Disk)'. Tasks slow to a crawl from heavy disk serialization/deserialization, and nodes crash with No space left on device.
Production Solution: Increase partition count to reduce per-partition memory payload to <128MB. Increase spark.memory.fraction to 0.75 and boost spark.executor.memoryOverhead to prevent off-heap spill thrashing.
spark-shuffle-spill-remedy.pyPython
1# 1. Calculate target partitions based on spill volume:
Scales state from 5M to 200M+ keys with zero heap growth
Problem / Challenge:Long-running Structured Streaming jobs with stateful windowing or flatMapGroupsWithState crash with OutOfMemoryError after 48 hours because default HDFS state store keeps everything in JVM heap.
Production Solution: Switch the streaming state store provider to RocksDB. RocksDB stores state in off-heap memory and local SSD SST files, committing only incremental state deltas to cloud storage.
spark-streaming-rocksdb-state-store.pyPython
1# Production Spark Structured Streaming configuration for RocksDB State Store
Restores Whole-Stage CodeGen; 4x-5x speedup on wide tables
Problem / Challenge:Wide schemas (300+ columns or massive nested CASE WHEN statements) silently disable Whole-Stage CodeGen with 'Code of method exceeds 64K bytes'. Pipeline performance collapses 5x.
Production Solution: Diagnose via explain(mode='codegen'). Split massive projections across intermediate local checkpoints, or tune spark.sql.codegen.maxFields to force Catalyst to split generated Java classes.
spark-whole-stage-codegen-decompilation.pyPython
1# 1. Restrict number of fields per generated class to prevent 64KB bytecode breach
5# 2. Check for Whole-Stage CodeGen failure in query plan:
6# In df.explain(): Look for "*" before operators (e.g. "*(1) Project").
7# Absence of "*" indicates CodeGen compilation failure and fallback to slow Volcano iterator!
8
9# 3. Production refactoring pattern: Break wide projections into 2 stages
10stage1_cols =[c for c in df.columns[:150]]
11stage2_cols =[c for c in df.columns[150:]]
12
13# Process in 2 smaller projections rather than 1 monolithic 300-column expression
14df_split1 = df.select(*stage1_cols,"id")
15df_split2 = df.select(*stage2_cols,"id")
16result_df = df_split1.join(df_split2,"id")
Critical• Low Effort
Remedying FetchFailed & Shuffle File Not Found
100% elimination of transient network timeout job aborts
Problem / Challenge:Jobs fail after hours with 'FetchFailedException: Shuffle file not found' or 'Connection reset by peer', triggering whole-stage recalculation cascades until maxFailures aborts the job.
Production Solution: Root causes: Executor GC pause > 120s or worker node preemption. Increase network timeout to 800s, enable retry backoff, and use external shuffle service or unregister shuffle outputs gracefully.
spark-shuffle-file-not-found.pyPython
1# Production Spark resiliency configuration block for network & shuffle
Decoupled deployment cycles, 0 Airflow redeployments for SQL changes
Problem / Challenge:Data engineering teams often blur architectural boundaries: writing warehouse SQL transformations directly inside Airflow PythonOperators or building complex external triggers inside dbt.
Production Solution: Airflow handles macro orchestration (cross-system dependencies, cloud IAM, sensor polling, alerting, triggering reverse ETL). dbt exclusively manages internal SQL dependency DAGs and data modeling.
orchestration-airflow-dbt-separation.pyPython
1# Airflow DAG definition: High-level orchestration boundary
Problem / Challenge:Generating one Airflow task per individual dbt model creates DAGs with 500+ tasks, exhausting Celery/Kubernetes workers with overhead and hitting warehouse connection limits.
Production Solution: Bundle dbt runs by business domain, SLA tier, or tag (e.g. tag:finance_daily or staging.*). Let dbt compile and thread the internal model graph in warehouse memory.
orchestration-dbt-job-bundling.shBash
1# Production dbt selection syntax: Bundle models intelligently
2
3# 1. Run only critical revenue models and their direct parents:
4# dbt run --select +tag:revenue_daily
5
6# 2. Run an entire domain Mart including upstream snapshots and staging:
7# dbt build --select tag:marketing_mart
8
9# 3. State-based Slim CI for PR validation (runs ONLY modified models & tests):
Zero false-positive zombie kills on multi-hour tasks
Problem / Challenge:Long-running Airflow tasks abruptly abort with 'TaskInstance marked as zombie: job.state is running, but process does not exist'. Pipelines fail unpredictably without worker error trace.
Production Solution: Identify root cause: 1) Pod killed by K8s OOM (SIGKILL 137), or 2) Celery worker heartbeat timeout under high CPU. Adjust scheduler zombie threshold and tune K8s liveness probes.
orchestration-airflow-zombie-task-triage.shBash
1# airflow.cfg production tuning
2[scheduler]
3# Increase zombie check interval to avoid premature kills during cluster CPU spikes
20# Prevent zombie kills by delegating execution asynchronously
21)
High Impact• Low Effort
Eliminating Top-Level Code in DAG Files
95% reduction in DAG parse time; scheduler CPU drops from 100% to 15%
Problem / Challenge:Connecting to databases, querying S3, or importing heavy libraries outside of task callables causes the Airflow Scheduler to execute those queries every 30 seconds, driving CPU to 100%.
Production Solution: Enforce strict top-level code hygiene. Move all SQL connections, REST calls, and Variable.get() calls inside task functions or use Airflow Jinja templates.
17return hook.get_records("SELECT id FROM tenants")
18
19@task
20defprocess_tenant(tenant_id):
21print(f"Processing tenant {tenant_id}")
22
23 tenants = fetch_active_tenants()
24 process_tenant.expand(tenant_id=tenants)
25
26pipeline_clean()
Critical• Medium Effort
Bulletproof Idempotent Backfills & Partitioning
100% idempotent pipeline runs; zero data duplication
Problem / Challenge:Running Airflow historical backfills (catchup=True) with dynamic datetime.now() calls corrupts target data, writes to wrong partitions, and duplicates millions of financial records.
Production Solution: Enforce data_interval_start / data_interval_end (or ds macros). Configure Spark with partitionOverwriteMode=dynamic to safely overwrite only targeted partitions without touching historical data.
13# 3. Dynamic overwrite: Replaces ONLY the partition matching event_date='2026-10-03'
14# All other 364 partition folders remain completely untouched!
15daily_events_df.write \
16.format("delta") \
17.mode("overwrite") \
18.partitionBy("event_date") \
19.save(gold_table_path)
High Impact• Low Effort
ADF: Data Integration Units (DIU) & Throughput
10x throughput speedup (25 MB/s -> 350+ MB/s)
Problem / Challenge:Copying multi-terabyte tables or millions of files in Azure Data Factory / Synapse pipelines crawls at 20 MB/s, causing SLA breaches on daily morning ingestion.
Production Solution: Scale Data Integration Units (DIU) from auto (4) to 32-128, and configure degreeOfCopyParallelism=16-32. Set enableStaging=true when loading into Synapse or Snowflake.
orchestration-adf-diu-concurrency.shJSON
1{
2"name":"CopyLargeDatasetToLakehouse",
3"type":"Copy",
4"typeProperties":{
5"source":{
6"type":"SqlDWSource",
7"partitionOption":"DynamicRange",
8"partitionSettings":{
9"partitionColumnName":"transaction_id",
10"partitionUpperBound":"1000000000",
11"partitionLowerBound":"1"
12}
13},
14"sink":{
15"type":"ParquetSink",
16"storeSettings":{
17"type":"AzureBlobFSWriteSettings",
18"copyBehavior":"PreserveHierarchy"
19}
20},
21"enableStaging":false,
22"dataIntegrationUnits":64,
23"parallelCopies":32
24}
25}
Critical• Low Effort
Airflow DB Connection Pool Starvation
Supports 500+ concurrent worker tasks without DB connection starvation
Problem / Challenge:Bursts of 40+ concurrent DAG runs crash with OperationalError: FATAL: remaining connection slots are reserved for non-replication superuser connections.
Production Solution: Deploy PgBouncer in transaction mode between Airflow and Postgres. Configure Airflow sql_alchemy_pool_size=10, sql_alchemy_max_overflow=20, and enable pool_recycle.
orchestration-airflow-db-connection-pool.shBash
1# airflow.cfg database connection pooling
2[database]
3# Set bounded pool sizes per component (Scheduler, Webserver, Worker)
4sql_alchemy_pool_size =10
5sql_alchemy_max_overflow =20
6sql_alchemy_pool_recycle =1800
7sql_alchemy_pool_pre_ping = True
8
9# Point Airflow to PgBouncer transaction-mode port (6432) instead of raw PG (5432)
Eliminates CU throttling and workspace pause events
Problem / Challenge:Multiple Microsoft Fabric notebooks scheduled at 00:00 trigger Capacity Unit (CU) throttling (HTTP 429), placing the entire workspace into a degraded queue state.
Production Solution: Implement burst smoothing: stagger pipeline schedules by 5-minute offsets, configure notebook activity retry policies with exponential backoff, and leverage background Fabric smoothing algorithms.
Problem / Challenge:Developers wrap pipeline logic in broad try/except blocks, catching generic Exception and printing warnings. This swallows cluster infrastructure faults and disables engine retry logic.
Production Solution: Allow system exceptions to surface naturally to the runtime driver so built-in mechanisms (such as Spark's spark.task.maxFailures or Airflow task retries) can automatically recover.
28# NOTE: Network timeouts, Spot node preemptions, and OOMs are UNCAUGHT!
29# Spark and Airflow will auto-retry the task up to 4 times with exponential backoff.
Architect Level• Medium Effort
SQL Triage: CPU-Bound vs Wait-Bottleneck
Instant triage in <30 seconds during outages
Problem / Challenge:Engineers waste hours optimizing indexes or query SQL when a database query is actually stuck on row-level lock contention, network latency, or storage throttling.
Production Solution: Inspect the ratio between CPU time and Elapsed time. CPU ~ Elapsed means compute/logical read bound. Elapsed >> CPU indicates wait-state bottlenecks (locks, disk I/O, network).
debugging-sql-server-cpu-vs-wait.sqlSQL
1-- T-SQL: Diagnose live active requests and wait state distribution
Problem / Challenge:Large dataset streaming and batch pipelines in Databricks/Fabric degrade silently, with batch duration slowly creeping up until an SLA is breached.
Production Solution: Query the system.lakeflow.pipelines table and Delta Lake event_log to detect duration spikes, skew metrics, and data expectation failure rates programmatically.
debugging-lakehouse-pipeline-telemetry.sqlSQL
1-- Query Delta Live Tables / Lakeflow system event log for runtime spikes
2SELECT
3timestamp,
4 pipeline_id,
5 pipeline_name,
6 event_type,
7 details:flow_progress:metrics:num_output_rows AS output_rows,
8 details:flow_progress:data_quality:dropped_records AS dropped_records,
12ANDtimestamp>=current_timestamp()-INTERVAL7 DAYS
13ORDERBYtimestampDESC;
Critical• Low Effort
Triaging Exit Code 137: OOMKilled vs JVM Heap
Eliminates 100% of recurring container SIGKILL loops
Problem / Challenge:Spark executor abruptly dies with 'Exit Code 137' or 'Container killed by YARN for exceeding memory limits'. Engineers increase spark.executor.memory, but the container keeps dying.
Production Solution: Exit 137 (128 + 9 = SIGKILL) means the Linux OS / Kubernetes cgroup killed the pod from the outside because off-heap usage (Python worker, Arrow C++ buffers, RocksDB) exceeded limits. Increase spark.executor.memoryOverhead!
11# 3. For Kubernetes, ensure memory limits match request + overhead with 10% safety margin:
12# spark.kubernetes.executor.limit.cores = 4
13# spark.kubernetes.executor.request.cores = 4
High Impact• Low Effort
Spark UI Event Timeline: Straggler Triage
Root cause identified in under 1 minute from Spark UI
Problem / Challenge:A stage with 500 tasks has 499 tasks finish in 5 seconds, but task #382 hangs for 45 minutes. You need to rapidly identify whether it is Data Skew, GC Pause, or Node Throttling.
Production Solution: Open Spark UI Stage detail -> Event Timeline. Compare 'Shuffle Read Records' and 'Shuffle Read Size' across summary metrics (75th percentile vs Max). Use the 3-step triage heuristic.
debugging-spark-ui-straggler-triage.pyPython
1# SPARK UI TRIAGE HEURISTIC CHEAT CODE:
2#
3# Step 1: Check Stage Summary Metrics Table
4# If: Max Shuffle Read Size >>> 75th percentile (e.g. 75th = 50MB, Max = 4.2GB)
5# ==> ROOT CAUSE: DATA SKEW. Solution: Salt join key or enable AQE skewJoin.
6#
7# Step 2: Check Event Timeline Bar Color
8# If: Blue (Computing) is small, but Orange (Task Deserialization) or Brown (GC Time) is huge
Reduces stop-the-world GC pause duration by up to 85%
Problem / Challenge:Spark executors on heaps > 16GB spend 15%-30% of task time in GC pauses, triggering executor heartbeat timeouts and sluggish task throughput.
Production Solution: Configure G1GC parameters to trigger concurrent marking earlier (IHOP=35%) and increase reserve head-room (15%) to avoid stop-the-world Full GC evacuations.
debugging-g1gc-pause-tuning.shBash
1# Production extraJavaOptions for Driver and Executors (G1GC)
100% elimination of concurrent write collision exceptions
Problem / Challenge:Concurrent streaming writes or parallel micro-batches writing to the same Delta Lake table fail with ConcurrentAppendException or ConcurrentTransactionException.
Production Solution: Isolate write operations by target partition, enable Delta Lake 3.0+ Row-Level Concurrency, and configure optimistic transaction retry backoffs.
11-- 3. Partition isolation: Ensure concurrent jobs write strictly to disjoint partitions
12-- Job A writes region='EU'; Job B writes region='US'
Critical• Medium Effort
Kafka Consumer Lag & Rebalance Storm Triage
Zero stop-the-world rebalance storms; steady-state lag recovery
Problem / Challenge:Streaming pipeline encounters a surge in data volume. Processing time exceeds max.poll.interval.ms, causing the coordinator to declare the consumer dead and triggering an endless rebalance storm.
Production Solution: Enforce CooperativeStickyAssignor to eliminate stop-the-world rebalances. Increase max.poll.interval.ms to 600s, decrease max.poll.records to 2500, and ensure heartbeat.interval.ms is 1/3 of session timeout.
Problem / Challenge:A Spark job is stuck at 99% progress for 2 hours with 0% CPU utilization and 0 network I/O. The stage neither progresses nor throws an error.
Production Solution: Go to Spark UI -> Executors -> Thread Dump. Search for threads in BLOCKED, WAITING, or TIMED_WAITING state. Look for socketRead0 (database connection hang or external REST API lock).
debugging-executor-thread-dump-deadlock.pyPython
1# 1. SPARK UI THREAD DUMP INTERPRETATION:
2# Look for stack traces ending in:
3# java.net.SocketInputStream.socketRead0
4# -> JDBC database query hanging on table lock without query timeout!
5
6# 2. PREVENTION: Always set socket and query timeouts on JDBC reads:
9.option("dbtable","(SELECT * FROM audit_log WITH (NOLOCK)) as t") \
10.option("loginTimeout","30") \
11.option("queryTimeout","180") \
12.option("socketTimeout","300") \
13.option("fetchsize","10000") \
14.load()
Architect Level• Medium Effort
OpenTelemetry Distributed Tracing in PySpark
End-to-end trace correlation across orchestrator, cloud storage, and Spark
Problem / Challenge:Errors occurring deep inside distributed batch or streaming transforms cannot be linked back to the originating Airflow run, API trigger, or upstream microservice.
Production Solution: Propagate W3C traceparent headers through DataFrame metadata and initialize OpenTelemetry spans across driver job phases, emitting telemetry directly to Datadog, Dynatrace, or Azure App Insights.
95% faster file scan times, elimination of small file syndrome
Problem / Challenge:Streaming append workloads generate millions of 100KB parquet files ('Small File Syndrome'), causing storage IOPS bottlenecks and 10x slower reads.
Production Solution: Use Liquid Clustering (CLUSTER BY) in Delta Lake 3.0+ or regular OPTIMIZE with Z-ORDER. Schedule VACUUM to purge obsolete historical snapshots safely.
sql-storage-delta-compaction-clustering.sqlSQL
1-- 1. Delta Lake 3.0+ Liquid Clustering (Dynamic multi-dimensional clustering)
2CREATETABLE sales_silver (
3 transaction_id STRING,
4 customer_id STRING,
5 transaction_date DATE,
6 amount DECIMAL(18,2)
7)
8USING DELTA
9CLUSTER BY(transaction_date, customer_id);
10
11-- 2. Compaction (Bin-packing small files into optimal 128MB-1GB parquet files)
Recovers 40%-70% storage cost with 0% risk of reader corruption
Problem / Challenge:Running aggressive VACUUM with low retention (e.g. RETAIN 0 HOURS) deletes active files, causing concurrent readers and streaming checkpoints to crash with FileNotFoundException.
Production Solution: Always retain at least 168 hours (7 days) in production. Run VACUUM with DRY RUN first to audit candidate files. Enable parallel file deletion for 10x faster metadata cleanup.
sql-storage-delta-vacuum-safety.sqlSQL
1-- 1. AUDIT FIRST: Dry Run reports files to be purged WITHOUT deleting bytes
2VACUUM sales_gold RETAIN 168 HOURS DRY RUN;
3
4-- 2. Speed up physical S3/ADLS blob deletion using multi-threaded worker pools
8-- 3. Execute production cleanup preserving time travel and active readers
9VACUUM sales_gold RETAIN 168 HOURS;
10
11-- 4. Never bypass retention checks in production! (DO NOT SET retentionDurationCheck.enabled = false)
High Impact• Low Effort
Deletion Vectors (DVs): Sub-Second Row Updates
80%-90% reduction in write I/O during CDC MERGE
Problem / Challenge:In traditional Parquet/Delta, updating or deleting 1 single row in a 1GB Parquet file forces Spark to re-write the entire 1GB file, causing extreme write amplification and slow MERGE queries.
Production Solution: Enable Delta Lake Deletion Vectors (delta.enableDeletionVectors=true). Updates write tiny roaring-bitmap files marking deleted offsets without touching the base Parquet file.
sql-storage-deletion-vectors.sqlSQL
1-- 1. Enable Deletion Vectors on new or existing table
2ALTERTABLE customer_silver SET TBLPROPERTIES (
3'delta.enableDeletionVectors'=true
4);
5
6-- 2. Execute high-frequency CDC row updates and GDPR deletes in milliseconds
7DELETEFROM customer_silver WHERE user_id ='user_98234';
8
9-- 3. Batch MERGE runs up to 10x faster because unmodified rows are not rewritten
Problem / Challenge:Streaming CDC into Apache Iceberg experiences either slow write latency and timeout (Copy-On-Write) or degraded query latency from reading hundreds of equality delete files (Merge-On-Read).
Production Solution: Configure write.delete.mode='merge-on-read' for high-throughput CDC pipelines. Pair with automated asynchronous compaction (rewrite_data_files) to periodically collapse deletes.
sql-storage-iceberg-cow-vs-mor.sqlSQL
1-- 1. Configure Iceberg table for fast low-latency streaming writes
2CREATETABLE lakehouse.telemetry_iceberg (
3 device_id STRING,
4 reading_time TIMESTAMP,
5 metrics MAP<STRING,DOUBLE>
6)
7USING ICEBERG
8TBLPROPERTIES (
9'write.delete.mode'='merge-on-read',
10'write.update.mode'='merge-on-read',
11'write.parquet.compression-codec'='zstd'
12);
13
14-- 2. Production scheduled compaction procedure (runs hourly in background):
15-- Bin-packs small files and merges equality/positional delete files into clean Parquet!
90% reduction in cloud storage metadata GET requests; 3x faster column decompression
Problem / Challenge:Generating Parquet files with tiny row groups (e.g. 5MB) causes massive footer metadata bloat, excessive HTTP range requests in S3/ADLS, and disabled SIMD vectorization.
Production Solution: Configure parquet.block.size=134217728 (128MB) and enable dictionary encoding. For high-throughput cloud storage, 128MB-256MB row groups yield optimal sequential reads.
sql-storage-parquet-row-group-tuning.pyPython
1# Optimal production Parquet write configuration in Spark
2spark = SparkSession.builder \
3.config("spark.hadoop.parquet.block.size",134217728)# 128MB Row Group
Problem / Challenge:A stored procedure in Azure SQL / Synapse runs in 200ms for 99% of executions, but suddenly spikes to 45 minutes because the query optimizer cached a plan for an atypical parameter value.
Production Solution: Use OPTION (OPTIMIZE FOR UNKNOWN) or assign procedure parameters to local variables inside the procedure body to force the optimizer to use average distribution statistics.
sql-storage-tsql-parameter-sniffing.sqlSQL
1-- ANTI-PATTERN: Parameter sniffing causes execution plan cache poisoning
2CREATEORALTERPROCEDURE GetCustomerTransactions
3@CustomerIDINT,@StartDateDATE
4AS
5BEGIN
6-- Query plan compiled based on first executed parameter values!
19OPTION(OPTIMIZEFOR UNKNOWN);-- Uses average density vector, never skew-poisons!
20END;
21GO
22
23-- PRODUCTION CHEAT CODE: Method 2 - Local Variables
24-- DECLARE @LocalCustID INT = @CustomerID; (Optimizer cannot sniff local variables!)
High Impact• Medium Effort
Resolving Distributed Hash Spills to TempDB
Eliminates TempDB disk contention; 4x-6x faster analytical joins
Problem / Challenge:In Azure Synapse Dedicated SQL Pools, Snowflake, or Databricks SQL, large hash joins and aggregations exceed allocated memory grants and spill gigabytes to disk, causing query timeouts.
Production Solution: In Synapse, scale the user Resource Class from SmallRC to StaticRC20/MediumRC. In Snowflake, scale warehouse size or prune wide columns to ensure hash tables fit in memory.
sql-storage-distributed-hash-spill.sqlSQL
1-- 1. T-SQL: Detect live queries spilling to TempDB in Synapse / SQL DW
2SELECT
3 r.request_id,
4 r.command,
5 r.total_elapsed_time,
6 m.resource_class,
7 m.requested_memory_kb,
8 m.granted_memory_kb
9FROM sys.dm_pdw_exec_requests r
10JOIN sys.dm_pdw_resource_waits rw ON r.request_id = rw.request_id
11JOIN sys.dm_pdw_exec_sessions s ON r.session_id = s.session_id;
12
13-- 2. PRODUCTION FIX: Grant 4x memory to ETL service principal
17-- Check query profile for: 'Bytes spilled to local storage' and 'Bytes spilled to remote storage'
18-- If remote spill > 0: Immediately scale warehouse from M to L, or refactor query to join on cluster key.
High Impact• Low Effort
Bloom Filter Indexes for High-Speed Lookups
95%-99% of Parquet files skipped on needle-in-a-haystack queries
Problem / Challenge:Point queries and dimension lookups searching for specific IDs (e.g. UUID, order_id, device_mac) in a 50TB Lakehouse table take minutes because Spark must scan every Parquet file.
Production Solution: Create Bloom Filter indexes on high-cardinality search columns. Bloom filters verify whether a value definitively does not exist in a file, skipping Parquet file downloads entirely.
sql-storage-bloom-filter-indexing.sqlSQL
1-- 1. Create Bloom Filter index on high-cardinality lookup key in Delta Lake
50% storage cost savings; zero ETL lag between Databricks and Snowflake
Problem / Challenge:Enterprises maintain duplicate data silos: Delta Lake tables for Databricks and Iceberg tables for Snowflake, doubling cloud storage bills and requiring complex ETL sync jobs.
Production Solution: Enable Delta Universal Format (UniForm). Delta automatically writes Iceberg metadata on commit, allowing Snowflake, Trino, and Fabric OneLake to query the exact same Parquet files zero-copy.
sql-storage-onelake-shortcut-delta-uniform.sqlSQL
1-- 1. Enable Delta UniForm on the table
2ALTERTABLE customer_gold SET TBLPROPERTIES (
3'delta.universalFormat.enabledFormats'='iceberg'
4);
5
6-- 2. Query from Snowflake as an External Iceberg Table directly pointing to ADLS/S3:
7-- Zero data copying! Snowflake reads the generated Iceberg metadata catalog.
13-- 3. In Microsoft Fabric: Create a OneLake Shortcut directly referencing the S3/ADLS path
14-- Fabric Direct Lake mode immediately serves Power BI reports with zero data duplication!
Critical• Low Effort
dbt Automated Data Integrity Gates
Zero downstream data corruption reaches executive marts
Problem / Challenge:Silent schema drift, null primary keys, and orphaned foreign key references leak into production Gold-layer reporting tables, corrupting downstream dashboards.
Production Solution: Enforce schema validation and dbt tests (unique, not_null, relationships, accepted_values) directly in dbt model YAML files. Fail the pipeline before publishing.
best-practices-dbt-data-integrity.shBash
1version: 2
2
3models:
4 - name: fct_customer_orders
5 description: "Gold enterprise fact table for financial order reporting"
Zero disk spill, 40-60% cloud compute cost savings
Problem / Challenge:Over-relying on cloud auto-scaling (Databricks/AWS Glue) inflates monthly cloud bills by 4x without fixing the root cause: shuffle spill to disk.
Production Solution: Monitor the Spark UI for Spill (Memory) and Spill (Disk). Allocate executor memory overhead correctly and configure spot workers with on-demand drivers.
60%-80% compute cost savings with 99.9% pipeline completion SLA
Problem / Challenge:Using 100% Spot instances saves 70% in compute cost, but cloud provider 2-minute preemption notices kill executors, causing task retry cascades and lost shuffle stages.
Production Solution: Configure Spark Node Decommissioning. When a spot interruption signal is received, Spark immediately stops scheduling tasks on the node and migrates shuffle/cached blocks to remaining workers.
9# - WORKER NODES: Spot with fallback to On-Demand when Spot market is tight
10# - Max worker failure tolerance:
11spark.conf.set("spark.task.maxFailures","8")
Architect Level• Medium Effort
Zero-Downtime Expand-Contract Schema Evolution
Zero downtime schema evolution; zero breaking changes across 50+ consumers
Problem / Challenge:Renaming or changing the type of a column in production breaks downstream BI dashboards and downstream ETL pipelines simultaneously, requiring high-risk synchronized maintenance windows.
Production Solution: Apply the Expand-Contract pattern: Phase 1 (Expand): Add new column and dual-write. Phase 2: Migrate readers. Phase 3 (Contract): Safely drop or null-out the old column after validation.
best-practices-expand-contract-schema.pyPython
1# PHASE 1 (EXPAND): Add new column, dual-write to both legacy & new formats
2# target schema: user_id, phone_number (legacy), phone_e164 (new standard)
10# PHASE 2: Migrate downstream consumer models to read 'phone_e164'
11
12# PHASE 3 (CONTRACT): After 30 days, clean up legacy column with Delta Column Mapping
13# ALTER TABLE users SET TBLPROPERTIES ('delta.columnMapping.mode' = 'name');
14# ALTER TABLE users DROP COLUMN phone_number;
Critical• Medium Effort
Runtime Data Contracts & Schema Quarantine
100% guarantee of clean Silver/Gold data; automated DLQ routing
Problem / Challenge:Upstream microservices change payload formats or send invalid null values, silently polluting Lakehouse Silver/Gold tables and breaking executive reports.
Production Solution: Enforce strict Pydantic / Pandera data contracts at the ingestion boundary. Valid records flow to Silver; invalid records are routed to an audit Dead Letter Queue (DLQ) without crashing.
24# Route invalid to DLQ S3/ADLS path, valid to Silver
25return valid_rows
Critical• Low Effort
Hardening Log Redaction & Secret Masking
Zero credential leaks in cluster logs and Spark UI
Problem / Challenge:Database credentials, API tokens, and AWS/Azure access keys get leaked into cluster driver logs, stdout, or the public Spark UI Environment tab.
Production Solution: Configure spark.redaction.regex to automatically mask credentials in the Spark UI and logs. Use cloud IAM Managed Identities or Key Vault secrets providers instead of hardcoding tokens.
best-practices-secrets-masking-audit.pyPython
1# 1. Spark session configuration for secret redaction in UI and logs
100% deterministic state reconciliation; zero lost updates
Problem / Challenge:Out-of-order CDC events cause older updates to overwrite newer inserts in target tables ('Lost Update Problem'), corrupting financial balances and customer states.
Production Solution: Include a monotonically increasing commit timestamp or watermark in the MERGE condition: WHEN MATCHED AND s.updated_at >= t.updated_at THEN UPDATE. Filter out obsolete CDC updates.
best-practices-idempotent-merge-watermark.sqlSQL
1-- Production Stateful CDC MERGE in Delta Lake
2MERGEINTO gold_customers AS target
3USING(
4-- Step 1: Deduplicate source batch to keep ONLY latest mutation per primary key
35%-50% reduction in non-production cloud compute waste
Problem / Challenge:Engineers leave expensive multi-node GPU or high-concurrency clusters running over the weekend, wasting tens of thousands of cloud dollars on zero workloads.
Production Solution: Enforce workspace-wide Databricks / EMR cluster policies: set hard auto-termination limits (15-20 min), mandate cost-center tags, and restrict expensive node types for dev work.
best-practices-finops-cluster-policies.shJSON
1{
2"autotermination_minutes":{
3"type":"range",
4"maxValue":20,
5"defaultValue":15,
6"isOptional":false
7},
8"node_type_id":{
9"type":"allowlist",
10"values":[
11"Standard_D4ds_v5",
12"Standard_D8ds_v5",
13"Standard_E8ds_v5"
14],
15"defaultValue":"Standard_D4ds_v5"
16},
17"custom_tags.CostCenter":{
18"type":"regex",
19"pattern":"^[A-Z]{3}-[0-9]{4}$",
20"isOptional":false
21},
22"spark_conf.spark.databricks.cluster.profile":{
23"type":"fixed",
24"value":"singleNode"
25}
26}
CRITICAL• MEDIUM
Zero-Downtime Schema Migration with gh-ost
Zero query stalls; replica lag strictly bounded under 1.5s; 100% rollback safety using preserved _old table.
Problem / Challenge:Running native ALTER TABLE on 500GB+ tables in production acquires exclusive Metadata Locks (MDL), blocking client queries, exhausting connection pools, and causing site-wide outages.
Production Solution: Execute non-blocking online schema change using gh-ost with dynamic throttling, replica lag monitoring, triggerless binlog streaming, and an atomic cutover.
cs-db-ghost-ddl.shBash
1# Run non-blocking schema migration with gh-ost
2gh-ost \
3--host="prod-mysql-primary.internal"\
4--user="migration_user"\
5--password="${DB_PASSWORD}"\
6--database="finance"\
7--table="transactions"\
8--alter="ADD COLUMN status_v2 VARCHAR(32) DEFAULT 'PENDING' NOT NULL, ADD INDEX idx_status_created (status_v2, created_at)"\
Replaces 64-shard scatter-gather with single-shard point routing, cutting P99 latency from 140ms to 2.4ms.
Problem / Challenge:Queries without explicit sharding keys or unmapped secondary columns broadcast scatter-gather queries across all 64 shards, saturating network bandwidth and CPU.
Production Solution: Inspect VTGate query execution plans using EXPLAIN FORMAT=VITESS and verify VSchema primary and lookup vindex bindings.
cs-db-vitess-vschema-debug.sqlSQL
1-- 1. Explain Vitess query plan to detect Scatter-Gather vs Single-Shard Route
Zero user reports of stale data post-mutation; primary database CPU remains protected under 35%.
Problem / Challenge:Users update profiles or submit orders, but immediate page reloads route to lagging read replicas, displaying stale data or 404s.
Production Solution: Capture the transaction GTID upon write commit and route subsequent user reads to replicas that have caught up, or pin temporarily to Primary.
Drops database memory usage by 80%; eliminates connection handshake latency (<1ms pool checkout vs 45ms TCP/SSL handshake).
Problem / Challenge:1,000+ serverless Lambda or Kubernetes microservice containers open direct database connections, exhausting OS file descriptors and driving PostgreSQL/MySQL CPU to 100% on context switching.
Production Solution: Deploy transaction-level connection pooling via PgBouncer or AWS RDS Proxy and tune max_connections and pool size formulas.
20# Client timeouts to prevent zombie connection holding
21server_idle_timeout =600
22client_idle_timeout =120
23query_timeout =30
24max_user_connections =10000
HIGH• LOW
Eliminate DynamoDB Hot Partitions via Key Salting
Zero ProvisionedThroughputExceededException errors; uniform distribution across DynamoDB storage partitions.
Problem / Challenge:A viral event or celebrity account sends 15,000 writes/sec to a single DynamoDB partition key, breaching the hard 1,000 WCU partition limit and throwing ProvisionedThroughputExceededException.
Production Solution: Distribute write traffic uniformly across multiple physical partitions using deterministic partition key salting.
cs-db-dynamodb-salted-keys.pyPython
1import boto3
2import random
3from typing import List, Dict, Any
4
5dynamodb = boto3.resource('dynamodb')
6table = dynamodb.Table('LiveEventTelemetry')
7
8NUM_PARTITION_SLICES =16# Distribute across 16 physical partitions (16,000 WCU capacity)
Eliminates write stall pauses; reduces P99 point-lookup read latency by 85% by keeping Level 0 file count under 8.
Problem / Challenge:Bursty write workloads in RocksDB, CockroachDB, or Cassandra cause Level 0 SSTables to accumulate (>25 files), triggering write stalls and read latency degradation.
Production Solution: Scale background compaction thread allocations, configure rate-limited disk bandwidth, and tune write buffer thresholds.
cs-db-lsm-compaction-triage.shINI
1# RocksDB / CockroachDB Storage Engine Tuning for Heavy Write Throughput
2[rocksdb]
3# 1. Increase Level 0 write-stall threshold to tolerate bursts
Problem / Challenge:Synapse Dedicated query hangs at 98% execution or runs 10x slower than normal due to data skew across 60 distributions.
Production Solution: Run DBCC PDW_SHOWSPACEUSED to identify skewed distributions where one distribution holds >20% of total rows. Create a high-cardinality synthetic hash key and rebuild the table with CTAS.
cs-synapse-skew-wlm.sqlSQL
1-- 1. Check row distribution across all 60 distributions
2DBCC PDW_SHOWSPACEUSED('dbo.FactSales');
3
4-- 2. Identify skewed column and rehash table on synthetic composite key
15RENAME OBJECT dbo.FactSales_Rehashed TO FactSales;
High Impact• Low Effort
Fabric Direct Lake Mode Fallback Troubleshooting
Restores sub-second VertiPaq memory paging
Problem / Challenge:Power BI dashboard suddenly drops from sub-second latency to 15-second timeouts due to silent fallback from Direct Lake to DirectQuery.
Production Solution: Inspect DAX Studio server timings for DirectQuery fallback events. Pre-compute DAX calculated columns upstream in Spark/dbt, strip high-cardinality decimal strings, and enforce RLS in the Semantic Model rather than Lakehouse SQL endpoint.
cs-fabric-directlake-tshoot.pyPython
1--1. In DAX Studio, verify Direct Lake query events
2-- Query: Check if event 'Direct Lake'or'DirectQuery'is triggered
3
4--2. Upstream Spark optimization: Pre-calculate all DAX calculated columns
Eliminates HTTP 429 throttling, cuts RU waste by 60%
Problem / Challenge:Cosmos DB client throws HTTP 429 (RequestRateTooLarge) errors despite provisioned RU/s appearing sufficient in Azure Monitor.
Production Solution: Locate the hot partition causing 10,000 RU/s saturation. Re-partition the container using a synthetic key appending a randomized or modulus hash suffix, and exclude high-cardinality audit fields from automatic indexing.
cs-cosmos-ru-tuning.shJavaScript
1// 1. Generate Synthetic Partition Key on document write
Problem / Challenge:Lakehouse storage bills grow uncontrollably and query scan times increase due to millions of dead Parquet file snapshots and small micro-files.
Production Solution: Execute OPTIMIZE with Liquid Clustering / Z-Ordering on primary join keys, followed by VACUUM to purge historical commit snapshots older than retention threshold.
cs-deltalake-vacuum-opt.sqlSQL
1-- 1. Compact small files into optimal 128MB-512MB chunks & cluster by join keys
2OPTIMIZE gold.fact_orders
3ZORDER BY(customer_id, order_date);
4
5-- 2. Verify retention threshold before purging
6-- Note: Default safety threshold is 168 hours (7 days)
7VACUUM gold.fact_orders RETAIN 168 HOURS;
8
9-- 3. Dry run check: count files eligible for deletion without deleting
Synapse COPY INTO Parquet Tuning & Error Handling Runbook
Load throughput scales from 15 MB/s to 1.2 GB/s; eliminates out-of-memory errors
Problem / Challenge:Synapse Dedicated SQL Pool COPY INTO statement fails with cryptic type conversion errors or crawls at 10 MB/s due to smallrc memory starvation and TempDB spilling.
Production Solution: Assign the ETL ingestion service principal to staticrc60 or xlargerc. Use explicit column mapping in the COPY INTO statement, configure Managed Identity authentication, and set MAXERRORS with an ADLS Gen2 ERRORFILE quarantine path.
cs-openformat-synapse-copyinto.sqlSQL
1-- 1. Elevate loading user memory class to prevent TempDB spill during columnstore compression
21-- 3. Verify row group quality post-load (ensure row count > 800,000 per rowgroup)
22SELECT
23 t.name AS table_name,
24 rg.row_group_id,
25 rg.total_rows,
26 rg.state_desc
27FROM sys.pdw_nodes_column_store_row_groups rg
28JOIN sys.pdw_nodes_tables nt ON rg.pdw_node_id = nt.pdw_node_id AND rg.object_id = nt.object_id
29JOIN sys.pdw_table_mappings tm ON nt.name = tm.physical_name
30JOIN sys.tables t ON tm.object_id = t.object_id
31WHERE t.name ='FactOnlineSales';
High Impact• Low Effort
Lakehouse Small File Compaction & S3/ADLS 503 Runbook
Eliminates HTTP 503 errors; 10x-50x reduction in query scan duration
Problem / Challenge:SQL loading pipelines and Spark queries experience catastrophic slowdowns and HTTP 503 SlowDown errors caused by scanning 200,000 tiny (50KB) files generated by streaming sinks.
Production Solution: Enable auto-compaction and optimized writes in the streaming writer. Deploy a scheduled bin-packing maintenance job using Delta OPTIMIZE with Z-Order clustering to target 256MB-512MB file sizes.
cs-openformat-small-file-compaction.sqlSQL
1-- 1. In Spark streaming jobs, enable auto-compaction and optimized writes
Zero arithmetic overflow exceptions; guarantees financial ledger accuracy
Problem / Challenge:Parquet-to-SQL ingestion fails with 'Arithmetic overflow error converting expression to data type numeric' because Spark generated DECIMAL(38,18) while SQL table expects DECIMAL(18,2).
Production Solution: Enforce explicit schema casting in upstream Spark Silver layers, configure Auto Loader schema rescue columns, and use staged CAST views in SQL.
3x-5x faster ingestion than raw JSON; preserves nested schemas flawlessly
Problem / Challenge:Loading nested or semi-structured Parquet files into Snowflake relational tables causes type mismatch errors or drops nested attributes.
Production Solution: Leverage Snowflake's vectorized Parquet scanner to query stage files directly with column-path syntax, or use MATCH_BY_COLUMN_NAME to resolve columns by name rather than position.
cs-openformat-snowflake-parquet-variant.sqlSQL
1-- 1. Create target table with typed relational columns and VARIANT for nested payloads
10x storage compression, 5x-10x CPU reduction via batch mode, 90%+ I/O eliminated
Problem / Challenge:Analytical aggregate queries scanning millions of rows in traditional rowstore tables suffer severe I/O bottlenecks and high CPU cycles due to row-by-row Volcano iteration and lack of columnar compression.
Production Solution: Deploy Clustered Columnstore Indexes with Dictionary Encoding, Bit-Packing, and RLE. Sort data by low-cardinality columns to maximize Run-Length runs and enable Segment Elimination. Leverage Batch Mode processing (~900 rows per vector) for 10x compression and 100x query acceleration.
Problem / Challenge:Enterprises attempt to build fragile custom metadata synchronization scripts across cross-tenant environments or assume Fabric workspace permissions control Purview governance assets, resulting in technical debt and compliance gaps.
Production Solution: Co-locate Fabric and Purview in the same Azure tenant for zero-copy native integration. Treat Purview as the non-destructive governance control plane (metadata enrichment in Purview does not modify source Fabric objects). Configure workspace permissions for automated lineage, implement separate compute engines for data quality rule execution, and enforce independent Purview RBAC.