Delta Lake Time Travel

Learn time travel with Delta Lake in this Databricks tutorial — hands-on steps, troubleshooting, and what to study next.

Focus: learn time travel with delta lake

Sponsored

You've just loaded a critical dataset into a Delta table, ran a transformation, and then realized — that transformation was a mistake. Every record is now corrupted. In traditional data warehousing, you'd scramble to restore from a backup or pray that someone had a point-in-time copy. With Delta Lake, you don't pray — you travel. Delta Lake's time travel capability lets you query, restore, and audit your data at any point in history, turning 'oops' into a five-second fix. This lesson is your practical guide to mastering this game-changing feature on Databricks.

The problem this lesson solves

The core pain point is simple: data is volatile and mistakes are inevitable. You might overwrite a table with bad data, run a buggy upsert, or accidentally DELETE millions of rows. Without versioning, recovery is a nightmare — you'd have to restore from external backups (if they exist), reprocess raw source data, or lose hours of work. Time travel eliminates this panic by making every version of a Delta table queryable and restorable. It also solves subtle problems like reproducibility: if your nightly ETL runs at 2 AM, but a colleague asks for the exact dataset from yesterday at noon, you can serve it instantly. Regulatory audits, customer dispute investigations, and A/B test analyses all rely on the ability to see data as it existed at a specific moment. Without time travel, these queries require complex CDC (change data capture) pipelines or slow, expensive full re-reads. Delta Lake's built-in versioning makes 'what was the data at this timestamp?' a first-class query.

Core concept / mental model

Think of a Delta table as a journal with a clear undo button. Every transaction — INSERT, UPDATE, DELETE, MERGE — writes a new entry (a version) to the table's transaction log. This log, stored in the _delta_log directory, is the heart of time travel. It's not a copy of the data; it's a record of changes. When you time travel, you're asking the query engine to reconstruct the table state by replaying (or, more efficiently, directly reading) the files referenced by a specific version.

Here's the key visualization:

  • Current state = the latest version's list of data files.
  • Version N = a snapshot that includes only files valid up to version N.
  • Timestamp = a specific point in time; Delta maps it to the closest version at or before that moment.

Delta Lake stores multiple versions of your data files, often using data files + deletion vectors (for small changes) or complete rewrites (for large updates). This design gives you flexibility: you can travel back weeks or months, depending on your retention settings. Unlike a database snapshot, which is a separate, heavy copy, Delta's versioning is incremental and lightweight — it stores deltas, not entire copies.

Pro tip: Think of VERSION AS OF as a database cursor and TIMESTAMP AS OF as a time machine. Both are equally valid; choose the one that fits your use case.

How it works step by step

Time travel relies on two core concepts: versions and timestamps. Here's the flow:

  1. Every write creates a version. The first write creates version 0, the next version 1, and so on. The _delta_log directory holds JSON files (e.g., 00000000000000000000.json) that describe each transaction.
  2. Choose your travel method. Use VERSION AS OF to query a specific version number, or TIMESTAMP AS OF to query as of a date/time. The syntax is the same regardless of whether you're using Spark SQL, PySpark, or Scala.
  3. Delta scans the log. For a given version, Delta reads the transaction log to determine which files are valid. It then reads only those files, ignoring newer changes.
  4. You query, or you restore. You can SELECT from a historical version (read-only time travel) or use RESTORE to overwrite the current state with a previous one.
  5. Cleanup is controlled. Old versions aren't stored forever. Delta uses VACUUM to delete files that are no longer needed, according to your retention policy.

The beauty is that this is all metadata-driven. The data files themselves are immutable; time travel just changes the 'lens' you view them through.

Hands-on walkthrough

Let's put this into practice with a PySpark example on Databricks. We'll create a Delta table, perform several operations, and then time travel.

Step 1: Create a versioned table

First, we set up a table and make a few changes:

from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate()

# Create table with initial data (version 0)
df = spark.range(1, 5).toDF("id")
df.write.format("delta").mode("overwrite").saveAsTable("time_travel_demo")

# Make a change (version 1)
df_append = spark.range(5, 9).toDF("id")
df_append.write.format("delta").mode("append").saveAsTable("time_travel_demo")

# Overwrite with wrong data (version 2)
df_wrong = spark.range(100, 105).toDF("id")
df_wrong.write.format("delta").mode("overwrite").saveAsTable("time_travel_demo")

print("Current data:")
spark.table("time_travel_demo").show()

Expected output:

Current data:
+---+
| id|
+---+
|100|
|101|
|102|
|103|
|104|
+---+

Step 2: Query a historical version

Now we travel back:

# Query version 1 (after the append)
print("Version 1 data:")
spark.sql("SELECT * FROM time_travel_demo VERSION AS OF 1").show()

# Query timestamp from earlier
from datetime import datetime
past_time = "2025-01-01T12:00:00"  # adjust to your timeline
print("Data as of timestamp:")
spark.sql(f"SELECT * FROM time_travel_demo TIMESTAMP AS OF '{past_time}'").show()

Expected output (for version 1):

+---+
| id|
+---+
|  1|
|  2|
|  3|
|  4|
|  5|
|  6|
|  7|
|  8|
+---+

The timestamp query returns the data as it existed at that point — if 2025-01-01T12:00:00 is before the first write, you might get an error (see troubleshooting).

Step 3: Restore to a previous version

When you're ready to fix the bad overwrite, restore version 1:

spark.sql("RESTORE TABLE time_travel_demo TO VERSION AS OF 1")
print("After restore, current data:")
spark.table("time_travel_demo").show()

Expected output:

+---+
| id|
+---+
|  1|
|  2|
|  3|
|  4|
|  5|
|  6|
|  7|
|  8|
+---+

After restore, the table's current version is now a new version (version 3) whose state matches version 1. This is important: RESTORE doesn't delete history; it creates a new version that points to the old data files.

Step 4: Check history

Use DESCRIBE HISTORY to see all versions:

spark.sql("DESCRIBE HISTORY time_travel_demo").select("version", "timestamp", "operation", "operationParameters").show(truncate=False)

Expected output (simplified):

+-------+-------------------+---------+----------------------------------------------------------------------+
|version|timestamp          |operation|operationParameters                                                    |
+-------+-------------------+---------+----------------------------------------------------------------------+
|3      |2025-01-15 10:30:00|RESTORE  |{...}
|2      |2025-01-15 10:20:00|WRITE    |{...}
|1      |2025-01-15 10:10:00|WRITE    |{...}
|0      |2025-01-15 10:00:00|WRITE    |{...}
+-------+-------------------+---------+----------------------------------------------------------------------+

This history is your audit trail — it shows exactly what operation changed the table and when.

Compare options / when to choose what

Time travel is powerful, but it's not the only recovery/audit tool. Here's how it stacks up against alternatives:

Option Use case Pros Cons
Time Travel (VERSION/TIMESTAMP AS OF) Read-only historical queries, quick restore Built-in, zero extra infrastructure, very fast Retention limited by logRetentionDuration and deletedFileRetentionDuration
RESTORE TABLE Make a previous version the current state Simple one-liner, keeps history Creates a new version — doesn't erase your mistake; requires write permissions
Full Table Backup (e.g., OPTIMIZE + export) Disaster recovery beyond retention window Durable, independent of table's retention Extra storage cost, manual process, can be stale
External CDC / audit tables Regulatory compliance with custom requirements Flexible, custom joins High engineering effort, complex pipelines

When to choose what:

  • Quick 'oops' fix: Use RESTORE TABLE. You'll be back in business in seconds.
  • Audit query: Use TIMESTAMP AS OF — perfect for 'what did we send this customer on June 1?'
  • Long-term archival: Time travel won't hold data forever. Set up a scheduled export or use Delta's VACUUM with a long retention for critical tables.
  • Version control for analytics: If you're building reproducible ML training sets, time travel ensures you can recreate the exact dataset that trained a model.

Troubleshooting & edge cases

Time travel is smooth, but here are common pitfalls and how to fix them:

1. Version doesn't exist

Error: The provided version 5 is not valid. The current version is 3.

Cause: You specified a version higher than the latest. Fix: Check DESCRIBE HISTORY to find a valid version. Use VERSION AS OF with a number between 0 and the latest, or omit it to get current data.

2. Timestamp out of range

Error: The provided timestamp (2025-01-01T12:00:00) is before the earliest available version.

Cause: Delta can't reconstruct data before the first recorded version. Fix: Use a later timestamp, or query DESCRIBE HISTORY to find the earliest version's timestamp.

3. VACUUM deleted your old versions

Symptom: You can travel back a week, but not a month.

Cause: VACUUM removes files older than deletedFileRetentionDuration (default 7 days). Fix: For critical tables, increase retention: ALTER TABLE ... SET TBLPROPERTIES ('delta.deletedFileRetentionDuration' = 'interval 30 days'). Remember: VACUUM is irreversible — don't run it blindly.

4. Wrong data after timestamp query

Symptom: TIMESTAMP AS OF returns data that doesn't match your expectation.

Cause: Timestamps map to the closest version at or before that time, which might be an earlier write than you intended. Fix: Use VERSION AS OF for precision, or refine the timestamp to align with a known write time (check DESCRIBE HISTORY).

5. Restore doesn't free up storage

Symptom: After RESTORE, your table still uses the same disk space.

Cause: RESTORE doesn't delete the 'bad' version's files; it just points the current state to older files. Those files still exist until you VACUUM (and even then, only after the retention period). Fix: Run VACUUM after you're certain you won't need the bad version again.

What you learned & what's next

You now know how to learn time travel with Delta Lake — from the underlying versioning mechanism to practical queries and restores. You can explain the core idea (a transaction log of immutable versions), complete a hands-on exercise (creating versions, querying with VERSION AS OF and TIMESTAMP AS OF, and restoring), and troubleshoot common issues like invalid versions or retention limits. You've also seen how it fits into a broader data engineering toolbox.

Ready for more? The next lesson in this Databricks track builds on your skills — you'll move from querying history to managing table history with VACUUM and retention policies, or perhaps dive into optimizing performance with Z-order and compaction. Either way, you're now equipped to treat every Delta table as a time machine, ready for any accidental overwrite or tricky audit request.

Practice recap

Try this mini-exercise: create a Delta table, append data, overwrite it with wrong data, then query version 0 and version 1. After that, restore to version 1 and run DESCRIBE HISTORY to confirm the version count increased. Finally, set delta.deletedFileRetentionDuration to '7 days' and run VACUUM to clean up unused files — just be sure you don't need that bad version anymore.

Common mistakes

  • Assuming time travel is permanent: by default, VACUUM removes files older than 7 days, so you can't query beyond that unless you adjust retention settings.
  • Using TIMESTAMP AS OF with a timestamp before the table's first write — Delta throws an error; always check DESCRIBE HISTORY for the earliest version.
  • Running RESTORE and expecting it to delete the bad version — it creates a new version that reuses old files, so you still need VACUUM to reclaim storage.
  • Forgetting that VERSION AS OF is 0-indexed; version 0 is the first write, not 1, so counting from DESCRIBE HISTORY can be off by one.

Variations

  1. In addition to VERSION AS OF and TIMESTAMP AS OF in Spark SQL, you can use the PySpark DataFrame API with spark.read.table('table').option('versionAsOf', 1).
  2. For automatic data retention, you can set delta.logRetentionDuration and delta.deletedFileRetentionDuration to control how long time travel is available.
  3. You can also use RESTORE TABLE with TIMESTAMP AS OF to restore to a specific point in time, not just a version number.

Real-world use cases

  • A data engineer restores a production table after a bad ETL run overwrites it with corrupted data — RESTORE TABLE ... TO VERSION AS OF brings it back in seconds.
  • An analyst audits a customer refund dispute by querying the transactional table as of a specific timestamp to show what was in the account that day.
  • A machine learning team reproduces a model's training dataset by using TIMESTAMP AS OF to recreate the exact feature snapshot from the day the model was trained.

Key takeaways

  • Every write to a Delta table creates a new version, tracked in the _delta_log, enabling time travel.
  • Use VERSION AS OF or TIMESTAMP AS OF to query historical data without altering the current state.
  • RESTORE TABLE lets you make a past version the current one, but it does not delete the old version.
  • Time travel is not unlimited — VACUUM and retention settings control how far back you can go.
  • Always check DESCRIBE HISTORY to understand your table's version timeline before querying or restore.
  • Time travel is invaluable for reproducible analytics, audits, and accidental data loss recovery.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.