pubkey / rxdb

A fast, local first, reactive Database for JavaScript Applications https://rxdb.info/

Home Page:https://rxdb.info/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

createRxDatabase(): A RxDatabase with the same name and adapter already exists.

iamsmkr opened this issue · comments

I am trying to create separate databases for each userId. The idea is to segregate the data for each user. In the code below you can see that if the currentUser has changed, we return pertaining db instance.

import { createRxDatabase, addRxPlugin } from 'rxdb';
import { getRxStorageDexie } from 'rxdb/plugins/storage-dexie';
import { heroSchema } from './Schema';
import { replicateCouchDB } from 'rxdb/plugins/replication-couchdb';
import { RxDBLeaderElectionPlugin } from 'rxdb/plugins/leader-election';
addRxPlugin(RxDBLeaderElectionPlugin);

const syncURL = 'http://' + window.location.hostname + ':10102/';
console.log('host: ' + syncURL);

let dbPromise = null;

const _create = async (userId) => {
    if (process.env.NODE_ENV === "development") {
        await import('rxdb/plugins/dev-mode').then(
            module => addRxPlugin(module.RxDBDevModePlugin)
        );
    }
    
    console.log('DatabaseService: creating database..');
    const db = await createRxDatabase({
        name: `heroesreactdb_${userId}`,
        storage: getRxStorageDexie()
    });
    console.log('DatabaseService: created database');
    window['db'] = db;

    // show leadership in title
    db.waitForLeadership().then(() => {
        console.log('isLeader now');
        document.title = '♛ ' + document.title;
    });

    // create collections
    console.log('DatabaseService: create collections');
    await db.addCollections({
        heroes: {
            schema: heroSchema,
            methods: {
                hpPercent() {
                    return this.hp / this.maxHP * 100;
                }
            }
        }
    });

    // hooks
    console.log('DatabaseService: add hooks');
    db.collections.heroes.preInsert(docObj => {
        const { color } = docObj;
        return db.collections.heroes.findOne({
            selector: { color }
        }).exec().then(has => {
            if (has !== null) {
                console.error('another hero already has the color ' + color);
                throw new Error('color already there');
            }
            return db;
        });
    });

    // sync
    console.log('DatabaseService: sync');
    await Promise.all(
        Object.values(db.collections).map(async (col) => {
            try {
                // create the CouchDB database
                await fetch(
                    syncURL + col.name + `_${userId}` + '/',
                    {
                        method: 'PUT'
                    }
                );
            } catch (err) { }
        })
    );
    console.log('DatabaseService: sync - start live');
    Object.values(db.collections).map(col => col.name).map(colName => {
        const url = `${syncURL}${colName}_${userId}/`;
        console.log('url: ' + url);
        const replicationState = replicateCouchDB({
            collection: db[colName],
            url,
            live: true,
            pull: {},
            push: {},
            autoStart: true
        });
        replicationState.error$.subscribe(err => {
            console.log('Got replication error:');
            console.dir(err);
        });
    });

    return db;
};

const storedUserId = localStorage.getItem('userId');
if (storedUserId === null) {
    localStorage.setItem('userId', '1');
}

let currentUser = null;
export const recreateDBPromise = () => {
    const newUser = localStorage.getItem('userId');
    if (newUser !== currentUser) {
        dbPromise = _create(newUser);
        currentUser = newUser;
    }
};

export const get = () => {
    recreateDBPromise();
    console.log('Current user:', currentUser);
    return dbPromise || _create(localStorage.getItem('userId'));
};

Here you can see we have a button that toggles between user ids. These user ids are stored in the localStorage and referred when creating db instances as shown above. Also, when userId is changed I also intend to navigate to the home screen which I have managed to mimic using react-router-dom useNavigate function.

Note: These approaches here are all contrived. I have tried simplify this example as much as possible to make a point. However, the issue does exists in the full blown app where I am trying to switch between google oauth users and navigate from the signin page to the home page.

Screen.Recording.2024-04-20.at.11.31.42.PM.mov

The problem that I face here is that despite application storage having separate dbs for both the user ids. Switching between user ids fails to fetch data.
Not sure if this is bug or it is something that I am doing wrong?

Screen.Recording.2024-04-20.at.11.32.49.PM.mov

Although I would like to point out that reloading the whole page using window.location.reload(); while navigating avoids this issue!

I have created a Pr to my fork so that it is easier to see the changes that were made to reproduce this issue: https://github.com/iamsmkr/rxdb/pull/1/files

You have to either cache the RxDatabase instance or call .destroy() on it before recreating it. It seems you are creating it twice for the same userId 1. This check is there to exactly prevent creating it twice.

I tried destroying the database instance:

let currentUser = null;
export const recreateDBPromise = async () => {
    const newUser = localStorage.getItem('userId');
    if (newUser !== currentUser) {
        if (database) {
            await database.destroy();
        }
        database = await _create(newUser);
        currentUser = newUser;
    }
    return database;
};

export const get = async () => {
    console.log('Current user:', currentUser);
    return await recreateDBPromise();
};

This results in destroy() api call getting stuck after a bunch of calls! Please have a look.

Screen.Recording.2024-04-21.at.11.22.50.AM.mov

Please make a PR with a test case.
You can start by editing this file: https://github.com/pubkey/rxdb/blob/master/test/unit/bug-report.test.ts

This issue has been automatically marked as stale because it has not had recent activity. It will be closed soon. If you still have a problem, make a PR with a test case or to prove that you have tried to fix the problem. Notice that only bugs in the rxdb premium plugins are ensured to be fixed by the maintainer. Everything else is expected to be fixed by the community, likely you must fix it by yourself.

commented

Issues are autoclosed after some time. If you still have a problem, make a PR with a test case or to prove that you have tried to fix the problem.