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

Cannot read property '_rev' of undefined error #939

Closed
rmisio opened this issue Mar 29, 2019 · 13 comments
Closed

Cannot read property '_rev' of undefined error #939

rmisio opened this issue Mar 29, 2019 · 13 comments

Comments

@rmisio
Copy link

rmisio commented Mar 29, 2019

Case

Maybe a bug...?

Issue

Info

  • Environment: browser
  • Adapter: IndexedDB
  • Stack: React

I'm getting the following exception when re-connecting to a database:

Uncaught (in promise) TypeError: Cannot read property '_rev' of undefined
    at checkSchema (schema-check.js:150)
    at hooks.js:70
    at Array.forEach (<anonymous>)
    at runPluginHooks (hooks.js:69)
    at Object.create (rx-schema.js:362)
    at RxDatabase._callee$ (rx-database.js:201)
    at tryCatch (runtime.js:63)
    at Generator.invoke [as _invoke] (runtime.js:282)
    at Generator.prototype.(anonymous function) [as next] (http://localhost:3000/static/js/43.chunk.js:318376:21)
    at asyncGeneratorStep (asyncToGenerator.js:3)
    at _next (asyncToGenerator.js:25)
    at asyncToGenerator.js:32
    at new Promise (<anonymous>)
    at RxDatabase.<anonymous> (asyncToGenerator.js:21)
    at RxDatabase.collection (rx-database.js:313)
    at database.js:37
    at Array.map (<anonymous>)
    at _callee$ (database.js:24)
    at tryCatch (runtime.js:62)
    at Generator.invoke [as _invoke] (runtime.js:288)
    at Generator.prototype.(anonymous function) [as next] (http://localhost:3000/static/js/43.chunk.js:1325:21)
    at asyncGeneratorStep (asyncToGenerator.js:3)
    at _next (asyncToGenerator.js:25)
checkSchema @ schema-check.js:150
(anonymous) @ hooks.js:70
runPluginHooks @ hooks.js:69
create @ rx-schema.js:362
_callee$ @ rx-database.js:201
tryCatch @ runtime.js:63
invoke @ runtime.js:282
prototype.(anonymous function) @ runtime.js:116
asyncGeneratorStep @ asyncToGenerator.js:3
_next @ asyncToGenerator.js:25
(anonymous) @ asyncToGenerator.js:32
(anonymous) @ asyncToGenerator.js:21
collection @ rx-database.js:313
(anonymous) @ database.js:37
_callee$ @ database.js:24
tryCatch @ runtime.js:62
invoke @ runtime.js:288
prototype.(anonymous function) @ runtime.js:114
asyncGeneratorStep @ asyncToGenerator.js:3
_next @ asyncToGenerator.js:25
Promise.catch (async)
(anonymous) @ auth.js:73
(anonymous) @ auth.js:31
(anonymous) @ index.js:8
(anonymous) @ index.js:53
dispatch @ VM2500:1
(anonymous) @ redux.js:463
handleLoginClick @ Login.js:77
callCallback @ react-dom.development.js:147
invokeGuardedCallbackDev @ react-dom.development.js:196
invokeGuardedCallback @ react-dom.development.js:250
invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:265
executeDispatch @ react-dom.development.js:571
executeDispatchesInOrder @ react-dom.development.js:596
executeDispatchesAndRelease @ react-dom.development.js:695
executeDispatchesAndReleaseTopLevel @ react-dom.development.js:704
forEachAccumulated @ react-dom.development.js:676
runEventsInBatch @ react-dom.development.js:844
runExtractedEventsInBatch @ react-dom.development.js:852
handleTopLevel @ react-dom.development.js:5027
batchedUpdates$1 @ react-dom.development.js:20269
batchedUpdates @ react-dom.development.js:2246
dispatchEvent @ react-dom.development.js:5107
interactiveUpdates$1 @ react-dom.development.js:20331
interactiveUpdates @ react-dom.development.js:2267
dispatchInteractiveEvent @ react-dom.development.js:5083

So, I have a login flow which connects you to a database. The first time I run it, it works. If I refresh the page and try and login again, no issue. But, if I login, logout and then re-login again, the above exception is happening on this code block:

  // create collections
  await Promise.all(collections.map(data => db.collection(data)));

As part of the logout flow I am calling destroy on the db instance and I have logs which show it completing.

Any idea what the issue may be?

@rmisio
Copy link
Author

rmisio commented Mar 29, 2019

It's so odd to me that if I re-fresh the page and login no issue. Just if I try login (create db),logout (destroy db) and try logging in again, I get the error.

If there's any additional clean up I need to do other than db.destroy, please let me know.

And another interesting observation, the second login could be to the same or a different DB, and either way you get the same error.

@pubkey
Copy link
Owner

pubkey commented Mar 29, 2019

Can you put a sleep between the destroy and re-create?
I think destroy is async and you should await it.

@rmisio
Copy link
Author

rmisio commented Mar 29, 2019

I think destroy is async and you should await it.

Yeah, what I do is I logout via the UI and then wait until I get a console output confirming the destroy before attempting to log in again.

      db.destroy()
        .then(() => {
          console.log('db destroyed');
        });

@pubkey
Copy link
Owner

pubkey commented Mar 29, 2019

Can it be that the database is not created when you call destroy? Does the error also apear if you wait some seconds before logout?

@rmisio
Copy link
Author

rmisio commented Mar 29, 2019

Ok, here's another piece of the puzzle. If I rollback versions of the following three packages, it works!

    "pouchdb-adapter-idb": "6.4.3",
    "rxdb": "7.6.1",
    "rxjs": "6.2.1",

So what has changed since then that could be causing this?

@pubkey
Copy link
Owner

pubkey commented Mar 29, 2019

Well, much has changed in the last breaking version :)
I think we should debug in the current version and find the problem.

@rmisio
Copy link
Author

rmisio commented Mar 29, 2019

Can it be that the database is not created when you call destroy?

Well, I assume db.destroy() would fail if you called it on un uncreated database, wouldn't it?

Also, my "create" flow doesn't return until the async creation processes are complete, so there would be no logout state in my UI because it would still be considered to be in the process of a log-in.

const _create = async (name, password) => {
  const db = await RxDB.create({
    name,
    adapter: 'idb',
    password
  });

  if (process.env.NODE_ENV === 'development') {
    // write to window for debugging
    window.db = db;
  }

  // create collections
  await Promise.all(collections.map(data => db.collection(data)));

  return db;
}

@rmisio
Copy link
Author

rmisio commented Mar 29, 2019

FWIW, that code is mostly from the react example in the repo. I imagine if you tweaked it to try a db destroy and recreation, the same issue would happen.

@pubkey
Copy link
Owner

pubkey commented Mar 29, 2019

Then it shouldn't be that hard to create a test-case :)

@rmisio
Copy link
Author

rmisio commented Mar 29, 2019

Then it shouldn't be that hard to create a test-case :)

oh man, you guys really make bug reporters work!

I''ll see if I could tweak that react example to reproduce it.

@rmisio
Copy link
Author

rmisio commented Apr 1, 2019

@pubkey Ok, so I just made some minimal tweaks to the React example and was able to reproduce it:

https://github.com/rmisio/rxdb/tree/master/examples/react

@rmisio
Copy link
Author

rmisio commented Apr 1, 2019

Ok, I figured out the root cause. This is directly from the React example:

  // create collections
  await Promise.all(collections.map(data => db.collection(data)));

Which is driven by a hard-coded collections array:

const collections = [
    {
        name: 'heroes',
        schema: require('./Schema.js').default,
        methods: {
            hpPercent() {
                return this.hp / this.maxHP * 100;
            }
        },
        sync: true
    }
];

The issue is that db.collection(data) is mutating that array in such a way that it bombs on it the second-time around.

So, if you make sure to give it a fresh object, the issue doesn't happen:

  // create collections
  await Promise.all(collections.map(data => db.collection(
    JSON.parse(JSON.stringify(data))
  )));

Not sure it should be modifying the argument like that, particularly in a way that makes it not useable again if you re-connect to the db. I think that's why many linters flag if you modify a function argument.

But, if that's unavoidable, at least the example should be modified and/or something to let users know their data structure will be modified in such a way that you won'd be able to call collection() again with it.

@pubkey
Copy link
Owner

pubkey commented Apr 4, 2019

@rmisio thank you for the research. This helped a lot.
I could reproduce and fix this problem in the attached commit
It will be shipped with the next release. Until that you can clone the arguments-object as a workarround

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