I Built a Snowflake Query Savings Estimator (Inspired by Greybeam)
I came across Greybeam while looking at approaches to Snowflake cost reduction. Their pitch is simple: most of your Snowflake queries are reading small amounts of data, and Snowflake charges a 60-second warehouse minimum every single time. A query that completes in 50ms still bills like 60 full seconds of an XS warehouse. That is $0.050 per query. Run 100 queries a day and you are spending $150/month on SELECT statements that DuckDB could handle for $18.
I built an open-source version of the estimator layer. Here is what it does and how it works.
The Core Problem
Snowflake’s credit model is designed for large, complex queries. It is genuinely good at those. The problem is that most BI workloads are not large complex queries. They are dashboards refreshing every few minutes, analysts running quick aggregations, and scheduled reports pulling filtered slices of data. 99% of these touch under 100MB of data.
For that class of query, DuckDB on a small EC2 instance is not just cheaper. It is faster, because there is no warehouse resume latency.
What I Built
The project has three Python modules.
classifier.py is the core. It uses sqlglot to parse every query into an AST and applies a decision tree to route each one.
The routing logic:
| Query pattern | Route |
|---|---|
| GRANT / REVOKE / USE / CALL | Snowflake |
| INSERT / UPDATE / DELETE | Snowflake |
| CREATE / DROP / ALTER | Snowflake |
exp.Command (unparseable DDL) | Snowflake |
| Estimated scan > 500 MB | Snowflake |
| Join count > 3 | Snowflake |
| CROSS JOIN detected | Snowflake |
| Everything else | DuckDB |
The key decision to use sqlglot’s AST instead of keyword matching matters more than it sounds. A naive if "INSERT" in sql.upper() breaks on queries that have INSERT inside a string literal, a comment, or a CASE WHEN alias. sqlglot parses the actual query structure.
import sqlglot
from sqlglot import exp
def classify(sql: str) -> str:
try:
tree = sqlglot.parse_one(sql, dialect="snowflake")
except Exception:
return "snowflake" # unparseable = don't risk it
# Hard rules: DDL and writes always stay on Snowflake
if isinstance(tree, (exp.Insert, exp.Update, exp.Delete,
exp.Create, exp.Drop, exp.Alter,
exp.Grant, exp.Revoke, exp.Command)):
return "snowflake"
# Count joins
join_count = len(list(tree.find_all(exp.Join)))
if join_count > 3:
return "snowflake"
# Cross joins are expensive regardless
if any(j.args.get("kind") == "CROSS"
for j in tree.find_all(exp.Join)):
return "snowflake"
return "duckdb"
cost.py does the credit math. It uses Snowflake’s actual formula: credits = (execution_ms / 3_600_000) * warehouse_multiplier. The warehouse multiplier is 1 for XS, 2 for S, 4 for M, and so on. It then computes the DuckDB alternative cost at a flat $0.75/hr on an 8 vCPU instance.
estimator.py is a Click CLI. It connects to Snowflake using snowflake-connector-python, pulls INFORMATION_SCHEMA.QUERY_HISTORY, classifies every query, and prints a Rich terminal report.
python estimator.py \
--account your-account \
--user your-user \
--warehouse XS \
--days 30
Results on a Real Snowflake Trial
I ran this against the query history from a Snowflake free trial where I ran a mix of setup queries (DDL, schema creation) and analytical queries.
37 queries analyzed. 54.1% routed to DuckDB. 45.9% correctly kept on Snowflake.
The Snowflake-only queries were exactly what you would expect: INSERT INTO demo_orders, CREATE OR REPLACE TABLE, UPDATE demo_orders, GRANT and USE statements. The classifier caught every single one correctly.
The 54% number is artificially low because a trial account query history is heavy on setup. Real BI workloads are SELECT-dominant and the Greybeam benchmark quotes 80-90% DuckDB routing on production workloads. On a dashboard-heavy account the number would be much higher.
What This Is Not
This is the estimator layer, not the execution layer. It classifies and projects savings. It does not actually route queries anywhere.
The next step would be a buenavista postgres-wire proxy that intercepts live queries and sends them to DuckDB instead of Snowflake. That proxy layer is the same pattern I used for the LanceDB-PuppyGraph connector. The hard piece after that is the S3 Iceberg sync: a CDC mirror of your Snowflake tables into your own S3 bucket that DuckDB reads from. That sync layer is where the real infrastructure work is.
This is also why Greybeam is a company and not just an open-source project. The estimator is approachable. The sync layer is not.
Code is on GitHub.