rxdb
Version:
A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/
113 lines (111 loc) • 4.68 kB
JavaScript
/**
* When a persistent RxStorage is used in more the one JavaScript process,
* the even stream of the changestream() function must be broadcasted to the other
* RxStorageInstances of the same databaseName+collectionName.
*
* In the past this was done by RxDB but it makes more sense to do this
* at the RxStorage level so that the broadcasting etc can all happen inside of a WebWorker
* and not on the main thread.
* Also it makes it less complex to stack up different RxStorages onto each other
* like what we do with the in-memory plugin.
*
* This is intended to be used inside of createStorageInstance() of a storage.
* Do not use this if the storage anyway broadcasts the events like when using MongoDB
* or in the future W3C might introduce a way to listen to IndexedDB changes.
*/
import { Subject } from 'rxjs';
import { mergeWith } from 'rxjs/operators';
import { BroadcastChannel } from 'broadcast-channel';
/**
* The broadcast-channel is reused by the databaseInstanceToken.
* This is required so that it is easy to simulate multi-tab usage
* in the test where different instances of the same RxDatabase must
* have different broadcast channels.
* But also it ensures that for each RxDatabase we only create a single
* broadcast channel that can even be reused in the leader election plugin.
*/
export var BROADCAST_CHANNEL_BY_TOKEN = new Map();
export function getBroadcastChannelReference(storageName, databaseInstanceToken, databaseName, refObject) {
var state = BROADCAST_CHANNEL_BY_TOKEN.get(databaseInstanceToken);
if (!state) {
state = {
/**
* We have to use the databaseName instead of the databaseInstanceToken
* in the BroadcastChannel name because different instances must end with the same
* channel name to be able to broadcast messages between each other.
*/
bc: new BroadcastChannel(['RxDB:', storageName, databaseName].join('|')),
refs: new Set()
};
BROADCAST_CHANNEL_BY_TOKEN.set(databaseInstanceToken, state);
}
state.refs.add(refObject);
return state.bc;
}
export function removeBroadcastChannelReference(databaseInstanceToken, refObject) {
var state = BROADCAST_CHANNEL_BY_TOKEN.get(databaseInstanceToken);
if (!state) {
return;
}
state.refs.delete(refObject);
if (state.refs.size === 0) {
BROADCAST_CHANNEL_BY_TOKEN.delete(databaseInstanceToken);
return state.bc.close();
}
}
export function addRxStorageMultiInstanceSupport(storageName, instanceCreationParams, instance,
/**
* If provided, that channel will be used
* instead of an own one.
*/
providedBroadcastChannel) {
if (!instanceCreationParams.multiInstance) {
return;
}
var broadcastChannel = providedBroadcastChannel ? providedBroadcastChannel : getBroadcastChannelReference(storageName, instanceCreationParams.databaseInstanceToken, instance.databaseName, instance);
var changesFromOtherInstances$ = new Subject();
var eventListener = msg => {
if (msg.storageName === storageName && msg.databaseName === instanceCreationParams.databaseName && msg.collectionName === instanceCreationParams.collectionName && msg.version === instanceCreationParams.schema.version) {
changesFromOtherInstances$.next(msg.eventBulk);
}
};
broadcastChannel.addEventListener('message', eventListener);
var oldChangestream$ = instance.changeStream();
var closed = false;
var sub = oldChangestream$.subscribe(eventBulk => {
if (closed) {
return;
}
broadcastChannel.postMessage({
storageName: storageName,
databaseName: instanceCreationParams.databaseName,
collectionName: instanceCreationParams.collectionName,
version: instanceCreationParams.schema.version,
eventBulk
});
});
instance.changeStream = function () {
return changesFromOtherInstances$.asObservable().pipe(mergeWith(oldChangestream$));
};
var oldClose = instance.close.bind(instance);
instance.close = async function () {
closed = true;
sub.unsubscribe();
broadcastChannel.removeEventListener('message', eventListener);
if (!providedBroadcastChannel) {
await removeBroadcastChannelReference(instanceCreationParams.databaseInstanceToken, instance);
}
return oldClose();
};
var oldRemove = instance.remove.bind(instance);
instance.remove = async function () {
closed = true;
sub.unsubscribe();
broadcastChannel.removeEventListener('message', eventListener);
if (!providedBroadcastChannel) {
await removeBroadcastChannelReference(instanceCreationParams.databaseInstanceToken, instance);
}
return oldRemove();
};
}
//# sourceMappingURL=rx-storage-multiinstance.js.map