Controlling Snowflake Cost When an ELT Tool Backfills Years of Data
Quick answer: An ELT tool's first sync loads all of a source's history at once, and even when the tool does not bill for that historical load, your Snowflake warehouse does. Give the tool its own loader warehouse with a fixed size and a single cluster, put a resource monitor and a statement timeout on it, and schedule large backfills deliberately. We learned this on a six-week project for a Series A SaaS company, where a three-year Stripe backfill used far more credits than expected.
Last updated: October 2026
Small companies adopt managed ELT tools because they remove the work of building connectors. The part that is easy to miss is that the first sync of every source is a bulk historical load, and it arrives at the warehouse all at once.
This article draws on a project for a 45-person B2B SaaS company, described in our SaaS analytics case study. Product data lived in PostgreSQL, billing in Stripe, support in Zendesk, marketing attribution in HubSpot and behaviour tracking in Mixpanel, and the head of engineering spent about three hours every Monday assembling a metrics email that was wrong roughly 30% of the time. Project details come from that engagement. The configuration examples are illustrative, written for this article.
Free historical loads still cost credits
We used Hevo Data to bring all five sources into Snowflake. Its pre-built connectors for Stripe, Zendesk, HubSpot and Mixpanel were each connected in under a day; Stripe took about two hours from setup to first data, including the historical backfill. That backfill loaded three years of invoice data in one go, and it used far more Snowflake credits than we had expected.
The cause is a split in who pays for what. Hevo's documentation says historical data ingestion is free, and its billable events are rows inserted or updated at the source after the pipeline is created. The compute that loads those historical rows into Snowflake is not free, because it runs on your warehouse. A tool that is cheap during its backfill can still produce the most expensive day of the month on the warehouse side.
Hevo's historical load is also all or nothing per pipeline. With Load Historical Data enabled, the entire table is fetched in the first run; disabled, only records written after the pipeline was created are loaded. For PostgreSQL there is no documented way to limit the history to a date range, so the choice is to load everything, skip particular objects, or load nothing historical.
Give the loader its own capped warehouse
Our fix was to set MAX_CLUSTER_COUNT to 1 on the warehouse Hevo loads through and accept slower initial loads in exchange for predictable cost. Capping clusters works because of how Snowflake scales. A standard warehouse keeps the size you give it; it does not resize itself. A multi-cluster warehouse, on Enterprise Edition, adds clusters when queries start to queue, which an ELT tool loading many tables in parallel can easily cause. Capping it at one cluster turns a burst of parallel loads into a queue, which takes longer and costs a known amount per hour.
CREATE WAREHOUSE IF NOT EXISTS elt_loader_wh
WAREHOUSE_SIZE = 'SMALL'
MIN_CLUSTER_COUNT = 1
MAX_CLUSTER_COUNT = 1 -- queue instead of scaling out
AUTO_SUSPEND = 60
AUTO_RESUME = TRUE
ENABLE_QUERY_ACCELERATION = FALSE
STATEMENT_TIMEOUT_IN_SECONDS = 7200;
-- Resource monitors can only be created by ACCOUNTADMIN.
CREATE RESOURCE MONITOR IF NOT EXISTS elt_loader_monthly
WITH CREDIT_QUOTA = 200
FREQUENCY = MONTHLY
START_TIMESTAMP = IMMEDIATELY
TRIGGERS ON 75 PERCENT DO NOTIFY
ON 100 PERCENT DO SUSPEND;
ALTER WAREHOUSE elt_loader_wh SET RESOURCE_MONITOR = elt_loader_monthly;
A few details make this setup safer:
- Query acceleration. Since Snowflake's 2026_06 behavior change bundle, new standard warehouses have query acceleration switched on by default with a maximum scale factor of 8, and it bills serverless credits. For a loader warehouse, setting it explicitly keeps the cost predictable.
- Statement timeout. STATEMENT_TIMEOUT_IN_SECONDS defaults to two days. When both the session and the warehouse set it, the lowest non-zero value applies, so a warehouse-level limit protects you even if the tool's session does not set one.
- Suspend behaviour. A resource monitor's SUSPEND action waits for running statements to finish, while SUSPEND_IMMEDIATE cancels them. For a loader, SUSPEND avoids leaving half-loaded tables.
- Scope. Resource monitors cover warehouses only. Serverless features such as Snowpipe need a budget instead.
Schedule the first sync of any large source for a quiet period, and tell whoever watches the bill before it starts.
Log-based CDC from PostgreSQL on RDS
Hevo's PostgreSQL connector uses logical replication by default, which on Amazon RDS means turning on rds.logical_replication. It is a static parameter, it cannot be changed in a default parameter group, and the change takes effect only after a reboot, so plan a brief maintenance window. In the project, this was a parameter group change and a reboot and nothing more.
# A custom parameter group is required; the default group cannot be modified. aws rds modify-db-parameter-group \ --db-parameter-group-name app-postgres-logical \ --parameters "ParameterName=rds.logical_replication,ParameterValue=1,ApplyMethod=pending-reboot" aws rds reboot-db-instance --db-instance-identifier app-postgres # Then, in psql, confirm: # SHOW rds.logical_replication; -- on # SHOW wal_level; -- logical # GRANT rds_replication TO hevo_user;
Hevo's documentation for RDS lists further settings: max_replication_slots and max_wal_senders of 5, wal_sender_timeout of 0, backup retention of at least three days, and the rds_replication role for the Hevo user. AWS warns that logical replication increases WAL volume and that a replication slot nobody reads from can fill the instance's storage, so add a storage alarm at the same time. Two limits are also worth knowing: Hevo's standard pipelines cannot replicate from a read replica, and for teams created after Hevo's Release 2.21, log-based pipelines default to, and cannot go below, a 30-minute sync frequency.
Agree the metric definitions before writing SQL
The hardest part of the project was not technical. It was getting the CEO, the VP of Sales and the head of marketing to agree what churn means. We spent a full afternoon at a whiteboard before any dbt model was written, and that meeting saved weeks of back-and-forth later.
- MRR: the sum of all active subscription line items at month-end, excluding one-time charges, prorated for mid-month changes.
- Churn: MRR lost from customers who fully cancelled, divided by beginning-of-month MRR.
- Net revenue retention: including expansion, contraction and churn.
Each metric became a single dbt model with a YAML description that the whole team reviewed in a pull request before it went live. Our article on building an MRR movement model in dbt covers the modelling and testing in more depth.
A BI tool sized to the team
We evaluated Looker and Preset as well as Metabase, and chose Metabase. At 45 people, Looker's modelling layer would have been a second abstraction on top of dbt that the team did not need, and Metabase's SQL-first interface suited the engineers who would be its main users. It reads directly from the dbt models in Snowflake.
The Monday email is now automated and goes out at 6am. In current versions of Metabase this is a dashboard subscription, which can send by email or Slack on an hourly, daily, weekly or monthly schedule, with CSV, XLSX or PDF attachments. Metabase began phasing out its older Pulses feature, which some guides still describe, when it introduced subscriptions in version 0.38. The whole platform, from five disconnected sources to self-serve analytics, took six weeks.
Related Articles
Frequently Asked Questions
Q: Does Hevo charge for historical loads?
Hevo's documentation says historical data ingestion is free, and billable events are rows inserted or updated at the source after the pipeline is created. The Snowflake warehouse that loads the historical data still consumes credits, so backfills can still be expensive on the warehouse side.
Q: Can a Snowflake warehouse change size on its own?
A standard warehouse keeps the size you set unless something runs ALTER WAREHOUSE. Multi-cluster warehouses, on Enterprise Edition, add clusters of the same size when queries queue. Adaptive warehouses are the exception: on those, Snowflake handles sizing and scaling automatically.
Q: How do I cap Snowflake spend for an ELT tool?
Give the tool a dedicated warehouse with a fixed size, MAX_CLUSTER_COUNT = 1 and an explicit query acceleration setting, attach a resource monitor that notifies and then suspends at a credit quota, and set STATEMENT_TIMEOUT_IN_SECONDS on the warehouse.
Q: How do I enable logical replication on Amazon RDS for PostgreSQL?
Set the static parameter rds.logical_replication to 1 in a custom DB parameter group attached to the instance, then reboot. Afterwards, wal_level reports logical. The default parameter group cannot be modified.
Q: What replaced Pulses in Metabase?
Dashboard subscriptions. Metabase introduced them in version 0.38, began phasing out Pulses at the same time, and now describes Pulses as deprecated. Subscriptions send a dashboard by email or Slack on a schedule, with optional CSV, XLSX or PDF attachments.
