fix(release): 修复发布一致性与并发边界
bat-rust / Build and test Rust (push) Canceled after 0s
bat-rust / Build and test Go API (push) Canceled after 0s

This commit is contained in:
2026-09-12 16:35:55 +08:00
parent 8d57a63697
commit 30d1cd77e8
20 changed files with 1100 additions and 102 deletions
+6 -2
View File
@@ -33,7 +33,7 @@ func (s *Server) serveCDN(w http.ResponseWriter, r *http.Request) {
http.Error(w, selectorErr.Error(), http.StatusBadRequest)
return
}
selected, selectErr := s.loadReleaseDistribution(r, channel, releaseID)
selected, selectErr := s.loadReleaseDistribution(r, channel, releaseID, rel)
if selectErr != nil {
http.Error(w, selectErr.Error(), http.StatusServiceUnavailable)
return
@@ -72,7 +72,11 @@ func (s *Server) serveCDN(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
return
}
if (explicitRelease || s.cfg.RequireIndexed) && s.cfg.VerifySize {
if explicitRelease && hasEntry && uint64(info.Size()) != entry.Bytes {
http.Error(w, "size mismatch with release index", http.StatusConflict)
return
}
if !explicitRelease && s.cfg.RequireIndexed && s.cfg.VerifySize {
if hasEntry && entry.Bytes > 0 && uint64(info.Size()) != entry.Bytes {
http.Error(w, "size mismatch with release index", http.StatusConflict)
return
+5
View File
@@ -107,6 +107,11 @@ paths:
in: query
schema:
type: string
- name: destination
in: query
description: Optional release-relative path whose localized bytes and BLAKE3 are revalidated.
schema:
type: string
- name: offset
in: query
schema:
+14 -9
View File
@@ -139,12 +139,13 @@ func (e *releaseSelectorError) Error() string {
return e.message
}
func (s *Server) loadReleaseDistribution(r *http.Request, channel, releaseID string) (*backendrpc.ReleaseDistributionPage, error) {
func (s *Server) loadReleaseDistribution(r *http.Request, channel, releaseID, destination string) (*backendrpc.ReleaseDistributionPage, error) {
return s.loadReleaseDistributionFrom(r, backendrpc.ReleaseDistributionParams{
Channel: channel,
ReleaseID: releaseID,
Offset: 0,
Limit: 1000,
Channel: channel,
ReleaseID: releaseID,
Destination: destination,
Offset: 0,
Limit: 1000,
})
}
@@ -154,6 +155,9 @@ func releaseDistributionParams(r *http.Request, channel, releaseID string) (back
ReleaseID: releaseID,
}
query := r.URL.Query()
if destination := strings.TrimSpace(query.Get("destination")); destination != "" {
params.Destination = destination
}
if raw := strings.TrimSpace(query.Get("offset")); raw != "" {
offset, err := strconv.ParseUint(raw, 10, 64)
if err != nil || uint64(int(^uint(0)>>1)) < offset {
@@ -196,10 +200,11 @@ func (s *Server) loadReleaseDistributionFrom(r *http.Request, params backendrpc.
all := append([]backendrpc.ReleaseDistributionEntry(nil), result.Entries...)
for offset := len(all); offset < result.Total; {
next, nextErr := backend.ReleaseDistribution(r.Context(), backendrpc.ReleaseDistributionParams{
Channel: params.Channel,
ReleaseID: params.ReleaseID,
Offset: offset,
Limit: pageSize,
Channel: params.Channel,
ReleaseID: params.ReleaseID,
Destination: params.Destination,
Offset: offset,
Limit: pageSize,
})
if nextErr != nil {
return nil, nextErr
+2 -1
View File
@@ -101,13 +101,14 @@ func TestReleaseHTTPForwardsTypedSelectionAndDoesNotFallback(t *testing.T) {
}
recorder = httptest.NewRecorder()
server.Handler().ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/v1/distribution?channel=localized&release_id=localized-1&offset=2&limit=10", nil))
server.Handler().ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/v1/distribution?channel=localized&release_id=localized-1&destination=TableBundles%2FTableCatalog.bytes&offset=2&limit=10", nil))
if recorder.Code != http.StatusOK {
t.Fatalf("distribution status=%d body=%s", recorder.Code, recorder.Body.String())
}
if len(backend.distributionParams) != 1 ||
backend.distributionParams[0].Channel != "localized" ||
backend.distributionParams[0].ReleaseID != "localized-1" ||
backend.distributionParams[0].Destination != "TableBundles/TableCatalog.bytes" ||
backend.distributionParams[0].Offset != 2 ||
backend.distributionParams[0].Limit != 10 {
t.Fatalf("distribution params=%#v", backend.distributionParams)
+5 -4
View File
@@ -189,10 +189,11 @@ type ReleaseListParams struct {
// ReleaseDistributionParams selects a verified release for distribution.
type ReleaseDistributionParams struct {
Channel string `json:"channel,omitempty"`
ReleaseID string `json:"release_id,omitempty"`
Offset int `json:"offset,omitempty"`
Limit int `json:"limit,omitempty"`
Channel string `json:"channel,omitempty"`
ReleaseID string `json:"release_id,omitempty"`
Offset int `json:"offset,omitempty"`
Limit int `json:"limit,omitempty"`
Destination string `json:"destination,omitempty"`
}
// ReleaseCleanupParams controls the dry-run/execute cleanup pair.