1use super::*;
2use crate::application::entity::metadata_to_json;
3use crate::replication::cdc::{server_json_to_wire_json, ChangeRecord};
4use crate::runtime::impl_core::current_connection_id;
5use std::time::{SystemTime, UNIX_EPOCH};
6
7impl RedDBRuntime {
8 fn latest_metadata_for(
9 &self,
10 collection: &str,
11 entity_id: u64,
12 ) -> Option<reddb_wire::replication::ChangeRecordJsonValue> {
13 self.inner
14 .db
15 .store()
16 .get_metadata(collection, EntityId::new(entity_id))
17 .map(|metadata| server_json_to_wire_json(metadata_to_json(&metadata)))
18 }
19
20 pub(crate) fn cdc_emit_no_cache_invalidate(
26 &self,
27 operation: crate::replication::cdc::ChangeOperation,
28 collection: &str,
29 entity_id: u64,
30 entity_kind: &str,
31 ) -> RedDBResult<u64> {
32 let lsn = self
33 .inner
34 .cdc
35 .emit(operation, collection, entity_id, entity_kind);
36
37 if self.inner.db.replication.is_some() {
39 let store = self.inner.db.store();
40 let entity = if operation == crate::replication::cdc::ChangeOperation::Delete {
41 None
42 } else {
43 store.get(collection, EntityId::new(entity_id))
44 };
45 let record = ChangeRecord {
46 term: self.current_replication_term(),
47 lsn,
48 timestamp: SystemTime::now()
49 .duration_since(UNIX_EPOCH)
50 .unwrap_or_default()
51 .as_millis() as u64,
52 operation,
53 collection: collection.to_string(),
54 entity_id,
55 entity_kind: entity_kind.to_string(),
56 entity_bytes: entity
57 .as_ref()
58 .map(|e| UnifiedStore::serialize_entity(e, store.format_version())),
59 metadata: self.latest_metadata_for(collection, entity_id),
60 refresh_records: None,
61 range_id: None,
62 ownership_epoch: None,
63 };
64 self.stage_or_publish_logical_record(record)?;
65 }
66 Ok(lsn)
67 }
68
69 pub(crate) fn cdc_emit_insert_batch_no_cache_invalidate(
70 &self,
71 collection: &str,
72 ids: &[EntityId],
73 entity_kind: &str,
74 ) -> RedDBResult<Vec<u64>> {
75 if ids.is_empty() {
76 return Ok(Vec::new());
77 }
78
79 if self.inner.db.replication.is_none() {
83 return Ok(self.inner.cdc.emit_batch_same_collection(
84 crate::replication::cdc::ChangeOperation::Insert,
85 collection,
86 entity_kind,
87 ids.iter().map(|id| id.raw()),
88 ));
89 }
90
91 ids.iter()
94 .map(|id| {
95 self.cdc_emit_no_cache_invalidate(
96 crate::replication::cdc::ChangeOperation::Insert,
97 collection,
98 id.raw(),
99 entity_kind,
100 )
101 })
102 .collect()
103 }
104
105 pub fn cdc_emit(
106 &self,
107 operation: crate::replication::cdc::ChangeOperation,
108 collection: &str,
109 entity_id: u64,
110 entity_kind: &str,
111 ) -> RedDBResult<u64> {
112 let lsn = self
113 .inner
114 .cdc
115 .emit(operation, collection, entity_id, entity_kind);
116 self.invalidate_result_cache_for_table(collection);
122
123 if self.inner.db.replication.is_some() {
125 let store = self.inner.db.store();
126 let entity = if operation == crate::replication::cdc::ChangeOperation::Delete {
127 None
128 } else {
129 store.get(collection, EntityId::new(entity_id))
130 };
131 let record = ChangeRecord {
132 term: self.current_replication_term(),
133 lsn,
134 timestamp: SystemTime::now()
135 .duration_since(UNIX_EPOCH)
136 .unwrap_or_default()
137 .as_millis() as u64,
138 operation,
139 collection: collection.to_string(),
140 entity_id,
141 entity_kind: entity_kind.to_string(),
142 entity_bytes: entity
143 .as_ref()
144 .map(|entity| UnifiedStore::serialize_entity(entity, store.format_version())),
145 metadata: self.latest_metadata_for(collection, entity_id),
146 refresh_records: None,
147 range_id: None,
148 ownership_epoch: None,
149 };
150 self.stage_or_publish_logical_record(record)?;
151 }
152 Ok(lsn)
153 }
154
155 pub(crate) fn cdc_emit_kv(
156 &self,
157 operation: crate::replication::cdc::ChangeOperation,
158 collection: &str,
159 key: &str,
160 entity_id: u64,
161 before: Option<crate::json::Value>,
162 after: Option<crate::json::Value>,
163 ) -> u64 {
164 let lsn = self
165 .inner
166 .cdc
167 .emit_kv(operation, collection, key, entity_id, before, after);
168 self.inner.kv_stats.incr_watch_events_emitted();
169 self.invalidate_result_cache_for_table(collection);
170 lsn
171 }
172
173 pub(crate) fn record_kv_watch_event(
174 &self,
175 operation: crate::replication::cdc::ChangeOperation,
176 collection: &str,
177 key: &str,
178 entity_id: u64,
179 before: Option<crate::json::Value>,
180 after: Option<crate::json::Value>,
181 ) {
182 if let Some(writer_xid) = self.current_xid() {
183 let conn_id = current_connection_id();
184 let event = crate::replication::cdc::KvWatchEvent {
185 collection: collection.to_string(),
186 key: key.to_string(),
187 op: operation,
188 before,
189 after,
190 lsn: 0,
191 committed_at: 0,
192 dropped_event_count: 0,
193 };
194 self.inner
195 .pending_kv_watch_events
196 .write()
197 .entry(conn_id)
198 .or_default()
199 .push((writer_xid, event));
200 return;
201 }
202
203 self.cdc_emit_kv(operation, collection, key, entity_id, before, after);
204 }
205
206 pub(crate) fn cdc_emit_prebuilt(
207 &self,
208 operation: crate::replication::cdc::ChangeOperation,
209 collection: &str,
210 entity: &UnifiedEntity,
211 entity_kind: &str,
212 metadata: Option<&crate::storage::Metadata>,
213 invalidate_cache: bool,
214 ) -> RedDBResult<u64> {
215 self.cdc_emit_prebuilt_with_columns(
216 operation,
217 collection,
218 entity,
219 entity_kind,
220 metadata,
221 invalidate_cache,
222 None,
223 )
224 }
225
226 pub(crate) fn cdc_emit_prebuilt_with_columns(
233 &self,
234 operation: crate::replication::cdc::ChangeOperation,
235 collection: &str,
236 entity: &UnifiedEntity,
237 entity_kind: &str,
238 metadata: Option<&crate::storage::Metadata>,
239 invalidate_cache: bool,
240 changed_columns: Option<Vec<String>>,
241 ) -> RedDBResult<u64> {
242 if invalidate_cache {
243 self.invalidate_result_cache();
244 }
245
246 let public_id = entity.logical_id().raw();
247 let lsn = self.inner.cdc.emit_with_columns(
248 operation,
249 collection,
250 public_id,
251 entity_kind,
252 changed_columns,
253 );
254
255 if self.inner.db.replication.is_some() {
256 let store = self.inner.db.store();
257 let record = ChangeRecord {
258 term: self.current_replication_term(),
259 lsn,
260 timestamp: SystemTime::now()
261 .duration_since(UNIX_EPOCH)
262 .unwrap_or_default()
263 .as_millis() as u64,
264 operation,
265 collection: collection.to_string(),
266 entity_id: entity.id.raw(),
267 entity_kind: entity_kind.to_string(),
268 entity_bytes: Some(UnifiedStore::serialize_entity(
269 entity,
270 store.format_version(),
271 )),
272 metadata: metadata
273 .map(metadata_to_json)
274 .map(server_json_to_wire_json)
275 .or_else(|| self.latest_metadata_for(collection, entity.id.raw())),
276 refresh_records: None,
277 range_id: None,
278 ownership_epoch: None,
279 };
280 self.stage_or_publish_logical_record(record)?;
281 }
282
283 Ok(lsn)
284 }
285
286 pub(crate) fn current_replication_term(&self) -> u64 {
287 self.inner.db.options().replication.term
288 }
289
290 pub(crate) fn cdc_emit_prebuilt_batch<'a, I>(
291 &self,
292 operation: crate::replication::cdc::ChangeOperation,
293 entity_kind: &str,
294 items: I,
295 invalidate_cache: bool,
296 ) -> RedDBResult<()>
297 where
298 I: IntoIterator<
299 Item = (
300 &'a str,
301 &'a UnifiedEntity,
302 Option<&'a crate::storage::Metadata>,
303 ),
304 >,
305 {
306 let items: Vec<(&str, &UnifiedEntity, Option<&crate::storage::Metadata>)> =
307 items.into_iter().collect();
308 if items.is_empty() {
309 return Ok(());
310 }
311
312 if invalidate_cache {
313 self.invalidate_result_cache();
314 }
315
316 for (collection, entity, metadata) in items {
317 self.cdc_emit_prebuilt(operation, collection, entity, entity_kind, metadata, false)?;
318 }
319 Ok(())
320 }
321
322 pub fn cdc_poll(
324 &self,
325 since_lsn: u64,
326 max_count: usize,
327 ) -> Vec<crate::replication::cdc::ChangeEvent> {
328 self.inner.cdc.poll(since_lsn, max_count)
329 }
330
331 pub fn cdc_current_lsn(&self) -> u64 {
334 self.inner.cdc.current_lsn()
335 }
336
337 pub fn replication_commit_lsn(&self) -> u64 {
340 if self.inner.db.replication.is_some() {
341 self.primary_logical_head_lsn()
342 } else {
343 self.cdc_current_lsn()
344 }
345 }
346
347 pub fn kv_watch_events_since(
348 &self,
349 collection: &str,
350 key: &str,
351 since_lsn: u64,
352 max_count: usize,
353 ) -> Vec<crate::replication::cdc::KvWatchEvent> {
354 self.kv_watch_batch_since(collection, key, since_lsn, max_count)
355 .0
356 }
357
358 pub(crate) fn kv_watch_batch_since(
359 &self,
360 collection: &str,
361 key: &str,
362 since_lsn: u64,
363 max_count: usize,
364 ) -> (Vec<crate::replication::cdc::KvWatchEvent>, u64) {
365 let events = self.inner.cdc.poll(since_lsn, max_count);
366 let scanned_through = events.last().map(|event| event.lsn).unwrap_or(since_lsn);
367 let visible = events
368 .into_iter()
369 .filter_map(|event| event.kv)
370 .filter(|event| event.collection == collection && event.key == key)
371 .filter(|event| crate::runtime::rls_injection::kv_watch_event_passes_rls(self, event))
372 .collect();
373 (visible, scanned_through)
374 }
375
376 pub fn kv_watch_events_since_prefix(
377 &self,
378 collection: &str,
379 prefix: &str,
380 since_lsn: u64,
381 max_count: usize,
382 ) -> Vec<crate::replication::cdc::KvWatchEvent> {
383 self.kv_watch_batch_since_prefix(collection, prefix, since_lsn, max_count)
384 .0
385 }
386
387 pub(crate) fn kv_watch_batch_since_prefix(
388 &self,
389 collection: &str,
390 prefix: &str,
391 since_lsn: u64,
392 max_count: usize,
393 ) -> (Vec<crate::replication::cdc::KvWatchEvent>, u64) {
394 let events = self.inner.cdc.poll(since_lsn, max_count);
395 let scanned_through = events.last().map(|event| event.lsn).unwrap_or(since_lsn);
396 let visible = events
397 .into_iter()
398 .filter_map(|event| event.kv)
399 .filter(|event| event.collection == collection && event.key.starts_with(prefix))
400 .filter(|event| crate::runtime::rls_injection::kv_watch_event_passes_rls(self, event))
401 .collect();
402 (visible, scanned_through)
403 }
404
405 pub(crate) fn kv_watch_subscribe<'a>(
406 &'a self,
407 collection: impl Into<String>,
408 key: impl Into<String>,
409 from_lsn: Option<u64>,
410 ) -> crate::runtime::kv_watch::KvWatchStream<'a> {
411 crate::runtime::kv_watch::KvWatchStream::subscribe(
412 &self.inner.cdc,
413 &self.inner.kv_stats,
414 collection,
415 key,
416 from_lsn,
417 self.kv_watch_idle_timeout_ms(),
418 )
419 }
420
421 pub(crate) fn kv_watch_subscribe_prefix<'a>(
422 &'a self,
423 collection: impl Into<String>,
424 prefix: impl Into<String>,
425 from_lsn: Option<u64>,
426 ) -> crate::runtime::kv_watch::KvWatchStream<'a> {
427 crate::runtime::kv_watch::KvWatchStream::subscribe_prefix(
428 &self.inner.cdc,
429 &self.inner.kv_stats,
430 collection,
431 prefix,
432 from_lsn,
433 self.kv_watch_idle_timeout_ms(),
434 )
435 }
436
437 pub(crate) fn kv_watch_idle_timeout_ms(&self) -> u64 {
438 self.config_u64("kv.watch.idle_timeout_ms", 60_000)
439 }
440}