Guide · Amazon ElastiCache · Global Datastore · Cross-Region · MCP Server Tools
ElastiCache Global Datastore for MCP Servers — Cross-Region Replication, Failover, Multi-Region Sessions
MCP servers deployed across multiple AWS regions face a common problem: how do you share session state, rate limit counters, and configuration between regions without introducing a cross-region round trip on every request? ElastiCache Global Datastore solves this with an active-passive architecture: one primary region accepts all writes and replicates asynchronously to up to two secondary regions at under 1 second of lag. MCP servers in secondary regions read locally (sub-millisecond), and writes travel to the primary region. When the primary region fails, AWS can promote a secondary to primary in minutes. This guide covers setup, topology, the failover procedure, operational constraints, and when Global Datastore makes sense versus a simpler single-region setup.
TL;DR
Global Datastore = one writable primary replication group + up to 2 read-only secondary replication groups in different regions. Replication lag is typically under 1 second (not zero — do not use for data requiring strict consistency across regions). Writes in secondary regions are proxied to the primary — this adds 50–150 ms cross-region latency to every write. After primary region failure, promote a secondary with FailoverGlobalReplicationGroupCommand — existing connections to the old primary must reconnect. Data tiering (r6gd) is not supported in Global Datastore. Each replication group is billed independently.
When to use Global Datastore
Global Datastore is worth its cost and operational complexity in specific scenarios:
- Multi-region session state: users who roam between regions (mobile apps, CDN-routed requests) can hit their session in any region without a cross-region DB query.
- Read-heavy global cache: product catalogs, feature flags, and configuration that is read thousands of times per second in every region but updated rarely can be served locally in each region from Global Datastore.
- Disaster recovery: if your primary region (us-east-1) goes down, you have a warm Redis replica in us-west-2 with <1 second of data lag, ready to promote without a cold-start rebuild.
- Rate limiting with regional fairness: rate limit counters written to the primary can be read in secondary regions — a user who exhausts their limit in us-east-1 is blocked in eu-west-1 within ~1 second of lag.
Do not use Global Datastore when: you need writes to succeed locally in every region (Global Datastore is active-passive, not active-active), you are using ElastiCache for Valkey (Global Datastore supports ElastiCache for Redis only, not Valkey), or your workload is write-heavy from multiple regions (all writes cross the inter-region link to the primary).
Architecture: primary + secondary replication groups
A Global Datastore consists of:
- Primary replication group (one region) — accepts reads and writes, replicates to secondaries asynchronously
- Secondary replication group(s) (up to 2 additional regions) — read-only; writes are accepted but proxied to the primary region (adds cross-region latency to writes)
// SDK: create a Global Datastore from an existing primary replication group
// (the primary group must already exist in the region you deploy this from)
import {
ElastiCacheClient,
CreateGlobalReplicationGroupCommand,
DescribeGlobalReplicationGroupsCommand,
} from '@aws-sdk/client-elasticache';
const client = new ElastiCacheClient({ region: 'us-east-1' });
// Step 1: Create the Global Datastore using the existing primary replication group
const { GlobalReplicationGroup } = await client.send(
new CreateGlobalReplicationGroupCommand({
GlobalReplicationGroupIdSuffix: 'mcp-prod', // becomes 'ldgnf-mcp-prod' with auto-prefix
PrimaryReplicationGroupId: 'mcp-cache-primary',
GlobalReplicationGroupDescription: 'MCP server session + rate limit cache',
}),
);
console.log('Global Datastore ID:', GlobalReplicationGroup?.GlobalReplicationGroupId);
// Step 2: Add a secondary replication group in eu-west-1
// (create an empty replication group in eu-west-1 first, then add it)
import { CreateReplicationGroupCommand } from '@aws-sdk/client-elasticache';
const euClient = new ElastiCacheClient({ region: 'eu-west-1' });
await euClient.send(new CreateReplicationGroupCommand({
ReplicationGroupId: 'mcp-cache-eu',
ReplicationGroupDescription: 'MCP cache EU secondary',
GlobalReplicationGroupId: GlobalReplicationGroup!.GlobalReplicationGroupId,
// Node type must match primary — use same CacheNodeType
CacheNodeType: 'cache.r7g.large',
NumCacheClusters: 2, // 1 primary + 1 replica within EU
}));
// Wait for status = 'available' before routing traffic
The GlobalReplicationGroupId is prefixed by AWS with a 5-character identifier (e.g. ldgnf-mcp-prod) — you cannot control this prefix. Use the Suffix parameter to make the ID recognizable.
Connecting MCP servers to their regional endpoint
Each replication group has its own cluster endpoint. MCP servers in a region connect to their local replication group endpoint — not to the Global Datastore ID or the primary endpoint in another region.
// us-east-1 MCP servers connect to the primary region cluster endpoint
const redisPrimary = new Redis({
host: process.env.ELASTICACHE_ENDPOINT, // us-east-1 cluster endpoint
port: 6379,
tls: {},
password: process.env.ELASTICACHE_AUTH_TOKEN,
});
// eu-west-1 MCP servers connect to the EU secondary cluster endpoint
// (same code — ELASTICACHE_ENDPOINT is different per-region deploy config)
const redisEU = new Redis({
host: process.env.ELASTICACHE_ENDPOINT, // eu-west-1 cluster endpoint
port: 6379,
tls: {},
password: process.env.ELASTICACHE_AUTH_TOKEN,
});
// Reads: served locally in each region (fast)
const session = await redis.get(`session:${sessionId}`);
// Writes: appear to succeed locally in secondary regions,
// but are actually proxied to the primary region asynchronously.
// From eu-west-1, this write travels to us-east-1 and back — adds ~100ms
await redis.set(`session:${sessionId}`, JSON.stringify(sessionData), 'EX', 3600);
Writes from secondary regions are proxied to the primary — your application does not need special logic to route writes, but you should account for the extra latency. For MCP server use cases where session writes happen once (login) and reads happen many times (every tool call), this tradeoff is favorable. For write-heavy workloads where every request writes, consider whether the write-proxy latency is acceptable.
Monitoring replication lag
The key metric for Global Datastore health is GlobalDatastoreReplicationLag in CloudWatch — the time in milliseconds between when data is written to the primary and when it appears in secondaries. Under normal conditions this is under 100 ms. Spikes above 1,000 ms indicate network issues between regions or a secondary under heavy load.
import { CloudWatchClient, GetMetricStatisticsCommand } from '@aws-sdk/client-cloudwatch';
async function getReplicationLag(globalReplicationGroupId: string): Promise {
const cw = new CloudWatchClient({ region: 'us-east-1' });
const now = new Date();
const fiveMinutesAgo = new Date(now.getTime() - 5 * 60 * 1000);
const { Datapoints } = await cw.send(new GetMetricStatisticsCommand({
Namespace: 'AWS/ElastiCache',
MetricName: 'GlobalDatastoreReplicationLag',
Dimensions: [
{ Name: 'GlobalReplicationGroupId', Value: globalReplicationGroupId },
],
StartTime: fiveMinutesAgo,
EndTime: now,
Period: 60,
Statistics: ['Maximum'],
}));
if (!Datapoints || Datapoints.length === 0) return 0;
return Math.max(...Datapoints.map(d => d.Maximum ?? 0));
}
// Alert if replication lag exceeds 2 seconds
const lagMs = await getReplicationLag('ldgnf-mcp-prod');
if (lagMs > 2000) {
console.error(`[global-datastore] ALERT: replication lag ${lagMs}ms > 2s threshold`);
}
Also monitor ReplicationBytes (data flowing between regions) and CurrConnections per replication group. Set a CloudWatch alarm on GlobalDatastoreReplicationLag > 5000 (5 seconds) to trigger an operational review before lag grows large enough to affect failover RPO.
Failover: promoting a secondary region
If the primary region is unavailable, you can promote a secondary replication group to become the new primary. The promotion takes 2–5 minutes. After promotion, the old primary becomes a secondary (if it recovers) and must be re-added manually.
import { FailoverGlobalReplicationGroupCommand } from '@aws-sdk/client-elasticache';
// From eu-west-1 (the region you want to promote)
const euClient = new ElastiCacheClient({ region: 'eu-west-1' });
await euClient.send(new FailoverGlobalReplicationGroupCommand({
GlobalReplicationGroupId: 'ldgnf-mcp-prod',
PrimaryRegion: 'eu-west-1', // promote this region to primary
PrimaryReplicationGroupId: 'mcp-cache-eu',
}));
console.log('Failover initiated — monitor DescribeGlobalReplicationGroups for status');
// Poll until the failover completes
async function waitForFailover(globalId: string, targetPrimary: string): Promise {
while (true) {
const { GlobalReplicationGroups } = await euClient.send(
new DescribeGlobalReplicationGroupsCommand({ GlobalReplicationGroupId: globalId }),
);
const group = GlobalReplicationGroups?.[0];
const newPrimary = group?.Members?.find(m => m.Role === 'PRIMARY');
if (newPrimary?.ReplicationGroupId === targetPrimary) {
console.log('Failover complete — new primary:', targetPrimary);
return;
}
await new Promise(r => setTimeout(r, 5000));
}
}
Connection updates after failover: your MCP servers in eu-west-1 were already connected to the eu-west-1 endpoint — no connection string change needed for eu-west-1 traffic. Any MCP servers in us-east-1 that were connected to the old primary will get connection errors during failover; ioredis will reconnect automatically once the old cluster stabilizes or you re-route DNS to the new primary.
RPO (Recovery Point Objective): approximately equal to the replication lag at the moment of failure — typically under 1 second, never zero. Any writes committed to the primary after the last replicated entry are permanently lost. For MCP server session data, losing 1 second of writes means users may need to re-authenticate — acceptable for most applications.
Global Datastore constraints and limitations
| Constraint | Detail |
|---|---|
| Engine | ElastiCache for Redis only. Not supported for ElastiCache for Valkey or Memcached. |
| Minimum Redis version | Redis 5.0.6 or later |
| Data tiering | Not supported — r6gd instances (NVMe SSD tiering) cannot be used in Global Datastore |
| Cluster mode | Cluster mode enabled replication groups are supported in Global Datastore |
| Number of secondary regions | Maximum 2 secondary replication groups |
| Parameter groups | All regional replication groups must use compatible parameter group families |
| Encryption | Encryption at rest and in-transit must be consistently configured — cannot mix encrypted and unencrypted groups |
| Deletion order | Cannot delete a Global Datastore with active secondary members. Remove secondaries first (DisassociateGlobalReplicationGroupCommand), then delete primary. |
| AUTH token | All replication groups in the Global Datastore must use the same AUTH token. Rotate all simultaneously. |
| Billing | Each replication group (primary + each secondary) is billed separately as a normal ElastiCache replication group. Cross-region data transfer costs are additional. |
Deletion: remove secondaries before primary
Attempting to delete the primary replication group while secondary members are still associated fails with InvalidGlobalReplicationGroupStateFault. Always disassociate secondaries first.
import {
DisassociateGlobalReplicationGroupCommand,
DeleteGlobalReplicationGroupCommand,
DeleteReplicationGroupCommand,
} from '@aws-sdk/client-elasticache';
// Step 1: Disassociate secondary regions
const euClient = new ElastiCacheClient({ region: 'eu-west-1' });
await euClient.send(new DisassociateGlobalReplicationGroupCommand({
GlobalReplicationGroupId: 'ldgnf-mcp-prod',
ReplicationGroupId: 'mcp-cache-eu',
ReplicationGroupRegion: 'eu-west-1',
}));
// Wait for 'available' status before proceeding
// Step 2: Delete the Global Datastore shell (retains the primary replication group)
const primaryClient = new ElastiCacheClient({ region: 'us-east-1' });
await primaryClient.send(new DeleteGlobalReplicationGroupCommand({
GlobalReplicationGroupId: 'ldgnf-mcp-prod',
RetainPrimaryReplicationGroup: true, // keep the primary cluster (default true)
}));
// Step 3: Optionally delete the now-independent regional groups
await euClient.send(new DeleteReplicationGroupCommand({
ReplicationGroupId: 'mcp-cache-eu',
}));
await primaryClient.send(new DeleteReplicationGroupCommand({
ReplicationGroupId: 'mcp-cache-primary',
}));
Related guides
- ElastiCache for Redis: VPC setup, parameter groups, ioredis connection
- ElastiCache auth token, TLS, zero-downtime ROTATE rotation, RBAC
- ElastiCache cluster mode: hash slots, MOVED/ASK redirects, CROSSSLOT
- ElastiCache caching patterns: cache-aside, write-through, TTL strategy
- ElastiCache rate limiting: Lua scripts, sliding window, token bucket