Skip to content

Commit 00f12ef

Browse files
committed
Created a new cluster for AzAffinity in CME, and added Standalone tests for CMD
Signed-off-by: Muhammad Awawdi <mawawdi@amazon.com>
1 parent a1ed3a0 commit 00f12ef

File tree

2 files changed

+313
-24
lines changed

2 files changed

+313
-24
lines changed

node/tests/GlideClient.test.ts

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
GlideString,
2222
ListDirection,
2323
ProtocolVersion,
24+
ReadFrom,
2425
RequestError,
2526
Script,
2627
Transaction,
@@ -1500,7 +1501,253 @@ describe("GlideClient", () => {
15001501
}
15011502
},
15021503
);
1504+
describe("GlideClient - AZAffinity Read Strategy Test", () => {
1505+
it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
1506+
"should route GET commands to all replicas with the same AZ using protocol %p",
1507+
async (protocol) => {
1508+
const az = "us-east-1a";
1509+
const GET_CALLS = 3;
1510+
const get_cmdstat = `cmdstat_get:calls=${GET_CALLS}`;
15031511

1512+
let client_for_config_set;
1513+
let client_for_testing_az;
1514+
1515+
try {
1516+
// Stage 1: Configure nodes
1517+
client_for_config_set = await GlideClient.createClient(
1518+
getClientConfigurationOption(
1519+
cluster.getAddresses(),
1520+
protocol,
1521+
),
1522+
);
1523+
1524+
// Skip test if version is below 8.0.0
1525+
if (cluster.checkIfServerVersionLessThan("8.0.0")) {
1526+
console.log(
1527+
"Skipping test: requires Valkey 8.0.0 or higher",
1528+
);
1529+
return;
1530+
}
1531+
1532+
await client_for_config_set.customCommand([
1533+
"CONFIG",
1534+
"RESETSTAT",
1535+
]);
1536+
await client_for_config_set.customCommand([
1537+
"CONFIG",
1538+
"SET",
1539+
"availability-zone",
1540+
az,
1541+
]);
1542+
1543+
// Stage 2: Create AZ affinity client and verify configuration
1544+
client_for_testing_az = await GlideClient.createClient(
1545+
getClientConfigurationOption(
1546+
cluster.getAddresses(),
1547+
protocol,
1548+
{
1549+
readFrom: "AZAffinity" as ReadFrom,
1550+
clientAz: az,
1551+
},
1552+
),
1553+
);
1554+
1555+
const azs = await client_for_testing_az.customCommand([
1556+
"CONFIG",
1557+
"GET",
1558+
"availability-zone",
1559+
]);
1560+
1561+
if (Array.isArray(azs) && azs.length > 0) {
1562+
const configItem = azs[0] as {
1563+
key: string;
1564+
value: string;
1565+
};
1566+
1567+
if (
1568+
configItem &&
1569+
configItem.key === "availability-zone"
1570+
) {
1571+
expect(configItem.value).toBe(az);
1572+
} else {
1573+
throw new Error("Invalid config item format");
1574+
}
1575+
} else {
1576+
throw new Error(
1577+
"Unexpected response format from CONFIG GET command",
1578+
);
1579+
}
1580+
1581+
// Stage 3: Set test data and perform GET operations
1582+
await client_for_testing_az.set("foo", "testvalue");
1583+
1584+
for (let i = 0; i < GET_CALLS; i++) {
1585+
await client_for_testing_az.get("foo");
1586+
}
1587+
1588+
// Stage 4: Verify GET commands were routed correctly
1589+
const info_result =
1590+
await client_for_testing_az.customCommand([
1591+
"INFO",
1592+
"COMMANDSTATS",
1593+
]);
1594+
1595+
if (
1596+
typeof info_result === "string" &&
1597+
info_result.includes(get_cmdstat)
1598+
) {
1599+
expect(info_result).toContain(get_cmdstat);
1600+
} else {
1601+
throw new Error(
1602+
"Unexpected response format from INFO command in standalone mode",
1603+
);
1604+
}
1605+
} finally {
1606+
// Cleanup
1607+
await client_for_config_set?.close();
1608+
await client_for_testing_az?.close();
1609+
}
1610+
},
1611+
);
1612+
});
1613+
describe("GlideClient - AZAffinity Routing to 1 replica", () => {
1614+
it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
1615+
"should route commands to single replica with AZ using protocol %p",
1616+
async (protocol) => {
1617+
const az = "us-east-1a";
1618+
const GET_CALLS = 3;
1619+
const get_cmdstat = `calls=${GET_CALLS}`;
1620+
1621+
let client_for_config_set;
1622+
let client_for_testing_az;
1623+
1624+
try {
1625+
// Stage 1: Configure nodes
1626+
client_for_config_set = await GlideClient.createClient(
1627+
getClientConfigurationOption(
1628+
cluster.getAddresses(),
1629+
protocol,
1630+
),
1631+
);
1632+
1633+
// Skip test if version is below 8.0.0
1634+
if (cluster.checkIfServerVersionLessThan("8.0.0")) {
1635+
console.log(
1636+
"Skipping test: requires Valkey 8.0.0 or higher",
1637+
);
1638+
return;
1639+
}
1640+
1641+
await client_for_config_set.customCommand([
1642+
"CONFIG",
1643+
"SET",
1644+
"availability-zone",
1645+
az,
1646+
]);
1647+
1648+
await client_for_config_set.customCommand([
1649+
"CONFIG",
1650+
"RESETSTAT",
1651+
]);
1652+
1653+
// Stage 2: Create AZ affinity client and verify configuration
1654+
client_for_testing_az = await GlideClient.createClient(
1655+
getClientConfigurationOption(
1656+
cluster.getAddresses(),
1657+
protocol,
1658+
{
1659+
readFrom: "AZAffinity",
1660+
clientAz: az,
1661+
},
1662+
),
1663+
);
1664+
await client_for_testing_az.set("foo", "testvalue");
1665+
1666+
for (let i = 0; i < GET_CALLS; i++) {
1667+
await client_for_testing_az.get("foo");
1668+
}
1669+
1670+
// Stage 4: Verify GET commands were routed correctly
1671+
const info_result =
1672+
await client_for_testing_az.customCommand([
1673+
"INFO",
1674+
"ALL",
1675+
]);
1676+
1677+
if (typeof info_result === "string") {
1678+
expect(info_result).toContain(get_cmdstat);
1679+
expect(info_result).toContain(
1680+
`availability_zone:${az}`,
1681+
);
1682+
} else {
1683+
throw new Error(
1684+
"Unexpected response format from INFO COMMANDSTATS command",
1685+
);
1686+
}
1687+
} finally {
1688+
await client_for_config_set?.close();
1689+
await client_for_testing_az?.close();
1690+
}
1691+
},
1692+
);
1693+
});
1694+
describe("GlideClient - AZAffinity with Non-existing AZ", () => {
1695+
it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
1696+
"should route commands to a replica when AZ does not exist using protocol %p",
1697+
async (protocol) => {
1698+
const GET_CALLS = 4;
1699+
const get_cmdstat = `cmdstat_get:calls=${GET_CALLS}`;
1700+
1701+
let client_for_testing_az;
1702+
1703+
try {
1704+
if (cluster.checkIfServerVersionLessThan("8.0.0")) {
1705+
console.log(
1706+
"Skipping test: requires Valkey 8.0.0 or higher",
1707+
);
1708+
return;
1709+
}
1710+
1711+
client_for_testing_az = await GlideClient.createClient(
1712+
getClientConfigurationOption(
1713+
cluster.getAddresses(),
1714+
protocol,
1715+
{
1716+
readFrom: "AZAffinity",
1717+
clientAz: "non-existing-az",
1718+
requestTimeout: 2000,
1719+
},
1720+
),
1721+
);
1722+
1723+
await client_for_testing_az.customCommand([
1724+
"CONFIG",
1725+
"RESETSTAT",
1726+
]);
1727+
1728+
for (let i = 0; i < GET_CALLS; i++) {
1729+
await client_for_testing_az.get("foo");
1730+
}
1731+
1732+
const info_result =
1733+
await client_for_testing_az.customCommand([
1734+
"INFO",
1735+
"COMMANDSTATS",
1736+
]);
1737+
1738+
if (typeof info_result === "string") {
1739+
expect(info_result).toContain(get_cmdstat);
1740+
} else {
1741+
throw new Error(
1742+
"Unexpected response format from INFO command",
1743+
);
1744+
}
1745+
} finally {
1746+
await client_for_testing_az?.close();
1747+
}
1748+
},
1749+
);
1750+
});
15041751
runBaseTests({
15051752
init: async (protocol, configOverrides) => {
15061753
const config = getClientConfigurationOption(

0 commit comments

Comments
 (0)