@@ -13,11 +13,13 @@ use aws_sdk_s3::operation::create_bucket::CreateBucketError;
1313use aws_sdk_s3:: primitives:: { ByteStream , SdkBody } ;
1414use aws_sdk_s3:: types:: CreateBucketConfiguration ;
1515use aws_sdk_s3:: Client ;
16+ use aws_smithy_types_convert:: date_time:: DateTimeExt ;
1617use bytes:: { Bytes , BytesMut } ;
1718use http_body:: { Frame as HttpFrame , SizeHint } ;
1819use libsql_sys:: name:: NamespaceName ;
1920use roaring:: RoaringBitmap ;
2021use tokio:: io:: { AsyncBufReadExt , AsyncRead , AsyncReadExt , BufReader } ;
22+ use tokio_stream:: Stream ;
2123use tokio_util:: sync:: ReusableBoxFuture ;
2224use zerocopy:: byteorder:: little_endian:: { U16 as lu16, U32 as lu32, U64 as lu64} ;
2325use zerocopy:: { AsBytes , FromBytes , FromZeroes } ;
@@ -28,7 +30,7 @@ use crate::io::compat::copy_to_file;
2830use crate :: io:: { FileExt , Io , StdIO } ;
2931use crate :: segment:: compacted:: CompactedSegmentDataHeader ;
3032use crate :: segment:: Frame ;
31- use crate :: storage:: { Error , RestoreOptions , Result , SegmentKey } ;
33+ use crate :: storage:: { Error , RestoreOptions , Result , SegmentInfo , SegmentKey } ;
3234use crate :: LIBSQL_MAGIC ;
3335
3436pub struct S3Backend < IO > {
@@ -306,6 +308,52 @@ impl<IO: Io> S3Backend<IO> {
306308
307309 Ok ( index)
308310 }
311+
312+ fn list_segments_inner < ' a > (
313+ & ' a self ,
314+ config : Arc < S3Config > ,
315+ namespace : & ' a NamespaceName ,
316+ _until : u64 ,
317+ ) -> impl Stream < Item = Result < SegmentInfo > > + ' a {
318+ async_stream:: try_stream! {
319+ let folder_key = FolderKey { cluster_id: & config. cluster_id, namespace } ;
320+ let lookup_key_prefix = s3_segment_index_lookup_key_prefix( & folder_key) ;
321+
322+ let mut continuation_token = None ;
323+ loop {
324+ let objects = self
325+ . client
326+ . list_objects_v2( )
327+ . bucket( & config. bucket)
328+ . prefix( lookup_key_prefix. clone( ) )
329+ . set_continuation_token( continuation_token. take( ) )
330+ . send( )
331+ . await
332+ . map_err( |e| Error :: unhandled( e, "failed to list bucket" ) ) ?;
333+
334+ for entry in objects. contents( ) {
335+ let key = entry. key( ) . expect( "misssing key?" ) ;
336+ let key_path: & Path = key. as_ref( ) ;
337+ let Some ( key) = SegmentKey :: validate_from_path( key_path, & folder_key. namespace) else { continue } ;
338+
339+ let infos = SegmentInfo {
340+ key,
341+ size: entry. size( ) . unwrap_or( 0 ) as usize ,
342+ created_at: entry. last_modified( ) . unwrap( ) . to_chrono_utc( ) . unwrap( ) ,
343+ } ;
344+
345+ yield infos;
346+ }
347+
348+ if objects. is_truncated( ) . unwrap_or( false ) {
349+ assert!( objects. next_continuation_token. is_some( ) ) ;
350+ continuation_token = objects. next_continuation_token;
351+ } else {
352+ break
353+ }
354+ }
355+ }
356+ }
309357}
310358
311359pub struct S3Config {
@@ -514,6 +562,15 @@ where
514562 . await ?;
515563 Ok ( file)
516564 }
565+
566+ fn list_segments < ' a > (
567+ & ' a self ,
568+ config : Self :: Config ,
569+ namespace : & ' a NamespaceName ,
570+ until : u64 ,
571+ ) -> impl Stream < Item = Result < SegmentInfo > > + ' a {
572+ self . list_segments_inner ( config, namespace, until)
573+ }
517574}
518575
519576#[ derive( Clone , Copy ) ]
0 commit comments