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

Custom error-handler with "retry" possibilities #2098

Open
angelaki opened this issue Nov 21, 2024 · 9 comments
Open

Custom error-handler with "retry" possibilities #2098

angelaki opened this issue Nov 21, 2024 · 9 comments

Comments

@angelaki
Copy link

Since I found nothing in the docs I wanted to try my luck here: is it possible to write a custom errorHandler for Dexie, that can just retry the last action causing the error?

Otherwise I'd neet to put in manually in every Promise but hey, that'd be a solution, too.

Thank you for your great effort with this lib!!

@angelaki
Copy link
Author

Doing some debugging on the client device, I noticed that (non reproducable) actually a Dexie.DatabaseClosedError is called. But neither gets the connection manually closed anywhere in my code nor was it created with autoOpen: false (https://dexie.org/docs/DexieErrors/Dexie.DatabaseClosedError).

Any idea on this? And question remains: some neat workaround for this, just trying to reconnect and re-run the last command (globally)?

@dfahlander
Copy link
Collaborator

DatabaseClosedError can happen if the underlying database is being closed for any reason, for example if someone deletes the database in devtools, if QuotaExceededError is has been thrown in an earlier request or if an UnknownError is thrown for some reason.

There are plans to reintroduce db.on.error but letting it work differently than before: It would be emitted no matter if an error is explicitely cached or not. The purpose would be to be able to work around browser issues in Chrome and Safari. The callback will have options to make any ongoing transaction retried or the current DB connection reconnected.

Subscribe to our release notes in case it might show up in an upcoming release.

What you can do until this is implemented, is to create a middleware that intercept transaction, does addEventListener('error', ...) on the transaction.

@dfahlander
Copy link
Collaborator

import Dexie from 'dexie';

const db = new Dexie('dbname');

db.use({
  stack: "dbcore", // The only stack supported so far.
  name: "ErrorHandler", // Optional name of your middleware
  create (downlevelDatabase) {
    // Return your own implementation of DBCore:
    return {
      ...downlevelDatabase, 
      transaction(...args) {
        const tx = downlevelDatabase.transaction(...args);
        // @ts-ignore
        tx.addEventListener('error', ev => {
          // your error handler
          console.log("Transaction error:", ev.target.error);
        });
        return tx;
      }
    };
  }
});

@angelaki
Copy link
Author

Thank you so much! But if I'm not mistaken this middleware is just able to catch the error, not retry the execution causing it? Guess that can only be achieved by catching in every execution / usage?

And: the customer noticed that this happens when the app turn from light to dark mode. I first was pretty confused because I saw totally no connection but the app's theme was set to auto! His device (IOS Tables) switches from day to night (probably doing some weired stuff under the hood, too) what probably causes IndexedDb to close it's connection.

But: shouldn't the current behavior of Dexie accept this? Shouldn't it just reconnect on next usage?

Thank you so much!

@dfahlander
Copy link
Collaborator

dfahlander commented Nov 22, 2024

But: shouldn't the current behavior of Dexie accept this? Shouldn't it just reconnect on next usage?

Dexie auto-opens the database when it is aware of it being closed and the autoOpen flag is set. In these cases it might not be aware of the database being closed.

Another thing you could try is the db.on('close', callback) event to detect when it is being closed from outside and there basically make dexie aware of it so it will auto-open.

let preventLoop = false;
db.on('close', () => {
  if (preventLoop) {
    // Prevent eternal loop. The 'close' event will be called again when
    // calling db.close() in next version of dexie (4.1.0)
    return; 
  }
  preventLoop = true;
  try {
    db.close({disableAutoOpen: false});
  } finally {
    preventLoop = false;
  }
});

This should be built-in so let's keep this open.

@angelaki
Copy link
Author

I'll give it a try and let you know if it (presumably) fixed the issue.

@angelaki
Copy link
Author

@dfahlander I just gave this.on('close', () => this.close({ disableAutoOpen: false })); a shot in the constructor but it didn't do the job: some devices still get a DatabaseClosedError.

Is there any chance right now for a near solution? Or would I need to manually catch / reconnect / retry every single usage of Dexie manually to exclude this error?

@dfahlander
Copy link
Collaborator

Ok. DatabaseClosedError can also be thrown if there was another error happening while the database was being opened, for example if an upgrader fails. To log the underlying reason for DatabaseClosedError, look at error.inner, which can be another error.

Also, try logging all errors by using the middleware example I posted in an earlier comment and log what errors happen on the transaction.

This information would be valuable for me finding a workaround for these situations.

If the DatabaseClosedError happens on a transaction, you could do a db.close({ disableAutoOpen: false }); db.open(); in the middleware when that error happens, but if the reason was an error during open, it would not be caught by the middleware but could be caught by calling db.open().catch(err => {...}) in the same module that defines db.

@angelaki
Copy link
Author

Probably no case of upgrade since it happens in usual usage. But we already log all client error stack on the server (just don't have access to it right now). Let you know as soon as I got a response. Ty for your support!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants