Spark CLI
MicrosoftOfficialPublished by Microsoft
- Skill
- Tool integration
- Software development
- MIT
- Low risk
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
Download the skill as a ZIP:
https://autogineer.com/agent-skills/microsoft/spark-cli/downloadUnzip 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.comcall must carryx-ms-fabric-skill: spark-cli(az rest:--headers "x-ms-fabric-skill=spark-cli"), including every LRO poll,fabric_lroand retry. Snippets omit it — add it anyway.
CRITICAL NOTES
To find the workspace details (including its ID) from workspace name: list all workspaces and, then, use JMESPath filtering
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
Skill disambiguation: use
spark-clifor notebook cell code (including%%sqlcells), 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 iseventhouse-cli; plain read-only T-SQL against a Warehouse or Lakehouse SQL endpoint issqldw-cli.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
| Mode | Use when the request ... | Example triggers | Read this first |
|---|---|---|---|
authoring | writes 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 readiness | write notebook code, notebook cell code, %%sql cell, run notebook, execute notebook, notebookutils, create materialized lake view, is this MLV query incremental-refresh ready | references/authoring.md |
consumption | runs interactive ad-hoc PySpark in a Lakehouse Livy session -- never a notebook | create a Livy session, run calculation in Livy, PySpark, DataFrame analysis, join tables across lakehouses, Delta time-travel | references/consumption.md |
operations | diagnoses a FAILED, unhealthy, throttled or slow Spark notebook / pipeline / Livy run | failed notebook, Spark Livy health, Spark OOM, why is my notebook slow, job diagnostics, 430 throttling | references/operations.md |
mlv | discovers or operates EXISTING Materialized Lake Views: Spark SQL discovery, refresh schedules, on-demand refresh, run history, cancellation, and refresh-failure classification | discover MLVs, list materialized lake views, schedule MLV, MLV run history, cancel refresh, trigger MLV refresh, diagnose MLV refresh failure | references/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.
| Mode | Terminal write |
|---|---|
authoring | for 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. |
consumption | none -- this mode is read-only |
operations | none -- this mode is read-only |
mlv | POST /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.
| Task | Reference | Notes |
|---|---|---|
| Finding Workspaces and Items in Fabric | COMMON-CLI.md | Mandatory -- read before resolving any workspace or item id |
| Fabric Topology & Key Concepts | COMMON-CORE.md | Item types, workspaces, capacities |
| Environment URLs | COMMON-CORE.md | Sovereign / non-public cloud hosts |
| Authentication & Token Acquisition | COMMON-CORE.md | Wrong audience = 401; read before any auth issue |
| Authentication Recipes | COMMON-CLI.md | az login flows and token acquisition |
| Core Control-Plane REST APIs | COMMON-CORE.md | Pagination, LRO polling, rate limiting |
| Gotchas & Troubleshooting | COMMON-CLI.md | az 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>.mdend 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>/*.mdsub-references and sharedcommon/*.mdfiles: 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 request | Mode | Reference to read |
|---|---|---|
| "Write a notebook cell that reads the bronze table and writes a silver Delta table." | authoring | references/authoring.md |
| "Create a Livy session and count the distinct customers in the lakehouse." | consumption | references/consumption.md |
| "My notebook run failed with an OOM -- work out why." | operations | references/operations.md |
| "Is this materialized lake view query ready for incremental refresh?" | authoring | references/authoring.md |
| "Show me the Spark SQL command that lists the MLVs in schema silver." | mlv | references/mlv.md |
| "Schedule a nightly refresh for the lakehouse MLV lineage and show me its run history." | mlv | references/mlv.md |
Files
- ATTRIBUTION.mdAdded by AutoGineer863 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md10.1 KiB
- apm.yml430 B
- references/authoring.md32.9 KiB
- references/authoring/resources/data-engineering-patterns.md24.3 KiB
- references/authoring/resources/development-workflow.md7.4 KiB
- references/authoring/resources/infrastructure-orchestration.md10.4 KiB
- references/authoring/resources/materialized-lake-view-patterns.md20.1 KiB
- references/authoring/resources/mlv-incremental-refresh-patterns.md11.4 KiB
- references/authoring/resources/notebook-api-operations.md14.2 KiB
- references/consumption.md12.3 KiB
- references/mlv.md32.8 KiB
- references/operations.md20.3 KiB
- references/operations/automated-diagnostic-workflow.md12.7 KiB
- references/operations/diagnostic-workflow.md7.3 KiB
- references/operations/job-diagnostics.md17.7 KiB
- references/operations/jobinsight-api.md7.2 KiB
- references/operations/performance-patterns.md17.4 KiB
- references/operations/pipeline-diagnosis.md20.8 KiB
- references/operations/session-health.md8.7 KiB
- references/operations/spark-history-server.md6.5 KiB
references/authoring/resources/data-engineering-patterns.md
Data Engineering Patterns — Skill Resource
Essential patterns and principles for PySpark data engineering in Microsoft Fabric.
Recommended patterns
Must
Always define explicit schemas for production data ingestion — avoid
inferSchema=truewhich adds overhead and inconsistencyUse Delta Lake format for all managed tables — provides ACID guarantees, time travel, and optimized reads
Validate data quality at ingestion boundaries — check nulls, data types, and business rules before persisting
Add metadata columns to track lineage —
ingestion_timestamp,source_system,pipeline_run_idfor debuggingHandle errors gracefully — wrap ingestion/transformation logic in try-except with proper logging and recovery
Use MERGE for upserts — leverage Delta Lake's
MERGE INTOfor incremental updates based on merge keysPartition large tables — use date or category columns for partition pruning to improve query performance
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
Batch processing over streaming unless real-time requirements exist — simpler to debug and monitor
Read-optimized writes for analytical workloads — use
.coalesce()or.repartition()to right-size output filesWindow functions over self-joins — more efficient for ranking, running totals, and lag/lead operations
Broadcast joins for small dimensions — use
.broadcast()hint when one table fits in memory (<100MB)Columnar operations over row-wise — leverage DataFrame/SQL API instead of UDFs when possible
Lazy evaluation mindset — build transformation chains, then execute with actions (
.write(),.count())
Avoid
Don't use
.collect()on large DataFrames — brings all data to driver, causes OOM errorsDon't chain multiple
.count()calls — each triggers a full scan; cache DataFrame if neededDon't ignore skew — salting keys or adaptive query execution prevents straggler tasks
Don't skip Delta optimization — run
OPTIMIZEandVACUUMregularly to prevent small file problemDon't hardcode paths or credentials — use parameters and secure configuration patterns
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]/pathOneLake:
abfss://[email protected]/lakehouse.Lakehouse/Files/pathPublic HTTP/HTTPS datasets: Download/copy to lakehouse
Files/...first, thenspark.readfrom 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 Type | When to Use | Example |
|---|---|---|
| Inner | Only matching rows needed | Transactions + valid customers |
| Left (left outer) | Keep all left rows, add right columns where match exists | Orders + optional shipping details |
| Broadcast | One table <100MB, other is large | Fact table + dimension table |
| Left Semi | Filter left table by existence in right (like IN subquery) | Customers who placed orders |
| Left Anti | Find 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 happenedConsider 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
CHECKconstraints withON 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 Case | Transformation | Best Choice | Rationale |
|---|---|---|---|
| Daily sales aggregation | SELECT date, SUM(amount) FROM transactions GROUP BY date | MLV | Pure SQL, periodic refresh, incremental on date |
| Customer lifetime value | DataFrame with complex Python UDFs, external API calls | Notebook | Python logic, not SQL-expressible |
| Bronze→Silver dedup | SELECT * FROM bronze.orders WHERE date > watermark (then filter duplicates in notebook OR use Delta MERGE for upsert) | Notebook or MERGE statement | MERGE provides idempotent dedup; easier in notebook than MLV |
| ML feature engineering | Complex PySpark with pandas UDFs, statistics, outlier detection | Notebook | Iterative logic, scikit-learn integration |
| Real-time KPI dashboard | Streaming aggregation over 5-minute windows | Eventstream | Real-time requirement; MLVs are batch |
| Gold layer metrics | SELECT product_id, AVG(rating), COUNT(*) FROM reviews GROUP BY product_id | MLV | Read-optimized, frequently queried, periodic refresh |
Cost/performance considerations:
| Factor | MLV | Notebook |
|---|---|---|
| Development time | Fast (SQL-only) | Moderate (PySpark code + testing) |
| Refresh overhead | Low (auto-incremental) | High (manual watermark logic) |
| Query performance | Optimized (pre-aggregated) | Depends on Delta optimization |
| Debugging | Limited (SQL errors only) | Full (logs, breakpoints, print statements) |
| Flexibility | Low (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:
Materialized Lake View patterns — design patterns, scheduling, when to use vs Delta tables
MLV incremental refresh patterns — IR blocker catalog, safe rewrites, CDF prerequisites
the
mlvmode — schedule, trigger, monitor, and cancel MLV refreshes via REST API (use when user asks to automate refresh, not author SQL)
Quick Decision Matrix
| Need | Use This |
|---|---|
| Summarize metrics by dimension | Aggregations (Pattern 1) |
| Rank/running totals preserving row detail | Window Functions (Pattern 2) |
| Combine data from two tables | Joins (Pattern 3) |
| SQL transformation with scheduled refresh + DQ | Materialized Lake View (Pattern 4) |
| Python logic / UDFs / complex algorithms | PySpark Notebook |
| Real-time streaming | Eventstream → 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 recordsOptional:
.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)orALTER 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 OPTIMIZERemove:
ALTER TABLE t CLUSTER BY NONERuntime 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 unavailableVACUUM:
VACUUM table_name RETAIN 168 HOURScleans up old file versions after retention periodPartition pruning: Query with partition columns in WHERE clause to skip irrelevant data
File compaction: Run
OPTIMIZEto 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 10Audit 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 writesspark.databricks.delta.optimizeWrite.binSize = 1073741824— Target 1GB file size for fewer small filesspark.databricks.delta.autoCompact.enabled = true— Automatic compaction during writesspark.microsoft.delta.optimize.fast.enabled = true— Fast optimization algorithmsspark.databricks.delta.properties.defaults.enableDeletionVectors = true— Efficient delete trackingspark.microsoft.delta.targetFileSize.adaptive.enabled = true— Adaptive file sizingspark.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 performancespark.databricks.delta.optimizeWrite.enabled = true— Balance write optimization with read efficiencyspark.microsoft.delta.snapshot.driverMode.enabled = true— Faster snapshot readsspark.sql.adaptive.enabled = true— Adaptive query executionspark.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 performancespark.databricks.delta.optimizeWrite.enabled = false— No write optimization overheadspark.sql.parquet.enableVectorizedReader = true— Vectorized Parquet readsspark.sql.files.maxPartitionBytes = 134217728— 128MB partition size for optimal parallelismspark.sql.adaptive.enabled = true— Optimize query plans based on runtime statsspark.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 tablesEfficient 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
pytestfixtures to create test Spark sessionAssert 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