GSI Overloading
GSI overloading is the technique of writing different values to the same GSI key attributes (gsi1pk, gsi1sk) depending on which entity type is being stored. One Global Secondary Index serves multiple distinct access patterns across multiple entity types.
Why overload
DynamoDB allows 20 GSIs per table. Each GSI adds write cost (every put to the main table also writes to the GSI). GSI overloading reduces both the number of GSIs needed and the per-write cost.
Without overloading, each entity-specific access pattern gets its own GSI:
- GSI for orders by status
- GSI for users by email
- GSI for products by category
With overloading, one GSI serves all three:
gsi1pk = STATUS#<status>for ordersgsi1pk = EMAIL#<email>for usersgsi1pk = CATEGORY#<cat>for products
How it works
The GSI key attributes (gsi1pk, gsi1sk) are generic column names. Each entity type writes different logical values to those same columns:
Order: gsi1pk = "STATUS#pending", gsi1sk = "ORDER#<ulid>"
User: gsi1pk = "EMAIL#alice@example.com", gsi1sk = "USER#<userId>"
Product: gsi1pk = "CATEGORY#electronics", gsi1sk = "PRODUCT#<productId>"
Querying the GSI with pk = STATUS#pending returns only Order items — because only Order items write that partition key value. User and Product items are in different GSI partitions.
The GSI serves three different access patterns with zero cross-contamination between entity types. It works because the partition key value acts as the discriminator.
The access pattern requirement
GSI overloading works when the access patterns share the same logical structure: “give me all items where attribute X = value Y, sorted by attribute Z.” The attribute names (X, Y, Z) can differ per entity type — but they’re all mapped to the same physical GSI columns.
It does not work when access patterns have fundamentally different shapes. If one entity needs a GSI with no sort key and another needs a range sort key, they can’t easily share a GSI.
In ElectroDB
ElectroDB makes GSI overloading explicit through named indexes and composite key templates:
// Order entity uses GSI1 for status queries
byStatus: {
index: "GSI1",
pk: { field: "gsi1pk", composite: ["status"], template: "STATUS#${status}" },
sk: { field: "gsi1sk", composite: ["orderId"], template: "ORDER#${orderId}" },
}
// User entity uses GSI1 for email lookups
byEmail: {
index: "GSI1",
pk: { field: "gsi1pk", composite: ["email"], template: "EMAIL#${email}" },
sk: { field: "gsi1sk", composite: ["userId"], template: "USER#${userId}" },
}
ElectroDB routes each entity’s queries to the correct GSI partition automatically.
Real example
The SaaS Multi-Tenant pattern uses 2 GSIs to serve 10 access patterns across 4 entity types. Without overloading, the same schema would require 6–8 GSIs. See DynamoDB too many GSIs for techniques to reduce GSI count.