Design Doc · Identity Team · In Review
Move User Data Into Its Own Service and Database
Press Enter to flag this paragraph.
Problem
The users, user_profiles, and credentials tables live in the monolith's shared Postgres instance alongside 212 other tables. Any team can read or write them, and three incidents in the last two quarters traced back to schema changes in users made outside the Identity team.
Proposal
We will create a User Service that owns these three tables in a dedicated Postgres 16 database. The service exposes a gRPC API, and no other service may connect to the users database directly.
The first version of the API covers GetUser, BatchGetUsers, CreateUser, UpdateUser, and DeleteUser. Responses include the full user and profile record, and callers that need a subset can ignore the fields they don't use.
Data Dependencies
Most reads of user data happen through SQL joins with orders, subscriptions, and support_tickets. Those joins will be rewritten to call BatchGetUsers and merge the results in application code. The finance and growth dashboards in Looker query the users table directly and will keep reading from the monolith's replica.
Twenty-three tables hold a user_id foreign key to users.id. Those constraints will be dropped at cutover, since the tables will no longer share a database. Owning teams can add validation in their own services if they need it.
Migration
During the migration, the monolith writes every change to both the shared database and the User Service. If the call to the User Service fails, the write to the shared database still commits and the error is logged.
Dual writes start first. A backfill job then copies the existing 18 million rows in batches of 5,000, skipping any row the dual write path has already written.
Rollout
The 40 call sites that read or write user data move to the new client library during Q2. Identity will publish the library and a migration guide, and product teams will pick up the changes as their roadmaps allow.
Once every call site uses the library, we switch reads to the User Service, stop dual writes, and drop the users tables from the shared database the same day.
Performance
The User Service runs in the same region and VPC as the monolith, so the added network hop should be negligible. Load testing will happen after the first call sites move.
Success
No code outside the User Service connects to the users database, and Identity ships schema changes to user tables without coordinating with other teams.
Open Questions
Whether the User Service should cache profiles in memory, and how stale a cached profile is allowed to be.