distributed_lock_mongo/handle.rs
1use distributed_lock_core::{error::LockResult, traits::LockHandle};
2use mongodb::{Collection, bson::doc};
3use tokio::sync::watch;
4
5use crate::document::MongoLockDocument;
6
7pub struct MongoLockHandle {
8 pub(crate) collection: Collection<MongoLockDocument>,
9 pub(crate) name: String,
10 pub(crate) lock_id: String,
11 pub(crate) lost_token: watch::Receiver<bool>,
12}
13
14impl LockHandle for MongoLockHandle {
15 fn lost_token(&self) -> &watch::Receiver<bool> {
16 &self.lost_token
17 }
18
19 async fn release(self) -> LockResult<()> {
20 // Release: delete document if _id == name AND lockId == lock_id
21 // In some cases we might just want to unset fields, but deleting is cleaner if we assume 1 lock = 1 doc.
22 // C# implementation seems to use the same document for the named lock, so maybe we should just clear fields?
23 // Let's check the C# implementation again or stick to "delete if matches".
24 // Actually, if we delete it, the next acquire will upsert it.
25 // But if there are other fields we want to preserve (which there aren't in MongoLockDocument), we might want to update.
26 // Given MongoLockDocument only contains lock info, deleting is probably fine and safest.
27
28 let filter = doc! {
29 "_id": &self.name,
30 "lockId": &self.lock_id
31 };
32
33 // We can just delete it.
34 // If we want to be "strictly" compatible with C# logic which might leave the document but "expired",
35 // we could just let it expire. But explicit release usually means "make it available now".
36 // Let's delete it.
37
38 self.collection
39 .delete_one(filter)
40 .await
41 .map_err(|e| distributed_lock_core::error::LockError::Connection(Box::new(e)))?;
42
43 Ok(())
44 }
45}