swarpc
Version:
Full type-safe RPC library for service worker -- move things off of the UI thread with ease!
68 lines (53 loc) • 1.75 kB
text/typescript
import { PendingRequest } from "./client.js";
import { scopeIsDedicated, scopeIsShared } from "./scopes.js";
/**
* Returns to which node to send the next request, given the state of the currently pending requests
*/
export function whoToSendTo(
nodes: undefined | Record<string, unknown>,
requests: Map<string, PendingRequest>,
): undefined | string {
if (!nodes) return undefined;
let chosen = Object.keys(nodes)[0];
const requestsPerNode = Map.groupBy(requests.values(), (req) => req.nodeId);
for (const node of Object.keys(nodes)) {
if (!requestsPerNode.has(node)) requestsPerNode.set(node, []);
}
for (const [node, reqs] of requestsPerNode.entries()) {
if (!node) continue;
// Send to the least busy node
if (reqs.length < requestsPerNode.get(chosen)!.length) chosen = node;
}
console.debug(
"[SWARPC Load balancer] Choosing",
chosen,
"load map is",
requestsPerNode,
);
return chosen;
}
export function nodeIdFromScope(
scope: WorkerGlobalScope,
_scopeType?: "dedicated" | "shared" | "service",
): string {
if (scopeIsDedicated(scope, _scopeType) || scopeIsShared(scope, _scopeType)) {
return scope.name;
}
return "(SW)";
}
/**
* Generate a random request ID, used to identify nodes in the client
* @source
*/
export function makeNodeId(): string {
return "N" + Math.random().toString(16).substring(2, 5).toUpperCase();
}
export const serviceWorkerNodeId = "(SW)" as const; // Fixed ID for the service worker, as there's only one
export function isServiceWorkerNodeId(id: string) {
return id === serviceWorkerNodeId;
}
export function nodeIdOrSW(
id: string | undefined,
): string | typeof serviceWorkerNodeId {
return id ?? serviceWorkerNodeId;
}