Design Figma's 100x Multi-Tenant Postgres & Real-Time Multiplayer Architecture
1. Problem Statement & Scope Clarification
System Mission
Design Figma's horizontal multi-tenant database scaling architecture and real-time multiplayer document synchronization engine. The system must support monthly active designers collaborating simultaneously on complex vector scene graphs, maintain sub-16ms () multiplayer editing latency across WebSockets, eliminate PostgreSQL connection exhaustion and lock contention by horizontally sharding databases across hundreds of RDS instances via a custom Rust SQL Proxy Router, and persist multi-gigabyte document snapshots reliably to Amazon S3.
The Dual-Engine Architecture Invariant: Figma separates document editing from relational metadata. The relational database (PostgreSQL) is NOT on the real-time keystroke/mouse-drag critical path. Active collaborative canvas sessions run entirely inside stateful, in-memory C++/Rust Multiplayer Sync Servers that reconcile vector tree mutations via Operational Transformation (OT) / Conflict-Free Replicated Data Types (CRDT). Relational PostgreSQL shards store only tenant metadata, permissions, and immutable operation changelogs, checkpointing document snapshots to Amazon S3.
Synthesizing vector architecture diagram...
Functional Requirements
- Real-Time Multiplayer Canvas Synchronization: Multiplex hundreds of simultaneous editors on a single design file over WebSockets, synchronizing vector transforms, text edits, and cursor positions with .
- Horizontal PostgreSQL Multi-Tenant Sharding: Scale relational database storage and throughput by by horizontally partitioning tenants across independent Amazon Aurora PostgreSQL shards based on
org_idandteam_id. Tenant placement is directory-based (an explicitorg_id -> shard_idmap), neverhash(org_id) % N: a static modulo would force a full re-hash of every tenant when a shard is added and would make moving one hot org to its own shard (§11, Q3) impossible. - Rust SQL AST Proxy Routing: Intercept all application SQL traffic; parse queries into Abstract Syntax Trees (ASTs), automatically inject shard routing keys, and strictly forbid cross-shard distributed transactions.
- Deterministic Conflict Resolution (CRDT / OT): Reconcile concurrent tree structural operations (e.g., User A groups shapes while User B modifies color) deterministically without data loss or canvas corruption.
- Periodic Snapshot Compaction & S3 Offload: Compact ephemeral in-memory operation streams into immutable binary scene graph snapshots flushed to Amazon S3, minimizing file load times on cold boot.
Non-Functional Requirements (SLAs & SLOs)
- Multiplayer Sync Latency: , , round-trip broadcast time between active editors.
- Relational Metadata Latency: , for sharded PostgreSQL point lookups through the Rust proxy.
- System Availability: uptime for document read/write sessions ( minutes downtime per year).
- Zero Cross-Shard Deadlocks: of write transactions must execute against a single physical database shard; cross-shard joins are rejected at the proxy layer.
- Data Durability Guarantee: 11 Nines () for canvas snapshots in Amazon S3; zero uncommitted operation loss via local sync server Write-Ahead Logs (WAL).
- CAP / PACELC Classification:
- Multiplayer Sync Engine: AP / PA/EL system. Real-time canvas editing prioritizes availability and low latency; concurrent edits converge eventually using deterministic CRDT/OT ordering.
- Relational Org/Billing Metadata: CP / PC/EC system enforcing strict ACID transactions on sharded PostgreSQL.
Out-of-Scope
- Complex server-side 3D ray tracing or real-time raster video rendering.
- Offline-only desktop native file storage formats (e.g., local
.figfile import/export converters). - Font licensing distribution and third-party plugin sandboxing runtimes.
2. Capacity & Scale Estimation (Back-of-the-Envelope Math)
Traffic & Multiplayer Concurrency Calculations
- Active Users: Monthly Active Users (MAU); Daily Active Users (DAU).
- Active Design Documents: design files stored across organizations.
- Peak Concurrent Multiplayer Editing Sessions:
- Average Active Editors per Session: (with peak collaboration sessions reaching during design critiques).
- Multiplayer Operation Ingress Throughput: Each active editor generates an average of (object drags, vector point shifts, text typing):
- WebSocket Egress Broadcast Bandwidth: Each op () is broadcast to other active editors in the room ( recipients):
Relational PostgreSQL Shard Sizing
- Relational Metadata Queries (File directory, permissions, user profiles):
- Why Monolithic Postgres Collapsed at 100x Scale:
A single large AWS RDS PostgreSQL instance (
db.r6i.32xlarge) hits hard limits at:- Connection limits: concurrent connections saturate backend worker processes.
- Lock contention:
pg_lockstable thrashing on shared organization tables. - Autovacuum failure: High write volume causes table bloat; autovacuum cannot keep up without disk I/O starvation.
- Horizontal Sharding Topology: Dividing across database shards (target per shard primary):
Canvas Scene Graph & Snapshot Storage Sizing
- Average Compacted File Snapshot: compressed binary format.
- Active Files Edited Daily: .
- Daily S3 Snapshot Storage:
- 3-Year Snapshot Horizon (with S3 Lifecycle Tiering): Lifecycle policy transitions snapshots older than 90 days to Amazon S3 Glacier Instant Retrieval, saving on storage costs.
3. AWS-First High-Level Architecture
Synthesizing vector architecture diagram...
Follow an edit from the top. The client keeps a WebSocket through the NLB to the one sync node that owns the open file. In the "Sync Node Architecture" panel, an event loop applies each user's operation to the file's in-memory scene graph, merging concurrent edits so all users converge, and appends it to a local write-ahead log before acknowledging. In the "Snapshot Offload & Object Storage Tier" panel, the node writes a compacted snapshot to S3 every 30 s and queues operation records to SQS. In the "Rust Database Proxy & Sharded PostgreSQL Tier" panel, workers write that metadata through Figma's Rust proxy, which maps org_id to one of about 100 Aurora PostgreSQL shards. Real-time editing runs entirely in memory; the databases receive only batched, durable summaries, which is what lets them scale.
Data Flow Walkthrough
- Multiplayer Session Initialization:
- The user opens a Figma document. The client establishes a persistent WebSocket connection through AWS NLB to the assigned Multiplayer Sync Server.
- If the document is not currently loaded in memory, the sync server fetches the latest compacted scene graph snapshot from Amazon S3 and replays any pending operations from the changelog table in sharded PostgreSQL.
- Real-Time Operation Broadcast Path:
- The designer drags a rectangle on the canvas. The client emits an atomic operation (
Op: MoveNode(node_id, dx=10, dy=20)). - The sync server's non-blocking
epollthread receives the binary frame, applies the transformation to the in-memory scene graph, resolves any OT/CRDT ordering conflicts, and commits the mutation to an ephemeral local NVMe write-ahead log. - The server immediately broadcasts the operation to all other connected clients in the session room over their open WebSockets ().
- The designer drags a rectangle on the canvas. The client emits an atomic operation (
- Periodic Snapshot Flushing:
- Every 30 seconds (or after 100 uncompacted operations), the sync server serializes the in-memory scene graph into a compact binary buffer and writes it asynchronously to Amazon S3.
- Relational Metadata Routing (Rust DB Proxy):
- When application microservices query tenant metadata (e.g.,
SELECT * FROM files WHERE org_id = 'org_492'), the query routes through the custom Rust DB Proxy. - The proxy parses the SQL into an AST, extracts the
org_idsharding key, looks the tenant up in the shard directory (org_id -> shard_id, theshard_idcolumn onorganizations, replicated into every proxy's memory), and forwards the query to the dedicated Aurora PostgreSQL primary node. - If a query lacks a sharding key or attempts a cross-shard join (
SELECT * FROM users JOIN teams ON ...), the proxy immediately throws an error:CROSS_SHARD_TRANSACTION_PROHIBITED.
- When application microservices query tenant metadata (e.g.,
End-to-End Request Tracing Walkthrough
| Step # | Event / Action | Component State | Distributed Transition | Output / Response |
|---|---|---|---|---|
| Step 1 | Designer opens design document | Web client cold boot | WebSocket handshake dispatched to AWS NLB; routed to Multiplayer Sync Node | Connection upgraded to WSS; session initiated |
| Step 2 | Canvas State Hydration | Sync Node in-memory load | Server checks memory; cache miss triggers fetch of latest snapshot from Amazon S3 | 2.5 MB binary snapshot loaded into RAM in |
| Step 3 | Replay Incremental Changelog | Sync Node catch-up | Node queries Sharded Postgres via Rust Proxy for operations newer than snapshot | 12 missing operations replayed; scene graph ready |
| Step 4 | Complete Canvas Sent to Client | Sync Node Client | Full scene graph binary stream transmitted over WebSocket | WebGL canvas renders document in |
| Step 5 | Designer drags vector node | Canvas input event | Client WebGL updates locally (optimistic); sends binary OpTransform frame | WSS frame delivered to Sync Node in |
| Step 6 | Operational Transformation & Tree Lock | Sync Node processing | Node applies transformation to in-memory tree; updates node version vector | In-memory scene graph updated; local NVMe WAL written |
| Step 7 | Real-Time Fanout Broadcast | Sync Node Room Peers | Server broadcasts transformed op to all 14 active collaborators in document | Collaborators' screens update node position () |
| Step 8 | Operation Staged for Persistence | Sync Node SQS | Operation enqueued into Amazon SQS canvas-ops-changelog queue | Ephemeral op safely decoupled from Postgres write path |
| Step 9 | Changelog Worker Ingestion | Worker Task active | Worker consumes batch of 50 operations; prepares SQL batch insert | Formats SQL: INSERT INTO file_operations (...) |
| Step 10 | Rust DB Proxy AST Analysis | Worker Rust Proxy | Proxy parses SQL AST; extracts org_id; resolves org_id -> shard_id from the cached tenant directory | Query routed to Aurora Postgres Shard 42 Primary |
| Step 11 | Shard Commit & Checkpoint | Shard 42 Aurora PostgreSQL | Transaction commits (WAL-durable heap insert + uq_file_seq B-tree update); ACKs to proxy | Changelog persisted with strict ACID guarantees |
| Step 12 | Snapshot Compaction to S3 | Sync Node timer (30s) | Server serializes scene graph; issues asynchronous PutObject to Amazon S3 | New master snapshot committed; old changelogs pruned |
4. API Interface Design & Wire Protocol
1. Multiplayer WebSocket Binary Wire Protocol (SyncMessage.proto)
protobufsyntax = "proto3"; package figma.multiplayer.v1; enum OpType { NODE_INSERT = 0; NODE_UPDATE = 1; NODE_DELETE = 2; CURSOR_MOVE = 3; SELECTION_CHANGE = 4; } message Vector2D { float x = 1; float y = 2; } message CanvasOp { OpType op_type = 1; string node_id = 2; string parent_id = 3; uint64 client_sequence = 4; uint64 server_sequence = 5; bytes serialized_properties = 6; // Compact binary vector/color payload } message SyncFrame { string file_id = 1; string user_id = 2; uint64 session_epoch_ms = 3; repeated CanvasOp operations = 4; Vector2D cursor_position = 5; }
2. REST Metadata API: Create Design File (POST /v1/files)
httpPOST /v1/files HTTP/1.1 Host: api.figma.com Content-Type: application/json X-Figma-Org-Id: org_8492019284 X-Figma-Team-Id: team_301928471 Authorization: Bearer <oauth_jwt_token> Idempotency-Key: 7f8a9b2c-3d4e-5f6a-7b8c-9d0e1f2a3b4c { "name": "Design System 3.0", "folder_id": "fld_92019283", "default_canvas_color": "#1E1E1E" }
Response: 201 Created
json{ "file_id": "file_01HZX894KMNPQ9283", "org_id": "org_8492019284", "team_id": "team_301928471", "name": "Design System 3.0", "assigned_shard_id": 42, "sync_endpoint_wss": "wss://multiplayer.figma.com/v1/connect?file_id=file_01HZX894KMNPQ9283", "created_at_epoch_ms": 1773648000000 }
3. Status Codes & Error Contracts
| HTTP Status | Reason Code | Error Contract Payload | Client Handling / Recovery Strategy |
|---|---|---|---|
200 OK | SUCCESS | Standard file metadata payload | Initialize editor canvas workspace |
400 Bad Request | CROSS_SHARD_REJECTED | {"error": "Cross-shard transaction prohibited by proxy"} | Refactor query to include explicit single org_id |
401 Unauthorized | TOKEN_EXPIRED | {"error": "Session token invalid or revoked"} | Refresh OAuth token in background |
403 Forbidden | ORG_PERMISSION_DENIED | {"error": "User lacks view access for organization"} | Display permission request dialog |
404 Not Found | FILE_NOT_FOUND | {"error": "Design document has been permanently deleted"} | Return user to dashboard workspace |
409 Conflict | SEQUENCE_DESYNC | {"error": "Client sequence desynchronized from server"} | Client requests full scene graph snapshot catch-up |
503 Service Unavailable | SHARD_FAILOVER | {"error": "Postgres shard failover in progress"} | Rust proxy pauses requests for 2s; retries on replica |
5. Data Models & Storage Architecture
Sharded PostgreSQL Relational Schema
All tables enforce a strict multi-tenant partitioning hierarchy: org_id team_id file_id. Every single table in the schema contains org_id to enable deterministic single-shard routing.
Synthesizing vector architecture diagram...
Read the hierarchy top-down: an ORGANIZATION contains TEAMs, a team owns FILEs, and each file has SNAPSHOTs (checkpoints stored in S3) and an OPERATION_LOG (the edits since the last snapshot, ordered by op_sequence). The key detail is that every table carries org_id, even where a parent ID would be enough, and ORGANIZATION records its shard_id. Since the proxy routes every query by org_id, all of an organization's rows land on one shard, so its joins, foreign keys and transactions stay local. Loading a file means reading its latest snapshot and replaying only the operations after it.
Production PostgreSQL DDL: Sharded Files & Snapshots
sql-- Deployed on each of the 100 Aurora PostgreSQL Shards CREATE TABLE files ( file_id VARCHAR(64) NOT NULL, org_id BIGINT NOT NULL, -- Sharding Key: Guarantees co-location team_id BIGINT NOT NULL, name VARCHAR(255) NOT NULL, current_version BIGINT NOT NULL DEFAULT 1, is_deleted BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (org_id, file_id) -- Composite PK includes Shard Key ); CREATE TABLE file_snapshots ( snapshot_id BIGSERIAL, org_id BIGINT NOT NULL, file_id VARCHAR(64) NOT NULL, version BIGINT NOT NULL, s3_uri VARCHAR(512) NOT NULL, -- e.g., 's3://figma-snapshots/org_42/file_109/v45.bin' byte_size BIGINT NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (org_id, snapshot_id), CONSTRAINT fk_file FOREIGN KEY (org_id, file_id) REFERENCES files (org_id, file_id) ON DELETE CASCADE ); CREATE TABLE file_operations_log ( op_id BIGSERIAL, org_id BIGINT NOT NULL, file_id VARCHAR(64) NOT NULL, op_sequence BIGINT NOT NULL, operation_payload BYTEA NOT NULL, -- Compact binary Protobuf operation committed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (org_id, op_id), CONSTRAINT uq_file_seq UNIQUE (org_id, file_id, op_sequence) ); CREATE INDEX idx_files_team ON files (org_id, team_id);
Unlock Complete Architecture & Production Runbooks
You have explored the free architectural preview (~42%). Spend 1 Coin to unlock the remaining 6 production deep-dive sections for a full 24 hours.