11use log:: { debug, error, log_enabled, trace} ;
2- use std:: cmp:: { min , Ordering } ;
2+ use std:: cmp:: { Ordering , min } ;
33use std:: fmt;
44use std:: fs:: { self , OpenOptions } ;
55use std:: io:: { Error , ErrorKind , Result } ;
@@ -698,6 +698,21 @@ fn padding(len: usize) -> usize {
698698 4usize . wrapping_sub ( len) & 7
699699}
700700
701+ /// Returns total number of bytes used on disk to store an entry of length `len`.
702+ ///
703+ /// Includes header, padding and CRC.
704+ ///
705+ /// | component | type |
706+ /// | ---------------------------- | ---- |
707+ /// | length | u64 |
708+ /// | data | |
709+ /// | padding | |
710+ /// | CRC(length + data + padding) | u32 |
711+ #[ cfg( test) ]
712+ pub fn entry_size_disk ( len : usize ) -> usize {
713+ len + entry_overhead ( len)
714+ }
715+
701716/// Returns the overhead of storing an entry of length `len`.
702717pub fn entry_overhead ( len : usize ) -> usize {
703718 padding ( len) + HEADER_LEN + CRC_LEN
@@ -710,12 +725,15 @@ pub fn segment_overhead() -> usize {
710725
711726#[ cfg( test) ]
712727mod test {
713- use std:: io:: ErrorKind ;
728+ use std:: { io:: ErrorKind , path :: Path } ;
714729 use tempfile:: { Builder , TempDir } ;
715730
716731 use super :: { Segment , padding} ;
717732
718- use crate :: test_utils:: EntryGenerator ;
733+ use crate :: {
734+ segment:: { HEADER_LEN , entry_size_disk} ,
735+ test_utils:: EntryGenerator ,
736+ } ;
719737
720738 #[ test]
721739 fn test_pad_len ( ) {
@@ -745,6 +763,12 @@ mod test {
745763 ( Segment :: create ( path, len) . unwrap ( ) , dir)
746764 }
747765
766+ fn load_segment ( dir : impl AsRef < Path > ) -> Segment {
767+ let mut path = dir. as_ref ( ) . to_path_buf ( ) ;
768+ path. push ( "sync-segment" ) ;
769+ Segment :: open ( path) . unwrap ( )
770+ }
771+
748772 fn init_logger ( ) {
749773 let _ = env_logger:: builder ( ) . is_test ( true ) . try_init ( ) ;
750774 }
@@ -780,6 +804,65 @@ mod test {
780804 check_append ( & mut create_segment ( 8 * 1024 * 1024 ) . 0 ) ;
781805 }
782806
807+ #[ test]
808+ fn test_truncate ( ) {
809+ init_logger ( ) ;
810+ let ( mut segment, dir) = create_segment ( 4096 ) ;
811+
812+ segment. append ( & b"0" . as_slice ( ) ) . unwrap ( ) ;
813+ segment. append ( & b"1" . as_slice ( ) ) . unwrap ( ) ;
814+ segment. append ( & b"2" . as_slice ( ) ) . unwrap ( ) ;
815+ segment. append ( & b"3" . as_slice ( ) ) . unwrap ( ) ;
816+
817+ // Truncate beyond the end is a no-op
818+ assert_eq ! ( segment. len( ) , 4 ) ;
819+ segment. truncate ( 4 ) ;
820+ assert_eq ! ( segment. len( ) , 4 ) ;
821+
822+ // Until we flush, flush offset remains zero
823+ assert_eq ! ( segment. flush_offset, 0 ) ;
824+ segment. flush ( ) . unwrap ( ) ;
825+ assert_eq ! ( segment. flush_offset, HEADER_LEN + entry_size_disk( 1 ) * 4 ) ;
826+
827+ // Truncate to keep one entry
828+ segment. truncate ( 1 ) ;
829+ assert_eq ! ( segment. len( ) , 1 ) ;
830+ assert_eq ! ( segment. flush_offset, HEADER_LEN + entry_size_disk( 1 ) ) ;
831+
832+ // Add a new items (index 2, 3), flush offset remains at index 1 until we flush
833+ segment. append ( & b"12345" . as_slice ( ) ) . unwrap ( ) ;
834+ segment. append ( & b"67890" . as_slice ( ) ) . unwrap ( ) ;
835+ assert_eq ! ( segment. len( ) , 3 ) ;
836+ assert_eq ! ( segment. flush_offset, HEADER_LEN + entry_size_disk( 1 ) ) ;
837+
838+ // Flush and reload
839+ // This was broken before <https://github.com/qdrant/wal/pull/99>, as it wouldn't fully
840+ // flush the last two appended operations
841+ segment. flush ( ) . unwrap ( ) ;
842+ assert_eq ! (
843+ segment. flush_offset,
844+ HEADER_LEN + entry_size_disk( 1 ) + entry_size_disk( 5 ) * 2 ,
845+ ) ;
846+ let mut segment = load_segment ( & dir) ;
847+ assert_eq ! ( segment. len( ) , 3 ) ;
848+ assert_eq ! (
849+ segment. flush_offset,
850+ HEADER_LEN + entry_size_disk( 1 ) + entry_size_disk( 5 ) * 2 ,
851+ ) ;
852+
853+ // Truncate all (clear) and assert flush offset is bumped
854+ segment. truncate ( 0 ) ;
855+ assert_eq ! ( segment. len( ) , 0 ) ;
856+ assert_eq ! ( segment. flush_offset, HEADER_LEN ) ;
857+
858+ // Flush and reload
859+ segment. flush ( ) . unwrap ( ) ;
860+ assert_eq ! ( segment. flush_offset, HEADER_LEN ) ;
861+ let segment = load_segment ( & dir) ;
862+ assert_eq ! ( segment. len( ) , 0 ) ;
863+ assert_eq ! ( segment. flush_offset, HEADER_LEN ) ;
864+ }
865+
783866 #[ test]
784867 fn test_create_dir_path ( ) {
785868 init_logger ( ) ;
0 commit comments