Sort only missing release assets once
This commit is contained in:
@@ -300,7 +300,14 @@ avoidable asymptotic cost without claiming measured release-time savings.
|
||||
|
||||
## 7. Order the missing upload set once — P3
|
||||
|
||||
Status: open.
|
||||
Status: complete.
|
||||
|
||||
**Completed:** Declaration validation now receives an unsorted collection.
|
||||
Classification collects only missing declarations, and that owned worklist is
|
||||
sorted in place once after the no-upload exits. Scheduling now takes
|
||||
O(`n + k log k`) metadata work while retaining filename order and the manifest
|
||||
last. Existing delivery/artifact tests passed, including manifest matching,
|
||||
upload ordering, conflict/unverifiable stops, and partial recovery.
|
||||
|
||||
**Evidence:** `orderedExpected`, `orderMissing`, and `uploadMissingAssets` in
|
||||
[shared asset delivery](../../internal/delivery/remote_assets.go).
|
||||
|
||||
@@ -114,13 +114,17 @@ func remoteAssetDigest(ctx context.Context, backend forge.Backend, handle any) (
|
||||
}
|
||||
|
||||
func uploadMissingAssets(ctx context.Context, backend forge.Backend, releaseHandle any, inspection Inspection) (result UploadResult, err error) {
|
||||
expected := orderedExpected(inspection.Assets)
|
||||
expected := make([]artifact.ExpectedAsset, len(inspection.Assets))
|
||||
for index, observation := range inspection.Assets {
|
||||
expected[index] = observation.Expected
|
||||
}
|
||||
if err := artifact.ValidateExpectedAssets(expected); err != nil {
|
||||
return UploadResult{State: Partial}, &forge.Failure{Kind: forge.FailureInvalid, Err: err}
|
||||
}
|
||||
result = UploadResult{State: Partial}
|
||||
preflightConflict := false
|
||||
preflightUnverifiable := false
|
||||
var missing []artifact.ExpectedAsset
|
||||
pending := make(map[string]struct{}, len(expected))
|
||||
// Keep the initial observation order and compact pending identities once,
|
||||
// including on early returns after partial or unresolved uploads.
|
||||
@@ -141,6 +145,7 @@ func uploadMissingAssets(ctx context.Context, backend forge.Backend, releaseHand
|
||||
case AssetMissing:
|
||||
result.Pending = append(result.Pending, item)
|
||||
pending[observation.Expected.Filename] = struct{}{}
|
||||
missing = append(missing, observation.Expected)
|
||||
case AssetConflicting:
|
||||
preflightConflict = true
|
||||
result.Conflicting = append(result.Conflicting, item)
|
||||
@@ -164,10 +169,8 @@ func uploadMissingAssets(ctx context.Context, backend forge.Backend, releaseHand
|
||||
return result, nil
|
||||
}
|
||||
|
||||
for _, item := range orderMissing(expected) {
|
||||
if _, needsUpload := pending[item.Filename]; !needsUpload {
|
||||
continue
|
||||
}
|
||||
orderMissing(missing)
|
||||
for _, item := range missing {
|
||||
content, err := assetContent(item)
|
||||
if err != nil {
|
||||
return result, &forge.Failure{Kind: forge.FailureInvalid, Err: fmt.Errorf("open local release asset %q: %w", item.Filename, err)}
|
||||
@@ -226,18 +229,16 @@ func reconcileAssetUpload(ctx context.Context, backend forge.Backend, releaseHan
|
||||
}
|
||||
}
|
||||
|
||||
func orderMissing(expected []artifact.ExpectedAsset) []artifact.ExpectedAsset {
|
||||
result := append([]artifact.ExpectedAsset(nil), expected...)
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
if result[i].Filename == artifact.ManifestFilename {
|
||||
func orderMissing(missing []artifact.ExpectedAsset) {
|
||||
sort.Slice(missing, func(i, j int) bool {
|
||||
if missing[i].Filename == artifact.ManifestFilename {
|
||||
return false
|
||||
}
|
||||
if result[j].Filename == artifact.ManifestFilename {
|
||||
if missing[j].Filename == artifact.ManifestFilename {
|
||||
return true
|
||||
}
|
||||
return result[i].Filename < result[j].Filename
|
||||
return missing[i].Filename < missing[j].Filename
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func assetContent(asset artifact.ExpectedAsset) (io.ReadCloser, error) {
|
||||
@@ -247,14 +248,6 @@ func assetContent(asset artifact.ExpectedAsset) (io.ReadCloser, error) {
|
||||
return os.Open(asset.Path)
|
||||
}
|
||||
|
||||
func orderedExpected(observations []AssetInspection) []artifact.ExpectedAsset {
|
||||
result := make([]artifact.ExpectedAsset, 0, len(observations))
|
||||
for _, observation := range observations {
|
||||
result = append(result, observation.Expected)
|
||||
}
|
||||
return orderMissing(result)
|
||||
}
|
||||
|
||||
func identity(asset artifact.ExpectedAsset) AssetIdentity {
|
||||
return AssetIdentity{Filename: asset.Filename, SHA256: asset.SHA256}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user