fix(acceptance): 解除八任务准入瓶颈
线上 P24 验收证明 Worker 注册容量为 48,但异步 admission 始终只有 8 条活跃租约。将单事务调度窗口覆盖当前 2 x P32 的 64 槽,避免多 Worker 在同一 FIFO 小批次上竞争形成隐性全局上限。\n\nGemini 流式请求补充可回放 GetBody 与精确 Content-Length,使带幂等键的 POST 遇到 HTTP/2 可重试传输错误时可安全重放;同时提前部署验收回调收集器,保证旧 Run side effects 能在接管前收口。\n\n已通过 Go 全量测试、go vet、gofmt、Shell bash -n、ShellCheck、验收与人工发布脚本测试、迁移安全检查,以及独立 PostgreSQL 48 条 admission/租约/River job 原子集成测试。
This commit is contained in:
@@ -37,6 +37,11 @@ const (
|
||||
upstreamHeader = "X-EasyAI-Acceptance-Upstream"
|
||||
)
|
||||
|
||||
const (
|
||||
geminiRequestPrefix = `{"contents":[{"role":"user","parts":[{"text":"将参考图编辑为蓝色赛博朋克风格,保留主体结构"},{"inlineData":{"mimeType":"image/png","data":"`
|
||||
geminiRequestSuffix = `"}}]}],"generationConfig":{"responseModalities":["IMAGE"]}}`
|
||||
)
|
||||
|
||||
var reportURLPattern = regexp.MustCompile(`(?i)(?:https?|postgres(?:ql)?):\/\/[^\s"'<>]+`)
|
||||
|
||||
type options struct {
|
||||
@@ -407,16 +412,8 @@ func runGemini(
|
||||
defer func() { <-slots }()
|
||||
requestStarted := time.Now()
|
||||
endpoint := opts.gateways[logicalIndex%len(opts.gateways)] + "/v1beta/models/" + url.PathEscape(opts.geminiModel) + ":generateContent"
|
||||
requestBody := streamGeminiRequestBody(paddedPNGVariant(inputBytes, geminiInputVariant(opts.runID+"/"+opts.executionID, logicalIndex)))
|
||||
req, err := http.NewRequestWithContext(
|
||||
ctx,
|
||||
http.MethodPost,
|
||||
endpoint,
|
||||
requestBody,
|
||||
)
|
||||
if err != nil {
|
||||
_ = requestBody.Close()
|
||||
}
|
||||
input := paddedPNGVariant(inputBytes, geminiInputVariant(opts.runID+"/"+opts.executionID, logicalIndex))
|
||||
req, err := newGeminiRequest(ctx, endpoint, input)
|
||||
if err == nil {
|
||||
opts.setHeaders(req, logicalIndex, realUpstream)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
@@ -481,7 +478,7 @@ func streamGeminiRequestBody(input []byte) io.ReadCloser {
|
||||
closeWithError := func(err error) {
|
||||
_ = writer.CloseWithError(err)
|
||||
}
|
||||
if _, err := io.WriteString(writer, `{"contents":[{"role":"user","parts":[{"text":"将参考图编辑为蓝色赛博朋克风格,保留主体结构"},{"inlineData":{"mimeType":"image/png","data":"`); err != nil {
|
||||
if _, err := io.WriteString(writer, geminiRequestPrefix); err != nil {
|
||||
closeWithError(err)
|
||||
return
|
||||
}
|
||||
@@ -494,7 +491,7 @@ func streamGeminiRequestBody(input []byte) io.ReadCloser {
|
||||
closeWithError(err)
|
||||
return
|
||||
}
|
||||
if _, err := io.WriteString(writer, `"}}]}],"generationConfig":{"responseModalities":["IMAGE"]}}`); err != nil {
|
||||
if _, err := io.WriteString(writer, geminiRequestSuffix); err != nil {
|
||||
closeWithError(err)
|
||||
return
|
||||
}
|
||||
@@ -503,6 +500,21 @@ func streamGeminiRequestBody(input []byte) io.ReadCloser {
|
||||
return reader
|
||||
}
|
||||
|
||||
func newGeminiRequest(ctx context.Context, endpoint string, input []byte) (*http.Request, error) {
|
||||
getBody := func() (io.ReadCloser, error) {
|
||||
return streamGeminiRequestBody(input), nil
|
||||
}
|
||||
body, _ := getBody()
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, body)
|
||||
if err != nil {
|
||||
_ = body.Close()
|
||||
return nil, err
|
||||
}
|
||||
request.GetBody = getBody
|
||||
request.ContentLength = int64(len(geminiRequestPrefix) + base64.StdEncoding.EncodedLen(len(input)) + len(geminiRequestSuffix))
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func runVideo(
|
||||
ctx context.Context,
|
||||
client *http.Client,
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -82,6 +83,37 @@ func TestStreamGeminiRequestBodyPreservesInput(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeminiRequestBodyCanBeReplayedAfterHTTP2Failure(t *testing.T) {
|
||||
input := paddedPNGVariant(256<<10, 91)
|
||||
request, err := newGeminiRequest(t.Context(), "https://gateway.example/v1beta/models/test:generateContent", input)
|
||||
if err != nil {
|
||||
t.Fatalf("new Gemini request: %v", err)
|
||||
}
|
||||
if request.GetBody == nil {
|
||||
t.Fatal("streaming Gemini request does not provide GetBody")
|
||||
}
|
||||
first, err := io.ReadAll(request.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("read first request body: %v", err)
|
||||
}
|
||||
_ = request.Body.Close()
|
||||
replayedBody, err := request.GetBody()
|
||||
if err != nil {
|
||||
t.Fatalf("replay Gemini request body: %v", err)
|
||||
}
|
||||
replayed, err := io.ReadAll(replayedBody)
|
||||
if err != nil {
|
||||
t.Fatalf("read replayed request body: %v", err)
|
||||
}
|
||||
_ = replayedBody.Close()
|
||||
if !bytes.Equal(first, replayed) {
|
||||
t.Fatal("replayed Gemini request body differs from the original")
|
||||
}
|
||||
if request.ContentLength != int64(len(first)) {
|
||||
t.Fatalf("content length=%d, want %d", request.ContentLength, len(first))
|
||||
}
|
||||
}
|
||||
|
||||
func TestVideoCombinationsProvide128UniqueInputs(t *testing.T) {
|
||||
images := make([]string, 16)
|
||||
for index := range images {
|
||||
|
||||
Reference in New Issue
Block a user