Storage is four things
| Component | What it is |
|---|---|
| Active bytes | Current table data |
| Time Travel bytes | Previous versions within DATA_RETENTION_TIME_IN_DAYS |
| Fail-safe bytes | 7 days after Time Travel for permanent tables (Snowflake-managed recovery) |
| Retained for clone | Data still referenced by clones after the source changed |
High-churn tables (full reloads, frequent updates/deletes) can have Time Travel + Fail-safe several times larger than active data.
1. Measure per table
SELECT table_catalog, table_schema, table_name,
ROUND(active_bytes / POWER(1024, 3), 1) AS active_gb,
ROUND(time_travel_bytes / POWER(1024, 3), 1) AS time_travel_gb,
ROUND(failsafe_bytes / POWER(1024, 3), 1) AS failsafe_gb,
ROUND(retained_for_clone_bytes / POWER(1024, 3), 1) AS clone_gb
FROM snowflake.account_usage.table_storage_metrics
WHERE deleted = FALSE
ORDER BY (time_travel_bytes + failsafe_bytes) DESC
LIMIT 30;
2. Retention per data class
| Data | Time Travel |
|---|---|
| Critical business tables | Enough to recover from mistakes (e.g. 7–30 days; max depends on edition) |
| Staging / intermediate | 0–1 day |
| Rebuildable marts | 1 day |
ALTER TABLE staging.orders_raw SET DATA_RETENTION_TIME_IN_DAYS = 1;
ALTER SCHEMA staging SET DATA_RETENTION_TIME_IN_DAYS = 1; -- default for new tables in the schema
Lowering retention removes Time Travel data beyond the new window, and it then moves into Fail-safe.
3. Transient tables for rebuildable data
Transient tables have no Fail-safe and at most 1 day of Time Travel. Use them for staging, intermediate dbt models and anything you can rebuild from source:
CREATE TRANSIENT SCHEMA staging_t DATA_RETENTION_TIME_IN_DAYS = 0;
In dbt: +transient: true (default for some adapters' table materialisations; check your config).
4. Reduce churn
| Pattern | Better |
|---|---|
Full CREATE OR REPLACE of large tables daily | Incremental models / MERGE of changed rows |
| Updating many rows to change one column | Design to append or update only changed rows |
| Frequent small DML on large tables | Batch changes |
Each rewrite of a micro-partition leaves the old version in Time Travel and Fail-safe.
5. Clones
Zero-copy clones are cheap until the source changes; then the clone keeps old micro-partitions alive. Drop dev/test clones when done, and refresh them instead of keeping many old ones.
SHOW TABLES LIKE '%_CLONE%' IN ACCOUNT;
6. Automatic clustering spend
SELECT table_name, ROUND(SUM(credits_used), 1) AS credits
FROM snowflake.account_usage.automatic_clustering_history
WHERE start_time > DATEADD(day, -30, CURRENT_TIMESTAMP())
GROUP BY 1 ORDER BY 2 DESC;
Clustering keys on high-churn tables can cost more than the query savings they bring. Keep keys only where pruning measurably improves important queries; suspend clustering otherwise (ALTER TABLE ... SUSPEND RECLUSTER).
7. Stages and other storage
- Internal stages with old load files:
LIST @stageandREMOVEafter loading (orPURGE = TRUEin COPY). - Old unused tables and schemas: check
ACCESS_HISTORY/ last query dates before dropping. - Search optimization and materialized views add storage and maintenance credits. Review their value.
Caution
Reducing Time Travel shortens your undo window for mistakes (see the dropped/overwritten table Sev1 post). Agree retention per data class with data owners before changing critical tables.
Checklist
- Storage split per table (active, TT, Fail-safe, clone)
- Retention set per data class
- Transient tables/schemas for rebuildable data
- Churn-heavy loads made incremental
- Old clones dropped
- Clustering spend reviewed
- Stages and unused tables cleaned