1#![forbid(unsafe_code)]
38
39mod aof;
40pub mod feed_meta;
41pub mod layout;
42mod replay;
43pub mod reshard;
44mod rewrite_fmt;
45mod shards_meta;
46mod snapshot_payload;
47
48pub use aof::{Aof, Fsync, RewritePlan, RewriteStats, write_aof_base};
49pub use replay::replay_aof;
50pub use shards_meta::{Routing, ShardsMeta, read_shards_meta, write_shards_meta};
51pub use kevy_resp::{Argv, ArgvView};
52pub use rewrite_fmt::dump_aof;
53pub(crate) use rewrite_fmt::{dump_store_to_buf, estimate_multibulk_bytes, write_multibulk};
54use kevy_store::Store;
55use kevy_store::Value;
56use std::fs::File;
58use std::io::{self, BufReader, BufWriter, Read, Write};
59use std::path::Path;
60
61const MAGIC: &[u8; 8] = b"KEVYSNAP";
71const VERSION: u8 = 4;
72const VERSION_FEED_CURSOR: u8 = 5;
79const VERSION_HASH_TTL: u8 = 6;
83const VERSION_RELATIVE_TTL: u8 = 2;
84const VERSION_ABSOLUTE_TTL: u8 = 3;
85
86const OP_EOF: u8 = 0;
89const OP_STR: u8 = 1;
90const OP_HASH: u8 = 2;
91const OP_LIST: u8 = 3;
92const OP_SET: u8 = 4;
93const OP_ZSET: u8 = 5;
94const OP_STREAM: u8 = 6;
95const OP_HFTTL: u8 = 7;
99
100pub(crate) const SNAPSHOT_BUF_CAP: usize = 1 << 20;
104
105pub trait SnapshotSource {
110 fn for_each_entry(&self, f: impl FnMut(&[u8], &Value, Option<u64>));
112
113 fn for_each_hash_ttl(&self, _f: impl FnMut(&[u8], &[u8], u64)) {}
117}
118
119impl SnapshotSource for Store {
120 fn for_each_entry(&self, f: impl FnMut(&[u8], &Value, Option<u64>)) {
121 self.snapshot_each(f);
122 }
123 fn for_each_hash_ttl(&self, f: impl FnMut(&[u8], &[u8], u64)) {
124 self.hash_ttl_each(f);
125 }
126}
127
128impl SnapshotSource for kevy_store::SnapshotView {
129 fn for_each_entry(&self, f: impl FnMut(&[u8], &Value, Option<u64>)) {
130 self.each(f);
131 }
132 fn for_each_hash_ttl(&self, f: impl FnMut(&[u8], &[u8], u64)) {
133 self.each_hash_ttl(f);
134 }
135}
136
137pub fn save_snapshot<S: SnapshotSource>(src: &S, path: &Path) -> io::Result<()> {
141 let tmp = write_snapshot_tmp(src, path)?;
142 std::fs::rename(&tmp, path)
143}
144
145pub fn write_snapshot_to<S: SnapshotSource, W: Write>(src: &S, sink: &mut W) -> io::Result<()> {
157 write_snapshot_to_with_cursor(src, sink, None)
158}
159
160pub fn write_snapshot_to_with_cursor<S: SnapshotSource, W: Write>(
165 src: &S,
166 sink: &mut W,
167 cursor: Option<(u64, u64)>,
168) -> io::Result<()> {
169 let mut fttl: Vec<(Vec<u8>, Vec<u8>, u64)> = Vec::new();
172 src.for_each_hash_ttl(|k, f, d| fttl.push((k.to_vec(), f.to_vec(), d)));
173 let mut w = BufWriter::with_capacity(SNAPSHOT_BUF_CAP, sink);
174 w.write_all(MAGIC)?;
175 let version = if !fttl.is_empty() {
176 VERSION_HASH_TTL
177 } else if cursor.is_some() {
178 VERSION_FEED_CURSOR
179 } else {
180 VERSION
181 };
182 w.write_all(&[version])?;
183 if version >= VERSION_FEED_CURSOR {
184 let (generation, offset) = cursor.unwrap_or((0, 0));
185 w.write_all(&generation.to_le_bytes())?;
186 w.write_all(&offset.to_le_bytes())?;
187 }
188 let now = kevy_store::now_unix_ms();
191 let mut err: Option<io::Error> = None;
193 src.for_each_entry(|key, value, ttl| {
194 let deadline = ttl.map(|ms| now.saturating_add(ms));
195 if err.is_none()
196 && let Err(e) = write_entry(&mut w, key, value, deadline)
197 {
198 err = Some(e);
199 }
200 });
201 if let Some(e) = err {
202 return Err(e);
203 }
204 for (k, f, d) in &fttl {
205 w.write_all(&[OP_HFTTL])?;
206 write_bytes(&mut w, k)?;
207 write_bytes(&mut w, f)?;
208 w.write_all(&d.to_le_bytes())?;
209 }
210 w.write_all(&[OP_EOF])?;
211 w.flush()?;
212 Ok(())
213}
214
215pub fn write_snapshot_tmp<S: SnapshotSource>(src: &S, path: &Path) -> io::Result<std::path::PathBuf> {
222 let tmp = tmp_path(path);
223 {
224 let mut file = File::create(&tmp)?;
225 write_snapshot_to(src, &mut file)?;
226 file.sync_all()?; }
228 Ok(tmp)
229}
230
231pub fn read_snapshot_cursor(path: &Path) -> io::Result<Option<(u64, u64)>> {
235 let mut r = BufReader::new(File::open(path)?);
236 let mut magic = [0u8; 8];
237 r.read_exact(&mut magic)?;
238 if &magic != MAGIC {
239 return Err(io::Error::new(io::ErrorKind::InvalidData, "kevy snapshot: bad magic"));
240 }
241 let version = read_u8(&mut r)?;
242 if version < VERSION_FEED_CURSOR {
243 return Ok(None);
244 }
245 let mut cur = [0u8; 16];
246 r.read_exact(&mut cur)?;
247 let generation = u64::from_le_bytes(cur[..8].try_into().expect("8 bytes"));
248 let offset = u64::from_le_bytes(cur[8..].try_into().expect("8 bytes"));
249 Ok(Some((generation, offset)))
250}
251
252pub fn load_snapshot(store: &mut Store, path: &Path) -> io::Result<()> {
255 let r = BufReader::new(File::open(path)?);
256 load_snapshot_from(store, r)
257}
258
259pub fn load_snapshot_from<R: Read>(store: &mut Store, mut r: R) -> io::Result<()> {
266 let mut magic = [0u8; 8];
267 r.read_exact(&mut magic)?;
268 if &magic != MAGIC {
269 return Err(io::Error::new(
270 io::ErrorKind::InvalidData,
271 "kevy snapshot: bad magic",
272 ));
273 }
274 let version = read_u8(&mut r)?;
275 if !(VERSION_RELATIVE_TTL..=VERSION_HASH_TTL).contains(&version) {
276 return Err(io::Error::new(
277 io::ErrorKind::InvalidData,
278 "kevy snapshot: bad version",
279 ));
280 }
281 if version >= VERSION_FEED_CURSOR {
282 let mut cur = [0u8; 16];
285 r.read_exact(&mut cur)?;
286 }
287 let absolute_ttl = version >= VERSION_ABSOLUTE_TTL;
293 let now = kevy_store::now_unix_ms();
294
295 loop {
296 let op = read_u8(&mut r)?;
297 if op == OP_EOF {
298 return Ok(());
299 }
300 if op == OP_HFTTL {
302 let key = read_bytes(&mut r)?;
303 let field = read_bytes(&mut r)?;
304 let mut d = [0u8; 8];
305 r.read_exact(&mut d)?;
306 store.load_hash_field_ttl(&key, &field, u64::from_le_bytes(d));
307 continue;
308 }
309 let raw_ttl = read_ttl(&mut r)?;
310 let ttl = if absolute_ttl {
311 raw_ttl.map(|deadline| deadline.saturating_sub(now))
312 } else {
313 raw_ttl
314 };
315 let key = read_bytes(&mut r)?;
316 match op {
317 OP_STR => {
318 let val = read_bytes(&mut r)?;
319 store.load_str(key, val, ttl);
320 }
321 OP_HASH => {
322 let n = read_u32(&mut r)? as usize;
323 let mut fields = Vec::with_capacity(n);
324 for _ in 0..n {
325 let f = read_bytes(&mut r)?;
326 let v = read_bytes(&mut r)?;
327 fields.push((f, v));
328 }
329 store.load_hash(key, fields, ttl);
330 }
331 OP_LIST => {
332 let n = read_u32(&mut r)? as usize;
333 let mut items = Vec::with_capacity(n);
334 for _ in 0..n {
335 items.push(read_bytes(&mut r)?);
336 }
337 store.load_list(key, items, ttl);
338 }
339 OP_SET => {
340 let n = read_u32(&mut r)? as usize;
341 let mut members = Vec::with_capacity(n);
342 for _ in 0..n {
343 members.push(read_bytes(&mut r)?);
344 }
345 store.load_set(key, members, ttl);
346 }
347 OP_ZSET => {
348 let n = read_u32(&mut r)? as usize;
349 let mut pairs = Vec::with_capacity(n);
350 for _ in 0..n {
351 let m = read_bytes(&mut r)?;
352 let score = f64::from_bits(read_u64(&mut r)?);
353 pairs.push((m, score));
354 }
355 store.load_zset(key, pairs, ttl);
356 }
357 OP_STREAM => {
358 let last_ms = read_u64(&mut r)?;
359 let last_seq = read_u64(&mut r)?;
360 let mxd_ms = read_u64(&mut r)?;
361 let mxd_seq = read_u64(&mut r)?;
362 let entries_added = read_u64(&mut r)?;
363 let n = read_u32(&mut r)? as usize;
364 let mut entries = Vec::with_capacity(n);
365 for _ in 0..n {
366 let ms = read_u64(&mut r)?;
367 let seq = read_u64(&mut r)?;
368 let nf = read_u32(&mut r)? as usize;
369 let mut fv = Vec::with_capacity(nf);
370 for _ in 0..nf {
371 let f = read_bytes(&mut r)?;
372 let v = read_bytes(&mut r)?;
373 fv.push((f, v));
374 }
375 entries.push((ms, seq, fv));
376 }
377 let groups = if version >= VERSION {
380 read_stream_groups(&mut r)?
381 } else {
382 Vec::new()
383 };
384 store.load_stream(
385 key,
386 entries,
387 (last_ms, last_seq),
388 (mxd_ms, mxd_seq),
389 entries_added,
390 groups,
391 ttl,
392 );
393 }
394 other => {
395 return Err(io::Error::new(
396 io::ErrorKind::InvalidData,
397 format!("kevy snapshot: unknown opcode {other}"),
398 ));
399 }
400 }
401 }
402}
403
404fn write_entry<W: Write>(w: &mut W, key: &[u8], value: &Value, ttl: Option<u64>) -> io::Result<()> {
406 let op = match value {
407 Value::Str(_) | Value::Int(_) | Value::ArcBulk(_) => OP_STR, Value::Hash(_) | Value::SmallHashInline(_) => OP_HASH,
410 Value::List(_) | Value::SmallListInline(_) => OP_LIST,
411 Value::Set(_) | Value::SmallSetInline(_) => OP_SET,
416 Value::ZSet(_) | Value::SmallZSetInline(_) => OP_ZSET,
417 Value::Stream(_) => OP_STREAM,
418 };
419 w.write_all(&[op])?;
420 write_ttl(w, ttl)?;
421 write_bytes(w, key)?;
422 match value {
423 Value::Str(v) => write_bytes(w, v.as_slice()),
424 Value::Int(n) => write_bytes(w, n.to_string().as_bytes()),
425 Value::ArcBulk(a) => write_bytes(w, a.as_ref()),
426 Value::Hash(h) => snapshot_payload::write_hash_payload(w, h),
427 Value::SmallHashInline(h) => snapshot_payload::write_small_hash_payload(w, h),
428 Value::List(l) => snapshot_payload::write_list_payload(w, l),
429 Value::SmallListInline(l) => snapshot_payload::write_small_list_payload(w, l),
430 Value::Set(set) => snapshot_payload::write_set_payload(w, set),
431 Value::SmallSetInline(s) => snapshot_payload::write_small_set_payload(w, s),
432 Value::ZSet(z) => snapshot_payload::write_zset_payload(w, z),
433 Value::SmallZSetInline(z) => snapshot_payload::write_small_zset_payload(w, z),
434 Value::Stream(s) => snapshot_payload::write_stream_payload(w, s),
435 }
436}
437
438pub(crate) fn write_stream_groups<W: Write>(w: &mut W, groups: &[kevy_store::LoadedGroup]) -> io::Result<()> {
443 w.write_all(&(groups.len() as u32).to_le_bytes())?;
444 for g in groups {
445 write_bytes(w, &g.name)?;
446 w.write_all(&g.last_delivered.0.to_le_bytes())?;
447 w.write_all(&g.last_delivered.1.to_le_bytes())?;
448 w.write_all(&(g.consumers.len() as u32).to_le_bytes())?;
449 for (name, last_seen_ms) in &g.consumers {
450 write_bytes(w, name)?;
451 w.write_all(&last_seen_ms.to_le_bytes())?;
452 }
453 w.write_all(&(g.pel.len() as u32).to_le_bytes())?;
454 for (ms, seq, consumer, delivery_time_ms, delivery_count) in &g.pel {
455 w.write_all(&ms.to_le_bytes())?;
456 w.write_all(&seq.to_le_bytes())?;
457 write_bytes(w, consumer)?;
458 w.write_all(&delivery_time_ms.to_le_bytes())?;
459 w.write_all(&delivery_count.to_le_bytes())?;
460 }
461 }
462 Ok(())
463}
464
465fn read_stream_groups<R: Read>(r: &mut R) -> io::Result<Vec<kevy_store::LoadedGroup>> {
467 let n = read_u32(r)? as usize;
468 let mut groups = Vec::with_capacity(n);
469 for _ in 0..n {
470 let name = read_bytes(r)?;
471 let last_delivered = (read_u64(r)?, read_u64(r)?);
472 let nc = read_u32(r)? as usize;
473 let mut consumers = Vec::with_capacity(nc);
474 for _ in 0..nc {
475 let cname = read_bytes(r)?;
476 consumers.push((cname, read_u64(r)?));
477 }
478 let np = read_u32(r)? as usize;
479 let mut pel = Vec::with_capacity(np);
480 for _ in 0..np {
481 let ms = read_u64(r)?;
482 let seq = read_u64(r)?;
483 let consumer = read_bytes(r)?;
484 let delivery_time_ms = read_u64(r)?;
485 let delivery_count = read_u32(r)?;
486 pel.push((ms, seq, consumer, delivery_time_ms, delivery_count));
487 }
488 groups.push(kevy_store::LoadedGroup { name, last_delivered, consumers, pel });
489 }
490 Ok(groups)
491}
492
493fn write_ttl<W: Write>(w: &mut W, ttl: Option<u64>) -> io::Result<()> {
494 match ttl {
495 Some(ms) => {
496 w.write_all(&[1u8])?;
497 w.write_all(&ms.to_le_bytes())?;
498 }
499 None => w.write_all(&[0u8])?,
500 }
501 Ok(())
502}
503
504fn read_ttl<R: Read>(r: &mut R) -> io::Result<Option<u64>> {
505 if read_u8(r)? == 1 {
506 Ok(Some(read_u64(r)?))
507 } else {
508 Ok(None)
509 }
510}
511
512fn tmp_path(path: &Path) -> std::path::PathBuf {
513 let mut s = path.as_os_str().to_owned();
514 s.push(".tmp");
515 s.into()
516}
517
518pub(crate) fn write_bytes<W: Write>(w: &mut W, b: &[u8]) -> io::Result<()> {
519 w.write_all(&(b.len() as u32).to_le_bytes())?;
520 w.write_all(b)
521}
522
523fn read_bytes<R: Read>(r: &mut R) -> io::Result<Vec<u8>> {
524 let len = read_u32(r)? as usize;
525 let mut buf = vec![0u8; len];
526 r.read_exact(&mut buf)?;
527 Ok(buf)
528}
529
530fn read_u8<R: Read>(r: &mut R) -> io::Result<u8> {
531 let mut b = [0u8; 1];
532 r.read_exact(&mut b)?;
533 Ok(b[0])
534}
535
536fn read_u32<R: Read>(r: &mut R) -> io::Result<u32> {
537 let mut b = [0u8; 4];
538 r.read_exact(&mut b)?;
539 Ok(u32::from_le_bytes(b))
540}
541
542fn read_u64<R: Read>(r: &mut R) -> io::Result<u64> {
543 let mut b = [0u8; 8];
544 r.read_exact(&mut b)?;
545 Ok(u64::from_le_bytes(b))
546}
547
548#[cfg(test)]
549mod tests;
550#[cfg(test)]
551mod tests_aof;
552#[cfg(test)]
553mod tests_rewrite;