惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
博客园_首页
罗磊的独立博客
云风的 BLOG
云风的 BLOG
J
Java Code Geeks
Last Week in AI
Last Week in AI
D
DataBreaches.Net
Jina AI
Jina AI
博客园 - Franky
大猫的无限游戏
大猫的无限游戏
Apple Machine Learning Research
Apple Machine Learning Research
V
V2EX
D
Docker
MongoDB | Blog
MongoDB | Blog
B
Blog RSS Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
宝玉的分享
宝玉的分享
Engineering at Meta
Engineering at Meta
The Cloudflare Blog
博客园 - 三生石上(FineUI控件)
有赞技术团队
有赞技术团队
人人都是产品经理
人人都是产品经理
H
Help Net Security
T
The Blog of Author Tim Ferriss

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
How I debugged a Delta Lake DESCRIBE HISTORY timeout (and...
Abhishek Amb · 2026-05-04 · via DEV Community

If you have ever run DESCRIBE HISTORY on a Delta table that receives streaming data every 60 seconds and watched it either hang for hours or crash with an OutOfMemoryError, you are not alone and you are not doing anything wrong. The problem is architectural, and once you understand the internals, the fix becomes a lot clearer.

Here is what I learned after digging into why this happens and what you can actually do about it.

How the Delta transaction log works
Every write to a Delta table, INSERT, UPDATE, DELETE, MERGE, schema change, gets recorded as a JSON file in a directory called _delta_log at the root of the table. Files are named with zero-padded twenty-digit integers:

_delta_log/
├── 00000000000000000000.json
├── 00000000000000000001.json
├── 00000000000000000002.json
...
├── 00000000000000000010.parquet  (checkpoint)

Enter fullscreen mode Exit fullscreen mode

Each JSON file contains an array of "actions":

{
  "commitInfo": {
    "timestamp": 1714915200000,
    "operation": "STREAMING UPDATE",
    "operationMetrics": {
      "numOutputRows": "1240",
      "scanTimeMs": "320"
    },
    "isolationLevel": "WriteSerializable",
    "isBlindAppend": true
  }
}

Enter fullscreen mode Exit fullscreen mode

{
  "add": {
    "path": "part-00001-abc123.snappy.parquet",
    "partitionValues": {},
    "size": 1048576,
    "stats": "{\"numRecords\":1240,\"minValues\":{...},\"maxValues\":{...}}"
  }
}

Enter fullscreen mode Exit fullscreen mode

Every 10 commits, Delta generates a Parquet checkpoint file that captures the entire active table state as a compressed, columnar snapshot. When you run a normal query, Spark reads the latest checkpoint and applies only the small delta of JSON commits after it, which is why standard queries stay fast.

Why DESCRIBE HISTORY cannot use checkpoints
This is the core issue. The Delta protocol explicitly drops commitInfo when writing checkpoints. Checkpoints are optimized for state reconstruction, not provenance. So when you run:

DESCRIBE HISTORY my_streaming_table;

Enter fullscreen mode Exit fullscreen mode

or in Python:

deltaTable.history().show()

Enter fullscreen mode Exit fullscreen mode

Spark gets zero benefit from checkpoints. It has to parse every JSON file in _delta_log from scratch to extract the commitInfo blocks.

A pipeline that triggers every 60 seconds generates 1,440 commits per day. After a year, that is over half a million JSON files Spark has to read sequentially for a single DESCRIBE HISTORY call.

The three things that actually make it slow

  • Cloud storage listing overhead

AWS S3, Azure ADLS, and GCS do not have real directory structures. Listing _delta_log requires paginated API calls. S3's ListObjectsV2 returns at most 1,000 keys per request, so listing one million JSON files means 1,000 sequential HTTP requests before a single read task is scheduled. This is a pure I/O bottleneck. Adding more workers does not help here.

  • Small file JSON parsing

JSON is row-based text. Each two-kilobyte file requires a separate TCP connection to open, a full text parse to find the nested commitInfo struct, and type casting on every field. Multiply that by millions of files and executor CPU gets overwhelmed.

  • Driver OOM on shuffle

After executor nodes parse the JSON files, they shuffle the commitInfo structs back to the driver for aggregation and sorting. The driver's JVM heap has to hold all of this at once. When millions of records with nested maps like operationMetrics and operationParameters hit the driver simultaneously, you get:

java.lang.OutOfMemoryError: GC overhead limit exceeded

Enter fullscreen mode Exit fullscreen mode

And the query dies.

What you can do about it
Reduce log retention (immediate impact)

ALTER TABLE my_streaming_table
SET TBLPROPERTIES (
  'delta.logRetentionDuration' = 'interval 7 days',
  'delta.deletedFileRetentionDuration' = 'interval 7 days'
);

Enter fullscreen mode Exit fullscreen mode

This tells Delta to purge old JSON commit files during checkpointing. DESCRIBE HISTORY will now only parse 7 days of history instead of 30. One constraint to know: starting with Databricks Runtime 18.0, logRetentionDuration must be greater than or equal to deletedFileRetentionDuration, otherwise you get a validation error.

Enable Minor Log Compaction (Delta 3.0+)

Delta 3.0 introduced Minor Log Compaction, which combines multiple sequential JSON commits into a single consolidated file:

_delta_log/00000100.00000200.compact.json

Enter fullscreen mode Exit fullscreen mode

This dramatically reduces the file count DESCRIBE HISTORY has to work through. It is enabled by default in modern runtimes, but you can explicitly control it with:

spark.conf.set(
  "spark.databricks.delta.deltaLog.minorCompaction.useForReads", "true"
)

Enter fullscreen mode Exit fullscreen mode

Use Unity Catalog system tables instead

For systematic auditing, querying system.access.audit is significantly faster than DESCRIBE HISTORY because it is a pre-optimized Delta table, not a raw JSON parse:

SELECT
  event_time,
  user_identity.email,
  action_name,
  request_params
FROM system.access.audit
WHERE request_params['table_full_name'] = 'my_catalog.my_schema.my_table'
ORDER BY event_time DESC;

Enter fullscreen mode Exit fullscreen mode

Similarly, system.query.history gives you execution metrics and durations for writes without ever touching the transaction log.

Upgrade driver memory

When you cannot avoid querying large histories, switch to a memory-optimized driver instance. On AWS, migrating from m5.xlarge to r5.4xlarge gives the JVM enough heap to aggregate millions of records without hitting OOM.

Medallion Architecture for high-frequency pipelines

If your pipeline runs MERGE operations frequently against a table that also gets queried, the pattern that works is to ingest raw streaming data as append-only writes into a Bronze table, then run a scheduled bulk MERGE on an hourly cadence into Silver or Gold. This keeps downstream tables clean while the Bronze table handles the commit volume.

Also worth looking at: Deletion Vectors (available in modern Databricks runtimes), which mark rows as logically deleted via compressed bitmap files instead of rewriting the entire Parquet file on every UPDATE or MERGE. This cuts AddFile and RemoveFile churn in the JSON commits significantly.

What I would do differently
If I were designing a high-frequency Kafka-to-Delta pipeline today, I would set a 7-day log retention from day one, enable Minor Log Compaction, route all compliance auditing to Unity Catalog system tables rather than DESCRIBE HISTORY, and extend the streaming trigger to at least 5 minutes unless the downstream business process genuinely needs sub-minute freshness. The transaction log bloat problem is much easier to prevent than to fix after the fact.