@@ -445,6 +445,7 @@ impl LogDb {
445445 storage. slate_read ( ) ,
446446 initial_next_sequence,
447447 segment_cache,
448+ initial_next_sequence,
448449 ) ) ) ;
449450
450451 let ( epoch_watcher, durable_sequence_rx, read_subscriber_task) = spawn_subscriber (
@@ -2818,6 +2819,78 @@ mod tests {
28182819 assert_eq ! ( log. count( Bytes :: from( "k" ) , 7 ..) . await . unwrap( ) , 3 ) ;
28192820 }
28202821
2822+ // ---- next_sequence (resumable scan) tests ----
2823+
2824+ #[ tokio:: test]
2825+ async fn next_sequence_lifts_idle_key_to_global_frontier ( ) {
2826+ // Writes land only on a hot key. A drained scan of an idle key should
2827+ // still resume at the global frontier — the whole point of the
2828+ // cross-key lift — rather than at 0.
2829+ let log = LogDb :: open ( test_config ( ) ) . await . unwrap ( ) ;
2830+ log. try_append ( vec ! [
2831+ Record {
2832+ key: Bytes :: from( "hot" ) ,
2833+ value: Bytes :: from( "v0" ) ,
2834+ } ,
2835+ Record {
2836+ key: Bytes :: from( "hot" ) ,
2837+ value: Bytes :: from( "v1" ) ,
2838+ } ,
2839+ Record {
2840+ key: Bytes :: from( "hot" ) ,
2841+ value: Bytes :: from( "v2" ) ,
2842+ } ,
2843+ ] )
2844+ . await
2845+ . unwrap ( ) ;
2846+
2847+ // `scan` runs `sync_reads`, so the read view (and its frontier) reflects
2848+ // the appends above before the iterator is built.
2849+ let mut iter = log. scan ( Bytes :: from ( "cold" ) , ..) . await . unwrap ( ) ;
2850+ assert ! (
2851+ iter. next( ) . await . unwrap( ) . is_none( ) ,
2852+ "the cold key has no records"
2853+ ) ;
2854+ assert_eq ! (
2855+ iter. next_sequence( ) ,
2856+ 3 ,
2857+ "idle-key scan resumes at the frontier set by the hot key's writes"
2858+ ) ;
2859+ }
2860+
2861+ #[ tokio:: test]
2862+ async fn next_sequence_resumes_past_consumed_tail ( ) {
2863+ let log = LogDb :: open ( test_config ( ) ) . await . unwrap ( ) ;
2864+ log. try_append ( vec ! [
2865+ Record {
2866+ key: Bytes :: from( "k" ) ,
2867+ value: Bytes :: from( "v0" ) ,
2868+ } ,
2869+ Record {
2870+ key: Bytes :: from( "k" ) ,
2871+ value: Bytes :: from( "v1" ) ,
2872+ } ,
2873+ ] )
2874+ . await
2875+ . unwrap ( ) ;
2876+
2877+ let mut iter = log. scan ( Bytes :: from ( "k" ) , ..) . await . unwrap ( ) ;
2878+ let mut last = None ;
2879+ while let Some ( e) = iter. next ( ) . await . unwrap ( ) {
2880+ last = Some ( e. sequence ) ;
2881+ }
2882+ assert_eq ! ( last, Some ( 1 ) ) ;
2883+ // Drained: resume one past the last record, at the frontier.
2884+ let resume = iter. next_sequence ( ) ;
2885+ assert_eq ! ( resume, 2 ) ;
2886+
2887+ // Resuming from there sees nothing new and reports the same frontier —
2888+ // the idle-poll steady state.
2889+ let mut iter2 = log. scan ( Bytes :: from ( "k" ) , resume..) . await . unwrap ( ) ;
2890+ assert ! ( iter2. next( ) . await . unwrap( ) . is_none( ) ) ;
2891+ assert_eq ! ( iter2. next_sequence( ) , 2 ) ;
2892+ }
2893+
28212894 // ---- durable_sequence tests ----
28222895
28232896 #[ tokio:: test]
0 commit comments