Open stx opened 2 years ago
It's weird that this is, two times, happening with detectOperatingSystem
in the stack trace. I also don't understand how SqlJsDatabase.runWithArgs
would call that in JS, maybe it's a weird dart2js optimization...?
In handleDriftException
, are you printing the original stack trace?
@simolus3 handleDriftException
was just Sentry.captureException(e, stackTrace: stackTrace);
and this is what Sentry collected.
This is what we've expanded it to (no error reports collected with it yet):
void handleDriftException(Object e, StackTrace? stackTrace) {
if (e is DriftRemoteException) {
Sentry.captureException(e.remoteCause, stackTrace: e.remoteStackTrace);
} else if (e is DriftWrappedException) {
Sentry.captureException(e.cause, stackTrace: e.trace, hint: e.message);
} else if (e is CouldNotRollBackException) {
Sentry.captureException(e.cause, stackTrace: e.originalStackTrace, hint: e.exception);
} else {
Sentry.captureException(e, stackTrace: stackTrace);
}
}
With extra error handling added (from Android 11 w/isolate):
String: SqliteException(1): cannot rollback - no transaction is active
Causing statement: ROLLBACK TRANSACTION
File "database.dart", line 129, in DatabaseImpl.execute
File "database.dart", line 204, in _VmDelegate._runWithArgs
File "database.dart", line 214, in _VmDelegate.runCustom
File "engines.dart", line 109, in _BaseExecutor.runCustom.<fn>
File "engines.dart", line 51, in _BaseExecutor._synchronized.<fn>
File "future.dart", line 296, in new Future.sync
File "synchronized.dart", line 17, in Lock.synchronized.callBlockAndComplete
File "server_impl.dart", line 226, in ServerImplementation._waitForTurn.<fn>
File "<asynchronous suspension>"
File "engines.dart", line 248, in _TransactionExecutor.rollback
File "<asynchronous suspension>"
File "server_impl.dart", line 197, in ServerImplementation._transactionControl
File "<asynchronous suspension>"
File "communication.dart", line 156, in DriftCommunication.setRequestHandler.<fn>.<fn>
Thanks for the additional info! You said no isolates, but there's a ServerImplementation
in the stack trace. I assume you're using web workers then? Also how can there be a _VmDelegate
on the web? That one uses dart:ffi
, so this seems pretty weird.
Thanks for the additional info! You said no isolates, but there's a
ServerImplementation
in the stack trace. I assume you're using web workers then? Also how can there be a_VmDelegate
on the web? That one usesdart:ffi
, so this seems pretty weird.
I went back through Sentry and it looks like it merged the issues by mistake. That trace was from Android 11, with an isolate.
Are you using something like INSERT OR ROLLBACK
anywhere (or a trigger using RAISE(ROLLBACK)
)? Drift assumes that it has full control over transactions, so it can get tripped up by statements closing the transaction behind its back.
I haven't really considered constructs like OR ROLLBACK
in the implementation to be honest. I think throwing a CouldNotRollBackException
and ensuring that the database is in a valid state afterwards may be the best action here.
Judging from your stack trace however, it doesn't seem like you're getting a CouldNotRollBackException
, which probably means that you're running into this catch block in drift. So what I think is happening here is:
COMMIT;
, which for some reason throws an exception.ROLLBACK;
which then fails because there's no transaction active?So far I don't understand how step 3 could fail. To my knowledge (I poked around with a bit of testing), transactions stay active if a COMMIT
fails. If you have more insights on how this might happen I'd love to write a proper test against sqlite3 for this, so far I'm using mocks.
In 5d6933d9b7e73d24c35f6ce5272b62dc65865373, I changed the implementation to throw a more descriptive error that also explains why the rollback was attempted in the first place. That won't solve your problem, but hopefully allow us to figure out what's going on here.
Thanks for taking the time and addressing this (and everything else) so thoughtfully. It really shows and we appreciate it.
To answer your question, nope, no rollbacks used anywhere. It’s a pretty vanilla SQL implementation. We do use batch inserts and deletes. Maybe a result of that?
There are situations where batches might run simultaneously. Possibly a result of that?
It could be because of that, but it's also happening on the web where simultaneous edits to the same database connection aren't possible (even if you had the app open in multiple tabs they'd use independent connections as far as sqlite3 is concerned). Let's see if the updated exception reporting to sentry or the more detailed rollback handling in drift will provide more insights here.
I am currently facing a similar error and I might have an idea what causes this error. In my case there a multiple queries that use a transaction and they can be started simultaneously. I think somehow the engine gets confused when a transaction is started before another transaction has finished. When I synchronize all queries using a Mutex, the error seams to be fixed.
Seen on Firefox and Chrome on production. Latest everything. Two different stacktraces, one with runMigrations listed and one without. No isolate.
Database open:
Migration strategy: