Sharding And Partitioning For Payment Data
Asked of: Software Engineer
Last updated

What's being tested
This topic tests whether you can design a payment-data system that scales horizontally without weakening correctness, isolation, or operability. The interviewer is probing your ability to choose a partition key, route requests, handle cross-shard queries, and reason about resharding and failures. Stripe cares because payment operations are high-value, retry-prone, and correctness-sensitive: duplicate charges, lost state transitions, or inconsistent reads are materially worse than an ordinary web-service failure.
Core knowledge
-
Sharding distributes rows across independent database instances, while partitioning divides data within a logical database. A useful design separates the logical data model from physical placement so shards can be moved, split, or replicated without changing API semantics.
-
Choose a key based on dominant access patterns and isolation boundaries. For merchant-owned payment objects,
merchant_idoften provides tenant locality; for globally queried objects, hashpayment_intent_idto distribute load. Never assume one key serves every query efficiently. -
Hash partitioning gives relatively even distribution:
shard = hash(key) mod N, though consistent hashing or a virtual-node ring reduces movement whenNchanges. Range partitioning supports ordered scans and time-based retention but risks hot ranges, especially for monotonically increasing IDs. -
A good shard key should offer high cardinality, stable ownership, predictable routing, and resistance to hotspots. Avoid status, currency, or country alone: their low cardinality creates concentrated partitions. A merchant with unusually high volume may require dedicated shards or subpartitioning.
-
Preserve idempotency at the application boundary and storage layer. An idempotency key should map to the original request result and be uniquely constrained within the correct scope, such as
(merchant_id, idempotency_key); retries must not create a second payment attempt. -
Model payment state transitions as guarded writes, not read-then-write logic. For example, update only when
status = 'requires_confirmation', check the affected-row count, and use a transaction or compare-and-set operation to prevent concurrent transitions from charging twice. -
Replication improves read capacity and availability but introduces replica lag. Route correctness-critical reads—such as immediately checking whether a capture succeeded—to the primary or use a session consistency token; stale replicas are acceptable only for explicitly tolerant views.
-
Cross-shard transactions are expensive and often unavailable. Prefer a local transaction plus an outbox record, then process asynchronous effects idempotently. If an operation spans payment state, ledger state, and notifications, define recovery and reconciliation rather than assuming distributed two-phase commit.
-
Secondary indexes must be co-located with the data they index or implemented as deliberately maintained lookup tables. A global index across shards creates a coordination bottleneck; a denormalized index should include the target shard and tolerate stale entries with repair or fallback lookup.
-
Resharding needs a migration protocol: dual-read or dual-write, backfill existing rows, verify counts/checksums, switch routing, and retain rollback capability. Virtual shards make movement incremental; production traffic must continue safely during copying and cutover.
-
Use consistent routing metadata rather than embedding shard arithmetic throughout application code. A directory can map tenant or virtual-shard IDs to physical databases, with cached reads, versioned assignments, and a safe behavior when metadata is unavailable.
-
Design observability around shard-level signals:
p99latency, error rate, replication lag, transaction conflicts, storage growth, queue age, and load skew. Alert on the hottest shard and on correctness symptoms such as duplicate idempotency conflicts or reconciliation mismatches.
Worked example
Design a sharded payment-data store is a representative design prompt for this topic. In the first 30 seconds, clarify the required operations—create, retrieve by ID, list by merchant and time range, update state, and reconcile—and ask about scale, latency, durability, and whether cross-merchant queries are required. I would declare that payment writes require strong correctness, retries are expected, and the primary lookup is by payment ID while merchant-scoped listing is also common. I would organize the answer around four pillars: data model and invariants, shard-key and routing strategy, consistency/idempotency, and migration/operations. A practical design could hash payment IDs for even distribution while maintaining a merchant-local index for listing, or use merchant-based placement if tenant isolation and locality dominate. I would explicitly flag the tradeoff: merchant-based sharding simplifies tenant queries but allows a large platform to create a hot shard, whereas payment-ID hashing balances load but makes merchant listings more complex. I would close by discussing replica reads, outbox-driven secondary indexes, resharding, and reconciliation; if I had more time, I would quantify capacity per shard and walk through a failed cutover.
A second angle
A different framing is designing a merchant activity feed or “list payments by customer and date” query over the same data. The correctness requirement for the primary payment record remains strong, but the read model can be eventually consistent if it is clearly separated from authorization or capture decisions. The main challenge shifts from write serialization to index design, pagination, and avoiding scatter-gather queries across every shard. A cursor containing the last sort key plus a stable tie-breaker is safer than offset pagination while records are being added concurrently.
Common pitfalls
Analytical mistake: Choosing merchant_id automatically because payments are merchant-owned ignores skew and payment-ID lookups. A stronger answer names the access patterns, estimates distribution, and explains whether large merchants receive dedicated capacity or a second-level partition.
Pitfall: “Hash everything by payment ID, then scan all shards for merchant history” sounds scalable for writes but produces unacceptable read amplification.
Communication mistake: Jumping directly into Kafka, databases, and hash functions without stating invariants makes the design difficult to evaluate. Lead with the operations, correctness requirements, scale assumptions, and the failure that must never happen—such as two successful charges for one idempotent request.
Depth mistake: Saying “use a transaction” does not explain what it protects across shards or retries. Specify the transaction boundary, guarded state transition, idempotency constraint, outbox behavior, and reconciliation path when an external side effect succeeds but the process crashes.
Connections
The interviewer may pivot to idempotency keys, leader/replica consistency, distributed transactions, outbox patterns, or consistent hashing. They may also ask how a ledger differs from mutable payment metadata: financial records generally require append-only, auditable invariants rather than ordinary row updates.
Further reading
-
Designing Data-Intensive Applications — Chapters on partitioning, replication, transactions, and stream processing provide the core distributed-systems vocabulary.
-
Stripe Engineering: Idempotency — Explains why retry-safe APIs matter for payment operations and how idempotent request handling shapes system design.
-
Google Spanner: “Spanner: Google’s Globally-Distributed Database” — Useful for understanding strongly consistent distributed storage and the cost of global coordination.
Related concepts
- Adobe Sharded Tenant Data And Transaction Integrity
- Adobe Multi-Tenant Sharding And Access Control
- Distributed Query Execution, Shuffle, And Skew
- Payment Processing And Ledger SystemsSystem Design
- Concurrency Control And Transactional Integrity In Payments
- Payment Systems: Ledgers, Idempotency, and Reconciliation