Cloud and Managed Database Services Questions
Running databases as managed cloud services rather than self-hosting them: choosing and provisioning managed relational and NoSQL offerings (for example Amazon RDS/Aurora/DynamoDB, Azure SQL Database/Cosmos DB, Google Cloud SQL/Spanner), sizing instances and storage, and designing an architecture (read replicas, connection proxies and pooling for managed or serverless compute) to meet a latency or availability target. It covers comparing provisioned versus serverless/autoscaling pricing and operational models, forecasting capacity and cost, and migrating a database onto or between managed deployments: moving a self-hosted database to a managed service, switching a workload from provisioned to serverless (or back), changing a single-AZ deployment to multi-AZ, and the questions to ask before adopting a new managed vendor. The core question is the split of responsibility and cost between the cloud provider and the engineering team: what the provider takes on (patching, infrastructure-level HA, automated backups) versus what remains the team's job (configuration, capacity and cost tuning, security posture like encryption and key rotation). It does not cover backup and restore mechanics, RPO/RTO planning, replication or sharding internals, or SQL-level query diagnostics.
Think of a managed database service you have personally deployed and operated in production (for example RDS, Aurora, Cloud SQL, DynamoDB, or Cosmos DB). Walk through how you approached capacity planning and your scaling strategy for it, and share one concrete outcome or lesson learned from running it in production.
Sample Answer
This question is scored on specificity, not on which service gets named. A strong answer names the actual signal that triggered a capacity decision (a real metric and threshold, not "we noticed it was slow"), the actual scaling mechanism chosen and why that one over the alternatives, and one concrete lesson that changed how the system was operated afterward, a real before-and-after in process, not a restated best practice.
What a strong answer actually contains
- A specific capacity signal, not a vague symptom. "We watched CPU" is not a signal; "the burst credit balance on a burstable instance class was heading toward zero every afternoon" is, because it names the actual metric, the actual pattern, and why that pattern meant trouble was coming rather than already fine. (A burstable instance class, for example AWS's T-family, earns credits while it runs below its baseline CPU level and spends those credits to run faster than that baseline when needed; once the credit balance reaches zero, performance drops back down to the lower baseline, which is the actual mechanism that turns a shrinking balance into a real, user-facing slowdown.)
- The scaling mechanism chosen, and why it beat the obvious alternative. Interviewers are listening for the reasoning that ruled out the simpler option, not just the option that was picked.
- One concrete lesson framed as a process change, something that is genuinely different about how the system, or the team, operates now versus before the incident, not a generic takeaway that would apply to any database anywhere.
What undermines this answer
A memorized capacity-planning checklist with no specific metric, no specific trigger, and no real outcome attached reads as rehearsed rather than experienced, which is precisely what this question is designed to surface. Naming an impressive-sounding managed service is not the point; the reasoning and the actual outcome are.
Illustrative example of the shape a strong answer takes
A production RDS (Amazon Relational Database Service) MySQL instance on a burstable instance class (db.t3) ran fine most of the day, but a daily afternoon analytics batch job steadily depleted its CPU credit balance, and by the third month, users started reporting slow page loads that correlated with when that job ran. The real signal was the CPUCreditBalance CloudWatch metric (the metric AWS documents specifically for T-family instance classes, distinct from BurstBalance, which tracks gp2 storage I/O credits and applies regardless of instance class) heading toward zero every afternoon, not flatlined at zero, which meant the instance was surviving that day by borrowing against a shrinking buffer, a materially different situation than simply being fine. The first instinct was to move the batch job to overnight, but that job fed a report a stakeholder needed by mid-afternoon, so the timing was not actually negotiable. The real fix was migrating that instance off the burstable instance class onto a non-burstable class sized for the batch job's sustained CPU demand, not just its average load, since a T-family instance's baseline CPU only refills credits while running below that baseline, and a daily job that regularly pushes CPU above baseline for long enough will eventually always run the balance to zero no matter how the alarm is tuned. The lesson that changed how the team operated afterward: CPU-credit alarms got set at 50% remaining, not a default-feeling 10%, because by the time the balance actually hit 10% there were maybe two hours of runway left before it became a customer-facing incident, nowhere near enough lead time to safely plan and execute an instance-class change on a production database. That alarm-threshold change, not the specific database engine involved, is the actual takeaway a strong answer should be able to name concretely.
Trade-offs and pitfalls
- A "lesson learned" that is really a restated best practice ("always monitor your database") without a specific behavior change attached does not clear the bar this question sets; the question explicitly asks what changed, not what is generally good practice.
- Candidates sometimes reach for the most technically impressive incident they can recall rather than the one they can describe with the most real detail; a smaller, well-specified incident beats a large, vaguely-remembered one here.
- The strongest answers make clear which part was a genuine surprise at the time (the credit balance pattern, in the example above) versus which part was a deliberate, reasoned trade-off (ruling out simply rescheduling the job); collapsing those two into one flat narrative loses the signal an interviewer is actually listening for.
Your team is evaluating three ways to run the primary database for the next three years: a managed RDBMS, a self-managed cluster on VMs, and a serverless database offering. The workload has a steady 2,000 writes/sec around the clock and bursty reads that spike to 150k/sec. Compare total cost of ownership and operational burden across the three options, and say which one you would recommend and why.
Sample Answer
The recommendation is a managed RDBMS (relational database management system), specifically Aurora, running a provisioned writer for the steady write load paired with an elastic (autoscaling or Aurora Serverless v2) reader fleet for the bursty read load, for a team that does not already run a large, cross-system database operations function. That single choice beats a self-managed cluster on raw total cost of ownership (TCO) once headcount is priced in, and it beats using a fully serverless model for everything because this workload's two halves have opposite cost profiles and only one of them actually benefits from paying a serverless premium.
What TCO actually includes
Comparing these three options on infrastructure cost alone misses most of the real difference:
- Infrastructure/compute cost: the number everyone compares first, instance or ACU-hour pricing.
- Operational headcount cost: patching, backup verification, failover drills, upgrade testing, and on-call coverage. For a self-managed cluster this is a real, ongoing engineer-time cost, not a one-time setup cost, and it does not shrink because the team is small.
- Incident cost: who owns "the replication broke at 3am" and how long it takes to fix it. A managed service's automated failover and vendor support reduce, but do not eliminate, this cost; a self-managed cluster puts more of it directly and immediately on the team.
How this workload's shape drives the recommendation
The workload has two very different profiles glued together: a steady 2,000 writes/sec around the clock, and reads that burst to 150k/sec. A steady write load is exactly the case where fixed, provisioned capacity is cost-efficient, since there is no meaningful idle time to avoid paying for; metering that side per request or per capacity-unit would mean paying a serverless premium for capacity that is basically always fully used anyway, which buys no real elasticity benefit. The bursty read load is the opposite case, illustrated below. Only two numbers here come from the question itself, the 150k/sec read peak and the 2,000 writes/sec baseline; the question does not state how reads are distributed across the day, so the baseline read rate and the burst-window length are explicit, stated, illustrative assumptions (a plausible steady daytime rate and a short afternoon burst), not measured or given values, included so the calculation is checkable rather than opaque:
# Only peak_read_rps (150,000) is given by the question. The rest of the read-side inputs
# below are ILLUSTRATIVE ASSUMPTIONS, stated explicitly, standing in for "some steady baseline
# most of the day, with a short burst," since the question does not specify the daily shape.
baseline_read_rps = 20_000 # assumed illustrative baseline, NOT given by the question
peak_read_rps = 150_000 # given by the question
baseline_hours_per_day = 21 # assumed illustrative quiet-hours span
peak_hours_per_day = 3 # assumed illustrative burst-hours span
time_weighted_avg = (baseline_read_rps * baseline_hours_per_day
+ peak_read_rps * peak_hours_per_day) / 24
peak_to_avg_ratio = peak_read_rps / time_weighted_avg
UNIT_RPS = 1_000
provisioned_unit_hours_per_day = (peak_read_rps / UNIT_RPS) * 24
elastic_unit_hours_per_day = ((baseline_read_rps / UNIT_RPS) * baseline_hours_per_day
+ (peak_read_rps / UNIT_RPS) * peak_hours_per_day)
print(f"Peak-to-average read ratio: {peak_to_avg_ratio:.2f}x")
print(f"A fleet sized statically for peak buys "
f"{provisioned_unit_hours_per_day/elastic_unit_hours_per_day:.2f}x the compute-hours "
f"an elastic fleet that tracks the curve needs")
years = 3
idle_unit_hours_over_3_years = (provisioned_unit_hours_per_day - elastic_unit_hours_per_day) * 365 * years
print(f"Over {years} years, that's {idle_unit_hours_over_3_years:,.0f} unit-hours of pure idle "
f"headroom if the read fleet were statically provisioned for peak")
Peak-to-average read ratio: 4.14x
A fleet sized statically for peak buys 4.14x the compute-hours an elastic fleet that tracks the curve needs
Over 3 years, that's 2,989,350 unit-hours of pure idle headroom if the read fleet were statically provisioned for peak
This is the crux of the recommendation: under this illustrative daily shape, a read fleet statically provisioned for the 150k/sec peak sits at roughly 4x more capacity than it needs most of the day, for three years straight, while the write side has no equivalent waste to eliminate because it is already steady. Aurora lets each side use the capacity model that fits it, provisioned for the writer, elastic for the readers, under one managed database, which is why "managed RDBMS" is the recommendation among the three named options rather than a hybrid outside them.
Why not self-managed, and why not fully serverless
A self-managed cluster on VMs (virtual machines) can win on raw per-unit infrastructure cost, but that comparison only holds before counting the engineer-hours spent on 24/7 patching, backup verification, failover drills, and upgrade testing, none of which shrink for a small team the way they might be amortized across many systems for a large, dedicated database operations group. For a team without that existing capacity already in place, the "savings" tend to show up instead as slower incident response and a real chance of a costly, avoidable outage. A fully serverless offering for the whole database (for example DynamoDB on-demand, or an all-serverless relational option) is well suited to the read side's burstiness but is paying an unnecessary per-unit premium on the write side, where the load is already steady and provisioned capacity is the cheaper, equally reliable fit.
Trade-offs and pitfalls
- This recommendation is not universal, and the caveat matters as much as the pick: an organization that already runs a large, mature database operations function across many systems can genuinely find self-managed cheaper, because the headcount cost is largely sunk or shared rather than incremental to this one database. The right 3-year call depends on team size and existing operational maturity, not a single rule that applies to every org.
- Committing to a Reserved Instance or Savings Plan rate for the steady write-side capacity locks in real savings for a genuinely steady, multi-year workload, but reduces flexibility if the write pattern changes meaningfully before the commitment term ends; size that commitment against the measured baseline, not against a hopeful growth projection.
- Elastic reader capacity is only as good as its configured ceiling: if the maximum autoscaling or Serverless v2 ACU bound is set too low, a burst beyond it still throttles or slows down exactly like a statically undersized fleet would, so "serverless" is not a substitute for correctly setting that maximum, it only removes the need to pay for it when it is not in use.
Evaluate the trade-offs of using a managed multi-region database service (for example Cloud Spanner, Cosmos DB, or Aurora Global Database) versus running a self-managed sharded cluster on VMs or Kubernetes, for a system handling roughly 10 million QPS reads and 200,000 writes per second. Cover latency, consistency, operational complexity, cost, scaling behavior, and disaster-recovery capabilities, and give your recommendation.
Sample Answer
Direct answer
At 10 million reads per second and 200,000 writes per second, the default recommendation is a managed multi-region service (Spanner-class horizontal partitioning or Cosmos DB-class tunable consistency) over a self-managed sharded cluster, unless the team already operates comparable distributed stateful infrastructure in-house and the consistency requirements are loose enough to accept asynchronous cross-region replication. At this scale the operational surface a managed vendor absorbs, quorum health (whether enough replicas are up and agreeing to keep accepting writes), rebalancing, and cross-region failover (automatically switching traffic to a healthy region if one goes down) across what turns out to be hundreds of nodes, is exactly the class of problem these services are purpose-built for. The worked example and framework below show why, and name what would flip the recommendation.
Framework: six axes
| Axis | Managed multi-region (for example Cloud Spanner, Cosmos DB, Aurora Global Database) | Self-managed sharded cluster (VMs or Kubernetes) |
|---|---|---|
| Latency | In-region reads and writes stay low: Cosmos DB's own documentation guarantees a 99th-percentile (P99) latency under 10 ms for reads and writes within a region, at any consistency level. Cross-region strong consistency is bounded by physics, not engineering: Cosmos DB states cross-region strong-consistency write latency as two times the round-trip time (RTT) between the two farthest regions, plus 10 ms P99. | Fully tunable: keep the write quorum inside one metro region for low latency, and replicate elsewhere asynchronously for disaster recovery (DR) only, if the business can accept that. Requires the team to design and prove this topology correct itself. |
| Consistency | Comes largely built in. Cosmos DB offers five levels (Strong, Bounded Staleness, Session, Consistent Prefix, Eventual). Spanner gives a single strong guarantee, external consistency (global linearizability, meaning all observers agree on the order transactions actually committed in), via TrueTime, a globally synchronized clock with a bounded uncertainty window that every commit waits out before acknowledging. | Owned entirely by the team. Single-shard transactions are ordinary single-node ACID (atomicity, consistency, isolation, durability). Cross-shard transactions need a distributed-transaction layer, for example two-phase commit (a protocol that coordinates an atomic commit across multiple shards), that the team designs, implements, and must prove correct under partition and retry. |
| Operational complexity | Patching, quorum membership, internal rebalancing, and backup mechanics are the vendor's job. The team owns schema design, indexing, provisioned or consumed throughput sizing, and query-level tuning. | Everything: failover automation, connection pooling and proxy layers, monitoring, and on-call runbooks for partition and quorum-loss scenarios are the team's to write and operate, with no vendor site reliability engineering (SRE) team absorbing pages. |
| Cost | Priced at a premium over commodity compute (consumption-based request units, or per-node or per-vCPU list pricing depending on the specific service and tier; check the current pricing page for the exact service and region before budgeting, since list prices change without a fixed schedule). The premium buys engineering time the team does not have to spend building the same capability. | Commodity VM or Kubernetes compute, storage, and network egress rates, but the true cost includes the engineering headcount to build and operate the sharding, failover, and DR tooling a managed service ships by default. That cost does not appear on the cloud bill. |
| Scaling behavior | Elastic and mostly automatic. Aurora Global Database adds up to 10 read-only secondary regions with storage-based replication typically under a second of lag, though only the primary region accepts direct writes (a "write forwarding" feature lets secondaries relay writes to the primary, at the cost of that round trip). Spanner and Cosmos DB partition writes horizontally across internal shards transparently to the application. | Fully within the team's control (shard key choice, resharding strategy, instance sizing), but every reshard is a project the team runs and risks itself, exactly the work automatic rebalancing would otherwise absorb. |
| Disaster recovery | Documented, tested guarantees. A Cloud Spanner multi-region configuration places two read-write regions (two replicas each) plus a witness region (a fifth voting replica that participates in the write quorum but does not serve reads), five voting replicas total; a write quorum needs the leader region's replica plus any two of the other four, so the configuration survives the complete loss of any one region with zero data loss, a recovery point objective (RPO) of zero. Aurora Global Database offers a planned switchover (no data loss) and an unplanned failover after a regional outage (a small, non-zero RPO bounded by the sub-second replication lag (the delay between a write completing in the primary region and arriving in the secondary) at the moment of failure). | Fully bespoke: cross-region async replication, backup and restore, or a warm standby are all buildable, but the recovery time objective (RTO) and RPO the system actually delivers are only as good as the team's own runbook and the disaster tests actually run, not a documented product guarantee. |
Worked example
Whichever path is chosen, the raw infrastructure needed at this scale is a useful reality check: 10 million reads/sec and 200,000 writes/sec is real online transaction processing (OLTP) hardware regardless of who operates it.
import math
# Inputs: the QPS figures come from the question; the rest are stated,
# illustrative capacity-planning assumptions (NOT vendor-measured benchmarks).
read_qps = 10_000_000
write_qps = 200_000
read_capacity_per_node = 50_000 # assumed sustained cached-read throughput of one well-provisioned OLTP node
write_capacity_per_shard = 8_000 # assumed sustained write throughput of one shard's single-writer primary
dr_regions = 3 # regions in the deployment
shards_needed = math.ceil(write_qps / write_capacity_per_shard)
total_read_nodes = math.ceil(read_qps / read_capacity_per_node)
read_nodes_per_shard = math.ceil(total_read_nodes / shards_needed)
nodes_per_shard_group = 1 + read_nodes_per_shard # 1 primary + its read nodes
total_nodes_single_region = shards_needed * nodes_per_shard_group
total_nodes_all_regions = total_nodes_single_region * dr_regions
print(f"shards needed (write-bound): {write_qps:,} / {write_capacity_per_shard:,} -> {shards_needed}")
print(f"read-serving nodes needed (read-bound): {read_qps:,} / {read_capacity_per_node:,} -> {total_read_nodes}")
print(f"read nodes per shard: {total_read_nodes} / {shards_needed} -> {read_nodes_per_shard}")
print(f"nodes per shard group (1 primary + reads): {nodes_per_shard_group}")
print(f"total nodes, single region: {shards_needed} x {nodes_per_shard_group} -> {total_nodes_single_region}")
print(f"total nodes, {dr_regions}-region deployment: {total_nodes_single_region} x {dr_regions} -> {total_nodes_all_regions}")
Output:
shards needed (write-bound): 200,000 / 8,000 -> 25
read-serving nodes needed (read-bound): 10,000,000 / 50,000 -> 200
read nodes per shard: 200 / 25 -> 8
nodes per shard group (1 primary + reads): 9
total nodes, single region: 25 x 9 -> 225
total nodes, 3-region deployment: 225 x 3 -> 675
Roughly 675 stateful nodes across three regions, on these stated assumptions. That number does not change based on who manages it: a managed multi-region service still provisions and rebalances something in that range internally, it simply does not page the team when a node in it fails. The real question is whether the team wants to own the failure domain for 675 stateful nodes directly, given that a self-managed cluster at this size effectively requires building an internal SRE function for exactly that fleet.
flowchart LR
subgraph MG["Managed multi-region service"]
C1["Client"] --> R1["Regional endpoint"]
R1 --> P1[("Primary region")]
P1 -. replicates .-> S1[("Secondary region A")]
P1 -. replicates .-> S2[("Secondary region B")]
end
subgraph SM["Self-managed sharded cluster"]
C2["Client"] --> RT["Shard router"]
RT --> SH1[("Shard 1 primary")]
RT --> SH2[("Shard 2 primary")]
SH1 --> RE1[("Shard 1 replica")]
SH2 --> RE2[("Shard 2 replica")]
end
In the managed topology, the vendor owns everything to the right of the regional endpoint: which replica leads, how replication and quorum work, how a lost region is detected and routed around. In the self-managed topology, the team owns and must operate the shard router, the primary and replica wiring per shard, and the logic for what happens when a shard's primary disappears.
Recommendation and what would flip it
Recommend the managed multi-region path by default at this scale. The deciding factors are the team's existing distributed-systems operational maturity (does it already run something like Vitess- or CockroachDB-class infrastructure at hundreds of nodes today), the actual consistency requirement (does the product need synchronous cross-region strong consistency, or would single-writer-per-shard with asynchronous DR replication genuinely be acceptable), and whether the cost delta at roughly 675 nodes' worth of infrastructure materially matters to the budget, since the managed premium multiplies across a large fleet at this scale rather than a handful of instances.
It flips to self-managed when all three line up: the team already operates comparable stateful infrastructure in-house, the workload tolerates a single-writer-per-shard model with asynchronous cross-region replication instead of synchronous strong consistency, and the cost savings at this node count are large enough to justify paying the engineering-time cost once, building the sharding, failover, and DR tooling, instead of paying the managed-service premium every month indefinitely. Defaulting to "managed is always safer" ignores that at 675 nodes the managed premium is not a rounding error, and a team that already has the operational muscle for a fleet this size may reasonably prefer to keep that premium as engineering budget instead.
Trade-offs and pitfalls
The dominant failure mode on this kind of question is picking a side without naming the deciding variable, or hedging into "it depends" without ever committing. A second common mistake is assuming managed multi-region services give multi-region strong consistency for free: Cosmos DB explicitly does not support strong consistency on a multi-region-write account, and even Spanner's external consistency costs real latency, a bounded commit-wait on every write, it is not free ordering. A third is treating "self-managed" as automatically cheaper: at 675 nodes, the engineering cost of building and operating correct distributed-transaction and failover logic in-house is usually larger, not smaller, than the managed premium, unless that capability already exists in the organization.
Compare provisioned capacity (reserved throughput) with serverless/autoscaling models for DynamoDB and Aurora Serverless v2. Discuss cost predictability, latency characteristics, cold-start behavior, throttling, operational overhead, and what kind of workload pattern each model suits best.
Sample Answer
Provisioned capacity means you reserve a fixed amount of throughput (how many read or write operations, or how much compute, the system can handle per second) up front and pay for it whether you use it or not; serverless/autoscaling means the service adjusts capacity to match load and you pay closer to actual consumption. The two managed products that expose this choice most directly are DynamoDB (provisioned throughput vs. on-demand) and Aurora (a fixed instance class vs. Serverless v2), and the right choice depends almost entirely on how predictable and how bursty the workload is.
DynamoDB: provisioned throughput vs. on-demand
| Provisioned | On-demand | |
|---|---|---|
| Cost predictability | High: you set read capacity units (RCU) and write capacity units (WCU) and pay a flat rate for them | Lower: billed per request, cost tracks traffic directly, which is predictable only if traffic is predictable |
| Latency | Same single-digit-millisecond latency as on-demand; capacity mode does not change per-request latency | Same latency profile as provisioned |
| Throttling behavior | Requests beyond provisioned capacity are throttled (ProvisionedThroughputExceededException) unless application auto scaling is configured, and auto scaling reacts to a CloudWatch alarm on a delay, not instantly | Scales automatically to serve the traffic, but a sudden spike well beyond the table's recent peak can still see brief throttling until DynamoDB provisions more partitions behind the scenes |
| Operational overhead | You own setting and tuning target utilization for auto scaling, and re-tuning it as traffic patterns shift | Effectively none: no capacity numbers to manage |
| Best workload pattern | Steady, forecastable traffic where the flat provisioned rate beats per-request pricing | Spiky, new, or hard-to-forecast traffic, or a small table where operational simplicity matters more than shaving cost |
Aurora: provisioned instance vs. Aurora Serverless v2
| Provisioned | Serverless v2 | |
|---|---|---|
| Cost predictability | High: fixed instance-class price regardless of load | Variable: billed in Aurora capacity unit (ACU) hours, each ACU is roughly 2 gibibytes (GiB) of memory plus corresponding CPU and networking, measured every second |
| Latency characteristics | Constant, matches the chosen instance class | Same engine, same query latency at a given capacity; latency only changes if the workload outgrows the current ACU level faster than it can scale up |
| Cold-start behavior | None: capacity is always provisioned | None for the normal 0.5 to 256 ACU scaling range, since scaling happens in place (typically the same host) without dropping connections; a cold-start-like delay only appears if you opt into "scale to zero" (available on newer engine versions with a minimum of 0 ACUs) and the cluster has to resume from a fully paused state |
| Throttling | N/A: fixed capacity either handles the load or the instance is CPU/memory saturated | Scales up automatically in increments as small as 0.5 ACU as load increases; a very sudden spike can still see brief elevated latency while capacity catches up, but there is no hard throttling wall the way DynamoDB provisioned mode has |
| Operational overhead | Manual: resizing the instance class is a deliberate, brief-outage operation you schedule | Low: you set a minimum and maximum ACU range once and the service scales within it continuously |
| Best workload pattern | Steady-state OLTP (online transaction processing) load where you can predict peak capacity and want the most predictable bill | Spiky or unpredictable workloads (promotions, seasonal traffic, dev/test environments that sit idle most of the day) where paying for idle provisioned capacity would be wasteful |
Worked example: turning a workload into numbers
DynamoDB. Say a product-catalog table needs to sustain a peak of 800 strongly consistent reads/sec (items averaging 3 KB) and 150 writes/sec (items averaging 1.5 KB). DynamoDB charges 1 RCU (read capacity unit) per strongly consistent read of an item up to 4 KB, and 1 WCU (write capacity unit) per write of an item up to 1 KB, rounding each item up to the next chunk boundary:
import math
peak_reads_per_sec = 800
peak_writes_per_sec = 150
read_item_kb = 3
write_item_kb = 1.5
rcu_per_read = math.ceil(read_item_kb / 4) # 3KB fits in one 4KB chunk -> 1 RCU
wcu_per_write = math.ceil(write_item_kb / 1) # 1.5KB needs two 1KB chunks -> 2 WCU
read_rcu_per_sec = peak_reads_per_sec * rcu_per_read
write_wcu_per_sec = peak_writes_per_sec * wcu_per_write
headroom = 1.2 # 20% buffer above measured peak
provisioned_rcu = math.ceil(read_rcu_per_sec * headroom)
provisioned_wcu = math.ceil(write_wcu_per_sec * headroom)
print(f"Peak RCU/sec needed: {read_rcu_per_sec}")
print(f"Peak WCU/sec needed: {write_wcu_per_sec}")
print(f"Provisioned RCU to reserve (20% headroom): {provisioned_rcu}")
print(f"Provisioned WCU to reserve (20% headroom): {provisioned_wcu}")
Peak RCU/sec needed: 800
Peak WCU/sec needed: 300
Provisioned RCU to reserve (20% headroom): 960
Provisioned WCU to reserve (20% headroom): 360
Under provisioned capacity, you reserve and pay for 960 RCU and 360 WCU every hour of the day, including overnight when actual traffic might fall to 50 reads/sec and 10 writes/sec (50 RCU and 20 WCU, under 6% of what is provisioned). Under on-demand, you are billed only for the RCU and WCU each request actually consumes, so that overnight trough costs proportionally less, at a higher per-unit rate for whichever units you do consume. This is the concrete version of the "idle capacity" trade-off in the table above: provisioned wins when the 800/150 peak is close to the round-the-clock average, on-demand wins when it is not.
Aurora Serverless v2. Say a workload idles near the 1 ACU minimum for 20 hours a day and spikes to 8 ACU for a 4-hour business-hours peak. An Aurora capacity unit (ACU) is billed per second, in ACU-hours:
idle_hours, idle_acu = 20, 1
peak_hours, peak_acu = 4, 8
daily_acu_hours = idle_hours * idle_acu + peak_hours * peak_acu
rate_per_acu_hour = 0.12 # AWS's published Aurora Serverless v2 rate, us-east-1 at time of writing; check current pricing for your region
daily_cost = daily_acu_hours * rate_per_acu_hour
monthly_cost = daily_cost * 30
provisioned_daily_acu_hours = 24 * peak_acu # a fixed instance sized for the peak, running all day
provisioned_monthly_cost = provisioned_daily_acu_hours * rate_per_acu_hour * 30
print(f"Serverless v2 ACU-hours/day: {daily_acu_hours}")
print(f"Serverless v2 monthly compute cost: ${monthly_cost:,.2f}")
print(f"Fixed-at-peak monthly compute cost: ${provisioned_monthly_cost:,.2f}")
Serverless v2 ACU-hours/day: 52
Serverless v2 monthly compute cost: $187.20
Fixed-at-peak monthly compute cost: $691.20
For this bursty, mostly-idle shape, Serverless v2 costs about 73% less than a provisioned instance sized to never fall short at the 4-hour daily peak, because the provisioned instance bills for 8-ACU-equivalent capacity for all 24 hours whether the workload needs it or not. A workload that instead sat near 8 ACU around the clock would flip this: Serverless v2 would bill close to the same 192 ACU-hours/day as the provisioned option, at a higher per-ACU-hour rate, making the fixed instance the cheaper choice.
Trade-offs and pitfalls
- Provisioned capacity is not "safer," it is differently risky: undersized provisioned throughput throttles real user traffic with a hard error, while serverless/on-demand's failure mode is a cost spike or brief latency bump, not a hard rejection. Which failure mode is worse depends on whether the business cares more about a predictable bill or never dropping a request.
- On-demand and Serverless v2 both have a per-unit price premium over well-utilized provisioned capacity. The trade only pays for itself when the workload's peak-to-average ratio is high enough that you would otherwise be paying for a lot of idle provisioned headroom; for a genuinely flat, 24/7 workload, provisioned is usually cheaper.
- Aurora Serverless v2's scale-to-zero option is attractive for dev/test but is a different feature from its normal 0.5 to 256 ACU scaling: only enable it where an occasional resume delay is acceptable, not on anything customer-facing.
- Mixing modes inside one system is a legitimate design, not a compromise: for example, a steady OLTP writer on a provisioned Aurora instance with Serverless v2 read replicas that scale independently to absorb bursty reporting traffic, or a DynamoDB table on provisioned capacity for its steady core traffic with on-demand reserved for a table that only exists to catch spiky, unpredictable event ingestion.
Explain what a managed database service is, and describe the primary trade-offs of using one (for example Amazon RDS/Aurora, Cloud SQL, DynamoDB) compared to self-hosting a database. For each trade-off, say what the cloud provider takes on and what stays the team's job, and give one concrete example of a choice that materially affected reliability or cost.
Sample Answer
A managed database service is one where the cloud provider operates the database engine on your behalf: it provisions the compute and storage, patches the host operating system and engine, runs automated backups, handles hardware failure and (if you turn it on) failover (automatically switching traffic to a healthy standby when the primary fails), and exposes a connection endpoint plus a control plane API instead of shell access to a box. Amazon RDS/Aurora and Google Cloud SQL are managed relational database services (structured tables, SQL, joins), while DynamoDB is a managed NoSQL key-value store (no fixed schema, no joins, items looked up by key), a fundamentally different kind of database. All three still count as "managed" in the sense used here: the provider operates the engine so your team doesn't have to. The core trade-off is that you give up low-level control (custom kernel tuning, exotic extensions, root access) in exchange for offloading undifferentiated operational work, at a price premium, and with the provider dictating the maintenance windows and upgrade cadence you have to plan around.
What the provider takes on vs. what stays your job
| Responsibility | Provider (managed service) | Still your job |
|---|---|---|
| Physical hardware, host OS patching | Yes | No |
| Engine minor-version patching | Yes, applied in a maintenance window | Choosing that window and testing compatibility first |
| Automated backups / snapshots | Yes, taken on a schedule | Verifying you can actually restore to your target recovery point objective (RPO), and testing that restore |
| High availability / failover orchestration | Yes, if you provision it (for example RDS Multi-AZ) | Deciding whether to pay for it, and making the app reconnect cleanly after a failover |
| Storage durability | Yes | Choosing storage type, size, and IOPS (input/output operations per second) for the workload |
| Schema design, indexing, query tuning | No | Entirely yours |
| Connection management | No | Pooling, sizing max connections, avoiding connection storms |
| Capacity planning / instance sizing | No | Entirely yours |
| Encryption mechanics (turning it on, managing keys) | Yes, once requested | Key policy, network exposure, credential rotation |
| Cost | You pay for the abstraction | Rightsizing and ongoing cost control are still yours |
The one-line version: the provider owns keeping the box alive. Your team still owns making the database correct, fast, and affordable for your specific workload. Nothing about "managed" removes the need for someone who understands query plans, isolation levels, or the data model.
Worked example: a concrete reliability and cost trade-off
A team running Amazon RDS for PostgreSQL decides against enabling Multi-AZ to save the roughly 2x compute cost of running a synchronous standby, reasoning that "RDS already takes automated backups, so we're covered." RDS's automated backups upload transaction logs to S3 (Amazon's object storage service) every 5 minutes, so in the single-AZ configuration the real RPO after an availability zone (AZ, an isolated data center within a cloud region) impairment is up to 5 minutes of lost writes, plus however long it takes to notice the instance is down and restore from the latest restorable point. The recovery time objective (RTO, how long recovery takes) is however long a full restore-from-snapshot takes for that data volume, easily tens of minutes for a large database. With Multi-AZ enabled, the standby is kept current by synchronous replication (the primary does not acknowledge a write until the standby has it too), so failover loses effectively no committed data, and AWS states that a Multi-AZ DB instance failover happens "as quickly as 60 seconds," with most failovers completing within one to two minutes. Skipping Multi-AZ was a deliberate cost/reliability trade: it saved money every month, but it converted "a few minutes of trouble" into "a real outage with real data loss" the one time an AZ actually failed. Neither choice is universally wrong: for a staging database that trade is probably fine; for the primary store behind checkout, it usually is not.
Trade-offs and pitfalls
- Managed does not mean hands-off. Query performance, index design, and data modeling determine most of what actually breaks in production, and none of that is automated away by the provider.
- "It's backed up" is not the same as "it meets our RPO." Point-in-time recovery from 5-minute log shipping is a materially weaker guarantee than a synchronous standby. Know which one you are actually relying on before an incident, not during one.
- Self-hosting keeps some levers a managed service takes away: custom compiled extensions, OS-level tuning (filesystem choice, kernel parameters), and full superuser access. If a workload genuinely needs one of those, that is a real reason to self-host, not just inertia.
- The cost model shifts from mostly fixed infrastructure and ops spend to a metered bill driven by instance size, storage, IOPS, and backup retention. A managed service can be cheaper than the fully loaded cost of an in-house operations team, or more expensive than a lean self-hosted setup at very large, steady scale. The crossover point depends on team size and existing operational maturity, not a universal rule.
Unlock Full Question Bank
Get access to all 14 Cloud and Managed Database Services interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.