p2p_chat/sync/engine/mailbox/
ack.rs

1//! This module contains logic for acknowledging messages in mailboxes.
2use anyhow::{anyhow, Result};
3use tracing::{debug, info, trace, warn};
4use uuid::Uuid;
5
6use crate::crypto::StorageEncryption;
7use crate::sync::retry::RetryPolicy;
8
9use super::super::SyncEngine;
10
11impl SyncEngine {
12    /// Acknowledges a list of messages across all known mailbox providers.
13    ///
14    /// This function attempts to send an acknowledgment for each message ID to
15    /// all discovered mailbox providers, ensuring that the messages are deleted
16    /// from the mailboxes. It utilizes a retry policy for robustness.
17    ///
18    /// # Arguments
19    ///
20    /// * `msg_ids` - A `Vec` of `Uuid`s representing the messages to acknowledge.
21    ///
22    /// # Errors
23    ///
24    /// This function will return an error if network communication fails, but
25    /// it attempts to acknowledge with multiple mailboxes for resilience.
26    pub async fn acknowledge_mailbox_messages(&self, msg_ids: Vec<Uuid>) -> Result<()> {
27        if msg_ids.is_empty() {
28            return Ok(());
29        }
30
31        let Some(network) = &self.network else {
32            debug!("No network handle available for mailbox ACK");
33            return Ok(());
34        };
35
36        let recipient_hash =
37            StorageEncryption::derive_recipient_hash(&self.identity.hpke_public_key());
38
39        info!(
40            "Acknowledging {} messages to {} mailboxes",
41            msg_ids.len(),
42            self.get_mailbox_providers().len()
43        );
44
45        let mut total_deleted = 0;
46        let mut successful_acks = 0;
47        let mut failed_acks = 0;
48
49        let retry_policy = RetryPolicy::fast_mailbox();
50
51        for peer_id in self.get_mailbox_providers().iter() {
52            let ack_result = retry_policy
53                .retry_with_jitter(|| async {
54                    network
55                        .mailbox_ack(*peer_id, recipient_hash, msg_ids.clone())
56                        .await
57                        .map_err(|e| anyhow!("ACK failed: {}", e))
58                })
59                .await;
60
61            match ack_result {
62                Ok(deleted_count) => {
63                    successful_acks += 1;
64                    total_deleted += deleted_count;
65                    if deleted_count > 0 {
66                        info!(
67                            "Mailbox {} confirmed deletion of {} messages",
68                            peer_id, deleted_count
69                        );
70                    } else {
71                        trace!("Mailbox {} had no messages to delete", peer_id);
72                    }
73                }
74                Err(e) => {
75                    failed_acks += 1;
76                    warn!(
77                        "Failed to ACK messages to mailbox {} after retries: {}",
78                        peer_id, e
79                    );
80                }
81            }
82        }
83
84        info!(
85            "ACK summary: {} messages deleted across {} mailboxes, {}/{} ACKs successful",
86            total_deleted,
87            successful_acks,
88            successful_acks,
89            successful_acks + failed_acks
90        );
91
92        if failed_acks > 0 {
93            warn!(
94                "Failed to ACK to {} mailboxes - messages may remain stored",
95                failed_acks
96            );
97        }
98
99        Ok(())
100    }
101}