Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
iamsmkr opened this issue Apr 20, 2024 · 4 comments

Comments

@iamsmkr
Copy link

iamsmkr commented Apr 20, 2024

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

@pubkey
Copy link
Owner

pubkey commented Apr 21, 2024

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.

@iamsmkr
Copy link
Author

iamsmkr commented Apr 21, 2024

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

@pubkey
Copy link
Owner

pubkey commented Apr 22, 2024

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

Copy link

stale bot commented Apr 29, 2024

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.

@stale stale bot added the stale label Apr 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants