@@ -47,7 +47,7 @@ use crate::backend::common::{
4747 Backend , DeleteResponse , GetResponse , HighVolumeBackend , MetadataResponse , PutResponse ,
4848 TieredGet , TieredMetadata , TieredWrite , Tombstone ,
4949} ;
50- use crate :: error:: { Error , Result } ;
50+ use crate :: error:: { Error , ErrorKind , RangeNotSatisfiableError , Result , ResultExt } ;
5151use crate :: gcp_auth:: PrefetchingTokenProvider ;
5252use crate :: id:: ObjectId ;
5353use crate :: stream:: { ChunkedBytes , ClientStream } ;
@@ -466,8 +466,7 @@ fn object_mutations(mut metadata: Metadata, payload: Vec<u8>) -> Result<[v2::Mut
466466 // Record the payload size in the metadata before persisting it.
467467 metadata. size = Some ( payload. len ( ) ) ;
468468
469- let metadata_bytes = serde_json:: to_vec ( & metadata)
470- . map_err ( |cause| Error :: serde ( "failed to serialize metadata" , cause) ) ?;
469+ let metadata_bytes = serde_json:: to_vec ( & metadata) . context ( "failed to serialize metadata" ) ?;
471470
472471 Ok ( [
473472 // NB: We explicitly delete the row to clear metadata on overwrite.
@@ -528,8 +527,7 @@ fn tombstone_mutations(tombstone: &Tombstone, now: SystemTime) -> Result<[v2::Mu
528527 family_name : family. to_owned ( ) ,
529528 column_qualifier : COLUMN_TOMBSTONE_META . to_owned ( ) ,
530529 timestamp_micros,
531- value : serde_json:: to_vec ( & tombstone_meta)
532- . map_err ( |cause| Error :: serde ( "failed to serialize tombstone" , cause) ) ?,
530+ value : serde_json:: to_vec ( & tombstone_meta) . context ( "failed to serialize tombstone" ) ?,
533531 } ) ) ,
534532 ] )
535533}
@@ -601,10 +599,10 @@ impl RowData {
601599 payload = cell. value ;
602600 }
603601 COLUMN_TOMBSTONE_META => {
604- tombstone_meta_opt =
605- Some ( serde_json:: from_slice ( & cell. value ) . map_err ( |cause| {
606- Error :: serde ( "failed to deserialize tombstone meta" , cause )
607- } ) ? ) ;
602+ tombstone_meta_opt = Some (
603+ serde_json:: from_slice ( & cell. value )
604+ . context ( "failed to deserialize tombstone meta" ) ? ,
605+ ) ;
608606 }
609607 COLUMN_METADATA => {
610608 if let Ok ( legacy_meta) =
@@ -617,10 +615,10 @@ impl RowData {
617615 expiration_policy : legacy_meta. expiration_policy ,
618616 } ) ;
619617 } else {
620- metadata_opt =
621- Some ( serde_json:: from_slice ( & cell. value ) . map_err ( |cause| {
622- Error :: serde ( "failed to deserialize metadata" , cause )
623- } ) ? ) ;
618+ metadata_opt = Some (
619+ serde_json:: from_slice ( & cell. value )
620+ . context ( "failed to deserialize metadata" ) ? ,
621+ ) ;
624622 }
625623 }
626624 _ => { }
@@ -683,10 +681,11 @@ fn parse_redirect_target(redirect_path: &[u8], tombstone_id: &ObjectId) -> Resul
683681 objectstore_metrics:: count!( "bigtable.empty_redirect_read" ) ;
684682 Ok ( tombstone_id. clone ( ) )
685683 } else {
686- let redirect_str = std:: str:: from_utf8 ( redirect_path)
687- . map_err ( |_| Error :: generic ( "invalid UTF-8 in redirect path" ) ) ?;
684+ let redirect_str = std:: str:: from_utf8 ( redirect_path) . map_err ( |_| {
685+ Error :: new ( ErrorKind :: Internal ) . context ( "invalid UTF-8 in redirect path" )
686+ } ) ?;
688687 ObjectId :: from_storage_path ( redirect_str)
689- . ok_or_else ( || Error :: generic ( "corrupt redirect path" ) )
688+ . ok_or_else ( || Error :: new ( ErrorKind :: Internal ) . context ( "corrupt redirect path" ) )
690689 }
691690}
692691
@@ -930,7 +929,9 @@ impl Backend for BigTableBackend {
930929 TieredGet :: Object ( metadata, content_range, payload) => {
931930 Ok ( Some ( ( metadata, content_range, payload) ) )
932931 }
933- TieredGet :: Tombstone ( _) => Err ( Error :: UnexpectedTombstone ) ,
932+ TieredGet :: Tombstone ( _) => {
933+ Err ( Error :: new ( ErrorKind :: Internal ) . context ( "unexpected tombstone" ) )
934+ }
934935 TieredGet :: NotFound => Ok ( None ) ,
935936 }
936937 }
@@ -939,7 +940,9 @@ impl Backend for BigTableBackend {
939940 async fn get_metadata ( & self , id : & ObjectId ) -> Result < MetadataResponse > {
940941 match self . get_tiered_metadata ( id) . await ? {
941942 TieredMetadata :: Object ( metadata) => Ok ( Some ( metadata) ) ,
942- TieredMetadata :: Tombstone ( _) => Err ( Error :: UnexpectedTombstone ) ,
943+ TieredMetadata :: Tombstone ( _) => {
944+ Err ( Error :: new ( ErrorKind :: Internal ) . context ( "unexpected tombstone" ) )
945+ }
943946 TieredMetadata :: NotFound => Ok ( None ) ,
944947 }
945948 }
@@ -1002,7 +1005,7 @@ impl HighVolumeBackend for BigTableBackend {
10021005 }
10031006 }
10041007
1005- Err ( Error :: generic ( "BigTable: race loop in put_non_tombstone" ) )
1008+ Err ( Error :: new ( ErrorKind :: Internal ) . context ( "BigTable: race loop in put_non_tombstone" ) )
10061009 }
10071010
10081011 #[ tracing:: instrument( level = "debug" , skip( self ) ) ]
@@ -1110,9 +1113,7 @@ impl HighVolumeBackend for BigTableBackend {
11101113 }
11111114 }
11121115
1113- Err ( Error :: generic (
1114- "BigTable: race loop in delete_non_tombstone" ,
1115- ) )
1116+ Err ( Error :: new ( ErrorKind :: Internal ) . context ( "BigTable: race loop in delete_non_tombstone" ) )
11161117 }
11171118
11181119 #[ tracing:: instrument( level = "debug" , skip( self , write) ) ]
@@ -1151,13 +1152,12 @@ impl HighVolumeBackend for BigTableBackend {
11511152/// required by BigTable, the resulting timestamp has millisecond precision, with the last digits at
11521153/// 0.
11531154fn ttl_to_micros ( ttl : Duration , from : SystemTime ) -> Result < i64 > {
1154- let deadline = from. checked_add ( ttl) . ok_or_else ( || Error :: Generic {
1155- context : format ! (
1155+ let deadline = from. checked_add ( ttl) . ok_or_else ( || {
1156+ Error :: new ( ErrorKind :: Internal ) . context ( format ! (
11561157 "TTL duration overflow: {} plus {}s cannot be represented as SystemTime" ,
11571158 humantime:: format_rfc3339_seconds( from) ,
11581159 ttl. as_secs( )
1159- ) ,
1160- cause : None ,
1160+ ) )
11611161 } ) ?;
11621162
11631163 system_time_to_micros ( deadline)
@@ -1170,19 +1170,15 @@ fn ttl_to_micros(ttl: Duration, from: SystemTime) -> Result<i64> {
11701170fn system_time_to_micros ( deadline : SystemTime ) -> Result < i64 > {
11711171 let millis = deadline
11721172 . duration_since ( SystemTime :: UNIX_EPOCH )
1173- . map_err ( |e| Error :: Generic {
1174- context : format ! (
1175- "unable to get duration since UNIX_EPOCH for SystemTime {}" ,
1176- humantime:: format_rfc3339_seconds( deadline)
1177- ) ,
1178- cause : Some ( Box :: new ( e) ) ,
1179- } ) ?
1173+ . context ( format ! (
1174+ "unable to get duration since UNIX_EPOCH for SystemTime {}" ,
1175+ humantime:: format_rfc3339_seconds( deadline)
1176+ ) ) ?
11801177 . as_millis ( ) ;
11811178
1182- ( millis * 1000 ) . try_into ( ) . map_err ( |e| Error :: Generic {
1183- context : format ! ( "failed to convert {millis}ms to i64 microseconds" ) ,
1184- cause : Some ( Box :: new ( e) ) ,
1185- } )
1179+ ( millis * 1000 )
1180+ . try_into ( )
1181+ . context ( format ! ( "failed to convert {millis}ms to i64 microseconds" ) )
11861182}
11871183
11881184/// Converts a wall-clock time to Bigtable's microsecond timestamp, saturating at `i64::MAX`
@@ -1233,10 +1229,7 @@ where
12331229 Ok ( res) => return Ok ( res) ,
12341230 Err ( e) if retry_count >= REQUEST_RETRY_COUNT || !is_retryable ( & e) => {
12351231 objectstore_metrics:: count!( "bigtable.failures" , action = context) ;
1236- return Err ( Error :: Generic {
1237- context : format ! ( "Bigtable: `{context}` failed" ) ,
1238- cause : Some ( Box :: new ( e) ) ,
1239- } ) ;
1232+ return Err ( Error :: from ( e) . context ( format ! ( "Bigtable: `{context}` failed" ) ) ) ;
12401233 }
12411234 Err ( e) => {
12421235 retry_count += 1 ;
@@ -1290,7 +1283,7 @@ fn apply_range(payload: Bytes, range: Option<ByteRange>) -> Result<(Option<Conte
12901283 let total = payload. len ( ) as u64 ;
12911284 let content_range = byte_range
12921285 . resolve ( total)
1293- . ok_or ( Error :: RangeNotSatisfiable { total } ) ?;
1286+ . ok_or_else ( || Error :: from ( RangeNotSatisfiableError { total } ) ) ?;
12941287
12951288 let sliced = payload. slice ( content_range. start as usize ..content_range. end as usize + 1 ) ;
12961289 Ok ( ( Some ( content_range) , sliced) )
@@ -1865,11 +1858,11 @@ mod tests {
18651858 // Legacy reads must error rather than leak tombstone data.
18661859 assert ! ( matches!(
18671860 backend. get_object( & hv_id, None ) . await ,
1868- Err ( Error :: UnexpectedTombstone )
1861+ Err ( e ) if e . kind ( ) == ErrorKind :: Internal
18691862 ) ) ;
18701863 assert ! ( matches!(
18711864 backend. get_metadata( & hv_id) . await ,
1872- Err ( Error :: UnexpectedTombstone )
1865+ Err ( e ) if e . kind ( ) == ErrorKind :: Internal
18731866 ) ) ;
18741867
18751868 // Idempotent retry: retry with the same target succeeds
@@ -2345,7 +2338,13 @@ mod tests {
23452338 let id = put_range_test_object ( & backend) . await ?;
23462339
23472340 match backend. get_object ( & id, Some ( ByteRange :: From ( 100 ) ) ) . await {
2348- Err ( Error :: RangeNotSatisfiable { total } ) => assert_eq ! ( total, 22 ) ,
2341+ Err ( err) if err. kind ( ) == ErrorKind :: RangeNotSatisfiable => {
2342+ let total = err
2343+ . source ( )
2344+ . and_then ( |s| s. downcast_ref :: < crate :: error:: RangeNotSatisfiableError > ( ) )
2345+ . map ( |e| e. total ) ;
2346+ assert_eq ! ( total, Some ( 22 ) ) ;
2347+ }
23492348 Ok ( _) => panic ! ( "expected RangeNotSatisfiable, got Ok" ) ,
23502349 Err ( e) => panic ! ( "expected RangeNotSatisfiable, got {e:?}" ) ,
23512350 }
0 commit comments