rust-indexed-db
rust-indexed-db copied to clipboard
Provide access to active transaction in callback provided to `OpenDbRequestBuilder::with_on_upgrade_needed{_fut}`
This pull request contains a number of breaking changes! @Alorel, I am planning to host a fork with these changes, but I wanted to provide them here as a pull request, in case any of them seem appealing to you.
Background
Recently, I created an issue in this repository outlining a change that prevented certain kinds of database upgrades (for details, see #66).
To summarize, the old upgrade callback was able to access the active version change transaction and, for instance, add an index to an existing object store. In more recent versions of this library, this is no longer possible as the values provided to the callback cannot be used to get access to the active transaction.
Changes
In order to support the behavior described above, I have made a number of changes - many of which are breaking changes.
The overarching change is that the active version change transaction is explicitly provided to the callback and the database is not provided to the callback. This seemed like a reasonable change, as Transaction::db provides a reference to the underlying Database, so existing codebases could relatively easily make their callbacks compatible - with minor exceptions detailed below.
Details on the changes are below.
Synchronous Upgrade Needed Callback
The signature of the callback provided to OpenDbRequestBuilder::with_on_upgrade_needed has changed to accept a reference to the active version change Transaction rather than the Database.
// original callback constraint
F: FnOnce(VersionChangeEvent, Database) -> crate::Result<()>
// modified callback constraint
F: FnOnce(VersionChangeEvent, &Transaction<'_>) -> crate::Result<()>
Note that the function that invokes the callback - OpenDbListener::listener - constructs the Transaction from the VersionChangeEvent with a newly added function, Transaction::from_raw_version_change_event.
Configuring Transaction behavior on Drop
Given that the callback above is synchronous, it is unable to commit the Transaction as Transaction::commit is asynchronous. Furthermore, the function which invokes the callback - OpenDbListener::listener - is also synchronous and has the same shortcoming. The consequence is that when the Transaction is Dropped at the end of OpenDbListener::listener, it is automatically aborted, and any changes made to the Database in the callback are lost.
In order to mitigate this issue, I have updated Transaction so that the Drop behavior is configurable. That is, one can set a field in the Transaction so that it is either committed or aborted when it is Dropped. By default, it is aborted, making this change backward compatible.
Asynchronous Upgrade Needed Callback
The signature of the callback provided to OpenDbRequestBuilder::with_on_upgrade_needed_fut has also changed to accept a reference to the active version change Transaction rather than the Database. And additionally, it uses the recently stabilized AsynFnOnce trait.
// original callback constraint
F: FnOnce(VersionChangeEvent, Database) -> Fut,
Fut: Future<Output = crate::Result<()>>
// modified callback constraint
F: AsyncFnOnce(VersionChangeEvent, &Transaction<'_>) -> crate::Result<()>
Note that using AsyncFnOnce requires updating the minimum supported Rust version to 1.85.0. I'm not entirely sure that using AsyncFnOnce is absolutely necessary, but I believe that without it, I was bumping up against the lifetime-related limitations detailed in rust-lang/rust#70263.
Caveat
The original callbacks provided to OpenDbRequestBuilder::with_on_upgrade_needed and OpenDbRequestBuilder::with_on_upgrade_needed_fut had access to an owned Database, while the updated callbacks only have access to a reference. This precludes calling the functions below, which consume the Database.
Database::delete- deletes theDatabaseDatabase::close- closes theDatabase
However, both of these functions seemed to me like operations that perhaps shouldn't occur in a version upgrade. And in the case of deleting a database, this seems to be in alignment with the official IndexedDB documentation, where deleting a database is an operation exposed through the IDBFactory and not the IDBDatabase.
UPDATE (2025/09/07): In order to get everything to pass the C.I. tests, I had to update the toolchain used to generate documentation, update the examples, and make some minimal changes to the code in order to accommodate new lint rules. Also, I force pushed some new commit messages so that they conform to the semantic pull request guidelines outlined in .github/semantic.yml.
Closes #66.