appcore_sync_sqlite/
work.rs1use crate::{SqliteSyncError, SqliteSyncResult, SqliteSyncStore};
13use appcore_sync::SyncMessage;
14use rusqlite::{Transaction, TransactionBehavior};
15
16pub struct SqliteSyncWork<'a> {
20 transaction: &'a Transaction<'a>,
21 config: &'a crate::SqliteSyncConfig,
22 operations: usize,
23}
24impl SqliteSyncStore {
25 pub fn transaction<T>(
29 &self,
30 action: impl FnOnce(&mut SqliteSyncWork<'_>) -> SqliteSyncResult<T>,
31 ) -> SqliteSyncResult<T> {
32 self.with_connection(|connection| {
33 let transaction = connection
34 .transaction_with_behavior(TransactionBehavior::Immediate)
35 .map_err(SqliteSyncError::database)?;
36 let result = action(&mut SqliteSyncWork {
37 transaction: &transaction,
38 config: self.config(),
39 operations: 0,
40 })?;
41 transaction.commit().map_err(SqliteSyncError::database)?;
42 Ok(result)
43 })
44 }
45}
46impl SqliteSyncWork<'_> {
47 pub fn append(&mut self, payload: &[u8], sequence: u64) -> SqliteSyncResult<usize> {
49 self.admit()?;
50 crate::log::append_in_transaction(self.transaction, payload, sequence)
51 }
52 pub fn enqueue(&mut self, message: &SyncMessage) -> SqliteSyncResult<bool> {
55 self.admit()?;
56 crate::outbox::validate_batch_id(&message.batch_id)
57 .map_err(|_| SqliteSyncError::CorruptRecord("outbox identity"))?;
58 let bytes =
59 crate::outbox_blob::encoded_message_bytes(message, self.config.max_outbox_record_bytes)
60 .map_err(|_| SqliteSyncError::CapacityExceeded("outbox record"))?;
61 let inserted = crate::outbox::enqueue_in_transaction(
62 self.transaction,
63 message,
64 bytes,
65 self.config.max_outbox_entries,
66 )?;
67 if !inserted {
68 let exists: bool = self
69 .transaction
70 .query_row(
71 "SELECT EXISTS(SELECT 1 FROM appcore_sync_outbox WHERE batch_id = ?1)",
72 [&message.batch_id],
73 |row| row.get(0),
74 )
75 .map_err(SqliteSyncError::database)?;
76 if !exists {
77 return Err(SqliteSyncError::CapacityExceeded("outbox"));
78 }
79 }
80 Ok(inserted)
81 }
82 pub fn checkpoint(
85 &mut self,
86 peer: &str,
87 sequence: u64,
88 hash: &str,
89 observed_head: u64,
90 ) -> SqliteSyncResult<()> {
91 self.admit()?;
92 appcore_sync::validate_checkpoint_head_v1(sequence, observed_head)
93 .map_err(|_| SqliteSyncError::CorruptRecord("future checkpoint"))?;
94 crate::checkpoint::validate_peer_id(peer)
95 .and_then(|()| crate::checkpoint::validate_hash(hash))
96 .map_err(|_| SqliteSyncError::CorruptRecord("checkpoint"))?;
97 let sequence =
98 i64::try_from(sequence).map_err(|_| SqliteSyncError::CapacityExceeded("checkpoint"))?;
99 crate::checkpoint::set_in_transaction(
100 self.transaction,
101 peer,
102 sequence,
103 hash,
104 self.config.max_checkpoints,
105 )
106 }
107 pub fn stage_object_write(
110 &mut self,
111 intent: &crate::ObjectWriteIntentV1,
112 ) -> SqliteSyncResult<bool> {
113 self.admit()?;
114 crate::materialization::stage(self.transaction, intent)
115 }
116 pub fn acknowledge(
119 &mut self,
120 receipt: &appcore_sync::SyncOutboxReceipt,
121 ) -> SqliteSyncResult<usize> {
122 self.admit()?;
123 crate::outbox::acknowledge_in_transaction(self.transaction, receipt)
124 }
125 fn admit(&mut self) -> SqliteSyncResult<()> {
126 if self.operations >= 1_024 {
127 return Err(SqliteSyncError::CapacityExceeded("transaction operation"));
128 }
129 self.operations += 1;
130 Ok(())
131 }
132}