Database transaction in drop handler

I have this situation where when an object representing a Client is dropped, I would like it to sync it's most recent state to a database.

One 'obvious' way to do this is via the Drop handler -- i.e. in the drop handler, we do some call that syncs the state.

However, this seems like a terrible idea in many ways.

What is a less terrible / idiomatic way to achieve this ?

Well, you can call a method that syncs it when you no longer need it.

I'd expect rollback on drop, and a consuming commit method, myself. It's a bit fiddly to skip the drop code in the consuming method, though.

3 Likes

Can you elaborate on why you feel Drop would be the wrong place to do this?

RAII is designed to let you clean up a resource when it goes out of scope, and I would consider synchronising your client with the database to fall under that umbrella.

Alternatively, if you just want to protect a section of code, you could create a specific guard object that holds onto the Client and calls the sync code on drop. Another option is to write a function which executes some closure and does the sync afterwards.

fn use_client<Ret>(
    client: &Client, 
    thunk: impl FnOnce(&Client) -> Ret,
) -> Ret {
    let ret = thunk(client);
    self.synchronise();

    ret
}
1 Like

Doing fallible operations, like I/O, in a Drop implementation can be awkward because the only way to report errors is via panic, which can easily lead to an abort instead of an unwind.

1 Like

Yeah, that's a good point. If so, the use_client()-based approach might be best.

You could also take the same attitude as std::fs::File... Flush to disk on drop and ignore any errors, but if people care about those errors they can make an explicit sync_all() call and handle the result.

1 Like