lmdb-zero icon indicating copy to clipboard operation
lmdb-zero copied to clipboard

How to track life cycle of Arc<Database>

Open katyo opened this issue 7 years ago • 2 comments

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?

katyo avatar Sep 18 '18 05:09 katyo

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.

AltSysrq avatar Sep 19 '18 02:09 AltSysrq

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.

katyo avatar Sep 19 '18 05:09 katyo