Compute vs Storage Cost Allocation in Separated Architecture Databases
Hidden data transfer costs undermine the clean billing promise of separated architectures.

Separating compute from storage was meant to fix a simple problem: paying for resources you don't need just because they're bolted to resources you do. The pitch made sense on paper. Compute is expensive and its demand swings hard from hour to hour, while storage is cheap and its demand barely moves.
The storage side of that logic still holds up well. A petabyte sitting in S3 costs about the same whether zero compute clusters are querying it or ten are running at once, and that bill is stable. It's predictable, and it gives finance teams something they can actually model month over month.
Independent scaling also opened the door to running more than one engine against the same data. Spark, Trino, Flink, and other tools can all read from the same data store without anyone duplicating it, which lets compute and storage bills decompose cleanly across different workloads. On a diagram, this looks like two meters, two bills, clean attribution.
That's the promise. The diagram just doesn't show what happens when a query actually has to travel from storage to compute and back, and that's where the clean version of this story starts to come apart.
The Hidden Cost Surface of S3-Routed Query Paths
The per-GB rate on an S3 bill is not what it costs to actually run a query against that data. Storage pricing, something like $0.023 per GB-month in us-east-1, is the part finance teams can see and plan around. They routinely miss the data transfer sitting underneath it.
If a query takes a NAT Gateway instead of a VPC endpoint, it pays a processing cost roughly double the per-GB storage rate, and that cost compounds on every query that takes that path. Nobody decides this at bill time. Somebody made that choice months earlier, when the network routing got configured, and it appears quietly on invoices long after anyone remembers making it.
That timing gap explains a pattern that confuses a lot of cost reviews: two teams running identical data volumes and identical query counts end up with wildly different effective costs, and the difference traces back to how their network routing was set up, not to how much compute or storage either team actually provisioned. Compute and storage can both look perfectly optimized and the bill still diverges by a wide margin, because the thing driving cost was never on either meter.
Research on disaggregated databases presented at VLDB 2025 names this tension directly. Physically separating components introduces communication overhead, and the research frames the degree of disaggregation as a tradeoff against performance. The same separation that makes a bill easy to read is the separation adding latency and transfer cost to every query that runs through it. Pulling storage and compute apart for billing clarity also pulls apart the physical path a query has to travel, and that path isn't free.
Computation pushdown moves selection and aggregation work closer to S3 instead of pulling raw data all the way back to compute before filtering it. That approach cuts both query runtime and cost at the same time, and it tells you something important about where the real fix lives. Tagging line items in a FinOps dashboard doesn't touch the problem. The problem sits in how the query path is built; the fix has to be architectural.
The architectural case for independent compute and storage billing still holds, but only when the query path to storage is physically optimized for it. If you keep hot data in a distributed NVMe cache mesh instead of sending every query through object storage, you keep cost attribution genuinely separate and predictable once real workload pressure hits. Platforms like ObsessionDB build this in directly: they place a caching layer between compute and S3 so the clean billing promise doesn't quietly fall apart the moment query volume climbs.
The Compute-Storage Boundary in Snowflake, BigQuery, Databricks, and Neon
"Separated compute and storage" sounds like one architecture. In practice it names at least four different billing models, and treating them as interchangeable is where a lot of FinOps cost reviews go wrong.
ObsessionDB draws this boundary with an added layer in between. The design separates compute from storage, with an NVMe cache mesh between them so hot data stays in memory and never routes through S3 at query time, and this eliminates the NAT Gateway and transfer cost surface that makes other S3-routed architectures unpredictable. Pricing follows compute hours rather than individual queries, so workloads that run a high volume of queries, including AI agents probing data in parallel, aren't charged extra just for asking more questions. The billing surface is capacity consumed, not queries issued.
Snowflake takes a different approach: storage, compute, and cloud services are physically separate layers that behave as one integrated system. Isolated virtual warehouses let separate teams run workloads without competing for the same compute, but if you leave a warehouse running, it accumulates credit charges whether or not it's executing a query. Storage is billed at a flat monthly rate per TB on-demand in AWS US regions, and compute runs on credits billed per second, rated against virtual warehouse-hours with a 60-second minimum each time a warehouse starts. The two meters run independently, and the dominant cost lever turns out to be compute waste, warehouses sitting idle, rather than anything on the storage side. Auto-suspend configuration becomes the main lever for controlling this: leave it off and an X-Large warehouse running overnight in Standard edition burns roughly $16 an hour.
BigQuery removes the cluster. It runs fully serverless on Google BigQuery, so there's no idle compute cost to manage in the first place. Instead, the on-demand model charges per terabyte of data scanned per query, so if you run one unoptimized query against a large, unpartitioned table, the bill can dwarf an entire month of storage spend. That pricing structure shapes behavior directly: teams learn to partition and cluster tables less for query speed and more to keep the scan volume, and therefore the bill, under control.
Costs accrue only while processing is active, which suits bursty workloads but requires cluster lifecycle discipline. The Databricks Unit, or DBU, normalizes compute pricing across different instance types and cloud providers. Serverless SQL carries the highest per-unit rate among Databricks options, but it also produces the least waste, since instant elasticity means paying for compute only when queries are actually running. Storage runs on a separate meter called DSUs, and write-heavy workloads consume more of them than read-heavy ones, so storage-adjacent cost tracks workload pattern as much as raw volume.
Neon, acquired by Databricks in May 2025, makes the compute-storage split about as visible as it gets. Its scale-to-zero model pauses compute entirely after a configurable idle period, so CU-hour charges stop while data in storage stays untouched and the storage bill keeps running. A database that exists but never gets queried accrues storage charges and nothing else, which is close to the purest version of the disaggregation promise.
The phrase "separated compute and storage" covers at least four structurally different billing models, and conflating them is the source of most FinOps misdiagnoses in this space. Each one draws the compute-storage line in a different place, and each produces its own cost behavior as a result. Picking between them is less about finding the "correct" architecture and more about matching the billing shape to the workload pattern actually running against it.
Why compute governance, not storage tier selection, is where cost savings live
Most cost reviews start with storage: pick a cheaper tier, archive cold data, compress what's left. Across the major cloud warehouse platforms, the bigger lever sits on the other side of the ledger. Auto-suspend configuration, query optimization, and workload isolation drive more savings than storage tier selection ever does, and that runs against the instinct most FinOps teams reach for first.
Storage in a separated architecture is already cheap and fairly predictable. Compute is the expensive resource, and what determines its cost is mostly invisible to anyone reviewing a bill after the fact: idle timeout settings, how clusters get sized, when workloads get scheduled, whether queries are written to avoid scanning more data than they need to.
Snowflake makes the point concrete. If you leave a warehouse running without auto-suspend configured, it produces a compute cost with no query attached at all, while the storage bill stays the same whether that warehouse runs or not. BigQuery forces the same discipline through its pricing structure rather than through a setting someone has to remember to flip: since cost tracks bytes scanned, partitioning and clustering function as compute governance tools, limiting how much work the engine does on each query. Databricks' serverless SQL option carries the highest per-DBU rate in its lineup but produces the least waste overall, because instant elasticity means compute runs only while queries execute, not while a cluster warms up or sits idle between jobs.
The pattern across all three points to the same conclusion. If storage and compute bills are clean and well separated, but the compute side is dominated by idle warehouses or scans that read far more data than needed, the fix is a workload design problem that needs engineering attention, not a FinOps label.
How agentic workloads break the compute cost models built for human query patterns
AI agents query databases in a way that makes per-query pricing and unoptimized S3 routing expensive by structure, and that's not because the agents do anything wrong. Their query pattern simply doesn't match the one these cost models were built around.
Database systems researcher Murat Demirbas describes what happens when a schema-blind LLM agent starts probing a database: it issues a flood of redundant queries, many of which are dead ends, in a spot where a human analyst would have run one focused sequence instead. The optimization target shifts as a result. Minimizing the latency of any single query matters less than minimizing the total time an agent spends interacting with the database before it gets a useful answer.
Why does the agent need so many attempts? Schema exploration has to happen before any query is worth running. An agent that doesn't understand the schema guesses at table names, assumes column types, and misses foreign key relationships, producing queries that scan billions of rows because the agent lacks the metadata to avoid it. For agentic telemetry data specifically, a rigid schema becomes an obstacle rather than a safeguard: agents need to ingest schemaless JSON and query nested fields directly, without a brittle ETL pipeline sitting in the way.
Under a per-query pricing model, that exploratory pattern, the probing, the dead ends, the iterative refinement, generates a bill that scales with curiosity rather than with value delivered. And under an S3-routed architecture, the problem compounds in a second direction: where a human analyst sends one query at a time, an agent may fire off dozens in parallel, each one pulling data through the same routing layer and multiplying the transfer cost surface accordingly.
Putting the two together clarifies the shape of the problem: an architecture built to serve human analysts, one query at a time, with occasional bursts, runs into trouble the moment it has to serve an agent sending dozens of queries at once, many of which teach it nothing. What would it take to build a system where that pattern doesn't punish the workload for existing?
What a database architecture that treats cost allocation and agentic access as first-class concerns requires
The cost allocation failures described throughout this piece and the agentic workload mismatch described above trace back to the same root: architectures built around human query patterns, with billing dimensions that stay coupled in ways the original diagram never showed. Fixing one without the other leaves half the problem standing.
On the routing side, the fix is architectural rather than procedural. If you keep hot data in a cache mesh and never route queries through S3, you remove the transfer cost surface before it can form. NAT Gateway charges stop compounding because the query path never reaches the gateway in the first place. That's a fix to how the system is built, applied before the fact rather than a tagging exercise layered on top of it afterward.
On the pricing side, billing by compute hour rather than by query removes the penalty that per-query models place on curiosity. An agent that probes a dataset dozens of times in parallel pays for the compute capacity it consumes over that period, not for the number of questions it happened to ask along the way.
On the isolation side, agents need compute that's isolated and read-only, running over shared data, with the same durability guarantees a human analyst would expect but without the risk of an exploratory query touching production directly.
The throughline across Snowflake, BigQuery, Databricks, and Neon is that each platform drew the compute-storage line in a different place, and each produced a different cost shape as a result. Those two observations together raise the real question: whether the architecture behind the bill was built for the queries actually running against it today.


