fabric-lakehouse-perf-remediate — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited fabric-lakehouse-perf-remediate (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Systematic toolkit for diagnosing and resolving performance issues in Microsoft Fabric Lakehouse environments. Covers Delta table health, Spark compute tuning, query optimization, and automated maintenance workflows.
When a user reports Lakehouse performance issues, work through these areas in order:
| Symptom | Root Cause | Action |
|---|---|---|
| Slow reads across all engines | Small files, no V-Order | Run OPTIMIZE VORDER, switch to readHeavy profile |
| Slow Spark queries only | Wrong shuffle partitions | Enable autotune or tune manually |
| Slow Power BI Direct Lake | Too many Parquet files/row groups | Run OPTIMIZE, check guardrail limits |
| Slow SQL analytics endpoint | Files under 400 MB, too many small files | OPTIMIZE with maxRecordsPerFile=2M |
| Write performance degraded | V-Order enabled on write-heavy workload | Switch to writeHeavy resource profile |
| Capacity throttled | Too many concurrent Spark jobs | Review concurrency limits, enable optimistic admission |
| Storage growing unexpectedly | VACUUM not running | Schedule VACUUM with 7-day retention |
| Streaming creates tiny files | No batching or trigger interval | Add processingTime trigger, run periodic OPTIMIZE |
Run in a Fabric notebook (Spark SQL):
-- Basic OPTIMIZE (bin-compaction)
OPTIMIZE lakehouse_name.schema_name.table_name;
-- OPTIMIZE with V-Order
OPTIMIZE lakehouse_name.schema_name.table_name VORDER;
-- OPTIMIZE with Z-Order on frequently filtered columns
OPTIMIZE lakehouse_name.schema_name.table_name ZORDER BY (column_name);
-- OPTIMIZE with both Z-Order and V-Order
OPTIMIZE lakehouse_name.schema_name.table_name ZORDER BY (column_name) VORDER;
-- OPTIMIZE specific partitions only
OPTIMIZE lakehouse_name.schema_name.table_name WHERE date_key >= '2025-01-01' VORDER;
-- VACUUM with default 7-day retention
VACUUM lakehouse_name.schema_name.table_name;
-- VACUUM with custom retention (requires safety check disabled)
VACUUM lakehouse_name.schema_name.table_name RETAIN 168 HOURS;Set at environment level or runtime. See resource-profiles.md for details.
# Check current profile
spark.conf.get('spark.fabric.resourceProfile')
# Switch to read-heavy for Spark queries
spark.conf.set("spark.fabric.resourceProfile", "readHeavyForSpark")
# Switch to read-heavy for Power BI Direct Lake
spark.conf.set("spark.fabric.resourceProfile", "readHeavyForPBI")
# Switch to write-heavy for ETL/ingestion
spark.conf.set("spark.fabric.resourceProfile", "writeHeavy")# Check current V-Order setting
spark.conf.get('spark.sql.parquet.vorder.default')
# Enable V-Order for read-heavy workloads
spark.conf.set('spark.sql.parquet.vorder.default', 'true')
# Disable V-Order for write-heavy ingestion
spark.conf.set('spark.sql.parquet.vorder.default', 'false')-- Enable autotune for automatic Spark SQL tuning
SET spark.ms.autotune.enabled=TRUE;
-- Disable autotune
SET spark.ms.autotune.enabled=FALSE;Autotune adjusts three key settings per query: spark.sql.shuffle.partitions, spark.sql.autoBroadcastJoinThreshold, and spark.sql.files.maxPartitionBytes. Requires 20-25 iterations to learn optimal settings. Only works on Runtime 1.1 and 1.2. Not compatible with high concurrency mode or private endpoints.
For best SQL analytics endpoint performance, target ~2 million rows and ~400 MB per Parquet file:
# Before data changes
spark.conf.set("spark.sql.files.maxRecordsPerFile", 2000000)
# Perform data operations (inserts, updates, deletes)
# ...
# After data changes, set max file size and optimize
spark.conf.set("spark.databricks.delta.optimize.maxFileSize", 4294967296)Then run OPTIMIZE on the affected tables.
Automate table maintenance via the Fabric REST API. See rest-api-maintenance.md for full details.
Endpoint:
POST https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/items/{lakehouseId}/jobs/instances?jobType=TableMaintenanceRequest body:
{
"executionData": {
"tableName": "my_table",
"schemaName": "dbo",
"optimizeSettings": {
"vOrder": "true",
"zOrderBy": ["frequently_filtered_column"]
},
"vacuumSettings": {
"retentionPeriod": "7.01:00:00"
}
}
}Monitor status:
GET https://api.fabric.microsoft.com/v1/workspaces/{workspaceId}/items/{lakehouseId}/jobs/instances/{operationId}When streaming data into a Lakehouse, prevent small file proliferation:
spark.microsoft.delta.optimizeWrite.enabled = true)# Example: Streaming with batching and partitioning
rawData = df \
.writeStream \
.format("delta") \
.option("checkpointLocation", "Files/checkpoint") \
.outputMode("append") \
.partitionBy("date_key") \
.trigger(processingTime="1 minute") \
.toTable("my_streaming_table")| Issue | Cause | Fix |
|---|---|---|
| OPTIMIZE command not recognized | Running in SQL analytics endpoint | Use Fabric notebook with Spark runtime |
| VACUUM fails with retention error | Retention < 7 days without safety override | Set spark.databricks.delta.retentionDurationCheck.enabled=false |
| Autotune not activating | Query too short (< 15 seconds) or wrong runtime | Use Runtime 1.1/1.2, ensure queries exceed 15s |
| Table maintenance API returns 409 | Another maintenance job running on same table | Wait for completion, jobs on different tables run in parallel |
| V-Order not applied after OPTIMIZE | Session/table property mismatch | Use OPTIMIZE table VORDER explicitly |
| Capacity throttled during maintenance | Maintenance consuming too many cores | Schedule during off-peak, reduce concurrent jobs |
Is the workload primarily reads or writes?
├── Read-heavy (dashboards, queries, analytics)
│ ├── Power BI Direct Lake → readHeavyForPBI profile + OPTIMIZE VORDER
│ └── Spark analytics → readHeavyForSpark profile + enable autotune
├── Write-heavy (ETL, ingestion, streaming)
│ └── writeHeavy profile + periodic OPTIMIZE + VACUUM on schedule
└── Mixed workload
├── Separate environments for read vs write paths
└── Use runtime spark.conf.set() to switch profiles per notebook~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.