@@ -798,9 +798,9 @@ impl<B: Blob> Append<B> {
798798 ///
799799 /// Since larger valid lengths are authoritative, a shorter CRC cannot simply be written next to
800800 /// the old CRC. We first stage the shorter slot with length 0, then make its length durable,
801- /// then clear the old slot's CRC bytes before clearing its length bytes. A crash during any
802- /// phase recovers either the old longer page or the new shorter page, but never loses the whole
803- /// page or fabricates a larger length.
801+ /// then clear the old slot's length bytes. A crash during any phase recovers either the old
802+ /// longer page or the new shorter page, but never loses the whole page or fabricates a larger
803+ /// length.
804804 async fn sync_partial_page_shrink (
805805 blob : & B ,
806806 page : u64 ,
@@ -842,15 +842,8 @@ impl<B: Blob> Append<B> {
842842 . checked_add ( old_slot_start as u64 )
843843 . ok_or ( Error :: OffsetOverflow ) ?;
844844 let len_size = std:: mem:: size_of :: < u16 > ( ) ;
845- let crc_size = CHECKSUM_SLOT_SIZE - len_size;
846- let old_crc_offset = old_slot_offset
847- . checked_add ( len_size as u64 )
848- . ok_or ( Error :: OffsetOverflow ) ?;
849-
850- // Clear CRC bytes before length bytes so torn invalidation cannot create a new valid slot.
851- blob. write_at ( old_crc_offset, vec ! [ 0u8 ; crc_size] ) . await ?;
852- blob. sync ( ) . await ?;
853845
846+ // A slot with length 0 is invalid regardless of its CRC.
854847 blob. write_at ( old_slot_offset, vec ! [ 0u8 ; len_size] ) . await ?;
855848 blob. sync ( ) . await ?;
856849
@@ -3058,69 +3051,73 @@ mod tests {
30583051 }
30593052
30603053 #[ test]
3061- fn test_resize_same_page_shrink_survives_interrupted_crc_invalidation ( ) {
3054+ fn test_resize_same_page_shrink_survives_interrupted_length_invalidation ( ) {
30623055 let executor = deterministic:: Runner :: default ( ) ;
30633056
30643057 executor. start ( |context| async move {
3065- let cache_ref = CacheRef :: from_pooler ( & context, PAGE_SIZE , NZUsize ! ( BUFFER_SIZE ) ) ;
3066- let data: Vec < u8 > = ( 0 ..50 ) . collect ( ) ;
3058+ const LARGE_PAGE_SIZE : NonZeroU16 = NZU16 ! ( 600 ) ;
3059+ const LARGE_BUFFER_SIZE : usize = 1_200 ;
3060+
3061+ let cache_ref =
3062+ CacheRef :: from_pooler ( & context, LARGE_PAGE_SIZE , NZUsize ! ( LARGE_BUFFER_SIZE ) ) ;
3063+ let data: Vec < u8 > = ( 0 ..300 ) . map ( |i| ( i % 251 ) as u8 ) . collect ( ) ;
30673064
30683065 let ( blob, size) = context
30693066 . open (
30703067 "test_partition" ,
3071- b"same_page_shrink_interrupted_invalidation " ,
3068+ b"same_page_shrink_interrupted_len_invalidation " ,
30723069 )
30733070 . await
30743071 . unwrap ( ) ;
3075- let append = Append :: new ( blob, size, BUFFER_SIZE , cache_ref. clone ( ) )
3072+ let append = Append :: new ( blob, size, LARGE_BUFFER_SIZE , cache_ref. clone ( ) )
30763073 . await
30773074 . unwrap ( ) ;
30783075 // Put the old authoritative CRC in slot 1, so the shorter CRC will be staged in slot
3079- // 0. The old slot is invalidated only after the shorter slot is durable .
3080- append. append ( & data[ ..40 ] ) . await . unwrap ( ) ;
3076+ // 0. The old length is above 255, so a one-byte tear changes the decoded length .
3077+ append. append ( & data[ ..255 ] ) . await . unwrap ( ) ;
30813078 append. sync ( ) . await . unwrap ( ) ;
3082- append. append ( & data[ 40 ..] ) . await . unwrap ( ) ;
3079+ append. append ( & data[ 255 ..] ) . await . unwrap ( ) ;
30833080 append. sync ( ) . await . unwrap ( ) ;
30843081 drop ( append) ;
30853082
30863083 let ( blob, size) = context
30873084 . open (
30883085 "test_partition" ,
3089- b"same_page_shrink_interrupted_invalidation " ,
3086+ b"same_page_shrink_interrupted_len_invalidation " ,
30903087 )
30913088 . await
30923089 . unwrap ( ) ;
3093- let faulty_blob = PartialWriteBlob :: new ( blob, 3 , 3 ) ;
3090+ let faulty_blob = PartialWriteBlob :: new ( blob, 3 , 1 ) ;
30943091 let write_count = faulty_blob. write_count ( ) ;
30953092 let failed_write_len = faulty_blob. failed_write_len ( ) ;
3096- let append = Append :: new ( faulty_blob, size, BUFFER_SIZE , cache_ref. clone ( ) )
3093+ let append = Append :: new ( faulty_blob, size, LARGE_BUFFER_SIZE , cache_ref. clone ( ) )
30973094 . await
30983095 . unwrap ( ) ;
30993096
31003097 assert ! (
3101- append. resize( 45 ) . await . is_err( ) ,
3102- "old-slot invalidation should fail"
3098+ append. resize( 40 ) . await . is_err( ) ,
3099+ "old-slot length invalidation should fail"
31033100 ) ;
31043101 assert_eq ! ( write_count. load( Ordering :: SeqCst ) , 3 ) ;
31053102 assert_eq ! (
31063103 failed_write_len. load( Ordering :: SeqCst ) ,
3107- CHECKSUM_SLOT_SIZE - std:: mem:: size_of:: <u16 >( )
3104+ std:: mem:: size_of:: <u16 >( )
31083105 ) ;
31093106 drop ( append) ;
31103107
31113108 let ( blob, size) = context
31123109 . open (
31133110 "test_partition" ,
3114- b"same_page_shrink_interrupted_invalidation " ,
3111+ b"same_page_shrink_interrupted_len_invalidation " ,
31153112 )
31163113 . await
31173114 . unwrap ( ) ;
3118- let append = Append :: new ( blob, size, BUFFER_SIZE , cache_ref)
3115+ let append = Append :: new ( blob, size, LARGE_BUFFER_SIZE , cache_ref)
31193116 . await
31203117 . unwrap ( ) ;
3121- assert_eq ! ( append. size( ) . await , 45 ) ;
3122- let read = append. read_at ( 0 , 45 ) . await . unwrap ( ) . coalesce ( ) ;
3123- assert_eq ! ( read. as_ref( ) , & data[ ..45 ] ) ;
3118+ assert_eq ! ( append. size( ) . await , 40 ) ;
3119+ let read = append. read_at ( 0 , 40 ) . await . unwrap ( ) . coalesce ( ) ;
3120+ assert_eq ! ( read. as_ref( ) , & data[ ..40 ] ) ;
31243121 } ) ;
31253122 }
31263123
0 commit comments