1pub mod encoder;
18pub(crate) mod entry_distributor;
19pub(crate) mod entry_reader;
20pub(crate) mod raw_entry_reader;
21
22use std::collections::HashMap;
23use std::mem;
24use std::sync::Arc;
25
26use api::v1::WalEntry;
27use common_error::ext::BoxedError;
28use common_telemetry::debug;
29use encoder::WalEntryEncoder;
30use entry_reader::NoopEntryReader;
31use futures::future::BoxFuture;
32use futures::stream::BoxStream;
33use snafu::ResultExt;
34use store_api::logstore::entry::Entry;
35use store_api::logstore::provider::Provider;
36use store_api::logstore::{AppendBatchResponse, LogStore};
37use store_api::storage::RegionId;
38
39use crate::error::{
40 BuildEntrySnafu, CheckWalHistorySnafu, DeleteWalSnafu, Result, WaitWalDurableSnafu,
41 WriteWalSnafu,
42};
43use crate::wal::entry_reader::{LogStoreEntryReader, WalEntryReader};
44use crate::wal::raw_entry_reader::{LogStoreRawEntryReader, RegionRawEntryReader};
45
46pub type EntryId = store_api::logstore::entry::Id;
48pub type WalEntryStream<'a> = BoxStream<'a, Result<(EntryId, WalEntry)>>;
50
51#[derive(Clone)]
58pub(crate) struct DurabilityBarrier(
59 Arc<dyn Fn(EntryId) -> BoxFuture<'static, Result<()>> + Send + Sync>,
60);
61
62impl DurabilityBarrier {
63 pub(crate) async fn wait(&self, entry_id: EntryId) -> Result<()> {
65 (self.0)(entry_id).await
66 }
67
68 #[cfg(test)]
70 pub(crate) fn noop() -> Self {
71 Self(Arc::new(|_| Box::pin(async { Ok(()) })))
72 }
73}
74
75impl std::fmt::Debug for DurabilityBarrier {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 f.write_str("DurabilityBarrier")
78 }
79}
80
81#[derive(Debug)]
85pub struct Wal<S> {
86 store: Arc<S>,
88}
89
90impl<S> Wal<S> {
91 pub fn new(store: Arc<S>) -> Self {
93 Self { store }
94 }
95
96 pub fn store(&self) -> &Arc<S> {
97 &self.store
98 }
99}
100
101impl<S> Clone for Wal<S> {
102 fn clone(&self) -> Self {
103 Self {
104 store: Arc::clone(&self.store),
105 }
106 }
107}
108
109impl<S: LogStore> Wal<S> {
110 pub fn writer(&self) -> WalWriter<S> {
112 WalWriter {
113 store: self.store.clone(),
114 entries: Vec::new(),
115 providers: HashMap::new(),
116 encoder: WalEntryEncoder::new(),
117 }
118 }
119
120 pub(crate) fn on_region_opened(
122 &self,
123 ) -> impl FnOnce(RegionId, EntryId, &Provider) -> BoxFuture<Result<()>> {
124 let store = self.store.clone();
125 move |region_id, last_entry_id, provider| -> BoxFuture<'_, Result<()>> {
126 if let Provider::Noop = provider {
127 debug!("Skip obsolete for region: {}", region_id);
128 return Box::pin(async move { Ok(()) });
129 }
130 Box::pin(async move {
131 store
132 .obsolete(provider, region_id, last_entry_id)
133 .await
134 .map_err(BoxedError::new)
135 .context(DeleteWalSnafu { region_id })
136 })
137 }
138 }
139
140 pub(crate) fn durability_barrier(
142 &self,
143 region_id: RegionId,
144 provider: &Provider,
145 ) -> DurabilityBarrier {
146 let store = self.store.clone();
147 let provider = provider.clone();
148 DurabilityBarrier(Arc::new(move |entry_id| {
149 let store = store.clone();
150 let provider = provider.clone();
151 Box::pin(async move {
152 if let Provider::Noop = provider {
153 return Ok(());
154 }
155 store
156 .wait_durable(&provider, entry_id)
157 .await
158 .map_err(BoxedError::new)
159 .context(WaitWalDurableSnafu { region_id })
160 })
161 }))
162 }
163
164 pub(crate) async fn check_region_history(
168 &self,
169 region_id: RegionId,
170 provider: &Provider,
171 entry_id: EntryId,
172 ) -> Result<()> {
173 if let Provider::Noop = provider {
174 return Ok(());
175 }
176 self.store
177 .check_region_history(provider, entry_id)
178 .await
179 .map_err(BoxedError::new)
180 .context(CheckWalHistorySnafu { region_id })
181 }
182
183 pub(crate) fn start_region_history(
186 &self,
187 region_id: RegionId,
188 provider: &Provider,
189 ) -> Result<()> {
190 if let Provider::Noop = provider {
191 return Ok(());
192 }
193 self.store
194 .start_region_history(provider)
195 .map_err(BoxedError::new)
196 .context(CheckWalHistorySnafu { region_id })
197 }
198
199 pub(crate) fn wal_entry_reader(
201 &self,
202 provider: &Provider,
203 region_id: RegionId,
204 ) -> Box<dyn WalEntryReader> {
205 match provider {
206 Provider::RaftEngine(_) => Box::new(LogStoreEntryReader::new(
207 LogStoreRawEntryReader::new(self.store.clone()),
208 )),
209 Provider::Kafka(_) | Provider::ObjectStore(_) => {
211 Box::new(LogStoreEntryReader::new(RegionRawEntryReader::new(
212 LogStoreRawEntryReader::new(self.store.clone()),
213 region_id,
214 )))
215 }
216 Provider::Noop => Box::new(NoopEntryReader),
217 }
218 }
219
220 pub fn scan<'a>(
223 &'a self,
224 region_id: RegionId,
225 start_id: EntryId,
226 provider: &'a Provider,
227 ) -> Result<WalEntryStream<'a>> {
228 let mut reader = self.wal_entry_reader(provider, region_id);
229 reader.read(provider, start_id)
230 }
231
232 pub async fn obsolete(
234 &self,
235 region_id: RegionId,
236 last_id: EntryId,
237 provider: &Provider,
238 ) -> Result<()> {
239 if let Provider::Noop = provider {
240 return Ok(());
241 }
242 self.store
243 .obsolete(provider, region_id, last_id)
244 .await
245 .map_err(BoxedError::new)
246 .context(DeleteWalSnafu { region_id })
247 }
248
249 pub async fn delete_namespace(&self, region_id: RegionId, provider: &Provider) -> Result<()> {
251 if let Provider::Noop = provider {
252 return Ok(());
253 }
254 self.store
255 .delete_namespace(provider)
256 .await
257 .map_err(BoxedError::new)
258 .context(DeleteWalSnafu { region_id })
259 }
260
261 pub async fn release_region(&self, region_id: RegionId, provider: &Provider) -> Result<()> {
263 if let Provider::Noop = provider {
264 return Ok(());
265 }
266 self.store
267 .release_region(provider, region_id)
268 .await
269 .map_err(BoxedError::new)
270 .context(DeleteWalSnafu { region_id })
271 }
272
273 pub async fn obsolete_all(&self, region_id: RegionId, provider: &Provider) -> Result<()> {
276 self.store
277 .obsolete_all(provider, region_id)
278 .await
279 .map_err(BoxedError::new)
280 .context(DeleteWalSnafu { region_id })
281 }
282}
283
284pub struct WalWriter<S: LogStore> {
286 store: Arc<S>,
288 entries: Vec<Entry>,
290 providers: HashMap<RegionId, Provider>,
292 encoder: WalEntryEncoder,
294}
295
296impl<S: LogStore> WalWriter<S> {
297 pub fn add_entry(
299 &mut self,
300 region_id: RegionId,
301 entry_id: EntryId,
302 wal_entry: &WalEntry,
303 provider: &Provider,
304 ) -> Result<()> {
305 let provider = self
307 .providers
308 .entry(region_id)
309 .or_insert_with(|| provider.clone());
310
311 let data = self.encoder.encode_to_vec(wal_entry);
312 let entry = self
313 .store
314 .entry(data, entry_id, region_id, provider)
315 .map_err(BoxedError::new)
316 .context(BuildEntrySnafu { region_id })?;
317
318 self.entries.push(entry);
319
320 Ok(())
321 }
322
323 pub async fn write_to_wal(&mut self) -> Result<AppendBatchResponse> {
325 let entries = mem::take(&mut self.entries);
328 self.store
329 .append_batch(entries)
330 .await
331 .map_err(BoxedError::new)
332 .context(WriteWalSnafu)
333 }
334}
335
336#[cfg(test)]
337mod tests {
338 use api::v1::helper::{tag_column_schema, time_index_column_schema};
339 use api::v1::{
340 ArrowIpc, BulkWalEntry, ColumnDataType, Mutation, OpType, Row, Rows, Value, bulk_wal_entry,
341 value,
342 };
343 use common_recordbatch::DfRecordBatch;
344 use common_test_util::flight::encode_to_flight_data;
345 use common_test_util::temp_dir::{TempDir, create_temp_dir};
346 use datatypes::arrow;
347 use datatypes::arrow::array::{ArrayRef, TimestampMillisecondArray};
348 use datatypes::arrow::datatypes::Field;
349 use datatypes::arrow_array::StringArray;
350 use futures::TryStreamExt;
351 use log_store::raft_engine::log_store::RaftEngineLogStore;
352 use log_store::test_util::log_store_util;
353 use store_api::storage::SequenceNumber;
354
355 use super::*;
356
357 struct WalEnv {
358 _wal_dir: TempDir,
359 log_store: Option<Arc<RaftEngineLogStore>>,
360 }
361
362 impl WalEnv {
363 async fn new() -> WalEnv {
364 let wal_dir = create_temp_dir("");
365 let log_store =
366 log_store_util::create_tmp_local_file_log_store(wal_dir.path().to_str().unwrap())
367 .await;
368 WalEnv {
369 _wal_dir: wal_dir,
370 log_store: Some(Arc::new(log_store)),
371 }
372 }
373
374 fn new_wal(&self) -> Wal<RaftEngineLogStore> {
375 let log_store = self.log_store.clone().unwrap();
376 Wal::new(log_store)
377 }
378 }
379
380 fn new_mutation(op_type: OpType, sequence: SequenceNumber, rows: &[(&str, i64)]) -> Mutation {
384 let rows = rows
385 .iter()
386 .map(|(str_col, int_col)| {
387 let values = vec![
388 Value {
389 value_data: Some(value::ValueData::StringValue(str_col.to_string())),
390 },
391 Value {
392 value_data: Some(value::ValueData::TimestampMillisecondValue(*int_col)),
393 },
394 ];
395 Row { values }
396 })
397 .collect();
398 let schema = vec![
399 tag_column_schema("tag", ColumnDataType::String),
400 time_index_column_schema("ts", ColumnDataType::TimestampMillisecond),
401 ];
402
403 Mutation {
404 op_type: op_type as i32,
405 sequence,
406 rows: Some(Rows { schema, rows }),
407 write_hint: None,
408 }
409 }
410
411 #[tokio::test]
412 async fn test_write_wal() {
413 let env = WalEnv::new().await;
414 let wal = env.new_wal();
415
416 let entry = WalEntry {
417 mutations: vec![
418 new_mutation(OpType::Put, 1, &[("k1", 1), ("k2", 2)]),
419 new_mutation(OpType::Put, 2, &[("k3", 3), ("k4", 4)]),
420 ],
421 bulk_entries: vec![],
422 };
423 let mut writer = wal.writer();
424 let region_id = RegionId::new(1, 1);
426 writer
427 .add_entry(
428 region_id,
429 1,
430 &entry,
431 &Provider::raft_engine_provider(region_id.as_u64()),
432 )
433 .unwrap();
434 let region_id = RegionId::new(1, 2);
436 writer
437 .add_entry(
438 region_id,
439 1,
440 &entry,
441 &Provider::raft_engine_provider(region_id.as_u64()),
442 )
443 .unwrap();
444 let region_id = RegionId::new(1, 2);
446 writer
447 .add_entry(
448 region_id,
449 2,
450 &entry,
451 &Provider::raft_engine_provider(region_id.as_u64()),
452 )
453 .unwrap();
454
455 writer.write_to_wal().await.unwrap();
457 }
458
459 fn build_record_batch(rows: &[(&str, i64)]) -> DfRecordBatch {
460 let schema = Arc::new(arrow::datatypes::Schema::new(vec![
461 Field::new("tag", arrow::datatypes::DataType::Utf8, false),
462 Field::new(
463 "ts",
464 arrow::datatypes::DataType::Timestamp(
465 arrow::datatypes::TimeUnit::Millisecond,
466 None,
467 ),
468 false,
469 ),
470 ]));
471
472 let tag = Arc::new(StringArray::from_iter_values(
473 rows.iter().map(|r| r.0.to_string()),
474 )) as ArrayRef;
475 let ts = Arc::new(TimestampMillisecondArray::from_iter_values(
476 rows.iter().map(|r| r.1),
477 )) as ArrayRef;
478 DfRecordBatch::try_new(schema, vec![tag, ts]).unwrap()
479 }
480
481 fn build_bulk_wal_entry(sequence_number: SequenceNumber, rows: &[(&str, i64)]) -> BulkWalEntry {
482 let rb = build_record_batch(rows);
483 let (schema, rb) = encode_to_flight_data(rb);
484 let max_ts = rows.iter().map(|r| r.1).max().unwrap();
485 let min_ts = rows.iter().map(|r| r.1).min().unwrap();
486 BulkWalEntry {
487 sequence: sequence_number,
488 max_ts,
489 min_ts,
490 timestamp_index: 1,
491 body: Some(bulk_wal_entry::Body::ArrowIpc(ArrowIpc {
492 schema: schema.data_header,
493 data_header: rb.data_header,
494 payload: rb.data_body,
495 })),
496 }
497 }
498
499 fn sample_entries() -> Vec<WalEntry> {
500 vec![
501 WalEntry {
502 mutations: vec![
503 new_mutation(OpType::Put, 1, &[("k1", 1), ("k2", 2)]),
504 new_mutation(OpType::Put, 2, &[("k3", 3), ("k4", 4)]),
505 ],
506 bulk_entries: vec![],
507 },
508 WalEntry {
509 mutations: vec![new_mutation(OpType::Put, 3, &[("k1", 1), ("k2", 2)])],
510 bulk_entries: vec![],
511 },
512 WalEntry {
513 mutations: vec![
514 new_mutation(OpType::Put, 4, &[("k1", 1), ("k2", 2)]),
515 new_mutation(OpType::Put, 5, &[("k3", 3), ("k4", 4)]),
516 ],
517 bulk_entries: vec![],
518 },
519 WalEntry {
520 mutations: vec![new_mutation(OpType::Put, 6, &[("k1", 1), ("k2", 2)])],
521 bulk_entries: vec![build_bulk_wal_entry(7, &[("k1", 8), ("k2", 9)])],
522 },
523 ]
524 }
525
526 fn check_entries(
527 expect: &[WalEntry],
528 expect_start_id: EntryId,
529 actual: &[(EntryId, WalEntry)],
530 ) {
531 for (idx, (expect_entry, (actual_id, actual_entry))) in
532 expect.iter().zip(actual.iter()).enumerate()
533 {
534 let expect_id_entry = (expect_start_id + idx as u64, expect_entry);
535 assert_eq!(expect_id_entry, (*actual_id, actual_entry));
536 }
537 assert_eq!(expect.len(), actual.len());
538 }
539
540 #[tokio::test]
541 async fn test_scan_wal() {
542 let env = WalEnv::new().await;
543 let wal = env.new_wal();
544
545 let entries = sample_entries();
546 let (id1, id2) = (RegionId::new(1, 1), RegionId::new(1, 2));
547 let ns1 = Provider::raft_engine_provider(id1.as_u64());
548 let ns2 = Provider::raft_engine_provider(id2.as_u64());
549 let mut writer = wal.writer();
550 writer.add_entry(id1, 1, &entries[0], &ns1).unwrap();
551 writer.add_entry(id2, 1, &entries[0], &ns2).unwrap();
553 writer.add_entry(id1, 2, &entries[1], &ns1).unwrap();
554 writer.add_entry(id1, 3, &entries[2], &ns1).unwrap();
555 writer.add_entry(id1, 4, &entries[3], &ns1).unwrap();
556
557 writer.write_to_wal().await.unwrap();
558
559 let stream = wal.scan(id1, 1, &ns1).unwrap();
561 let actual: Vec<_> = stream.try_collect().await.unwrap();
562 check_entries(&entries, 1, &actual);
563
564 let stream = wal.scan(id1, 2, &ns1).unwrap();
566 let actual: Vec<_> = stream.try_collect().await.unwrap();
567 check_entries(&entries[1..], 2, &actual);
568
569 let stream = wal.scan(id1, 5, &ns1).unwrap();
571 let actual: Vec<_> = stream.try_collect().await.unwrap();
572 assert!(actual.is_empty());
573 }
574
575 #[tokio::test]
576 async fn test_obsolete_wal() {
577 let env = WalEnv::new().await;
578 let wal = env.new_wal();
579
580 let entries = sample_entries();
581 let mut writer = wal.writer();
582 let region_id = RegionId::new(1, 1);
583 let ns = Provider::raft_engine_provider(region_id.as_u64());
584 writer.add_entry(region_id, 1, &entries[0], &ns).unwrap();
585 writer.add_entry(region_id, 2, &entries[1], &ns).unwrap();
586 writer.add_entry(region_id, 3, &entries[2], &ns).unwrap();
587
588 writer.write_to_wal().await.unwrap();
589
590 wal.obsolete(region_id, 2, &ns).await.unwrap();
592
593 let mut writer = wal.writer();
595 writer.add_entry(region_id, 4, &entries[3], &ns).unwrap();
596 writer.write_to_wal().await.unwrap();
597
598 let stream = wal.scan(region_id, 1, &ns).unwrap();
600 let actual: Vec<_> = stream.try_collect().await.unwrap();
601 check_entries(&entries[2..], 3, &actual);
602 }
603}