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
|
## 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
|
**Evidence:** `orderedExpected`, `orderMissing`, and `uploadMissingAssets` in
|
||||||
[shared asset delivery](../../internal/delivery/remote_assets.go).
|
[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) {
|
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 {
|
if err := artifact.ValidateExpectedAssets(expected); err != nil {
|
||||||
return UploadResult{State: Partial}, &forge.Failure{Kind: forge.FailureInvalid, Err: err}
|
return UploadResult{State: Partial}, &forge.Failure{Kind: forge.FailureInvalid, Err: err}
|
||||||
}
|
}
|
||||||
result = UploadResult{State: Partial}
|
result = UploadResult{State: Partial}
|
||||||
preflightConflict := false
|
preflightConflict := false
|
||||||
preflightUnverifiable := false
|
preflightUnverifiable := false
|
||||||
|
var missing []artifact.ExpectedAsset
|
||||||
pending := make(map[string]struct{}, len(expected))
|
pending := make(map[string]struct{}, len(expected))
|
||||||
// Keep the initial observation order and compact pending identities once,
|
// Keep the initial observation order and compact pending identities once,
|
||||||
// including on early returns after partial or unresolved uploads.
|
// including on early returns after partial or unresolved uploads.
|
||||||
@@ -141,6 +145,7 @@ func uploadMissingAssets(ctx context.Context, backend forge.Backend, releaseHand
|
|||||||
case AssetMissing:
|
case AssetMissing:
|
||||||
result.Pending = append(result.Pending, item)
|
result.Pending = append(result.Pending, item)
|
||||||
pending[observation.Expected.Filename] = struct{}{}
|
pending[observation.Expected.Filename] = struct{}{}
|
||||||
|
missing = append(missing, observation.Expected)
|
||||||
case AssetConflicting:
|
case AssetConflicting:
|
||||||
preflightConflict = true
|
preflightConflict = true
|
||||||
result.Conflicting = append(result.Conflicting, item)
|
result.Conflicting = append(result.Conflicting, item)
|
||||||
@@ -164,10 +169,8 @@ func uploadMissingAssets(ctx context.Context, backend forge.Backend, releaseHand
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range orderMissing(expected) {
|
orderMissing(missing)
|
||||||
if _, needsUpload := pending[item.Filename]; !needsUpload {
|
for _, item := range missing {
|
||||||
continue
|
|
||||||
}
|
|
||||||
content, err := assetContent(item)
|
content, err := assetContent(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, &forge.Failure{Kind: forge.FailureInvalid, Err: fmt.Errorf("open local release asset %q: %w", item.Filename, err)}
|
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 {
|
func orderMissing(missing []artifact.ExpectedAsset) {
|
||||||
result := append([]artifact.ExpectedAsset(nil), expected...)
|
sort.Slice(missing, func(i, j int) bool {
|
||||||
sort.Slice(result, func(i, j int) bool {
|
if missing[i].Filename == artifact.ManifestFilename {
|
||||||
if result[i].Filename == artifact.ManifestFilename {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if result[j].Filename == artifact.ManifestFilename {
|
if missing[j].Filename == artifact.ManifestFilename {
|
||||||
return true
|
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) {
|
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)
|
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 {
|
func identity(asset artifact.ExpectedAsset) AssetIdentity {
|
||||||
return AssetIdentity{Filename: asset.Filename, SHA256: asset.SHA256}
|
return AssetIdentity{Filename: asset.Filename, SHA256: asset.SHA256}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user