Small team running procurement dashboards for independent hospitals. Each hospital is a separate inbound feed needing its own private reporting plus a consolidated roll-up.
We add ~4 new hospitals per quarter(additive) — so ~19 within a year, plausibly low hundreds over a few years.
Today it’s S3/CSV end to end: Excel → S3 → Lambda (Excel to CSV, one per hospital per subject area) → Glue DataBrew (per-hospital “bronze” recipe, then a consolidating “silver”) → Glue Spark (joins) → gold CSV in S3 → QuickSight SPICE.
- ~3.4 GB raw Excel,
- ~342 MB curated across 6 subject areas (orders, requisitions, inventory, adjustments, contracts, savings)
- Daily batch, no real-time requirement
- ~7 Lambdas, 17 DataBrew jobs, 6 Glue Spark jobs, 13 Step Functions state machines
The problem isn’t scale - it’s that schema knowledge lives in code.Onboarding one hospital means building ~9 artefacts across 4 services, because each hospital’s file layout is hard-coded in Lambda environment variables and DataBrew recipes.
Secondary issues: the nightly job truncates the curated layer before rebuilding it (a failed step leaves dashboards empty), and there’s no typing, dedupe or lineage back to a source file.
Target shape : Excel → S3 (raw landing, unchanged) → metadata-driven ingest → relational warehouse → QuickSight
Growth assumption: +4 hospitals per quarter, additive — roughly 19 by end of year one, plausibly low hundreds over a few years. So onboarding has to become configuration rather than deployment, and cost has to scale sublinearly.
Current plan: Aurora Serverless v2 PostgreSQL, min capacity 0 ACU. S3 stays as raw landing plus a Parquet archive for ad-hoc/ML via Athena. Column mappings become config rows so onboarding is an INSERT rather than a deployment. Facts HASH-partitioned 64 ways on`hospital_id` + RLS.
Ruled out: Redshift Serverless (8 RPU floor is ~10x our total spend), RDS provisioned (~$50/mo buying peak capacity 24/7 — the smallest sensible instance covers the entire growth curve), DynamoDB (query model — no joins or server-side aggregation, and access is ad-hoc analytical).
Sizing I’d like challenged: ~90 ACU-hours/month fixed + ~3 ACU-hours per hospital, giving ~$12/mo at 3 hospitals and ~$18/mo at 19.
Questions:
1. Is `min_capacity = 0` realistic in production? My costing assumes the cluster genuinely pauses ~20 hours/day. What silently prevents auto-pause in practice?
2. HASH vs LIST partitioning for tenant isolation? LIST-per-hospital means thousands of partitions and DDL on every onboarding at scale. Is HASH + RLS the right call, or should I look at schema-per-tenant / database-per-tenant?
3. What actually triggers a move to Redshift Serverless — a data volume, concurrency or query-pattern threshold, rather than “when Postgres starts hurting”?
4. Is metadata-driven ingest sound at this scale? Column mappings as table rows rather than code is the core bet here. Anyone regretted this pattern as source formats drift?
Cost-sensitive, daily batch is fine. Small team, comfortable with PostgreSQL, no Spark or Redshift experience. Single region, must stay AWS-native.