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, r: R) -> io::Result<()> {
266 load_snapshot_filtered(store, r, |_| true)
267}
268
269pub fn load_snapshot_filtered<R: Read>(
275 store: &mut Store,
276 mut r: R,
277 keep: impl Fn(&[u8]) -> bool,
278) -> io::Result<()> {
279 let mut magic = [0u8; 8];
280 r.read_exact(&mut magic)?;
281 if &magic != MAGIC {
282 return Err(io::Error::new(
283 io::ErrorKind::InvalidData,
284 "kevy snapshot: bad magic",
285 ));
286 }
287 let version = read_u8(&mut r)?;
288 if !(VERSION_RELATIVE_TTL..=VERSION_HASH_TTL).contains(&version) {
289 return Err(io::Error::new(
290 io::ErrorKind::InvalidData,
291 "kevy snapshot: bad version",
292 ));
293 }
294 if version >= VERSION_FEED_CURSOR {
295 let mut cur = [0u8; 16];
298 r.read_exact(&mut cur)?;
299 }
300 let absolute_ttl = version >= VERSION_ABSOLUTE_TTL;
306 let now = kevy_store::now_unix_ms();
307
308 loop {
309 let op = read_u8(&mut r)?;
310 if op == OP_EOF {
311 return Ok(());
312 }
313 if op == OP_HFTTL {
315 let key = read_bytes(&mut r)?;
316 let field = read_bytes(&mut r)?;
317 let mut d = [0u8; 8];
318 r.read_exact(&mut d)?;
319 if keep(&key) {
320 store.load_hash_field_ttl(&key, &field, u64::from_le_bytes(d));
321 }
322 continue;
323 }
324 let raw_ttl = read_ttl(&mut r)?;
325 let ttl = if absolute_ttl {
326 raw_ttl.map(|deadline| deadline.saturating_sub(now))
327 } else {
328 raw_ttl
329 };
330 let key = read_bytes(&mut r)?;
331 match op {
332 OP_STR => {
333 let val = read_bytes(&mut r)?;
334 if keep(&key) {
335 store.load_str(key, val, ttl);
336 }
337 }
338 OP_HASH => {
339 let n = read_u32(&mut r)? as usize;
340 let mut fields = Vec::with_capacity(n);
341 for _ in 0..n {
342 let f = read_bytes(&mut r)?;
343 let v = read_bytes(&mut r)?;
344 fields.push((f, v));
345 }
346 if keep(&key) {
347 store.load_hash(key, fields, ttl);
348 }
349 }
350 OP_LIST => {
351 let n = read_u32(&mut r)? as usize;
352 let mut items = Vec::with_capacity(n);
353 for _ in 0..n {
354 items.push(read_bytes(&mut r)?);
355 }
356 if keep(&key) {
357 store.load_list(key, items, ttl);
358 }
359 }
360 OP_SET => {
361 let n = read_u32(&mut r)? as usize;
362 let mut members = Vec::with_capacity(n);
363 for _ in 0..n {
364 members.push(read_bytes(&mut r)?);
365 }
366 if keep(&key) {
367 store.load_set(key, members, ttl);
368 }
369 }
370 OP_ZSET => {
371 let n = read_u32(&mut r)? as usize;
372 let mut pairs = Vec::with_capacity(n);
373 for _ in 0..n {
374 let m = read_bytes(&mut r)?;
375 let score = f64::from_bits(read_u64(&mut r)?);
376 pairs.push((m, score));
377 }
378 if keep(&key) {
379 store.load_zset(key, pairs, ttl);
380 }
381 }
382 OP_STREAM => {
383 let last_ms = read_u64(&mut r)?;
384 let last_seq = read_u64(&mut r)?;
385 let mxd_ms = read_u64(&mut r)?;
386 let mxd_seq = read_u64(&mut r)?;
387 let entries_added = read_u64(&mut r)?;
388 let n = read_u32(&mut r)? as usize;
389 let mut entries = Vec::with_capacity(n);
390 for _ in 0..n {
391 let ms = read_u64(&mut r)?;
392 let seq = read_u64(&mut r)?;
393 let nf = read_u32(&mut r)? as usize;
394 let mut fv = Vec::with_capacity(nf);
395 for _ in 0..nf {
396 let f = read_bytes(&mut r)?;
397 let v = read_bytes(&mut r)?;
398 fv.push((f, v));
399 }
400 entries.push((ms, seq, fv));
401 }
402 let groups = if version >= VERSION {
405 read_stream_groups(&mut r)?
406 } else {
407 Vec::new()
408 };
409 if keep(&key) {
410 store.load_stream(
411 key,
412 entries,
413 (last_ms, last_seq),
414 (mxd_ms, mxd_seq),
415 entries_added,
416 groups,
417 ttl,
418 );
419 }
420 }
421 other => {
422 return Err(io::Error::new(
423 io::ErrorKind::InvalidData,
424 format!("kevy snapshot: unknown opcode {other}"),
425 ));
426 }
427 }
428 }
429}
430
431fn write_entry<W: Write>(w: &mut W, key: &[u8], value: &Value, ttl: Option<u64>) -> io::Result<()> {
433 let op = match value {
434 Value::Str(_) | Value::Int(_) | Value::ArcBulk(_) => OP_STR, Value::Hash(_) | Value::SmallHashInline(_) => OP_HASH,
437 Value::List(_) | Value::SmallListInline(_) => OP_LIST,
438 Value::Set(_) | Value::SmallSetInline(_) => OP_SET,
443 Value::ZSet(_) | Value::SmallZSetInline(_) => OP_ZSET,
444 Value::Stream(_) => OP_STREAM,
445 };
446 w.write_all(&[op])?;
447 write_ttl(w, ttl)?;
448 write_bytes(w, key)?;
449 match value {
450 Value::Str(v) => write_bytes(w, v.as_slice()),
451 Value::Int(n) => write_bytes(w, n.to_string().as_bytes()),
452 Value::ArcBulk(a) => write_bytes(w, a.as_ref()),
453 Value::Hash(h) => snapshot_payload::write_hash_payload(w, h),
454 Value::SmallHashInline(h) => snapshot_payload::write_small_hash_payload(w, h),
455 Value::List(l) => snapshot_payload::write_list_payload(w, l),
456 Value::SmallListInline(l) => snapshot_payload::write_small_list_payload(w, l),
457 Value::Set(set) => snapshot_payload::write_set_payload(w, set),
458 Value::SmallSetInline(s) => snapshot_payload::write_small_set_payload(w, s),
459 Value::ZSet(z) => snapshot_payload::write_zset_payload(w, z),
460 Value::SmallZSetInline(z) => snapshot_payload::write_small_zset_payload(w, z),
461 Value::Stream(s) => snapshot_payload::write_stream_payload(w, s),
462 }
463}
464
465pub(crate) fn write_stream_groups<W: Write>(w: &mut W, groups: &[kevy_store::LoadedGroup]) -> io::Result<()> {
470 w.write_all(&(groups.len() as u32).to_le_bytes())?;
471 for g in groups {
472 write_bytes(w, &g.name)?;
473 w.write_all(&g.last_delivered.0.to_le_bytes())?;
474 w.write_all(&g.last_delivered.1.to_le_bytes())?;
475 w.write_all(&(g.consumers.len() as u32).to_le_bytes())?;
476 for (name, last_seen_ms) in &g.consumers {
477 write_bytes(w, name)?;
478 w.write_all(&last_seen_ms.to_le_bytes())?;
479 }
480 w.write_all(&(g.pel.len() as u32).to_le_bytes())?;
481 for (ms, seq, consumer, delivery_time_ms, delivery_count) in &g.pel {
482 w.write_all(&ms.to_le_bytes())?;
483 w.write_all(&seq.to_le_bytes())?;
484 write_bytes(w, consumer)?;
485 w.write_all(&delivery_time_ms.to_le_bytes())?;
486 w.write_all(&delivery_count.to_le_bytes())?;
487 }
488 }
489 Ok(())
490}
491
492fn read_stream_groups<R: Read>(r: &mut R) -> io::Result<Vec<kevy_store::LoadedGroup>> {
494 let n = read_u32(r)? as usize;
495 let mut groups = Vec::with_capacity(n);
496 for _ in 0..n {
497 let name = read_bytes(r)?;
498 let last_delivered = (read_u64(r)?, read_u64(r)?);
499 let nc = read_u32(r)? as usize;
500 let mut consumers = Vec::with_capacity(nc);
501 for _ in 0..nc {
502 let cname = read_bytes(r)?;
503 consumers.push((cname, read_u64(r)?));
504 }
505 let np = read_u32(r)? as usize;
506 let mut pel = Vec::with_capacity(np);
507 for _ in 0..np {
508 let ms = read_u64(r)?;
509 let seq = read_u64(r)?;
510 let consumer = read_bytes(r)?;
511 let delivery_time_ms = read_u64(r)?;
512 let delivery_count = read_u32(r)?;
513 pel.push((ms, seq, consumer, delivery_time_ms, delivery_count));
514 }
515 groups.push(kevy_store::LoadedGroup { name, last_delivered, consumers, pel });
516 }
517 Ok(groups)
518}
519
520fn write_ttl<W: Write>(w: &mut W, ttl: Option<u64>) -> io::Result<()> {
521 match ttl {
522 Some(ms) => {
523 w.write_all(&[1u8])?;
524 w.write_all(&ms.to_le_bytes())?;
525 }
526 None => w.write_all(&[0u8])?,
527 }
528 Ok(())
529}
530
531fn read_ttl<R: Read>(r: &mut R) -> io::Result<Option<u64>> {
532 if read_u8(r)? == 1 {
533 Ok(Some(read_u64(r)?))
534 } else {
535 Ok(None)
536 }
537}
538
539fn tmp_path(path: &Path) -> std::path::PathBuf {
540 let mut s = path.as_os_str().to_owned();
541 s.push(".tmp");
542 s.into()
543}
544
545pub(crate) fn write_bytes<W: Write>(w: &mut W, b: &[u8]) -> io::Result<()> {
546 w.write_all(&(b.len() as u32).to_le_bytes())?;
547 w.write_all(b)
548}
549
550fn read_bytes<R: Read>(r: &mut R) -> io::Result<Vec<u8>> {
551 let len = read_u32(r)? as usize;
552 let mut buf = vec![0u8; len];
553 r.read_exact(&mut buf)?;
554 Ok(buf)
555}
556
557fn read_u8<R: Read>(r: &mut R) -> io::Result<u8> {
558 let mut b = [0u8; 1];
559 r.read_exact(&mut b)?;
560 Ok(b[0])
561}
562
563fn read_u32<R: Read>(r: &mut R) -> io::Result<u32> {
564 let mut b = [0u8; 4];
565 r.read_exact(&mut b)?;
566 Ok(u32::from_le_bytes(b))
567}
568
569fn read_u64<R: Read>(r: &mut R) -> io::Result<u64> {
570 let mut b = [0u8; 8];
571 r.read_exact(&mut b)?;
572 Ok(u64::from_le_bytes(b))
573}
574
575#[cfg(test)]
576mod tests;
577#[cfg(test)]
578mod tests_aof;
579#[cfg(test)]
580mod tests_rewrite;