fix(metrics): 补齐媒体结果存储读取与兼容响应指标
增加对象存储 GET、结果 URL 签名和同步 Base64 限流指标,使生产轮询可直接验证零对象下载,并区分签名失败、容量等待超时和 20MiB 超限。\n\n验证:API 全量 go test、go vet、gofmt 与 git diff --check。
This commit is contained in:
@@ -2,6 +2,7 @@ package runner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -371,10 +372,16 @@ func TestObjectStorageReadUsesChannelRetryPolicy(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
payload, err := readObjectStorageWithRetries(t.Context(), adapter, "media/request_asset/object.png")
|
||||
var observed []string
|
||||
payload, err := readObjectStorageWithRetriesObserved(t.Context(), adapter, "media/request_asset/object.png", func(event string, provider string, bytes int, _ time.Duration) {
|
||||
observed = append(observed, fmt.Sprintf("%s:%s:%d", event, provider, bytes))
|
||||
})
|
||||
if err != nil || string(payload) != "stored-payload" || calls.Load() != 2 {
|
||||
t.Fatalf("payload=%q calls=%d err=%v", payload, calls.Load(), err)
|
||||
}
|
||||
if got := strings.Join(observed, ","); got != "read_failure:s3:0,read_success:s3:14" {
|
||||
t.Fatalf("observed=%s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestObjectStorageAuthFailureSkipsSameChannelRetry(t *testing.T) {
|
||||
|
||||
@@ -292,7 +292,7 @@ func (s *Service) readRequestAssetBytes(ctx context.Context, asset store.Request
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return readObjectStorageWithRetries(ctx, adapter, asset.ObjectKey)
|
||||
return readObjectStorageWithRetriesObserved(ctx, adapter, asset.ObjectKey, s.observeObjectStorage)
|
||||
}
|
||||
if strings.TrimSpace(asset.LocalPath) != "" {
|
||||
payload, err := os.ReadFile(asset.LocalPath)
|
||||
@@ -331,10 +331,22 @@ func (s *Service) readRequestAssetBytes(ctx context.Context, asset store.Request
|
||||
}
|
||||
|
||||
func readObjectStorageWithRetries(ctx context.Context, adapter *objectStorageAdapter, objectKey string) ([]byte, error) {
|
||||
return readObjectStorageWithRetriesObserved(ctx, adapter, objectKey, nil)
|
||||
}
|
||||
|
||||
func readObjectStorageWithRetriesObserved(ctx context.Context, adapter *objectStorageAdapter, objectKey string, observe func(string, string, int, time.Duration)) ([]byte, error) {
|
||||
maxRetries, delays := uploadRetrySchedule(adapter.channel.RetryPolicy, adapter.channel.Provider)
|
||||
var lastErr error
|
||||
for attempt := 0; attempt <= maxRetries; attempt++ {
|
||||
startedAt := time.Now()
|
||||
payload, err := adapter.get(ctx, objectKey)
|
||||
if observe != nil {
|
||||
event := "read_success"
|
||||
if err != nil {
|
||||
event = "read_failure"
|
||||
}
|
||||
observe(event, adapter.channel.Provider, len(payload), time.Since(startedAt))
|
||||
}
|
||||
if err == nil {
|
||||
return payload, nil
|
||||
}
|
||||
@@ -366,7 +378,13 @@ func (s *Service) requestAssetAccessURL(ctx context.Context, asset store.Request
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
return adapter.presignGet(asset.ObjectKey, objectStorageSignedURLTTL)
|
||||
value, err := adapter.presignGet(asset.ObjectKey, objectStorageSignedURLTTL)
|
||||
if err != nil {
|
||||
s.observeResultURLSigning("failure")
|
||||
return "", err
|
||||
}
|
||||
s.observeResultURLSigning("success")
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (s *Service) fileStorageChannelForAsset(ctx context.Context, asset store.RequestAsset) (store.FileStorageChannel, error) {
|
||||
|
||||
@@ -251,6 +251,13 @@ func (s *Service) observeResultStorage(source string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) observeResultURLSigning(outcome string) {
|
||||
observer, ok := s.billingMetrics.(interface{ ObserveResultURLSigning(string) })
|
||||
if ok {
|
||||
observer.ObserveResultURLSigning(outcome)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Execute(ctx context.Context, task store.GatewayTask, user *auth.User) (Result, error) {
|
||||
return s.execute(ctx, task, user, nil)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user