@@ -45,7 +45,14 @@ pub struct EcrState {
4545 pub account_settings : BTreeMap < String , String > ,
4646 /// Layer upload state machine keyed by `uploadId`. Each entry is
4747 /// tied to a specific repository.
48- #[ serde( default ) ]
48+ ///
49+ /// NOT persisted: the per-upload spool file lives under the system temp
50+ /// dir (`spool_path`) and never survives a restart, so persisting the
51+ /// upload metadata alone would leave orphan entries whose next
52+ /// `UploadLayerPart` opens a missing spool and 500s. Dropping in-flight
53+ /// uploads on restart keeps the map consistent with the (gone) spools; a
54+ /// stale client simply re-initiates, matching ECR's finite upload lifetime.
55+ #[ serde( skip) ]
4956 pub layer_uploads : BTreeMap < String , LayerUpload > ,
5057 /// Pull-time update exclusions keyed by IAM principal ARN. These
5158 /// are registry-level per the Smithy model.
@@ -411,3 +418,39 @@ pub struct ReplicationDestination {
411418 pub region : String ,
412419 pub registry_id : String ,
413420}
421+
422+ #[ cfg( test) ]
423+ mod tests {
424+ use super :: * ;
425+
426+ #[ test]
427+ fn layer_uploads_are_not_persisted_across_snapshot ( ) {
428+ // In-progress uploads spool to the system temp dir, which does not
429+ // survive a restart. Persisting the upload metadata alone would leave
430+ // an orphan entry whose next `UploadLayerPart` opens a missing spool
431+ // and 500s (bug-hunt 4.2), so `layer_uploads` is `#[serde(skip)]`.
432+ let mut state = EcrState :: new ( "123456789012" , "us-east-1" ) ;
433+ state. layer_uploads . insert (
434+ "upload-1" . to_string ( ) ,
435+ LayerUpload {
436+ upload_id : "upload-1" . to_string ( ) ,
437+ repository_name : "repo" . to_string ( ) ,
438+ created_at : Utc :: now ( ) ,
439+ spool_path : "/tmp/fakecloud-ecr-upload-upload-1" . to_string ( ) ,
440+ last_byte_received : 42 ,
441+ } ,
442+ ) ;
443+
444+ let json = serde_json:: to_string ( & state) . unwrap ( ) ;
445+ assert ! (
446+ !json. contains( "upload-1" ) ,
447+ "in-progress uploads must not be serialized"
448+ ) ;
449+
450+ let restored: EcrState = serde_json:: from_str ( & json) . unwrap ( ) ;
451+ assert ! (
452+ restored. layer_uploads. is_empty( ) ,
453+ "in-progress uploads must not survive a restart"
454+ ) ;
455+ }
456+ }
0 commit comments