Worked example — estimating a three-tier application's move onto Google Cloud

The method applied end to end to one small, recognisable estate: eight VMs, a SQL Server database, an object store, a queue and a dashboard set. What gets measured, which parity rows apply, what the differences imply, and what the estimate says and refuses to say.

markdownexamplev1J0P2-B8GEstage: designtrust: officialfamily: overviewservice: migration-centerinterface: anyworked examplemigration estimatethree-tier applicationright-sizingSQL Server to PostgreSQLegresswavescorrectness riskobservability baseline

Created 21 Oct 2026 · Modified 23 Oct 2026 · 1 version

Worked example — a three-tier application

The estate is illustrative. Every Google Cloud property cited is sourced from the documents in this hub; the workload's own numbers are placeholders, labelled as such, so the shape of the arithmetic is the lesson rather than the totals.

The estate, as inventoried

ComponentAs found
web tier4 VMs behind a load balancer, one region
app tier4 VMs, same region
databaseSQL Server, one primary with a standby
object storagea bucket of documents, "most of it old"
queueone queue, one consumer whose only job is writing events to the warehouse
warehousea cluster-shaped data warehouse
observabilityvendor-SDK tracing, 40 dashboards, 60 alerting policies

Step 1 — measure (the eight inputs)

InputFound
utilisationweb tier averages ~15% CPU; app tier ~35%. Placeholder, and the most important number here
dependenciesapp → database, app → queue, web → app. Nothing crosses into the warehouse synchronously
bytes to movedatabase and bucket; the bucket dominates
identities, keysone shared service principal, one long-lived key, 6 secrets
observability volumelogs GB/day, metric cardinality, trace volume
dashboards / policies40 / 60
permissions actually useda small subset of what is granted
request profilequeries/day, bytes scanned, messages/day and average message size

What step 1 already decides: the web tier at 15% utilisation is the single largest cost line in the estimate and the single largest saving. Nothing else in this document moves as much money.

Step 2 — map the names

Compute Engine for the VMs; Cloud SQL or AlloyDB for the database; Cloud Storage for the bucket; Pub/Sub for the queue; BigQuery for the warehouse; Cloud Logging, Monitoring and Trace for observability. One global VPC with a regional subnet.

Then stop trusting the map, per guides/parity-and-migration-method.md phase 2.

Step 3 — the differences that apply, per family

FamilyThe row that applies hereWhat it implies
computeutilisation is 15–35%; custom machine types exist only for N and E seriesright-size onto a general-purpose series where a custom shape is possible, and round up elsewhere. The biggest line
databasesSQL Server → Cloud SQL for PostgreSQL and → AlloyDB are documented heterogeneous pathstwo options: same-engine (simple, keeps the licence) or cross-engine (drops the licence, and the schema, procedures and data-access code become the project). Price both
storageARCHIVE commits a 365-day minimum; the cold classes carry retrieval fees"most of it old" is only cheap if nobody reads it. Get the access pattern before choosing a class, and re-tune any lifecycle rule carried in
messaginga Pub/Sub BigQuery subscription writes messages in with no code; subscriptions are at-least-once by defaultthe consumer is deleted — code, deploy, monitoring and on-call. And whatever consumer survives must be idempotent
dataa warehouse node count does not convertthe inputs are bytes stored and bytes scanned per query per day, or slot demand. Nothing else is honest
networkinga Google Cloud VPC is globalone network, one subnet. No transit object to reproduce
identitythe one long-lived keyworkload identity federation deletes it. The migration is the cheapest moment to do that
observabilityOpenTelemetry is the recommended instrumentationre-instrument on the source first; 40 dashboards and 60 policies are re-created, and 100 is the effort number

And one correctness item, not a cost item: nothing here runs as a function, so the Lambda-concurrency risk in guides/parity/compute.md §2 does not apply. Checking that it does not apply is part of the method — the risk is silent when it does.

Step 4 — waves, from the dependency graph

WaveContentsWhy
1the bucketnothing synchronous depends on it. Move it early, mirror new writes by event
2app tier + database + queuethey talk to each other. Splitting them pays egress on every transaction
3web tierdepends only on the app tier
4the warehousenothing calls it synchronously

Pre-flight each wave's destination quotas. A wave that fails half-done leaves the source drained and the target incomplete.

Step 5 — the three costs no calculator contains

  1. source-side egress on the bucket and the database — billed by the source cloud, over the whole mirroring

window;

  1. the phase-3 work: schema conversion (if cross-engine), consumer idempotency, re-instrumentation, 100

dashboards and policies, role mapping from the permissions actually used;

  1. the destination's commitments — a cold storage class's minimum duration, started on arrival.

What the estimate says — and refuses to say

Says:

  • a compute range, from measured utilisation, with the rounding named per machine family;
  • two database options, priced separately, with the cross-engine one carrying an explicit application-work

line;

  • one component deleted (the queue consumer) and one key deleted;
  • one network instead of several, and no transit object;
  • a wave sequence derived from measured dependencies;
  • an egress figure in bytes, with the window it is charged over.

Refuses to say:

  • whether the SQL and stored procedures port — that is per schema, and it is assessment work;
  • whether a Google Cloud machine shape performs like the source's — no published table supports that claim;
  • whether the application behaves the same afterwards — the observability baseline is what makes that

measurable rather than arguable;

  • whether to migrate at all. This hub documents and never acts.

Then

Execute wave 1, re-run step 1 against the result, and re-estimate. One executed wave replaces more assumptions than any further analysis.

Read this document as markdown — the same bytes an agent receives.