reddb_server/runtime/
impl_dml_chain.rs1use super::*;
10use crate::storage::query::unified::UnifiedResult;
11
12impl RedDBRuntime {
13 pub fn chain_tip_for_collection(
19 &self,
20 collection: &str,
21 ) -> Option<crate::runtime::blockchain_kind::ChainTipFull> {
22 self.try_chain_tip_for_collection(collection).ok().flatten()
23 }
24
25 pub fn try_chain_tip_for_collection(
26 &self,
27 collection: &str,
28 ) -> RedDBResult<Option<crate::runtime::blockchain_kind::ChainTipFull>> {
29 let store = self.inner.db.store();
30 if !super::blockchain_kind::try_is_chain(&store, collection)? {
31 return Ok(None);
32 }
33 let mut cache = self.inner.chain_tip_cache.lock();
34 if let Some(existing) = cache.get(collection) {
35 return Ok(Some(existing.clone()));
36 }
37 let Some(scanned) = super::blockchain_kind::try_chain_tip_full(&store, collection)? else {
38 return Ok(None);
39 };
40 cache.insert(collection.to_string(), scanned.clone());
41 Ok(Some(scanned))
42 }
43
44 pub fn verify_chain_for_collection(
46 &self,
47 collection: &str,
48 ) -> Option<crate::runtime::blockchain_kind::VerifyChainOutcome> {
49 self.try_verify_chain_for_collection(collection)
50 .ok()
51 .flatten()
52 }
53
54 pub fn try_verify_chain_for_collection(
55 &self,
56 collection: &str,
57 ) -> RedDBResult<Option<crate::runtime::blockchain_kind::VerifyChainOutcome>> {
58 let store = self.inner.db.store();
59 let Some(outcome) = super::blockchain_kind::try_verify_chain_outcome(&store, collection)?
60 else {
61 return Ok(None);
62 };
63 if !outcome.ok {
64 self.inner
66 .chain_integrity_broken
67 .lock()
68 .insert(collection.to_string(), true);
69 super::blockchain_kind::try_persist_integrity_flag(&store, collection, true)?;
70 }
71 Ok(Some(outcome))
72 }
73
74 pub fn clear_chain_integrity_flag(&self, collection: &str) -> bool {
75 self.try_clear_chain_integrity_flag(collection)
76 .unwrap_or(false)
77 }
78
79 pub fn try_clear_chain_integrity_flag(&self, collection: &str) -> RedDBResult<bool> {
80 let store = self.inner.db.store();
81 if !super::blockchain_kind::try_is_chain(&store, collection)? {
82 return Ok(false);
83 }
84 super::blockchain_kind::try_persist_integrity_flag(&store, collection, false)?;
85 self.inner
86 .chain_integrity_broken
87 .lock()
88 .insert(collection.to_string(), false);
89 Ok(true)
90 }
91
92 pub(super) fn is_chain_integrity_broken(&self, collection: &str) -> RedDBResult<bool> {
93 if let Some(broken) = self.inner.chain_integrity_broken.lock().get(collection) {
94 return Ok(*broken);
95 }
96 let persisted = super::blockchain_kind::try_is_integrity_broken_persisted(
97 &self.inner.db.store(),
98 collection,
99 )?
100 .unwrap_or(false);
101 self.inner
102 .chain_integrity_broken
103 .lock()
104 .insert(collection.to_string(), persisted);
105 Ok(persisted)
106 }
107
108 pub(crate) fn ensure_integrity_tombstones_loaded(&self) -> RedDBResult<bool> {
113 use std::sync::atomic::Ordering;
114 match self
115 .inner
116 .integrity_tombstones_state
117 .load(Ordering::Relaxed)
118 {
119 1 => return Ok(false),
120 2 => return Ok(true),
121 _ => {}
122 }
123 let mut guard = self.inner.integrity_tombstones.lock();
126 if self
127 .inner
128 .integrity_tombstones_state
129 .load(Ordering::Relaxed)
130 == 0
131 {
132 let ranges = crate::runtime::integrity_tombstone::load_ranges(&self.inner.db.store())?;
133 let present = !ranges.is_empty();
134 *guard = ranges;
135 self.inner
136 .integrity_tombstones_state
137 .store(if present { 2 } else { 1 }, Ordering::Relaxed);
138 }
139 Ok(self
140 .inner
141 .integrity_tombstones_state
142 .load(Ordering::Relaxed)
143 == 2)
144 }
145
146 pub fn record_integrity_tombstone(&self, table: &str, lo: u64, hi: u64) -> RedDBResult<()> {
152 self.record_integrity_tombstones(&[
153 crate::runtime::integrity_tombstone::TombstoneRange::new(table, lo, hi),
154 ])
155 }
156
157 pub(crate) fn record_integrity_tombstones(
158 &self,
159 ranges: &[crate::runtime::integrity_tombstone::TombstoneRange],
160 ) -> RedDBResult<()> {
161 use std::sync::atomic::Ordering;
162 if ranges.is_empty() {
163 return Ok(());
164 }
165 if ranges.iter().any(|range| range.lo > range.hi) {
166 return Err(RedDBError::Query(
167 "integrity tombstone range is reversed".into(),
168 ));
169 }
170 self.ensure_integrity_tombstones_loaded()?;
171 let mut guard = self.inner.integrity_tombstones.lock();
172 guard.extend_from_slice(ranges);
173 self.inner
176 .integrity_tombstones_state
177 .store(2, Ordering::Relaxed);
178 crate::runtime::integrity_tombstone::persist_ranges(&self.inner.db.store(), &guard)?;
179 Ok(())
180 }
181
182 pub fn integrity_tombstone_ranges(
186 &self,
187 ) -> RedDBResult<Vec<crate::runtime::integrity_tombstone::TombstoneRange>> {
188 self.ensure_integrity_tombstones_loaded()?;
189 Ok(self.inner.integrity_tombstones.lock().clone())
190 }
191
192 pub fn filter_integrity_tombstoned(&self, result: &mut UnifiedResult) -> RedDBResult<()> {
197 if !self.ensure_integrity_tombstones_loaded()? {
198 return Ok(());
199 }
200 let guard = self.inner.integrity_tombstones.lock();
201 if guard.is_empty() {
202 return Ok(());
203 }
204 let before = result.records.len();
205 result.records.retain(|record| {
206 !crate::runtime::integrity_tombstone::record_tombstoned(&guard, record)
207 });
208 if result.records.len() != before {
209 result.pre_serialized_json = None;
210 }
211 Ok(())
212 }
213}