Skip to content

Commit

Permalink
ADD check to ensure remote has same RxDB version (#6674)
Browse files Browse the repository at this point in the history
  • Loading branch information
pubkey authored Dec 10, 2024
1 parent b5c5af9 commit 5f32567
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 4 deletions.
1 change: 1 addition & 0 deletions docs-src/docs/releases/16.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ I completely rewrote them and improved performance especially on initial load wh
- If the handler of a [RxPipeline](../rx-pipeline.md) throws an error, block the whole pipeline and emit the error to the outside.
- Throw error when dexie.js RxStorage is used with optional index fields [#6643](https://github.com/pubkey/rxdb/pull/6643#issuecomment-2505310082).
- Fix IndexedDB bug: Some people had problems with the IndexedDB RxStorage that opened up collections very slowly. If you had this problem, please try out this new version.
- Add check to ensure remote instances are build with the same RxDB version. This is to ensure if you update RxDB and forget to rebuild your workers, it will throw instead of causing strange problems.

## Other

Expand Down
5 changes: 4 additions & 1 deletion src/plugins/dev-mode/error-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export const ERROR_MESSAGES = {
COL21: 'The RxCollection is closed or removed already, either from this JavaScript realm or from another, like a browser tab',
CONFLICT: 'Document update conflict. When changing a document you must work on the previous revision',
COL22: '.bulkInsert() and .bulkUpsert() cannot be run with multiple documents that have the same primary key',
COL23: 'In the open-source version of RxDB, the amount of collections that can exist in parallel is limited to '+NON_PREMIUM_COLLECTION_LIMIT+'. If you already purchased the premium access, you can remove this limit: https://rxdb.info/rx-collection.html#faq',
COL23: 'In the open-source version of RxDB, the amount of collections that can exist in parallel is limited to ' + NON_PREMIUM_COLLECTION_LIMIT + '. If you already purchased the premium access, you can remove this limit: https://rxdb.info/rx-collection.html#faq',

// rx-document.js
DOC1: 'RxDocument.get$ cannot get observable of in-array fields because order cannot be guessed',
Expand Down Expand Up @@ -256,6 +256,9 @@ export const ERROR_MESSAGES = {
DXE1: 'non-required index fields are not possible with the dexie.js RxStorage: https://github.com/pubkey/rxdb/pull/6643#issuecomment-2505310082',
// removed in 15.0.0, added boolean index support to dexie storage - DXE1: 'The dexie.js RxStorage does not support boolean indexes, see https://rxdb.info/rx-storage-dexie.html#boolean-index',

// plugins/storage-remote
RM1: 'Cannot communicate with a remote that was build on a different RxDB version. Did you forget to rebuild your workers when updating RxDB?',

/**
* Should never be thrown, use this for
* null checks etc. so you do not have to increase the
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/storage-remote-websocket/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export function startRxStorageRemoteWebsocketServer(
send(msg) {
const ws = getFromMapOrThrow(websocketByConnectionId, msg.connectionId);
ws.send(JSON.stringify(msg));
}
},
fakeVersion: options.fakeVersion
};
const exposeState = exposeRxStorageRemote(exposeSettings);

Expand Down
5 changes: 5 additions & 0 deletions src/plugins/storage-remote-websocket/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ export type RxStorageRemoteWebsocketServerOptions = ServerOptions & {
storage?: RxStorage<any, any>;
database?: RxDatabase<any, any, any>;
customRequestHandler?: CustomRequestHandler<any, any>;
/**
* Used in tests to simulate what happens if the remote
* was build on a different RxDB version.
*/
fakeVersion?: string;
};

export type RxStorageRemoteWebsocketServerState = {
Expand Down
15 changes: 14 additions & 1 deletion src/plugins/storage-remote/remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import type {
} from '../../types/index.d.ts';
import {
deepEqual,
ensureNotFalsy
ensureNotFalsy,
RXDB_VERSION
} from '../../plugins/utils/index.ts';
import { createAnswer, createErrorAnswer } from './storage-remote-helpers.ts';
import type {
Expand All @@ -17,6 +18,7 @@ import type {
RxStorageRemoteExposeType
} from './storage-remote-types.ts';
import { getChangedDocumentsSince } from '../../rx-storage-helper.ts';
import { newRxError } from '../../rx-error.ts';

/**
* Run this on the 'remote' part,
Expand Down Expand Up @@ -79,9 +81,20 @@ export function exposeRxStorageRemote(settings: RxStorageRemoteExposeSettings):
}
}

const mustBeRxDBVersion = settings.fakeVersion ? settings.fakeVersion : RXDB_VERSION;
settings.messages$.pipe(
filter(msg => msg.method === 'create')
).subscribe(async (msg) => {
if (msg.version !== mustBeRxDBVersion) {
settings.send(createErrorAnswer(msg, newRxError('RM1', {
args: {
mainVersion: msg.version,
remoteVersion: mustBeRxDBVersion
}
})));
return;
}

const connectionId = msg.connectionId;

/**
Expand Down
4 changes: 3 additions & 1 deletion src/plugins/storage-remote/rx-storage-remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import type {
RxStorageRemoteSettings
} from './storage-remote-types.ts';
import { closeMessageChannel, getMessageChannel } from './message-channel-cache.ts';
import { ensureRxStorageInstanceParamsAreCorrect } from '../../rx-storage-helper.ts';


export class RxStorageRemote implements RxStorage<RxStorageRemoteInternals, any> {
Expand Down Expand Up @@ -90,6 +89,7 @@ export class RxStorageRemote implements RxStorage<RxStorageRemoteInternals, any>
messageChannel.send({
connectionId,
method: 'create',
version: RXDB_VERSION,
requestId,
params
});
Expand Down Expand Up @@ -124,6 +124,7 @@ export class RxStorageRemote implements RxStorage<RxStorageRemoteInternals, any>
messageChannel.send({
connectionId,
method: 'custom',
version: RXDB_VERSION,
requestId,
params: data
});
Expand Down Expand Up @@ -203,6 +204,7 @@ export class RxStorageInstanceRemote<RxDocType> implements RxStorageInstance<RxD
const message: MessageToRemote = {
connectionId: this.internals.connectionId,
requestId,
version: RXDB_VERSION,
method: methodName,
params
};
Expand Down
12 changes: 12 additions & 0 deletions src/plugins/storage-remote/storage-remote-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ export type MessageToRemote = {
*/
requestId: string;
method: keyof RxStorageInstance<any, any, any> | 'create' | 'custom';
/**
* We send the RxDB version to the remote
* to ensure we are communicating with an RxDB instance
* of the same version. This is to prevent bugs
* when people forget to rebuild their workers.
*/
version: string;
params:
RxStorageInstanceCreationParams<any, any> | // used in the create call
any[] | // used to call RxStorageInstance methods
Expand Down Expand Up @@ -76,6 +83,11 @@ export type RxStorageRemoteExposeSettingsBase = {
send(msg: MessageFromRemote): void;
messages$: Observable<MessageToRemote>;
customRequestHandler?: CustomRequestHandler<any, any>;
/**
* Used in tests to simulate what happens if the remote
* was build on a different RxDB version.
*/
fakeVersion?: string;
};

export type RxStorageRemoteExposeSettingsRxDatabase = RxStorageRemoteExposeSettingsBase & {
Expand Down
52 changes: 52 additions & 0 deletions test/unit/rx-storage-remote.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '../../plugins/storage-remote-websocket/index.mjs';
import { getRxStorageMemory } from '../../plugins/storage-memory/index.mjs';
import { wrappedValidateAjvStorage } from '../../plugins/validate-ajv/index.mjs';
import { assertThrows } from 'async-test-util';

describeParallel('rx-storage-remote.test.ts', () => {
/**
Expand Down Expand Up @@ -350,6 +351,57 @@ describeParallel('rx-storage-remote.test.ts', () => {
await database.close();
});
});
describe('other', () => {
/**
* Many people forgot to rebuild their webworkers and shared workers when updating
* RxDB which lead to strange bugs.
* To prevent this, the remote storage itself should ensure that it only communicates
* with remote instances that have the same RxDB version.
*/
it('should throw when the remote was build on a different RxDB version', async () => {
const port = await nextPort();

const database = await createRxDatabase({
name: randomToken(10),
storage: memoryStorageWithValidation
});
await database.addCollections({
one: {
schema: schemas.human
},
two: {
schema: schemas.human
}
});
const server = await startRxStorageRemoteWebsocketServer({
port,
database,
fakeVersion: 'wrong-version'
});
assert.ok(server);

const storage = getRxStorageRemoteWebsocket({
url: 'ws://localhost:' + port,
mode: 'collection'
});

await assertThrows(
() => storage.createStorageInstance({
databaseInstanceToken: randomToken(10),
databaseName: randomToken(10),
collectionName: 'one',
devMode: true,
multiInstance: false,
options: {},
schema: fillWithDefaultSettings(schemas.human)
}),
Error,
['RM1', 'wrong-version']
);

await database.close();
});
});
describe('custom requests', () => {
it('should send the message and get the answer', async () => {
const port = await nextPort();
Expand Down

0 comments on commit 5f32567

Please sign in to comment.