Generated banner for Spark CLI
Generated logo for Spark CLI

Spark CLI

MicrosoftOfficialPublished by Microsoft

  • Skill
  • Tool integration
  • Software development
  • MIT
  • Low risk

From the author

Manages Fabric Spark work, including notebook cell code with %%configure, %%sql, PySpark and notebookutils, named notebook runs, Livy sessions, triage of failed or OOM notebook and pipeline Spark runs, and the Materialized Lake View (MLV) lifecycle. Load it before writing MLV SQL, since CREATE MATERIALIZED LAKE VIEW and its CONSTRAINT clause are Fabric-only. KQL materialized views belong to eventhouse-cli.
— the description by Microsoft

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

spark-cli is a mode-based dispatcher designed to handle the full lifecycle of Fabric Spark operations. It provides structured guidance for authoring notebook code, managing interactive Livy sessions, diagnosing Spark job failures, and operating Materialized Lake Views (MLV). The skill enforces strict operational boundaries, requiring users to select a specific mode (authoring, consumption, operations, or mlv) and consult the corresponding reference documentation before executing any commands to ensure correct API usage and payload construction.

What it does

  • Authors and manages Fabric notebook cell code including PySpark, Scala, and %%sql.
  • Facilitates interactive data analysis through Livy session management.
  • Diagnoses failed, slow, or OOM Spark jobs and pipeline runs.
  • Manages the complete lifecycle of Materialized Lake Views, including definition, scheduling, and refresh monitoring.
  • Provides standardized patterns for data engineering, schema management, and error handling in Fabric.
  • Ensures proper telemetry and authentication for all Fabric API interactions.

When to use it

  • When writing or updating code within Fabric notebooks.
  • When performing ad-hoc data analysis using interactive Livy sessions.
  • When investigating performance bottlenecks or failures in Spark jobs.
  • When creating, scheduling, or troubleshooting Materialized Lake Views.
  • When you need to ensure your Fabric API calls follow mandatory telemetry and authentication standards.

Advantages

  • Provides a structured, mode-based approach to prevent configuration errors.
  • Centralizes complex Spark and MLV management tasks into a single interface.
  • Enforces best practices for data engineering and schema management.
  • Reduces ambiguity by clearly defining the scope of Spark-related operations versus other CLI tools.

Usage examples

“Write a notebook cell that reads a bronze table and writes a silver Delta table.”
The agent enters 'authoring' mode, reads the relevant reference, and generates the appropriate PySpark code using Delta Lake patterns.
“My notebook run failed with an OOM error, can you help me diagnose it?”
The agent enters 'operations' mode, reads the diagnostic workflow reference, and guides the user through analyzing the job logs and metrics.
“Schedule a nightly refresh for the lakehouse MLV and show me its run history.”
The agent enters 'mlv' mode, reads the MLV reference, and executes the necessary API calls to configure the schedule and retrieve the history.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/microsoft/spark-cli/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip spark-cli.zip -d .agents/skills

Documentation by Microsoft

Telemetry — MANDATORY. Every api.fabric.microsoft.com call must carry x-ms-fabric-skill: spark-cli (az rest: --headers "x-ms-fabric-skill=spark-cli"), including every LRO poll, fabric_lro and retry. Snippets omit it — add it anyway.

CRITICAL NOTES

  1. To find the workspace details (including its ID) from workspace name: list all workspaces and, then, use JMESPath filtering

  2. To find the item details (including its ID) from workspace ID, item type, and item name: list all items of that type in that workspace and, then, use JMESPath filtering

  3. Skill disambiguation: use spark-cli for notebook cell code (including %%sql cells), named notebook runs, Livy sessions, Spark failure triage, and everything about a Materialized Lake View -- writing the definition, reviewing a query for incremental-refresh readiness, and scheduling, refreshing, monitoring or diagnosing an existing one. A KQL materialized view in an Eventhouse is eventhouse-cli; plain read-only T-SQL against a Warehouse or Lakehouse SQL endpoint is sqldw-cli.

  4. Hard routing boundary: never execute an Eventhouse/KQL materialized-view request from this skill. Route it to eventhouse-cli; if that skill is unavailable, state that the request cannot be completed in the current skill context and stop without calling Fabric APIs or creating artifacts.

Fabric Spark and Materialized Lake Views -- CLI Skill

This one skill owns Fabric Spark: notebook cell authoring, notebook runs, Livy-session analysis, Spark failure diagnostics, and the whole Materialized Lake View lifecycle.

It is a mode dispatcher and contains NO procedures. Pick the mode that matches the request from the table below, then read the matching references/<mode>.md file end to end with your file-reading tool BEFORE issuing a single command. That file holds the endpoints, payload shapes, templates and gotchas; acting without it produces wrong payloads and wrong results.

Mode selection

ModeUse when the request ...Example triggersRead this first
authoringwrites notebook cell code (PySpark, Scala, SparkR, %%sql, %%configure), runs a notebook by name and reports a NORMAL run status, or authors a Materialized Lake View definition or reviews an MLV query for incremental-refresh readinesswrite notebook code, notebook cell code, %%sql cell, run notebook, execute notebook, notebookutils, create materialized lake view, is this MLV query incremental-refresh readyreferences/authoring.md
consumptionruns interactive ad-hoc PySpark in a Lakehouse Livy session -- never a notebookcreate a Livy session, run calculation in Livy, PySpark, DataFrame analysis, join tables across lakehouses, Delta time-travelreferences/consumption.md
operationsdiagnoses a FAILED, unhealthy, throttled or slow Spark notebook / pipeline / Livy runfailed notebook, Spark Livy health, Spark OOM, why is my notebook slow, job diagnostics, 430 throttlingreferences/operations.md
mlvdiscovers or operates EXISTING Materialized Lake Views: Spark SQL discovery, refresh schedules, on-demand refresh, run history, cancellation, and refresh-failure classificationdiscover MLVs, list materialized lake views, schedule MLV, MLV run history, cancel refresh, trigger MLV refresh, diagnose MLV refresh failurereferences/mlv.md
Mode boundary rule

Mode is decided by the artefact and the outcome, not by the language. A notebook cell is always authoring even when the cell is %%sql. A Livy session is always consumption. A Spark run that FAILED or is unhealthy is operations; a run that succeeded is reported by authoring. For a Materialized Lake View the VERB decides: writing or reviewing the definition is authoring, while discovering, scheduling, refreshing, monitoring or diagnosing existing MLVs is mlv. If discovery must be executed, switch to consumption for Livy or authoring for a notebook only after reading the discovery command from references/mlv.md.

If a request genuinely spans modes, handle them one at a time and read each reference before you start that part. If the mode is ambiguous after reading this table, ask one short clarifying question instead of guessing.

Terminal write -- the step you must not skip

Reading the reference and planning the change is NOT completing the task. Each mutating mode ends with one state-changing call. If you did not issue it, nothing was persisted -- say so explicitly rather than reporting success.

ModeTerminal write
authoringfor an EXISTING notebook, POST .../notebooks/{id}/updateDefinition to save the cell; a NEW notebook needs POST /v1/workspaces/{ws}/items first; for a run request, trigger the job via the Jobs API. Printing cell code into the chat is not saving or running it.
consumptionnone -- this mode is read-only
operationsnone -- this mode is read-only
mlvPOST /v1/workspaces/{ws}/lakehouses/{lakehouse}/jobs/refreshMaterializedLakeViews/instances for an on-demand refresh, or the schedule create/update/delete call for a scheduling request. Reporting what the schedule would be is not creating it.

Before you report the task done, confirm the terminal call returned success and, where the reference documents a readback, read the artefact back to prove the change landed.

Shared essentials (all modes)

Resolve the workspace and item first; every mode depends on it.

TaskReferenceNotes
Finding Workspaces and Items in FabricCOMMON-CLI.mdMandatory -- read before resolving any workspace or item id
Fabric Topology & Key ConceptsCOMMON-CORE.mdItem types, workspaces, capacities
Environment URLsCOMMON-CORE.mdSovereign / non-public cloud hosts
Authentication & Token AcquisitionCOMMON-CORE.mdWrong audience = 401; read before any auth issue
Authentication RecipesCOMMON-CLI.mdaz login flows and token acquisition
Core Control-Plane REST APIsCOMMON-CORE.mdPagination, LRO polling, rate limiting
Gotchas & TroubleshootingCOMMON-CLI.mdaz rest audience, shell escaping, token expiry

Rules

MUST
  • Select exactly one mode from the table above before doing anything else.

  • Treat vague requests such as "set up my data" as insufficiently scoped. Ask what artefact, source, destination, and outcome the user wants; never infer intent by finding and running an existing notebook or job.

  • Read references/<mode>.md end to end, as your FIRST tool call, before the first command of that mode. Read it ONCE, in a single full read: do not re-open it, do not grep it again, and do not page through it. You already have it.

  • Apply the same read-once discipline to every other file you load, including nested references/<mode>/*.md sub-references and shared common/*.md files: open only the ones you actually need, read each ONCE end to end, and never grep or re-open one you already loaded.

  • Resolve workspace and item ids by listing and filtering, never by guessing a GUID.

  • Stop after routing any Eventhouse/KQL materialized-view request. Unavailable sibling skills are a blocking boundary, not permission to implement their workload directly.

  • Announce a mode switch explicitly when the request crosses a boundary.

  • Treat the reference as instructions, never as the deliverable. After reading it, RUN the documented commands against the live workspace and report the real results. Quoting what the reference says instead of executing it does not answer the request.

PREFER
  • The narrowest mode that satisfies the request.

  • Reading exactly ONE mode reference. Load a second only when the request genuinely spans modes, and say so before you do.

  • Reporting the mode you chose in your first response so the user can correct you.

AVOID
  • Acting from this dispatcher alone -- it intentionally omits the operational detail.

  • Answering with a summary of the reference instead of executing it.

  • Re-reading or re-grepping a reference you already loaded; it costs turns and tokens.

  • Mutating anything while in a read-only mode.

  • Loading a different skill for work this family already owns (see CRITICAL NOTES 3).

  • Attempting an out-of-scope request through raw REST calls when the owning skill is unavailable. Name the owning skill and stop; do not improvise by creating Fabric artefacts outside spark-cli's scope.

Examples

User requestModeReference to read
"Write a notebook cell that reads the bronze table and writes a silver Delta table."authoringreferences/authoring.md
"Create a Livy session and count the distinct customers in the lakehouse."consumptionreferences/consumption.md
"My notebook run failed with an OOM -- work out why."operationsreferences/operations.md
"Is this materialized lake view query ready for incremental refresh?"authoringreferences/authoring.md
"Show me the Spark SQL command that lists the MLVs in schema silver."mlvreferences/mlv.md
"Schedule a nightly refresh for the lakehouse MLV lineage and show me its run history."mlvreferences/mlv.md

Files

references/authoring/resources/data-engineering-patterns.md

Data Engineering Patterns — Skill Resource

Essential patterns and principles for PySpark data engineering in Microsoft Fabric.

Must
  1. Always define explicit schemas for production data ingestion — avoid inferSchema=true which adds overhead and inconsistency

  2. Use Delta Lake format for all managed tables — provides ACID guarantees, time travel, and optimized reads

  3. Validate data quality at ingestion boundaries — check nulls, data types, and business rules before persisting

  4. Add metadata columns to track lineage — ingestion_timestamp, source_system, pipeline_run_id for debugging

  5. Handle errors gracefully — wrap ingestion/transformation logic in try-except with proper logging and recovery

  6. Use MERGE for upserts — leverage Delta Lake's MERGE INTO for incremental updates based on merge keys

  7. Partition large tables — use date or category columns for partition pruning to improve query performance

  8. If a real public source URL is provided, ingest from that source — download/copy into lakehouse Files/ first, then load with Spark from lakehouse paths (do not replace with synthetic inline rows)

Prefer
  1. Batch processing over streaming unless real-time requirements exist — simpler to debug and monitor

  2. Read-optimized writes for analytical workloads — use .coalesce() or .repartition() to right-size output files

  3. Window functions over self-joins — more efficient for ranking, running totals, and lag/lead operations

  4. Broadcast joins for small dimensions — use .broadcast() hint when one table fits in memory (<100MB)

  5. Columnar operations over row-wise — leverage DataFrame/SQL API instead of UDFs when possible

  6. Lazy evaluation mindset — build transformation chains, then execute with actions (.write(), .count())

Avoid
  1. Don't use .collect() on large DataFrames — brings all data to driver, causes OOM errors

  2. Don't chain multiple .count() calls — each triggers a full scan; cache DataFrame if needed

  3. Don't ignore skew — salting keys or adaptive query execution prevents straggler tasks

  4. Don't skip Delta optimization — run OPTIMIZE and VACUUM regularly to prevent small file problem

  5. Don't hardcode paths or credentials — use parameters and secure configuration patterns

  6. Don't mix append and overwrite carelessly — understand partition scope for .mode("overwrite")


Data Ingestion Principles

Schema Management

Guide LLM to define explicit schemas with nullable constraints, data type validation, and business context comments.

Note: This section refers to data schemas (DataFrame structure). For lakehouse schemas (databases/namespaces for organizing tables), see SPARK-AUTHORING-CORE.md Lakehouse Schema Organization.

Source Format Handling
  • CSV/TSV: Explicit schema, header option

  • Parquet/ORC: Columnar formats with embedded schema

  • JSON: multiLine option for nested objects

  • ADLS Gen2: abfss://[email protected]/path

  • OneLake: abfss://[email protected]/lakehouse.Lakehouse/Files/path

  • Public HTTP/HTTPS datasets: Download/copy to lakehouse Files/... first, then spark.read from lakehouse paths for stable runtime behavior

Validation Patterns
  • Completeness: Filter nulls in required fields

  • Referential integrity: Join with dimensions, flag orphans

  • Business rules: Domain-specific checks (amount > 0, date ranges)

  • Duplicates: dropDuplicates or groupBy to identify

Error Handling Strategy
  • Try-except blocks with specific exceptions

  • Contextual logging

  • Dead letter queues for invalid records

  • Retry logic for transient failures


Transformation Patterns

This section helps you choose the right transformation approach for your data pipeline. Each pattern follows a consistent structure: WHAT (definition), WHY (benefits/trade-offs), WHEN (decision criteria), and HOW (examples).


Pattern 1: Aggregations (GROUP BY + Aggregate Functions)

WHAT:
Summarize data by grouping rows and applying aggregate functions (SUM, COUNT, AVG, MIN, MAX, COLLECT_LIST).

WHY:

  • ✅ Single-pass computation (efficient)

  • ✅ Built-in Spark optimization (adaptive query execution)

  • ✅ Can combine multiple aggregates in one .agg() call

  • ⚠️ Large cardinality GROUP BY (millions of unique keys) may cause memory pressure

WHEN to use:

  • Summarizing metrics by dimension (e.g., revenue by region, counts by product)

  • Calculating totals, averages, or other statistical measures

  • Creating reporting tables or dashboards

  • Preparing data for visualization

Examples:

from pyspark.sql import functions as F

# Daily sales summary
df.groupBy("order_date") \
  .agg(
    F.sum("amount").alias("total_sales"),
    F.count("order_id").alias("order_count"),
    F.countDistinct("customer_id").alias("unique_customers")
  )

# Multi-dimensional aggregation
df.groupBy("product_category", "region", "order_date") \
  .agg(F.avg("discount_percent").alias("avg_discount"))

Alternative: If aggregation + periodic refresh → consider Materialized Lake View (see Pattern 4 below).


Pattern 2: Window Functions (Partitioned Analytics)

WHAT:
Perform calculations across rows related to the current row without collapsing them into groups. Common functions: ROW_NUMBER(), RANK(), LAG(), LEAD(), SUM() OVER (...), AVG() OVER (...).

WHY:

  • ✅ More efficient than self-joins for ranking/running totals

  • ✅ Preserves row-level detail while adding analytical columns

  • ✅ Supports complex ordering and partitioning logic

  • ⚠️ Large partitions can cause shuffle overhead (consider repartitioning by partition key first)

WHEN to use:

  • Ranking: Top N per category, percentile ranks

  • Running calculations: Cumulative sums, moving averages

  • Lag/Lead comparisons: Month-over-month change, previous transaction

  • Deduplication: ROW_NUMBER() + filter(rank == 1) to pick most recent (note: QUALIFY and window functions block MLV incremental refresh — keep window functions OUT of MLV; apply ranking in downstream notebook for IR-eligible pipelines. See mlv-incremental-refresh-patterns.md § Pattern 1)

Examples:

from pyspark.sql import functions as F
from pyspark.sql.window import Window

# Top 3 products per category by sales
window_spec = Window.partitionBy("category").orderBy(F.desc("sales"))
df.withColumn("rank", F.row_number().over(window_spec)) \
  .filter(F.col("rank") <= 3)

# Running total and previous value
window_running = Window.partitionBy("customer_id").orderBy("order_date") \
                       .rowsBetween(Window.unboundedPreceding, Window.currentRow)
df.withColumn("cumulative_spent", F.sum("amount").over(window_running)) \
  .withColumn("previous_order", F.lag("order_date", 1).over(window_running))

Alternative: Self-joins are less efficient but sometimes unavoidable for complex multi-step logic.


Pattern 3: Joins (Combining Datasets)

WHAT:
Merge two DataFrames based on a common key. Types: inner, left (left outer), right, full (full outer), cross, left_semi, left_anti.

WHY:

  • ✅ Standard relational operation for combining data

  • ✅ Broadcast joins (<100MB dimension table) avoid shuffle — very fast

  • ⚠️ Large-to-large joins can cause shuffle and spill to disk

  • ⚠️ Skewed keys (one key with millions of rows) create straggler tasks

WHEN to use:

Join TypeWhen to UseExample
InnerOnly matching rows neededTransactions + valid customers
Left (left outer)Keep all left rows, add right columns where match existsOrders + optional shipping details
BroadcastOne table <100MB, other is largeFact table + dimension table
Left SemiFilter left table by existence in right (like IN subquery)Customers who placed orders
Left AntiFind rows in left NOT in right (like NOT IN)Customers who never placed orders

Examples:

# Inner join: transactions + customer details
transactions.join(customers, on="customer_id", how="inner")

# Broadcast join: large fact table + small dimension
from pyspark.sql.functions import broadcast
fact_table.join(broadcast(dim_table), on="product_id")

# Left join: keep all orders, add customer name if exists
orders.join(customers, on="customer_id", how="left") \
      .select("order_id", "amount", "customer_name")

Tips:

  • For skewed joins, use salting (add random suffix to key)

  • Use df.explain() to verify broadcast join actually happened

  • Consider pre-filtering both DataFrames before joining to reduce shuffle


Pattern 4: Materialized Lake Views (Declarative Transformations)

WHAT:
A Fabric-native feature that materializes transformation results as Delta tables with automatic refresh, data quality constraints, and query optimization. Two authoring modes: Spark SQL (with incremental refresh) or PySpark (full refresh only).

WHY:

  • ✅ Declarative: Define WHAT to compute, not HOW (Fabric handles refresh scheduling)

  • ✅ Auto-refresh: Spark SQL MLVs support incremental refresh; PySpark MLVs use full refresh

  • ✅ Query-optimized: Pre-aggregated, indexed, faster than querying raw tables

  • ✅ Data quality: Built-in CHECK constraints with ON MISMATCH DROP|FAIL

  • ⚠️ Spark SQL MLVs: No Python UDFs, but support incremental refresh

  • ⚠️ PySpark MLVs: Support UDFs and complex logic, but full refresh only (no incremental)

  • ⚠️ Batch-only: Not suitable for real-time streaming

  • ⚠️ Incremental limitations: Some SQL patterns block incremental refresh (see mlv-incremental-refresh-patterns.md)

WHEN to use:

Use this decision tree to choose between MLVs and PySpark notebooks:

┌─ Is transformation logic pure SQL (SELECT/WHERE/JOIN/GROUP BY)?
│
├─ YES ─┐
│       ├─ Does it need periodic refresh (not real-time)?
│       │
│       ├─ YES ─┐
│       │       ├─ Would incremental refresh save cost (source is append-only + CDF enabled + query uses supported SQL constructs)?
│       │       │
│       │       ├─ YES → ✅ **Use Materialized Lake View**
│       │       │          (Declarative, auto-incremental, query-optimized)
│       │       │
│       │       └─ NO  → ⚠️  **MLV or Notebook**
│       │                   (Small tables: MLV. Large full-refresh: consider notebook with optimizations)
│       │
│       └─ NO (real-time) → ❌ **Use Structured Streaming Notebook/Job Definition + Eventstream**
│                               (MLVs are batch-only; use Spark Structured Streaming for real-time ingestion)
│
└─ NO (Python/complex logic) → ❌ **Use PySpark Notebook**
                                   (UDFs, iterative algorithms, external APIs, custom validation)

Concrete examples:

Use CaseTransformationBest ChoiceRationale
Daily sales aggregationSELECT date, SUM(amount) FROM transactions GROUP BY dateMLVPure SQL, periodic refresh, incremental on date
Customer lifetime valueDataFrame with complex Python UDFs, external API callsNotebookPython logic, not SQL-expressible
Bronze→Silver dedupSELECT * FROM bronze.orders WHERE date > watermark (then filter duplicates in notebook OR use Delta MERGE for upsert)Notebook or MERGE statementMERGE provides idempotent dedup; easier in notebook than MLV
ML feature engineeringComplex PySpark with pandas UDFs, statistics, outlier detectionNotebookIterative logic, scikit-learn integration
Real-time KPI dashboardStreaming aggregation over 5-minute windowsEventstreamReal-time requirement; MLVs are batch
Gold layer metricsSELECT product_id, AVG(rating), COUNT(*) FROM reviews GROUP BY product_idMLVRead-optimized, frequently queried, periodic refresh

Cost/performance considerations:

FactorMLVNotebook
Development timeFast (SQL-only)Moderate (PySpark code + testing)
Refresh overheadLow (auto-incremental)High (manual watermark logic)
Query performanceOptimized (pre-aggregated)Depends on Delta optimization
DebuggingLimited (SQL errors only)Full (logs, breakpoints, print statements)
FlexibilityLow (SQL-only)High (any Python/Spark API)

When to switch from MLV to Notebook:

  • MLV hits SQL limitations (no UDFs, complex Python logic needed)

  • Incremental refresh blockers appear (see mlv-incremental-refresh-patterns.md)

  • Debugging requires inspecting intermediate steps (MLV is declarative, no step-by-step visibility)

When to switch from Notebook to MLV:

  • Notebook logic simplifies to pure SQL after refactoring

  • Manual watermark/incremental logic becomes complex — let MLV handle it

  • Transformation is stable and needs less frequent iteration

See also:


Quick Decision Matrix
NeedUse This
Summarize metrics by dimensionAggregations (Pattern 1)
Rank/running totals preserving row detailWindow Functions (Pattern 2)
Combine data from two tablesJoins (Pattern 3)
SQL transformation with scheduled refresh + DQMaterialized Lake View (Pattern 4)
Python logic / UDFs / complex algorithmsPySpark Notebook
Real-time streamingEventstream → Lakehouse

Example Approaches

Customer Segmentation: Use window functions for lifetime metrics, when().otherwise() for classification, temporal dimensions for recency

Product Analytics: Join with dimensions, aggregate by category, rank with row_number(), compute percentiles


Delta Lake Best Practices

MERGE Operations (Upserts)

When to use:

  • Incremental loads where source sends changed/new records

  • Slowly changing dimensions (SCD Type 1 or Type 2)

  • Deduplication scenarios

Guide LLM to generate MERGE with:

  • .merge(source_df, "target.id = source.id") on unique key

  • .whenMatchedUpdateAll() to update existing records

  • .whenNotMatchedInsertAll() to insert new records

  • Optional: .whenMatchedDelete() for hard deletes based on condition

Optimization Strategies

Tell LLM to include:

  • Liquid Clustering (preferred over Z-Order and partitioning in Runtime 2.0+): Declarative, change-friendly clustering that replaces static partitioning and manual Z-Order maintenance

    • Create: CREATE TABLE t (...) CLUSTER BY (col1, col2) or ALTER TABLE t CLUSTER BY (col1, col2)

    • Apply: OPTIMIZE table_name (columns stored in metadata, no need to specify)

    • Full recluster: OPTIMIZE table_name FULL (rebuilds all Z-Cubes)

    • Change columns anytime: ALTER TABLE t CLUSTER BY (new_col) — applies on next OPTIMIZE

    • Remove: ALTER TABLE t CLUSTER BY NONE

    • Runtime 2.0 incremental mode: Only reclusters unclustered/unhealthy/small files (eliminates full-rewrite amplification)

    • Pick 1–4 columns from frequent WHERE clauses; Hilbert curve for 2+ columns

    • CAUTION (Runtime 1.3): Full Z-Cube rewrite every run — do NOT combine with auto compaction

  • Z-Ordering (legacy/Runtime 1.3): OPTIMIZE table_name ZORDER BY (frequently_filtered_column) — use only when liquid clustering is unavailable

  • VACUUM: VACUUM table_name RETAIN 168 HOURS cleans up old file versions after retention period

  • Partition pruning: Query with partition columns in WHERE clause to skip irrelevant data

  • File compaction: Run OPTIMIZE to combine small files into right-sized files (128MB-1GB)

Time Travel

Use cases:

  • Point-in-time queries: spark.read.format("delta").option("versionAsOf", 5).load(path)

  • Rollback bad writes: Restore to previous version with RESTORE TABLE table_name TO VERSION AS OF 10

  • Audit trail: Query historical data for compliance, debugging

Spark Session Configurations for Performance

Guide LLM to configure Spark sessions based on workload type:

Write-Heavy Workloads (Bronze Layer - High-Volume Ingestion):

  • spark.microsoft.delta.parquet.vorder.enabled = false — Disable V-Order for faster writes

  • spark.databricks.delta.optimizeWrite.binSize = 1073741824 — Target 1GB file size for fewer small files

  • spark.databricks.delta.autoCompact.enabled = true — Automatic compaction during writes

  • spark.microsoft.delta.optimize.fast.enabled = true — Fast optimization algorithms

  • spark.databricks.delta.properties.defaults.enableDeletionVectors = true — Efficient delete tracking

  • spark.microsoft.delta.targetFileSize.adaptive.enabled = true — Adaptive file sizing

  • spark.native.enabled = true — Use native execution engine (Velox)

  • spark.gluten.delta.columnMapping.name.enabled = true — Column mapping for schema evolution

Balanced Workloads (Silver Layer - Mixed Read/Write):

  • spark.microsoft.delta.parquet.vorder.enabled = true — Enable V-Order for better read performance

  • spark.databricks.delta.optimizeWrite.enabled = true — Balance write optimization with read efficiency

  • spark.microsoft.delta.snapshot.driverMode.enabled = true — Faster snapshot reads

  • spark.sql.adaptive.enabled = true — Adaptive query execution

  • spark.sql.adaptive.coalescePartitions.enabled = true — Dynamic partition coalescing

Read-Heavy Workloads (Gold Layer - Analytics & Reporting):

  • spark.microsoft.delta.parquet.vorder.enabled = true — V-Order for maximum read performance

  • spark.databricks.delta.optimizeWrite.enabled = false — No write optimization overhead

  • spark.sql.parquet.enableVectorizedReader = true — Vectorized Parquet reads

  • spark.sql.files.maxPartitionBytes = 134217728 — 128MB partition size for optimal parallelism

  • spark.sql.adaptive.enabled = true — Optimize query plans based on runtime stats

  • spark.databricks.delta.stalenessLimit = 0 — Always use latest snapshot

When to apply these configs:

  • Pass during Livy session creation: "conf": {"spark.config.key": "value"}

  • Set in notebook first cell before any Spark operations

  • Configure at workspace level for consistent defaults

  • Override per-job for specific workload requirements

Runtime 2.0 Performance Features (Spark 4.1, Delta 4.2)

⚠️ Public Preview: Fabric Runtime 2.0 (Spark 4.1, Delta Lake 4.x) is in Public Preview. Delta Lake 4.x features are experimental and should not be enabled for tables that interoperate across Fabric workloads. Only recommend the features below once the customer has explicitly accepted preview risk and validated on a non-production workspace — do not present them as default production guidance.

Native Execution Engine (NEE): Significant performance improvement over open-source Spark via vectorized C++ execution. See NEE documentation for current benchmarks.

NEE must be enabled before the session starts — either via the environment-level Acceleration setting or with %%configure in the notebook's first cell. A post-start spark.conf.set("spark.native.enabled", ...) does not reliably enable it.

%%configure
{
    "driverMemory": "56g",
    "executorMemory": "56g",
    "conf": {
        "spark.native.enabled": "true"
    }
}

Keep Livy resource fields such as driverMemory and executorMemory at the top level. Use conf only for Spark properties.

# Supported automatically once enabled:
# - Python UDFs (scalar and vectorized/Pandas UDFs)
# - Scala UDFs
# - Complex types: arrays, maps, structs (explode, array_contains, map_keys, etc.)
# - Vectorized CSV parsing
# - OPTIMIZE on liquid clustered tables

Efficient Scaledown (Remote Shuffle Manager): Decouples shuffle data from executor lifetime for improved compute efficiency. Requires NEE enabled (set at session start, above). Set the full stack at session start via %%configure so it applies before Spark initializes. See RSM documentation for the complete option list and benchmarks.

%%configure
{
    "conf": {
        "spark.remote.shuffle.enabled": "true",
        "spark.sql.rsm.decisionlayer.enabled.level": "stage",
        "spark.sql.adaptive.shuffleWrite.enabled": "true",
        "spark.storage.decommission.shuffleBlocks.enabled": "true",
        "spark.storage.decommission.shuffleBlocks.cleanup": "true",
        "spark.storage.decommission.shuffleBlocks.migrateToFallbackStorage": "true",
        "spark.storage.decommission.fallbackStorage.cleanUp": "true",
        "spark.dynamicAllocation.preventShutdownExecutorWithCache": "false",
        "spark.dynamicAllocation.excludeDeltaSnapshotCache": "true"
    }
}

Key benefits:

  • Large shuffles → Azure Blob Storage (fault-tolerant, eliminates FetchFailedException)

  • Small shuffles → local disk (low latency), migrated on decommission

  • Autoscaler releases nodes immediately after task completion

  • Prerequisites: NEE enabled + Autoscale recommended

Incremental Liquid Clustering (Runtime 2.0+ only):

Incremental clustering and auto-reclustering are enabled by default in Runtime 2.0, so you normally don't set anything — OPTIMIZE reclusters only new, changed, or unhealthy files instead of rewriting whole Z-Cubes. The documented session toggles (both default true) are:

# Master switch — set to False only to revert to full-rewrite behavior
spark.conf.set("spark.microsoft.delta.optimize.clustering.strategy.incremental", "true")
# Auto-detect and recluster overlapping files (applies only when incremental is on)
spark.conf.set("spark.microsoft.delta.optimize.clustering.strategy.incremental.autoRecluster", "true")

See Liquid clustering — Configuration reference for the full set of tuning options.

Runtime 2.0 Key Components:

  • Apache Spark 4.1, Delta Lake 4.2, Python 3.13, Java 21, Scala 2.13

  • PySpark native plotting API, Python Data Source API, Python UDTFs

  • Structured Streaming Arbitrary State API v2

  • Breaking change: Python upgrade requires re-publishing all Environments with libraries

  • Deprecated: WASB protocol for GPv2 accounts — use ABFS instead

  • Deprecated: SparkR (may be removed in future versions)


Quality Assurance Strategies

Testing Levels

Guide LLM to implement:

Unit Testing (local Spark):

  • Test transformation logic with small sample DataFrames

  • Use pytest fixtures to create test Spark session

  • Assert row counts, column values, schema correctness

  • Focus on business logic in isolation

Integration Testing (Fabric API):

  • Validate workspace/lakehouse creation succeeded

  • Test notebook deployment via REST API

  • Verify Livy session creation and code execution

  • Check end-to-end data flow through bronze → silver → gold

Data Quality Checks (production):

  • Row count validation: compare source vs target

  • Schema validation: ensure expected columns exist with correct types

  • Null checks: flag unexpected nulls in required fields

  • Range checks: validate numeric values within expected bounds

  • Freshness checks: ensure data updated within SLA timeframe

Quality Gates

Define when pipelines should fail:

  • Critical failures: schema mismatch, zero rows ingested, primary key violations

  • Warnings: elevated null rate, data volume anomaly (>20% change), late arrival

  • Monitoring: track ingestion lag, transformation duration, error rates over time

Logging and Observability

Prompt LLM to generate:

  • Structured logging: JSON-formatted logs with timestamp, severity, context

  • Metrics emission: log key counts (rows processed, errors, duration) for monitoring

  • Error context: capture input values, stack traces, environment details for debugging

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

What our scanner noticed· Low risk

  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Microsoft
Publisher
Microsoft (Official)
Source
microsoft/skills-for-fabric · skills/spark-cli · commit f8fac11
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Also distributed in
microsoft/skills-for-fabric

AutoGineer is not affiliated with or endorsed by Microsoft or Microsoft.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download spark-cli.zip