Skip to content

Commit

Permalink
Node: enable routing by node address. (#1021)
Browse files Browse the repository at this point in the history
Co-authored-by: nihohit <[email protected]>
  • Loading branch information
shachlanAmazon and nihohit authored Feb 26, 2024
1 parent d8c056e commit ade0377
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#### Changes

- Node: Allow routing Cluster requests by address. ([#1021](https://github.com/aws/glide-for-redis/pull/1021))

## 0.2.0 (2024-02-11)

#### Changes
Expand Down
37 changes: 36 additions & 1 deletion node/src/RedisClusterClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ export type SlotKeyTypes = {
key: string;
};

/// Route command to specific node.
export type RouteByAddress = {
type: "routeByAddress";
/**
* DNS name of the host.
*/
host: string;
/**
* The port to access on the node. If port is not provided, `host` is assumed to be in the format `{hostname}:{port}`.
*/
port?: number;
};

export type Routes =
| SingleNodeRoute
/**
Expand All @@ -75,7 +88,8 @@ export type SingleNodeRoute =
/**
* Route request to the node that contains the slot that the given key matches.
*/
| SlotKeyTypes;
| SlotKeyTypes
| RouteByAddress;

function toProtobufRoute(
route: Routes | undefined
Expand Down Expand Up @@ -124,6 +138,27 @@ function toProtobufRoute(
slotId: route.id,
}),
});
} else if (route.type === "routeByAddress") {
let port = route.port;
let host = route.host;

if (port === undefined) {
const split = host.split(":");

if (split.length !== 2) {
throw new Error(
"No port provided, expected host to be formatted as `{hostname}:{port}`. Received " +
host
);
}

host = split[0];
port = Number(split[1]);
}

return redis_request.Routes.create({
byAddressRoute: { host, port },
});
}
}

Expand Down
69 changes: 69 additions & 0 deletions node/tests/RedisClusterClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,75 @@ describe("RedisClusterClient", () => {
TIMEOUT
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`route by address reaches correct node_%p`,
async (protocol) => {
const client = await RedisClusterClient.createClient(
getOptions(cluster.ports(), protocol)
);
const result = (await client.customCommand(
["cluster", "nodes"],
"randomNode"
)) as string;

// check that routing without explicit port works
const host =
result
.split("\n")
.find((line) => line.includes("myself"))
?.split(" ")[1]
.split("@")[0] ?? "";

if (!host) {
throw new Error("No host could be parsed");
}

const secondResult = (await client.customCommand(
["cluster", "nodes"],
{
type: "routeByAddress",
host,
}
)) as string;

expect(result).toEqual(secondResult);

const [host2, port] = host.split(":");

// check that routing with explicit port works
const thirdResult = (await client.customCommand(
["cluster", "nodes"],
{
type: "routeByAddress",
host: host2,
port: Number(port),
}
)) as string;

expect(result).toEqual(thirdResult);

client.close();
},
TIMEOUT
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`fail routing by address if no port is provided_%p`,
async (protocol) => {
const client = await RedisClusterClient.createClient(
getOptions(cluster.ports(), protocol)
);
expect(() =>
client.info(undefined, {
type: "routeByAddress",
host: "foo",
})
).toThrowError();
client.close();
},
TIMEOUT
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`config get and config set transactions test_%p`,
async (protocol) => {
Expand Down

0 comments on commit ade0377

Please sign in to comment.