zeppelin_embed/ingest/
delete_matching.rs1use crate::lifecycle::{QueryError, Store, StoreError, StoreState};
5use crate::meta::Predicate;
6use crate::planner::PlanError;
7
8use super::{DocId, IngestError, PurgeError};
9
10#[derive(Clone, Debug, Eq, PartialEq)]
12pub struct DeleteMatchingReport {
13 deleted_ids: Vec<DocId>,
14 generation: u64,
15}
16
17impl DeleteMatchingReport {
18 #[must_use]
20 pub fn deleted_ids(&self) -> &[DocId] {
21 &self.deleted_ids
22 }
23
24 #[must_use]
27 pub const fn generation(&self) -> u64 {
28 self.generation
29 }
30}
31
32#[derive(Debug)]
34pub enum DeleteMatchingError {
35 Predicate(PlanError),
37 Query(QueryError),
39 Delete(IngestError),
41 Purge(PurgeError),
44}
45
46impl std::fmt::Display for DeleteMatchingError {
47 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 match self {
49 Self::Predicate(error) => write!(formatter, "delete filter: {error}"),
50 Self::Query(error) => write!(formatter, "delete match: {error}"),
51 Self::Delete(error) => write!(formatter, "delete: {error}"),
52 Self::Purge(error) => write!(formatter, "delete purge: {error}"),
53 }
54 }
55}
56
57impl std::error::Error for DeleteMatchingError {
58 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
59 match self {
60 Self::Predicate(error) => Some(error),
61 Self::Query(error) => Some(error),
62 Self::Delete(error) => Some(error),
63 Self::Purge(error) => Some(error),
64 }
65 }
66}
67
68impl From<StoreError> for DeleteMatchingError {
69 fn from(error: StoreError) -> Self {
70 Self::Purge(PurgeError::Store(error))
71 }
72}
73
74impl Store {
75 pub fn delete_matching(
90 &self,
91 predicate: &Predicate,
92 ) -> Result<DeleteMatchingReport, DeleteMatchingError> {
93 crate::planner::validate_predicate(predicate, &self.schema)
94 .map_err(DeleteMatchingError::Predicate)?;
95 let _maintenance = self
96 .maintenance
97 .lock()
98 .map_err(|_| StoreError::Synchronization {
99 component: "delete matching",
100 })?;
101 let state = self
102 .state
103 .lock()
104 .map_err(|_| StoreError::Synchronization { component: "state" })?;
105 match *state {
106 StoreState::Open => {}
107 StoreState::Closing => return Err(StoreError::Closing.into()),
108 StoreState::Closed => return Err(StoreError::Closed.into()),
109 }
110 let writer_lock = self
111 .writer_lock
112 .lock()
113 .map_err(|_| StoreError::Synchronization {
114 component: "writer lock",
115 })?;
116 if writer_lock.is_none() {
117 return Err(StoreError::ReadOnly.into());
118 }
119 self.require_no_snapshot_views()?;
120 let mut wal = self
121 .wal_writer
122 .lock()
123 .map_err(|_| StoreError::Synchronization {
124 component: "WAL writer",
125 })?;
126 let writer = wal.as_mut().ok_or(StoreError::ReadOnly)?;
127 let (generation, active) = {
128 let active = self
129 .active
130 .lock()
131 .map_err(|_| StoreError::Synchronization {
132 component: "active segment",
133 })?;
134 let current = active.as_ref().ok_or(StoreError::Closed)?;
135 (current.generation, std::sync::Arc::clone(¤t.segment))
136 };
137 let snapshot = self
138 .snapshot
139 .read()
140 .map_err(|_| StoreError::Synchronization {
141 component: "published snapshot",
142 })?
143 .as_ref()
144 .cloned()
145 .ok_or(StoreError::Closed)?;
146 let ids = self
147 .live_document_ids_matching(&active, snapshot.segments(), predicate)
148 .map_err(DeleteMatchingError::Query)?;
149 drop(snapshot);
150 drop(active);
151 let report = if ids.is_empty() {
152 DeleteMatchingReport {
153 deleted_ids: ids,
154 generation,
155 }
156 } else {
157 self.delete_and_purge_locked(writer, ids)?
158 };
159 drop(wal);
160 drop(writer_lock);
161 drop(state);
162 Ok(report)
163 }
164
165 fn delete_and_purge_locked(
169 &self,
170 writer: &mut super::StoreWal,
171 ids: Vec<DocId>,
172 ) -> Result<DeleteMatchingReport, DeleteMatchingError> {
173 let vfs = self.vfs.as_ref();
174 let available = self
175 .available_purge_bytes()
176 .map_err(DeleteMatchingError::Purge)?;
177 let token = self
178 .schedule_purge_locked(&ids, available, vfs)
179 .map_err(DeleteMatchingError::Purge)?;
180 if let Err(error) = self.delete_with_writer(writer, &ids, &[]) {
181 self.abandon_scheduled_purge_locked(vfs)
184 .map_err(DeleteMatchingError::Purge)?;
185 return Err(DeleteMatchingError::Delete(error));
186 }
187 let purged = self
188 .complete_physical_purge_locked(vfs, writer, token)
189 .map_err(DeleteMatchingError::Purge)?;
190 Ok(DeleteMatchingReport {
191 deleted_ids: ids,
192 generation: purged.generation(),
193 })
194 }
195}