risingwave_backup/
meta_snapshot.rs1use std::collections::HashSet;
16use std::fmt::{Display, Formatter};
17use std::future::Future;
18use std::hash::Hasher;
19use std::mem::size_of;
20
21use bytes::{Buf, BufMut, Bytes, BytesMut};
22use futures::StreamExt;
23use risingwave_hummock_sdk::HummockRawObjectId;
24use risingwave_hummock_sdk::version::HummockVersion;
25use risingwave_object_store::object::{
26 MonitoredStreamingReader, ObjectDataStream, ObjectDataStreamReader, ObjectStreamingUploader,
27};
28use tokio::io::AsyncReadExt;
29use twox_hash::XxHash64;
30
31use crate::MetaSnapshotId;
32use crate::error::{BackupError, BackupResult};
33
34pub trait Metadata: Display + Send + Sync {
35 fn encode_to_writer(
36 &self,
37 writer: SnapshotPayloadWriter,
38 ) -> impl Future<Output = BackupResult<()>> + Send;
39
40 fn decode_from_reader(
41 reader: SnapshotPayloadReader,
42 ) -> impl Future<Output = BackupResult<Self>> + Send
43 where
44 Self: Sized;
45
46 fn hummock_version_ref(&self) -> &HummockVersion;
47
48 fn hummock_version(self) -> HummockVersion;
49
50 fn storage_url(&self) -> BackupResult<String>;
51
52 fn storage_directory(&self) -> BackupResult<String>;
53
54 fn table_change_log_object_ids(&self) -> HashSet<HummockRawObjectId>;
55}
56
57#[derive(Debug, Default, Clone, PartialEq)]
58pub struct MetaSnapshot<T: Metadata> {
59 pub format_version: u32,
60 pub id: MetaSnapshotId,
61 pub metadata: T,
63}
64
65impl<T: Metadata> MetaSnapshot<T> {
66 pub async fn encode_to_uploader(&self, uploader: ObjectStreamingUploader) -> BackupResult<()> {
67 let mut writer = SnapshotPayloadWriter::new(uploader);
68
69 let mut header = BytesMut::with_capacity(size_of::<u32>() + size_of::<u64>());
70 header.put_u32_le(self.format_version);
71 header.put_u64_le(self.id);
72 writer.write_snapshot_bytes(header.freeze()).await?;
73
74 self.metadata.encode_to_writer(writer).await
75 }
76
77 pub async fn decode_from_stream(reader: MonitoredStreamingReader) -> BackupResult<Self> {
78 let mut reader =
79 SnapshotPayloadReader::new(ObjectDataStreamReader::new(reader.into_stream()));
80 let (format_version, id) = reader.read_snapshot_header().await?;
81 let metadata = T::decode_from_reader(reader).await?;
82 Ok(Self {
83 format_version,
84 id,
85 metadata,
86 })
87 }
88}
89
90pub struct SnapshotPayloadWriter {
91 uploader: ObjectStreamingUploader,
92 hasher: XxHash64,
93 size: usize,
94}
95
96impl SnapshotPayloadWriter {
97 pub fn new(uploader: ObjectStreamingUploader) -> Self {
98 Self {
99 uploader,
100 hasher: XxHash64::with_seed(0),
101 size: 0,
102 }
103 }
104
105 pub async fn write_snapshot_bytes(&mut self, data: Bytes) -> BackupResult<()> {
106 self.hasher.write(data.as_ref());
107 self.size += data.len();
108 self.uploader.write_bytes(data).await?;
109 Ok(())
110 }
111
112 pub async fn finish(mut self) -> BackupResult<usize> {
113 let mut checksum = BytesMut::with_capacity(size_of::<u64>());
114 checksum.put_u64_le(self.hasher.finish());
115 let checksum = checksum.freeze();
116 self.size += checksum.len();
117 self.uploader.write_bytes(checksum).await?;
118 self.uploader.finish().await?;
119 Ok(self.size)
120 }
121}
122
123type SnapshotPayloadStreamReader = ObjectDataStreamReader<ObjectDataStream>;
124
125pub struct SnapshotPayloadReader {
126 reader: SnapshotPayloadStreamReader,
127 hasher: XxHash64,
128}
129
130impl SnapshotPayloadReader {
131 pub fn new(reader: SnapshotPayloadStreamReader) -> Self {
132 Self {
133 reader,
134 hasher: XxHash64::with_seed(0),
135 }
136 }
137
138 pub async fn read_snapshot_header(&mut self) -> BackupResult<(u32, MetaSnapshotId)> {
139 let format_version = self.read_u32_le().await?;
140 let id = self.read_u64_le().await?;
141 Ok((format_version, id))
142 }
143
144 pub async fn read_u32_le(&mut self) -> BackupResult<u32> {
145 let bytes = self.read_exact(size_of::<u32>()).await?;
146 Ok(u32::from_le_bytes(
147 bytes[..].try_into().expect("u32 length"),
148 ))
149 }
150
151 pub async fn read_u64_le(&mut self) -> BackupResult<u64> {
152 let bytes = self.read_exact(size_of::<u64>()).await?;
153 Ok(u64::from_le_bytes(
154 bytes[..].try_into().expect("u64 length"),
155 ))
156 }
157
158 pub async fn read_exact(&mut self, len: usize) -> BackupResult<BytesMut> {
159 let mut bytes = BytesMut::with_capacity(len);
160 bytes.resize(len, 0);
161 self.reader.read_exact(&mut bytes).await?;
162 self.hasher.write(&bytes);
163 Ok(bytes)
164 }
165
166 pub async fn skip_exact(&mut self, mut len: usize) -> BackupResult<()> {
167 const BUFFER_SIZE: usize = 1024;
168 let mut buf = [0; BUFFER_SIZE];
169 while len > 0 {
170 let read_len = len.min(BUFFER_SIZE);
171 self.reader.read_exact(&mut buf[..read_len]).await?;
172 self.hasher.write(&buf[..read_len]);
173 len -= read_len;
174 }
175 Ok(())
176 }
177
178 pub async fn read_to_end(mut self) -> BackupResult<Vec<u8>> {
179 let mut data = vec![];
180 self.reader.read_to_end(&mut data).await?;
181 if data.len() < size_of::<u64>() {
182 return Err(BackupError::Decoding(
183 anyhow::anyhow!("meta snapshot is missing checksum").into(),
184 ));
185 }
186
187 let checksum = data.split_off(data.len() - size_of::<u64>());
188 self.hasher.write(&data);
189 self.verify_checksum(&checksum)?;
190 Ok(data)
191 }
192
193 pub async fn finish_after_skipping_to_end(self) -> BackupResult<()> {
194 let Self { reader, mut hasher } = self;
195 let mut bytes_stream = reader.into_bytes_stream();
196 let mut tail = BytesMut::with_capacity(size_of::<u64>());
197
198 while let Some(bytes) = bytes_stream.next().await.transpose()? {
199 let payload_len = tail.len() + bytes.len();
200 if payload_len <= size_of::<u64>() {
201 tail.extend_from_slice(&bytes);
202 continue;
203 }
204
205 let payload_len = payload_len - size_of::<u64>();
206 if payload_len <= tail.len() {
207 hasher.write(&tail[..payload_len]);
208 tail.advance(payload_len);
209 tail.extend_from_slice(&bytes);
210 } else {
211 let bytes_payload_len = payload_len - tail.len();
212 hasher.write(&tail);
213 hasher.write(&bytes[..bytes_payload_len]);
214 tail.clear();
215 tail.extend_from_slice(&bytes[bytes_payload_len..]);
216 }
217 }
218
219 if tail.len() != size_of::<u64>() {
220 return Err(BackupError::Decoding(
221 anyhow::anyhow!("meta snapshot is missing checksum").into(),
222 ));
223 }
224 Self::verify_checksum_with_hasher(&hasher, &tail)
225 }
226
227 pub async fn finish(mut self) -> BackupResult<()> {
228 let mut checksum = [0; size_of::<u64>()];
229 self.reader.read_exact(&mut checksum).await?;
230 self.verify_checksum(&checksum)?;
231
232 let mut trailing = [0; 1];
233 let n = self.reader.read(&mut trailing).await?;
234 if n != 0 {
235 return Err(BackupError::Decoding(
236 anyhow::anyhow!("unexpected bytes after meta snapshot checksum").into(),
237 ));
238 }
239 Ok(())
240 }
241
242 fn verify_checksum(&self, checksum: &[u8]) -> BackupResult<()> {
243 Self::verify_checksum_with_hasher(&self.hasher, checksum)
244 }
245
246 fn verify_checksum_with_hasher(hasher: &XxHash64, checksum: &[u8]) -> BackupResult<()> {
247 let expected = u64::from_le_bytes(checksum.try_into().expect("u64 length"));
248 let found = hasher.finish();
249 if expected != found {
250 return Err(BackupError::ChecksumMismatch { expected, found });
251 }
252 Ok(())
253 }
254}
255
256pub(crate) fn read_u32_le(buf: &mut &[u8]) -> BackupResult<u32> {
257 if buf.remaining() < 4 {
258 return Err(BackupError::Other(anyhow::anyhow!(
259 "metadata snapshot is truncated while reading u32"
260 )));
261 }
262 Ok(buf.get_u32_le())
263}
264
265impl<T: Metadata> Display for MetaSnapshot<T> {
266 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
267 writeln!(f, "format_version: {}", self.format_version)?;
268 writeln!(f, "id: {}", self.id)?;
269 writeln!(f, "{}", self.metadata)?;
270 Ok(())
271 }
272}