Skip to main content

mito2/wal/
raw_entry_reader.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::sync::Arc;
16
17use async_stream::try_stream;
18use common_error::ext::BoxedError;
19use futures::stream::BoxStream;
20use snafu::ResultExt;
21use store_api::logstore::LogStore;
22use store_api::logstore::entry::Entry;
23use store_api::logstore::provider::Provider;
24use store_api::storage::RegionId;
25use tokio_stream::StreamExt;
26
27use crate::error::{self, Result};
28use crate::wal::EntryId;
29
30/// A stream that yields [Entry].
31pub type EntryStream<'a> = BoxStream<'a, Result<Entry>>;
32
33/// [RawEntryReader] provides the ability to read [Entry] from the underlying [LogStore].
34pub(crate) trait RawEntryReader: Send + Sync {
35    fn read(&self, provider: &Provider, start_id: EntryId) -> Result<EntryStream<'static>>;
36}
37
38/// Implement the [RawEntryReader] for the [LogStore].
39pub struct LogStoreRawEntryReader<S> {
40    store: Arc<S>,
41}
42
43impl<S> LogStoreRawEntryReader<S> {
44    pub fn new(store: Arc<S>) -> Self {
45        Self { store }
46    }
47}
48
49impl<S: LogStore> RawEntryReader for LogStoreRawEntryReader<S> {
50    fn read(&self, provider: &Provider, start_id: EntryId) -> Result<EntryStream<'static>> {
51        let store = self.store.clone();
52        let provider = provider.clone();
53        let stream = try_stream!({
54            let mut stream = store
55                .read(&provider, start_id)
56                .await
57                .map_err(BoxedError::new)
58                .with_context(|_| error::ReadWalSnafu {
59                    provider: provider.clone(),
60                })?;
61
62            while let Some(entries) = stream.next().await {
63                let entries =
64                    entries
65                        .map_err(BoxedError::new)
66                        .with_context(|_| error::ReadWalSnafu {
67                            provider: provider.clone(),
68                        })?;
69
70                for entry in entries {
71                    yield entry
72                }
73            }
74        });
75
76        Ok(Box::pin(stream))
77    }
78}
79
80/// A [RawEntryReader] reads [RawEntry] belongs to a specific region.
81pub struct RegionRawEntryReader<R> {
82    reader: R,
83    region_id: RegionId,
84}
85
86impl<R> RegionRawEntryReader<R>
87where
88    R: RawEntryReader,
89{
90    pub fn new(reader: R, region_id: RegionId) -> Self {
91        Self { reader, region_id }
92    }
93}
94
95impl<R> RawEntryReader for RegionRawEntryReader<R>
96where
97    R: RawEntryReader,
98{
99    fn read(&self, ctx: &Provider, start_id: EntryId) -> Result<EntryStream<'static>> {
100        let mut stream = self.reader.read(ctx, start_id)?;
101        let region_id = self.region_id;
102
103        let stream = try_stream!({
104            while let Some(entry) = stream.next().await {
105                let entry = entry?;
106                if entry.region_id() == region_id {
107                    yield entry
108                }
109            }
110        });
111
112        Ok(Box::pin(stream))
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use std::sync::Arc;
119
120    use futures::{TryStreamExt, stream};
121    use store_api::logstore::entry::{Entry, NaiveEntry};
122    use store_api::logstore::{AppendBatchResponse, EntryId, LogStore, SendableEntryStream};
123    use store_api::storage::RegionId;
124
125    use super::*;
126    use crate::error;
127
128    #[derive(Debug)]
129    struct MockLogStore {
130        entries: Vec<Entry>,
131    }
132
133    #[async_trait::async_trait]
134    impl LogStore for MockLogStore {
135        type Error = error::Error;
136
137        async fn stop(&self) -> Result<(), Self::Error> {
138            unreachable!()
139        }
140
141        async fn append_batch(
142            &self,
143            _entries: Vec<Entry>,
144        ) -> Result<AppendBatchResponse, Self::Error> {
145            unreachable!()
146        }
147
148        async fn read(
149            &self,
150            _provider: &Provider,
151            _id: EntryId,
152        ) -> Result<SendableEntryStream<'static, Entry, Self::Error>, Self::Error> {
153            Ok(Box::pin(stream::iter(vec![Ok(self.entries.clone())])))
154        }
155
156        async fn create_namespace(&self, _ns: &Provider) -> Result<(), Self::Error> {
157            unreachable!()
158        }
159
160        async fn delete_namespace(&self, _ns: &Provider) -> Result<(), Self::Error> {
161            unreachable!()
162        }
163
164        async fn list_namespaces(&self) -> Result<Vec<Provider>, Self::Error> {
165            unreachable!()
166        }
167
168        async fn obsolete(
169            &self,
170            _provider: &Provider,
171            _region_id: RegionId,
172            _entry_id: EntryId,
173        ) -> Result<(), Self::Error> {
174            unreachable!()
175        }
176
177        async fn obsolete_all(
178            &self,
179            _provider: &Provider,
180            _region_id: RegionId,
181        ) -> Result<(), Self::Error> {
182            unreachable!()
183        }
184
185        fn entry(
186            &self,
187            _data: Vec<u8>,
188            _entry_id: EntryId,
189            _region_id: RegionId,
190            _provider: &Provider,
191        ) -> Result<Entry, Self::Error> {
192            unreachable!()
193        }
194
195        fn latest_entry_id(&self, _provider: &Provider) -> Result<EntryId, Self::Error> {
196            unreachable!()
197        }
198    }
199
200    #[tokio::test]
201    async fn test_raw_entry_reader() {
202        let provider = Provider::raft_engine_provider(RegionId::new(1024, 1).as_u64());
203        let expected_entries = vec![Entry::Naive(NaiveEntry {
204            provider: provider.clone(),
205            region_id: RegionId::new(1024, 1),
206            entry_id: 1,
207            data: vec![1],
208        })];
209        let store = MockLogStore {
210            entries: expected_entries.clone(),
211        };
212
213        let reader = LogStoreRawEntryReader::new(Arc::new(store));
214        let entries = reader
215            .read(&provider, 0)
216            .unwrap()
217            .try_collect::<Vec<_>>()
218            .await
219            .unwrap();
220        assert_eq!(expected_entries, entries);
221    }
222
223    #[tokio::test]
224    async fn test_raw_entry_reader_filter() {
225        let provider = Provider::raft_engine_provider(RegionId::new(1024, 1).as_u64());
226        let all_entries = vec![
227            Entry::Naive(NaiveEntry {
228                provider: provider.clone(),
229                region_id: RegionId::new(1024, 1),
230                entry_id: 1,
231                data: vec![1],
232            }),
233            Entry::Naive(NaiveEntry {
234                provider: provider.clone(),
235                region_id: RegionId::new(1024, 2),
236                entry_id: 2,
237                data: vec![2],
238            }),
239            Entry::Naive(NaiveEntry {
240                provider: provider.clone(),
241                region_id: RegionId::new(1024, 3),
242                entry_id: 3,
243                data: vec![3],
244            }),
245        ];
246        let store = MockLogStore {
247            entries: all_entries.clone(),
248        };
249
250        let expected_region_id = RegionId::new(1024, 3);
251        let reader = RegionRawEntryReader::new(
252            LogStoreRawEntryReader::new(Arc::new(store)),
253            expected_region_id,
254        );
255        let entries = reader
256            .read(&provider, 0)
257            .unwrap()
258            .try_collect::<Vec<_>>()
259            .await
260            .unwrap();
261        assert_eq!(
262            all_entries
263                .into_iter()
264                .filter(|entry| entry.region_id() == expected_region_id)
265                .collect::<Vec<_>>(),
266            entries
267        );
268    }
269}