Skip to content

Commit 8620b28

Browse files
authored
FIX #7008 migrate schema with multiple connected storages causes error (#7036)
* FIX migrate schema with multiple connected storages causes error #7008 * REMOVE log * FIX sqlite * FIX sqlite * FIX type
1 parent 6147b98 commit 8620b28

File tree

7 files changed

+154
-9
lines changed

7 files changed

+154
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
- ADD [Localstorage RxStorage](https://rxdb.info/rx-storage-localstorage.html).
1010
- ADD SQLite Storage example with tauri [#6939](https://github.com/pubkey/rxdb/pull/6939).
1111
- ADD `closeDuplicates` flag as alternative to `ignoreDuplicate` to `createRxDatabase` [#7013](https://github.com/pubkey/rxdb/pull/7013).
12-
12+
- FIX migrate schema with multiple connected storages causes error [#7008](https://github.com/pubkey/rxdb/pull/7008)
1313
<!-- ADD new changes here! -->
1414

1515
<!-- /CHANGELOG NEWEST -->

src/plugins/migration-schema/rx-migration-state.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ import {
3434
ensureNotFalsy,
3535
errorToPlainJson,
3636
getDefaultRevision,
37-
getDefaultRxDocumentMeta
37+
getDefaultRxDocumentMeta,
38+
promiseWait
3839
} from '../utils/index.ts';
3940
import type {
4041
MigrationStatusUpdate,
@@ -54,6 +55,7 @@ import {
5455
import {
5556
META_INSTANCE_SCHEMA_TITLE,
5657
awaitRxStorageReplicationFirstInSync,
58+
awaitRxStorageReplicationInSync,
5759
cancelRxStorageReplication,
5860
defaultConflictHandler,
5961
getRxReplicationMetaInstanceSchema,
@@ -337,7 +339,7 @@ export class RxMigrationState {
337339
) {
338340
const replicationMetaStorageInstance = await this.database.storage.createStorageInstance({
339341
databaseName: this.database.name,
340-
collectionName: 'rx-migration-state-meta-' + this.collection.name + '-' + this.collection.schema.version,
342+
collectionName: 'rx-migration-state-meta-' + oldStorage.collectionName + '-' + oldStorage.schema.version,
341343
databaseInstanceToken: this.database.token,
342344
multiInstance: this.database.multiInstance,
343345
options: {},
@@ -361,7 +363,7 @@ export class RxMigrationState {
361363
keepMeta: true,
362364
identifier: [
363365
'rx-migration-state',
364-
this.collection.name,
366+
oldStorage.collectionName,
365367
oldStorage.schema.version,
366368
this.collection.schema.version
367369
].join('-'),
@@ -431,6 +433,7 @@ export class RxMigrationState {
431433
});
432434

433435
await awaitRxStorageReplicationFirstInSync(replicationState);
436+
await awaitRxStorageReplicationInSync(replicationState);
434437
await cancelRxStorageReplication(replicationState);
435438

436439
await this.updateStatusQueue;

src/plugins/storage-memory/memory-helper.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ export function ensureNotRemoved(
3333
instance: RxStorageInstanceMemory<any>
3434
) {
3535
if (instance.internals.removed) {
36-
throw new Error('removed');
36+
throw new Error(
37+
'removed already ' +
38+
instance.databaseName + ' - ' + instance.collectionName +
39+
' - ' + instance.schema.version
40+
);
3741
}
3842
}
3943

src/plugins/storage-sqlite/sqlite-helpers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ export function getSQLiteUpdateSQL<RxDocType>(
159159
};
160160

161161

162-
const TX_QUEUE_BY_DATABASE: WeakMap<SQLiteDatabaseClass, Promise<void>> = new WeakMap();
162+
export const TX_QUEUE_BY_DATABASE: WeakMap<SQLiteDatabaseClass, Promise<void>> = new WeakMap();
163163
export function sqliteTransaction(
164164
database: SQLiteDatabaseClass,
165165
sqliteBasics: SQLiteBasics<any>,

src/plugins/storage-sqlite/sqlite-storage-instance.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ import {
3030
RX_STORAGE_NAME_SQLITE,
3131
sqliteTransaction,
3232
getDataFromResultRow,
33-
getSQLiteInsertSQL
33+
getSQLiteInsertSQL,
34+
TX_QUEUE_BY_DATABASE
3435
} from './sqlite-helpers.ts';
3536
import type {
3637
SQLiteBasics,
@@ -123,7 +124,7 @@ export class RxStorageInstanceSQLite<RxDocType> implements RxStorageInstance<
123124
async () => {
124125
if (this.closed) {
125126
this.openWriteCount$.next(this.openWriteCount$.getValue() - 1);
126-
throw new Error('SQLite.bulkWrite() already closed ' + this.tableName + ' context: ' + context);
127+
throw new Error('SQLite.bulkWrite(' + context + ') already closed ' + this.tableName + ' context: ' + context);
127128
}
128129
const result = await this.all(
129130
database,
@@ -373,6 +374,11 @@ export class RxStorageInstanceSQLite<RxDocType> implements RxStorageInstance<
373374
}
374375

375376
async close(): Promise<void> {
377+
const queue = TX_QUEUE_BY_DATABASE.get(await this.internals.databasePromise);
378+
if (queue) {
379+
await queue;
380+
}
381+
376382
if (this.closed) {
377383
return this.closed;
378384
}

test/unit.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import './unit/rx-storage-helper.test.ts';
2525

2626
import './unit/rx-storage-dexie.test.ts';
2727
import './unit/rx-storage-remote.test.ts';
28-
2928
import './unit/instance-of-check.test.ts';
3029
import './unit/rx-schema.test.ts';
3130
import './unit/bug-report.test.ts';

test/unit/migration-schema.test.ts

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,139 @@ describe('migration-schema.test.ts', function () {
778778
});
779779
});
780780
describeParallel('issues', () => {
781+
it('#7008 migrate schema with multiple connected storages', async () => {
782+
// create a schema
783+
const mySchema = {
784+
version: 0,
785+
primaryKey: 'passportId',
786+
type: 'object',
787+
properties: {
788+
passportId: {
789+
type: 'string',
790+
maxLength: 100
791+
},
792+
firstName: {
793+
type: 'string'
794+
},
795+
lastName: {
796+
type: 'string'
797+
},
798+
age: {
799+
type: 'integer',
800+
minimum: 0,
801+
maximum: 150
802+
}
803+
}
804+
};
805+
806+
// create a schema to migrate to
807+
const mySchema2 = {
808+
version: 1,
809+
primaryKey: 'passportId',
810+
type: 'object',
811+
properties: {
812+
passportId: {
813+
type: 'string',
814+
maxLength: 100
815+
},
816+
fullName: {
817+
type: 'string'
818+
},
819+
age: {
820+
type: 'integer',
821+
minimum: 0,
822+
maximum: 150
823+
}
824+
}
825+
};
826+
827+
const name = randomToken(10);
828+
const db = await createRxDatabase({
829+
name,
830+
storage: config.storage.getStorage(),
831+
eventReduce: true,
832+
ignoreDuplicate: true
833+
});
834+
// create a collection
835+
const collections = await db.addCollections({
836+
mycollection: {
837+
schema: mySchema
838+
}
839+
});
840+
841+
// create a replication state - this adds a new connected storage
842+
const replicationState = replicateRxCollection({
843+
collection: collections.mycollection,
844+
replicationIdentifier: 'replication-1',
845+
push: {
846+
handler: () => {
847+
return Promise.resolve([]);
848+
}
849+
},
850+
pull: {
851+
handler: () => {
852+
return Promise.resolve({ checkpoint: null, documents: [] });
853+
}
854+
}
855+
});
856+
857+
// create another replication state - this adds an additional connected storage
858+
const replicationState2 = replicateRxCollection({
859+
collection: collections.mycollection,
860+
replicationIdentifier: 'replication-2',
861+
push: {
862+
handler: () => {
863+
return Promise.resolve([]);
864+
}
865+
},
866+
pull: {
867+
handler: () => {
868+
return Promise.resolve({ checkpoint: null, documents: [] });
869+
}
870+
}
871+
});
872+
873+
// wait until initial replication is done
874+
await Promise.all([
875+
replicationState.awaitInitialReplication(),
876+
replicationState2.awaitInitialReplication()
877+
]);
878+
879+
// insert a document
880+
await collections.mycollection.insert({
881+
passportId: 'foobar',
882+
firstName: 'Bob',
883+
lastName: 'Kelso',
884+
age: 56
885+
});
886+
887+
await db.close();
888+
889+
const db2 = await createRxDatabase({
890+
name,
891+
storage: config.storage.getStorage(),
892+
eventReduce: true,
893+
ignoreDuplicate: true
894+
});
895+
896+
// create a collection with the new schema
897+
const collections2 = await db2.addCollections({
898+
mycollection: {
899+
schema: mySchema2,
900+
migrationStrategies: {
901+
1: (oldDoc: any) => {
902+
oldDoc.fullName = oldDoc.firstName + ' ' + oldDoc.lastName;
903+
delete oldDoc.lastName;
904+
delete oldDoc.firstName;
905+
return oldDoc;
906+
}
907+
}
908+
}
909+
});
910+
const docs = await collections2.mycollection.find().exec();
911+
assert.strictEqual(docs.length, 1);
912+
await db2.close();
913+
});
781914
it('#212 migration runs into infinity-loop', async () => {
782915
const dbName = randomToken(10);
783916
const schema0 = {

0 commit comments

Comments
 (0)