Skip to main content

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}