Skip to content

Commit df8541e

Browse files
authored
WIP REFACTOR multiInstance step 1 (pubkey#3870)
* REFACTOR multiInstance step 1 * FIX lokijs multiInstance events * CHANGE do not reuse the same worker * CHANGE test dexie-worker instead of loki-worker * FIX dexie-worker tests * FIX randomly failing tests * FIX slow test * FIX timeout test * FIX test * FIX test * FIX tests * FIX tests * FIX tests * FIX timeouts * FIX teimouts * FIX timeouts * FIX ci * FIX ci * FIX timeouts * FIX pouchdb multi instance support * FIX timeouts * FIX timeouts * FIX update lwt * FIX * FIX randomly failing tests * FIX randomly failing test * FIX random failing test * UPDATE node js * REFACTOR correct re-use of the broadcast channel * FIX cleanup of broadcast channels * FIX timeout * FIX do not use multi instance in rx-storage-replication tests * FIX stuff * FIX lokijs tests * FIX imports * FIX logs
1 parent a2f3f8c commit df8541e

40 files changed

+930
-537
lines changed

.github/workflows/main.yml

+5-5
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ jobs:
178178

179179
# run the node tests for the LokiJS RxStorage in a different
180180
# task to run in parallel.
181-
node-lokijs-worker:
181+
node-dexie-worker:
182182
runs-on: ubuntu-20.04
183183
steps:
184184
- uses: actions/checkout@v2
@@ -205,11 +205,11 @@ jobs:
205205
- name: build
206206
run: npm run build
207207

208-
- name: npm run test:node:lokijs-worker
209-
run: npm run test:node:lokijs-worker
208+
- name: npm run test:node:dexie-worker
209+
run: npm run test:node:dexie-worker
210210

211-
- name: npm run test:fast:lokijs-worker
212-
run: npm run test:fast:lokijs-worker
211+
- name: npm run test:fast:dexie-worker
212+
run: npm run test:fast:dexie-worker
213213

214214
# run the node tests for the Dexie.js RxStorage in a different
215215
# task to run in parallel.

.nvmrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v18.3.0
1+
v18.4.0

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
<!-- CHANGELOG NEWEST -->
55

6+
- The worker RxStorage will no longer reuse the worker so that multiple RxDatabase instances can use different workers for better performance.
7+
68
<!-- ADD new changes here! -->
79

810
<!-- /CHANGELOG NEWEST -->

docs-src/rx-storage-worker.md

+29-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33
With the worker plugin, you can put the `RxStorage` of your database inside of a WebWorker (in browsers) or a Worker Thread (in node.js). By doing so, you can take CPU load from the main process and move it into the worker's process which can improve the percieved performance of your application. RxDB uses [threads.js](https://github.com/andywer/threads.js/) to create the Worker process an to communicate with it.
44

5-
In theory you can put any `RxStorage` implementation into a worker. For now this is only tested with the [LokiJS RxStorage](./rx-storage-lokijs.md).
6-
7-
8-
95
## On the worker process
106

117
```ts
@@ -85,6 +81,35 @@ const database = await createRxDatabase({
8581
```
8682

8783

84+
## One worker per database
85+
86+
Each call to `getRxStorageWorker()` will create a different worker instance so that when you have more then one `RxDatabase`, each database will have its own JavaScript worker process.
87+
88+
To reuse the worker instance in more then one `RxDatabase`, you can store the output of `getRxStorageWorker()` into a variable an use that one. Reusing the worker can decrease the initial page load, but you might get slower database operations.
89+
90+
```ts
91+
// Call getRxStorageWorker() exactly once
92+
const workerStorage = getRxStorageWorker({
93+
statics: RxStorageLokiStatics,
94+
workerInput: 'path/to/worker.js'
95+
});
96+
97+
// use the same storage for both databases.
98+
const databaseOne = await createRxDatabase({
99+
name: 'database-one',
100+
storage: workerStorage
101+
});
102+
const databaseTwo = await createRxDatabase({
103+
name: 'database-two',
104+
storage: workerStorage
105+
});
106+
107+
```
108+
109+
110+
111+
112+
88113
--------------------------------------------------------------------------------
89114

90115
If you are new to RxDB, you should continue [here](./rx-storage-sharding.md)

orga/before-next-major.md

+5
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ Having mixed types causes many confusion, for example when the type is `['string
8282
you could run a query selector like `$gt: 10` where it now is not clear if the string `foobar` is matching or not.
8383

8484

85+
86+
## Remove RxDatabase.broadcastChannel
87+
The broadcast channel has been moved out of the RxDatabase and is part of the RxStorage. So we should not longer expose the getter.
88+
89+
8590
## Remove temporary documents
8691

8792
https://github.com/pubkey/rxdb/pull/3777#issuecomment-1120669088

package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,20 @@
3636
"postinstall": "node scripts/postinstall.js || echo \"ignore\"",
3737
"test": "npm run test:node && npm run test:browser",
3838
"// test:fast": "run tests in the fast-mode. Most of them will run in parrallel, skips tests that are known slow",
39-
"test:fast": "npm run test:fast:memory && npm run test:fast:pouchdb && npm run test:fast:lokijs && npm run test:fast:lokijs-worker && npm run test:fast:dexie",
39+
"test:fast": "npm run test:fast:memory && npm run test:fast:pouchdb && npm run test:fast:lokijs && npm run test:fast:dexie-worker && npm run test:fast:dexie",
4040
"test:fast:pouchdb": "npm run transpile && rimraf -rf pouch__all_dbs__ && cross-env DEFAULT_STORAGE=pouchdb NODE_ENV=fast mocha --config ./config/.mocharc.js ./test_tmp/unit.test.js",
4141
"test:fast:memory": "npm run transpile && rimraf -rf pouch__all_dbs__ && cross-env DEFAULT_STORAGE=memory NODE_ENV=fast mocha --config ./config/.mocharc.js ./test_tmp/unit.test.js",
4242
"test:fast:lokijs": "npm run transpile && cross-env DEFAULT_STORAGE=lokijs NODE_ENV=fast mocha --config ./config/.mocharc.js ./test_tmp/unit.test.js",
4343
"test:fast:dexie": "npm run transpile && cross-env DEFAULT_STORAGE=dexie NODE_ENV=fast mocha --config ./config/.mocharc.js ./test_tmp/unit.test.js",
44-
"test:fast:lokijs-worker": "npm run transpile && npm run build:workers && rimraf -rf pouch__all_dbs__ && cross-env DEFAULT_STORAGE=lokijs-worker NODE_ENV=fast mocha --config ./config/.mocharc.js ./test_tmp/unit.test.js",
44+
"test:fast:dexie-worker": "npm run transpile && npm run build:workers && rimraf -rf pouch__all_dbs__ && cross-env DEFAULT_STORAGE=dexie-worker NODE_ENV=fast mocha --config ./config/.mocharc.js ./test_tmp/unit.test.js",
4545
"// test:fast:loop": "runs tests in the fast-mode in a loop. Use this to debug tests that only fail sometimes",
4646
"test:fast:loop": "npm run test:fast && npm run test:fast:loop",
4747
"test:fast:loop:lokijs": "npm run test:fast:lokijs && npm run test:fast:loop:lokijs",
48-
"test:node": "npm run test:node:pouchdb && npm run test:node:lokijs && npm run test:node:lokijs-worker",
48+
"test:node": "npm run test:node:pouchdb && npm run test:node:lokijs && npm run test:node:dexie",
4949
"test:node:pouchdb": "npm run transpile && cross-env DEFAULT_STORAGE=pouchdb mocha --expose-gc --config ./config/.mocharc.js ./test_tmp/unit.test.js",
5050
"test:node:memory": "npm run transpile && cross-env DEFAULT_STORAGE=memory mocha --expose-gc --config ./config/.mocharc.js ./test_tmp/unit.test.js",
5151
"test:node:lokijs": "npm run transpile && cross-env DEFAULT_STORAGE=lokijs mocha --expose-gc --config ./config/.mocharc.js ./test_tmp/unit.test.js",
52-
"test:node:lokijs-worker": "npm run transpile && npm run build:workers && cross-env DEFAULT_STORAGE=lokijs-worker mocha --expose-gc --config ./config/.mocharc.js ./test_tmp/unit.test.js",
52+
"test:node:dexie-worker": "npm run transpile && npm run build:workers && cross-env DEFAULT_STORAGE=dexie-worker mocha --expose-gc --config ./config/.mocharc.js ./test_tmp/unit.test.js",
5353
"test:node:dexie": "npm run transpile && cross-env DEFAULT_STORAGE=dexie mocha --expose-gc --config ./config/.mocharc.js ./test_tmp/unit.test.js",
5454
"test:node:pouchdb:loop": "npm run test:node:pouchdb && npm run test:node:pouchdb:loop",
5555
"test:node:lokijs:loop": "npm run test:node:lokijs && npm run test:node:lokijs:loop",
@@ -72,7 +72,7 @@
7272
"test:circular": "npm run build && madge --circular ./dist/es/index.js",
7373
"test:performance:pouchdb": "npm run transpile && cross-env STORAGE=pouchdb mocha --config ./config/.mocharc.js ./test_tmp/performance.test.js --unhandled-rejections=strict --expose-gc",
7474
"test:performance:lokijs": "npm run transpile && cross-env STORAGE=lokijs mocha --config ./config/.mocharc.js ./test_tmp/performance.test.js --unhandled-rejections=strict --expose-gc",
75-
"test:performance:lokijs-worker": "npm run transpile && cross-env STORAGE=lokijs-worker mocha --config ./config/.mocharc.js ./test_tmp/performance.test.js --unhandled-rejections=strict --expose-gc",
75+
"test:performance:dexie-worker": "npm run transpile && cross-env STORAGE=dexie-worker mocha --config ./config/.mocharc.js ./test_tmp/performance.test.js --unhandled-rejections=strict --expose-gc",
7676
"couch:start": "docker run -d -p 5984:5984 --rm --name rxdb-couchdb couchdb:2.1.1",
7777
"couch:stop": "docker rm -f rxdb-couchdb",
7878
"test:couchdb": "npm run transpile && cross-env DEFAULT_STORAGE=pouchdb mocha --config ./config/.mocharc.js ./test_tmp/couch-db-integration.test.js",
@@ -264,4 +264,4 @@
264264
"webpack-bundle-analyzer": "4.5.0",
265265
"webpack-cli": "4.9.2"
266266
}
267-
}
267+
}

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export {
8080
export * from './rx-storage-helper';
8181

8282
export * from './rx-storage-replication';
83+
export * from './rx-storage-multiinstance';
8384

8485
export * from './custom-index';
8586
export * from './query-planner';

src/plugins/dev-mode/error-messages.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export const ERROR_MESSAGES = {
5959
DB8: 'RxDatabase.create(): A RxDatabase with the same name and adapter already exists.\n' +
6060
'Make sure to use this combination only once or set ignoreDuplicate to true if you do this intentional',
6161
DB9: 'createRxDatabase(): Adapter not added. Use addPouchPlugin(require(\'pouchdb-adapter-[adaptername]\'));',
62-
DB10: 'createRxDatabase(): To use leveldown-adapters, you have to add the leveldb-plugin. Use addRxPlugin(require(\'pouchdb-adapter-leveldb\'));',
62+
DB10: 'createRxDatabase(): To use leveldown-adapters, you have to add the leveldb-plugin. Use addPouchPlugin(require(\'pouchdb-adapter-leveldb\'));',
6363
DB11: 'createRxDatabase(): Invalid db-name, folder-paths must not have an ending slash',
6464

6565
// rx-collection

src/plugins/dexie/rx-storage-dexie.ts

-3
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@ export const RxStorageDexieStatics: RxStorageStatics = {
3737
});
3838
},
3939
hashKey: 'md5',
40-
doesBroadcastChangestream() {
41-
return false;
42-
},
4340
prepareQuery<RxDocType>(
4441
schema: RxJsonSchema<RxDocumentData<RxDocType>>,
4542
mutateableQuery: FilledMangoQuery<RxDocType>

src/plugins/dexie/rx-storage-instance-dexie.ts

+6
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import {
4444
import { dexieQuery } from './dexie-query';
4545
import { getPrimaryFieldOfPrimaryKey } from '../../rx-schema-helper';
4646
import { getUniqueDeterministicEventKey } from '../../rx-storage-helper';
47+
import { addRxStorageMultiInstanceSupport } from '../../rx-storage-multiinstance';
4748

4849
let instanceId = now();
4950

@@ -419,5 +420,10 @@ export async function createDexieStorageInstance<RxDocType>(
419420
settings
420421
);
421422

423+
addRxStorageMultiInstanceSupport(
424+
params,
425+
instance
426+
);
427+
422428
return instance;
423429
}

src/plugins/leader-election.ts

+32-3
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,23 @@ import {
77
LeaderElector,
88
BroadcastChannel
99
} from 'broadcast-channel';
10+
import {
11+
getBroadcastChannelReference,
12+
removeBroadcastChannelReference
13+
} from '../rx-storage-multiinstance';
1014

1115
import type {
1216
RxDatabase,
1317
RxPlugin
1418
} from '../types';
1519
import {
16-
ensureNotFalsy,
1720
PROMISE_RESOLVE_TRUE
1821
} from '../util';
1922

2023
const LEADER_ELECTORS_OF_DB: WeakMap<RxDatabase, LeaderElector> = new WeakMap();
2124
const LEADER_ELECTOR_BY_BROADCAST_CHANNEL: WeakMap<BroadcastChannel, LeaderElector> = new WeakMap();
2225

26+
2327
/**
2428
* Returns the leader elector of a broadcast channel.
2529
* Used to ensure we reuse the same elector for the channel each time.
@@ -33,9 +37,28 @@ export function getLeaderElectorByBroadcastChannel(broadcastChannel: BroadcastCh
3337
return elector;
3438
}
3539

36-
40+
/**
41+
* @overwrites RxDatabase().leaderElector for caching
42+
*/
3743
export function getForDatabase(this: RxDatabase): LeaderElector {
38-
const broadcastChannel = ensureNotFalsy(this.broadcastChannel);
44+
45+
46+
const broadcastChannel = getBroadcastChannelReference(
47+
this.token,
48+
this.name,
49+
this
50+
);
51+
52+
/**
53+
* Clean up the reference on RxDatabase.destroy()
54+
*/
55+
const oldDestroy = this.destroy.bind(this);
56+
this.destroy = function () {
57+
removeBroadcastChannelReference(this.token, this);
58+
return oldDestroy();
59+
}
60+
61+
3962
let elector = getLeaderElectorByBroadcastChannel(broadcastChannel);
4063
if (!elector) {
4164
elector = getLeaderElectorByBroadcastChannel(broadcastChannel);
@@ -44,6 +67,12 @@ export function getForDatabase(this: RxDatabase): LeaderElector {
4467
elector
4568
);
4669
}
70+
71+
/**
72+
* Overwrite for caching
73+
*/
74+
this.leaderElector = () => elector;
75+
4776
return elector;
4877
}
4978

src/plugins/local-documents/local-documents-helper.ts

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import type {
1818
RxLocalDocumentData,
1919
RxStorage
2020
} from '../../types';
21+
import { randomCouchString } from '../../util';
2122

2223
const LOCAL_DOC_STATE_BY_PARENT: WeakMap<LocalDocumentParent, Promise<LocalDocumentState>> = new WeakMap();
2324

@@ -27,6 +28,7 @@ export function createLocalDocStateByParent(parent: LocalDocumentParent): void {
2728
const collectionName = parent.database ? parent.name : '';
2829
const statePromise = (async () => {
2930
let storageInstance = await createLocalDocumentStorageInstance(
31+
database.token,
3032
database.storage,
3133
database.name,
3234
collectionName,
@@ -101,13 +103,15 @@ export function getLocalDocStateByParent(parent: LocalDocumentParent): Promise<L
101103

102104

103105
export function createLocalDocumentStorageInstance(
106+
databaseInstanceToken: string,
104107
storage: RxStorage<any, any>,
105108
databaseName: string,
106109
collectionName: string,
107110
instanceCreationOptions: any,
108111
multiInstance: boolean
109112
) {
110113
return storage.createStorageInstance<RxLocalDocumentData>({
114+
databaseInstanceToken,
111115
databaseName: databaseName,
112116
/**
113117
* Use a different collection name for the local documents instance
@@ -134,7 +138,9 @@ export async function removeLocalDocumentsStorageInstance(
134138
databaseName: string,
135139
collectionName: string
136140
) {
141+
const databaseInstanceToken = randomCouchString(10);
137142
const storageInstance = await createLocalDocumentStorageInstance(
143+
databaseInstanceToken,
138144
storage,
139145
databaseName,
140146
collectionName,

src/plugins/lokijs/loki-save-queue.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ export class LokiSaveQueue {
6464
* Because LokiJS is a in-memory database,
6565
* we can just wait until the JavaScript process is idle
6666
* via requestIdlePromise(). Then we know that nothing important
67-
* is running at the moment. Also we wait at least wait 100ms
68-
* to ensure it has enough time to free up stuff.
67+
* is running at the moment.
6968
*/
7069
await requestIdlePromise().then(() => requestIdlePromise());
7170

0 commit comments

Comments
 (0)