lmdb-zero
lmdb-zero copied to clipboard
How to track life cycle of Arc<Database>
I need to track final dropping of Database object in Arc. In particular I need delete databases (i.e. by calling Database::delete()) which was removed by the application when all references to it is dead.
My idea is a newtype wrapper for Database. I created the struct WrappedDatabase and implemented Deref trait for this newtype, but it still doesn't allows me to create cursors using Arc<WrappedDatabase> because it requires something which implements Into<Supercow<'a, Database>>.
Are anybody known a right solution?
Reading between the lines, does WrappedDatabase call Database::delete() in its Drop implementation? The rest of this comment assumes that's the case, but if not, could you share the definition of struct WrappedDatabase and an example of where Database::delete() is called?
I'd initially suggest looking for any way to avoid doing that, but I assume you've already looked at other options.
What you can do here depends on whether you want a Cursor alone to be able to prevent the database deletion. That is, do you need to handle the case where no objects other than Cursor(s) hold a reference to the Database in question?
If "no", I would suggest having an Arc<Database> inside your WrappedDatabase, and cloning that inner Arc when constructing the cursor. Then, in the Drop implementation, you can try_unwrap it to gain ownership and delete it, as long as all cursors have been dropped meanwhile.
If "yes", you're in a stickier situation, but I think you can make it work. Supercow is flexible enough to be able to work with custom smart pointer types; you need to implement ConstDeref in addition to Deref. Then, where you're currently need Into<Supercow<'a, Database>>, use Supercow::shared to do the conversion in your code.
However, consider the fact that deleting a database requires starting and committing a write transaction. If you already have a write transaction open (retained by the Cursor or otherwise), the call to Database::delete() will deadlock. This again goes back to my first point, though you could work around it by arranging for a separate worker thread to call Database::delete() so that blocking to start the new write transaction wouldn't interfere with closing the current one.
Great thanks for detailed explanation.
Yes, my WrappedDatabase implements Drop trait to call Database::delete() when database was marked as dead.
And "yes" I'm in a stickier situation with cursors, because I need delete database when no cursors which use it.
Currently I delete databases when storage is dropped.
I tried to play with supercow, but unsuccesfully yet now. It seems I need to wrap Arc to get it work. I think this is more proper way.