Multi-region DB decision: US/Korea users, should we split the DB in two?
Discussing whether to split the DB in two for US/Korea users. The conclusion is 'one DB for now'. Splitting data by region is a compliance (data residency) solution, not a speed solution, and it breaks our app's cross-user contribution links. Speed is solved by function/DB colocation -> media CDN -> (later) read replicas. Splitting before measuring is self-harm. (Discussion memo — not an implementation.)
The question
"US users and Korea users are different — can we create 2 DBs to separate them? I want to think about this seriously later."
At first I vaguely wondered "if the users are different, should the DB be split too," but digging in, two different questions were mixed together.
What had to be separated first: what does "different" mean
- UX/language differences — English screen / Korean screen, copy branching. → This is already solved by the i18n (ko/en,
lib/copy.ts) I built. No matter where the data is stored, showing each user a different language/experience is the app layer's job. - Where the data is stored — latency and law (data residency). → This is the real domain of "splitting the DB."
The two are completely separate. The real reason I was trying to split was speed/latency (worried it'd be slow if a US user round-trips all the way to the Seoul DB every time). So looking only from that angle —
Core conclusion: splitting data by region is not a "speed" solution
Splitting data in two is a tool for data residency (compliance). Doing it for speed is picking the wrong tool. Reasons:
- Splitting into 2 only gets faster when each user always hits only their own region's DB, and the cost of that is high (the trap below).
- The real culprit of latency is not "which country the DB is in" but the serverless function ↔ DB round-trip. If a Vercel function runs in the US and the DB is in Seoul, each query crosses the Pacific once (~200ms). With 3–4 queries per request, that alone is 600–800ms. This is the real body of the slowness.
- So the solution isn't splitting the DB but putting the function and DB in the same region.
A trap unique to our app: cross-user contribution links
곁 has contribution links (confirmed in code: app/api/links/route.ts issues a token, and someone else comes in via app/api/c/[token]/route.ts and leaves a word in the owner's keepsake box).
If you split the DB by region:
When a link created by a Korea user is opened by a friend in the US, that token isn't in the US DB, so it won't resolve. Sharing/contribution breaks at the region boundary.
To save this, you'd have to embed a region in the link or keep a small shared mapping table → complexity spikes. On top of that, 2 DBs = management, billing, migration, and backup all doubled.
The right ladder to solve speed (cheapest and most effective first)
- Put the DB in the primary user region, and match the Vercel function region to it. 곁 is Korea-first → Supabase Seoul + function region
icn1. US users cross the ocean once, but this app is a lightweight personal-memo type, so the perceived impact is small. - Media goes on a CDN (S3 + CloudFront). What users perceive as speed is photos/voice/video, and a CDN edge already solves regional latency for that. Text query latency is small.
- Pooled connections (Supabase pooler) — removes serverless cold-connection latency. Essential regardless of region.
- Reduce the number of queries per request / caching — cutting the round-trip count is bigger than a faster DB.
- When US traffic actually grows → read replicas. This is the textbook solution for regional latency: US functions read from a nearby replica, and writes go to the main DB. You get all the read-speed benefits of 2 DBs while keeping the data as one → contribution links don't break.
Decision
- One DB for now. At the 0-users stage, 2-DB is self-harm for a nonexistent problem.
- The region is Seoul, and the Vercel functions are matched to Seoul (
icn1). - Cheap future-proofing: if you later just add
region/localecolumns tousers, you can route replicas/splits by that tag when it's truly needed. Keep the door open but keep today's complexity at 0. - The cases where you really should go to 2 are (a) legal data residency, (b) measured serious latency, (c) two completely independent products — only when it's one of these. Get serious again then.
One line
Split data by region only because of compliance. Solve speed with function–DB colocation → media CDN → (later) read replicas, and all three keep one logical dataset to keep cross-user features alive. Don't shard data for latency you haven't measured.
(This post is a discussion/decision memo. There was no code change — the app already runs on a single DATABASE_URL, and this decision keeps that design.)