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:

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:

// 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

ConstraintDetail
EngineElastiCache for Redis only. Not supported for ElastiCache for Valkey or Memcached.
Minimum Redis versionRedis 5.0.6 or later
Data tieringNot supported — r6gd instances (NVMe SSD tiering) cannot be used in Global Datastore
Cluster modeCluster mode enabled replication groups are supported in Global Datastore
Number of secondary regionsMaximum 2 secondary replication groups
Parameter groupsAll regional replication groups must use compatible parameter group families
EncryptionEncryption at rest and in-transit must be consistently configured — cannot mix encrypted and unencrypted groups
Deletion orderCannot delete a Global Datastore with active secondary members. Remove secondaries first (DisassociateGlobalReplicationGroupCommand), then delete primary.
AUTH tokenAll replication groups in the Global Datastore must use the same AUTH token. Rotate all simultaneously.
BillingEach 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