/** * this plugin adds the leader-election-capabilities to nxdb */ import { createLeaderElection, LeaderElector, BroadcastChannel } from 'broadcast-channel'; import { getBroadcastChannelReference, removeBroadcastChannelReference } from 'nxdb-old/src/rx-storage-multiinstance'; import type { RxDatabase, RxPlugin } from 'nxdb-old/src/types'; import { PROMISE_RESOLVE_TRUE, getFromMapOrCreate } from 'nxdb-old/src/plugins/utils'; const LEADER_ELECTORS_OF_DB: WeakMap = new WeakMap(); const LEADER_ELECTOR_BY_BROADCAST_CHANNEL: WeakMap = new WeakMap(); /** * Returns the leader elector of a broadcast channel. * Used to ensure we reuse the same elector for the channel each time. */ export function getLeaderElectorByBroadcastChannel(broadcastChannel: BroadcastChannel): LeaderElector { return getFromMapOrCreate( LEADER_ELECTOR_BY_BROADCAST_CHANNEL, broadcastChannel, () => createLeaderElection(broadcastChannel) ); } /** * @overwrites RxDatabase().leaderElector for caching */ export function getForDatabase(this: RxDatabase): LeaderElector { const broadcastChannel = getBroadcastChannelReference( this.token, this.name, this ); /** * Clean up the reference on RxDatabase.destroy() */ const oldDestroy = this.destroy.bind(this); this.destroy = function () { removeBroadcastChannelReference(this.token, this); return oldDestroy(); }; let elector = getLeaderElectorByBroadcastChannel(broadcastChannel); if (!elector) { elector = getLeaderElectorByBroadcastChannel(broadcastChannel); LEADER_ELECTORS_OF_DB.set( this, elector ); } /** * Overwrite for caching */ this.leaderElector = () => elector; return elector; } export function isLeader(this: RxDatabase): boolean { if (!this.multiInstance) { return true; } return this.leaderElector().isLeader; } export function waitForLeadership(this: RxDatabase): Promise { if (!this.multiInstance) { return PROMISE_RESOLVE_TRUE; } else { return this.leaderElector() .awaitLeadership() .then(() => true); } } /** * runs when the database gets destroyed */ export function onDestroy(db: RxDatabase) { const has = LEADER_ELECTORS_OF_DB.get(db); if (has) { has.die(); } } export const nxdb = true; export const prototypes = { RxDatabase: (proto: any) => { proto.leaderElector = getForDatabase; proto.isLeader = isLeader; proto.waitForLeadership = waitForLeadership; } }; export const NxDBLeaderElectionPlugin: RxPlugin = { name: 'leader-election', nxdb, prototypes, hooks: { preDestroyRxDatabase: { after: onDestroy } } };