reddb_server/runtime/
impl_serverless.rs1use super::*;
2
3impl RedDBRuntime {
4 pub fn serverless_file_plan(&self) -> Option<reddb_file::ServerlessFilePlan> {
5 let data_path = self.inner.db.options().data_path.as_ref()?;
6 let generation = self
7 .primary_logical_head_lsn()
8 .max(self.cdc_current_lsn())
9 .max(1);
10 Some(reddb_file::ServerlessFilePlan::for_data_path(
11 data_path, generation,
12 ))
13 }
14
15 fn serverless_file_plan_for_generation(
16 &self,
17 generation: u64,
18 ) -> Option<reddb_file::ServerlessFilePlan> {
19 let plan = self.serverless_file_plan()?;
20 Some(plan.for_generation(generation))
21 }
22
23 fn serverless_local_cache_for_generation(
24 &self,
25 generation: u64,
26 ) -> Option<reddb_file::ServerlessLocalCache> {
27 let plan = self.serverless_file_plan_for_generation(generation)?;
28 Some(plan.local_cache())
29 }
30
31 fn serverless_collection_snapshot_bytes(&self, collection: &str) -> RedDBResult<Vec<u8>> {
32 let store = self.inner.db.store();
33 let _publication = store.vault_publication_lock.lock();
34 let pending_vault_versions = store.pending_vault_versions.read();
35 let source = self
36 .inner
37 .db
38 .store()
39 .get_collection(collection)
40 .ok_or_else(|| {
41 RedDBError::Internal(format!("serverless collection not found: {collection}"))
42 })?;
43 let snapshot = crate::storage::unified::UnifiedStore::with_config(
44 crate::storage::unified::UnifiedStoreConfig::default(),
45 );
46 let mut error: Option<RedDBError> = None;
47 source.for_each_entity(|entity| {
48 if pending_vault_versions.contains(&entity.id) {
49 return true;
50 }
51 let cloned = entity.clone();
52 match snapshot.insert_auto(collection, cloned) {
53 Ok(id) => {
54 if let Some(metadata) = source.get_metadata(entity.id) {
55 if let Err(err) = snapshot.set_metadata(collection, id, metadata) {
56 error = Some(RedDBError::Internal(err.to_string()));
57 return false;
58 }
59 }
60 true
61 }
62 Err(err) => {
63 error = Some(RedDBError::Internal(err.to_string()));
64 false
65 }
66 }
67 });
68 if let Some(error) = error {
69 return Err(error);
70 }
71 Ok(snapshot.to_binary_dump_bytes())
72 }
73
74 pub fn publish_serverless_generation(
75 &self,
76 ) -> RedDBResult<Option<reddb_file::ServerlessGenerationPointer>> {
77 let Some(base_plan) = self.serverless_file_plan() else {
78 return Ok(None);
79 };
80 let store = self.inner.db.store();
81 let _publication = store.vault_publication_lock.lock();
82 self.flush()?;
83 let next_generation = match base_plan.read_current_pointer_verified() {
84 Ok(pointer) => base_plan
85 .generation
86 .max(pointer.generation.saturating_add(1)),
87 Err(reddb_file::RdbFileError::Io(err))
88 if err.kind() == std::io::ErrorKind::NotFound =>
89 {
90 base_plan.generation
91 }
92 Err(err) => {
93 return Err(RedDBError::InvalidOperation(format!(
94 "corrupt serverless generation: {err}"
95 )));
96 }
97 };
98 let plan = reddb_file::ServerlessFilePlan::new(
99 base_plan.root,
100 base_plan.namespace,
101 next_generation,
102 )
103 .with_cache_policy(base_plan.cache_policy);
104 let mut extent_index = reddb_file::ServerlessExtentIndex::new(plan.generation);
105 let mut collections = self.inner.db.store().list_collections();
106 collections.sort();
107 collections.dedup();
108 if collections.is_empty() {
109 collections.push("__database__".to_string());
110 }
111 let mut collection_data = Vec::new();
112 for collection in collections {
113 let payload = if collection == "__database__" {
114 self.inner.db.store().to_binary_dump_bytes()
115 } else {
116 self.serverless_collection_snapshot_bytes(&collection)?
117 };
118 let offset = collection_data.len() as u64;
119 collection_data.extend_from_slice(&payload);
120 extent_index.push(
121 plan.collection_data_extent_ref(collection, offset, &payload, true)
122 .map_err(|err| RedDBError::Internal(err.to_string()))?,
123 );
124 }
125 let secondary_index =
126 reddb_file::ServerlessSecondaryIndex::from_extent_index(&extent_index);
127 let pointer = plan
128 .publish_core_generation(&extent_index, &collection_data, &secondary_index.encode())
129 .map_err(|err| RedDBError::Internal(err.to_string()))?;
130 Ok(Some(pointer))
131 }
132
133 pub fn read_current_serverless_generation_verified(
134 &self,
135 ) -> RedDBResult<Option<reddb_file::ServerlessGenerationPointer>> {
136 let Some(plan) = self.serverless_file_plan() else {
137 return Ok(None);
138 };
139 match plan.read_current_pointer_verified() {
140 Ok(pointer) => Ok(Some(pointer)),
141 Err(reddb_file::RdbFileError::Io(err))
142 if err.kind() == std::io::ErrorKind::NotFound =>
143 {
144 Ok(None)
145 }
146 Err(err) => Err(RedDBError::InvalidOperation(format!(
147 "corrupt serverless generation: {err}"
148 ))),
149 }
150 }
151
152 pub fn hydrate_current_serverless_collection(
153 &self,
154 collection: &str,
155 ) -> RedDBResult<Option<Vec<reddb_file::ServerlessHydratedRange>>> {
156 let Some(pointer) = self.read_current_serverless_generation_verified()? else {
157 return Ok(None);
158 };
159 let Some(plan) = self.serverless_file_plan_for_generation(pointer.generation) else {
160 return Ok(None);
161 };
162 let secondary =
163 reddb_file::ServerlessSecondaryIndex::read_from_path(plan.secondary_index_path())
164 .map_err(|err| {
165 RedDBError::InvalidOperation(format!("corrupt serverless generation: {err}"))
166 })?;
167 let hydration = secondary.hydration_plan_for_collection(collection);
168 plan.hydrate_local_plan(&hydration)
169 .map(Some)
170 .map_err(|err| {
171 RedDBError::InvalidOperation(format!("serverless hydrate failed: {err}"))
172 })
173 }
174
175 pub fn hydrate_current_serverless_key(
176 &self,
177 collection: &str,
178 key: &[u8],
179 ) -> RedDBResult<Option<Vec<reddb_file::ServerlessHydratedRange>>> {
180 let Some(pointer) = self.read_current_serverless_generation_verified()? else {
181 return Ok(None);
182 };
183 let Some(plan) = self.serverless_file_plan_for_generation(pointer.generation) else {
184 return Ok(None);
185 };
186 let index = reddb_file::ServerlessExtentIndex::read_from_path(plan.extent_index_path())
187 .map_err(|err| {
188 RedDBError::InvalidOperation(format!("corrupt serverless generation: {err}"))
189 })?;
190 let hydration = index.hydration_plan_for_key(collection, key);
191 plan.hydrate_local_plan(&hydration)
192 .map(Some)
193 .map_err(|err| {
194 RedDBError::InvalidOperation(format!("serverless hydrate failed: {err}"))
195 })
196 }
197
198 pub fn hydrate_current_serverless_range(
199 &self,
200 collection: &str,
201 range_start: &[u8],
202 range_end: &[u8],
203 ) -> RedDBResult<Option<Vec<reddb_file::ServerlessHydratedRange>>> {
204 let Some(pointer) = self.read_current_serverless_generation_verified()? else {
205 return Ok(None);
206 };
207 let Some(plan) = self.serverless_file_plan_for_generation(pointer.generation) else {
208 return Ok(None);
209 };
210 let index = reddb_file::ServerlessExtentIndex::read_from_path(plan.extent_index_path())
211 .map_err(|err| {
212 RedDBError::InvalidOperation(format!("corrupt serverless generation: {err}"))
213 })?;
214 let hydration = index
215 .hydration_plan_for_range(collection, range_start, range_end)
216 .map_err(|err| RedDBError::InvalidOperation(err.to_string()))?;
217 plan.hydrate_local_plan(&hydration)
218 .map(Some)
219 .map_err(|err| {
220 RedDBError::InvalidOperation(format!("serverless hydrate failed: {err}"))
221 })
222 }
223
224 pub fn hydrate_current_serverless_key_cached(
225 &self,
226 collection: &str,
227 key: &[u8],
228 ) -> RedDBResult<Option<Vec<reddb_file::ServerlessHydratedRange>>> {
229 let Some(pointer) = self.read_current_serverless_generation_verified()? else {
230 return Ok(None);
231 };
232 let Some(plan) = self.serverless_file_plan_for_generation(pointer.generation) else {
233 return Ok(None);
234 };
235 let Some(cache) = self.serverless_local_cache_for_generation(pointer.generation) else {
236 return Ok(None);
237 };
238 let index = reddb_file::ServerlessExtentIndex::read_from_path(plan.extent_index_path())
239 .map_err(|err| {
240 RedDBError::InvalidOperation(format!("corrupt serverless generation: {err}"))
241 })?;
242 let hydration = index.hydration_plan_for_key(collection, key);
243 plan.hydrate_local_plan_cached(&hydration, &cache)
244 .map(Some)
245 .map_err(|err| {
246 RedDBError::InvalidOperation(format!("serverless hydrate failed: {err}"))
247 })
248 }
249
250 pub fn prefetch_current_serverless_hot_extents_cached(
251 &self,
252 ) -> RedDBResult<Option<Vec<reddb_file::ServerlessHydratedRange>>> {
253 let Some(pointer) = self.read_current_serverless_generation_verified()? else {
254 return Ok(None);
255 };
256 let Some(plan) = self.serverless_file_plan_for_generation(pointer.generation) else {
257 return Ok(None);
258 };
259 let Some(cache) = self.serverless_local_cache_for_generation(pointer.generation) else {
260 return Ok(None);
261 };
262 let index = reddb_file::ServerlessExtentIndex::read_from_path(plan.extent_index_path())
263 .map_err(|err| {
264 RedDBError::InvalidOperation(format!("corrupt serverless generation: {err}"))
265 })?;
266 plan.prefetch_hot_extents_cached(&index, &cache)
267 .map(Some)
268 .map_err(|err| {
269 RedDBError::InvalidOperation(format!("serverless hydrate failed: {err}"))
270 })
271 }
272}