actionhero
Version:
The reusable, scalable, and quick node.js API server for stateless and stateful applications
94 lines (87 loc) • 2.76 kB
text/typescript
import * as uuid from "uuid";
import { config, api, id, log } from "./../index";
export namespace redis {
export interface PubSubMessage {
[key: string]: any;
}
/**
* Publish a message to all other Actionhero nodes in the cluster. Will be authenticated against `api.config.serverToken`
* ```js
* let payload = {
* messageType: 'myMessageType',
* serverId: api.id,
* serverToken: api.config.general.serverToken,
* message: 'hello!'
* }
*
* await api.redis.publish(payload)
* ```
*/
export async function publish(payload: object | Array<any>) {
const channel = config.general.channel;
const connection = api.redis.clients.client;
const stringPayload = JSON.stringify(payload);
if (connection.status !== "close" && connection.status !== "end") {
return connection.publish(channel, stringPayload);
} else {
log(`cannot send message, redis disconnected`, "error", {
channel,
payload,
});
}
}
/**
* Invoke a command on all servers in this cluster.
*/
export async function doCluster<T>(
method: string,
args: Array<any> = [],
connectionId?: string,
waitForResponse: boolean = false,
): Promise<T extends any ? T : unknown> {
const messageId = uuid.v4();
const payload = {
messageType: "do",
serverId: id,
serverToken: config.general.serverToken,
messageId: messageId,
method: method,
connectionId: connectionId,
args: args, // [1,2,3]
};
// we need to be sure that we build the response-handling promise before sending the request to Redis
// it is possible for another node to get and work the request before we resolve our write
// see https://github.com/actionhero/actionhero/issues/1244 for more information
if (waitForResponse) {
return new Promise(async (resolve, reject) => {
const timer = setTimeout(
() => reject(new Error("RPC Timeout")),
config.general.rpcTimeout,
);
api.redis.rpcCallbacks[messageId] = { timer, resolve, reject };
try {
await redis.publish(payload);
} catch (e) {
clearTimeout(timer);
delete api.redis.rpcCallbacks[messageId];
throw e;
}
});
} else {
return redis.publish(payload) as T extends any ? T : unknown;
}
}
export async function respondCluster(
messageId: string,
response: PubSubMessage,
) {
const payload = {
messageType: "doResponse",
serverId: id,
serverToken: config.general.serverToken,
messageId: messageId,
response: response, // args to pass back, including error
};
await redis.publish(payload);
}
}