Every snapshot stays readable until it is expired, so you can query the table as it was by snapshot ID or by time. The history table lists when each snapshot became current:
"""Time travel: the orders table as of each earlier snapshot, by ID and by timestamp."""
from lake import spark
hist = spark.sql("""SELECT h.made_current_at AS at, h.snapshot_id, s.operation,
h.is_current_ancestor AS on_main FROM booknest.orders.history h
JOIN booknest.orders.snapshots s USING (snapshot_id) ORDER BY at""").collect()
for h in hist:
v = f"VERSION AS OF {h.snapshot_id}" # the table as of that snapshot
n = spark.sql(f"SELECT count(*) FROM booknest.orders {v}").first()[0]
print(f"{h.at:%H:%M:%S} {h.snapshot_id:<20} {h.operation:<8} main {h.on_main!s:<5} {n:>6}")
t = hist[0].at.strftime("%Y-%m-%d %H:%M:%S.%f") # the moment of the first commit
n = spark.sql(f"SELECT count(*) FROM booknest.orders TIMESTAMP AS OF '{t}'").first()[0]
print(f"TIMESTAMP AS OF '{t}': {n} orders")Output
23:48:54 698235251123635788 append main True 66761 23:48:59 55956214015426586 append main True 100000 23:49:24 3651595841866468341 delete main False 99996 23:49:26 55956214015426586 append main True 100000 23:50:24 4637335077536227122 replace main True 100000 TIMESTAMP AS OF '2026-10-01 23:48:54.537000': 66761 orders
The history tells the story of Apache Iceberg Tables In Depth and Partition Evolution: two loads, the delete, the rollback that made the 2026 snapshot current again, and the June rewrite (replace, same rows). The rolled-back delete is off main but still readable. Trino 403,499 writes the same queries as FOR VERSION AS OF and FOR TIMESTAMP AS OF. Time travel reaches only unexpired snapshots, and expire_snapshots removes those older than five days by default, so keep what audits need with a tag.