perf(storage): 拦截任务二进制并本地暂存结果
原因:任务标准结果中的 Base64、Data URI 和 Buffer 会进入 PostgreSQL JSON,导致 TOAST 与备份体积快速增长。 影响:新增统一 JSON 持久化关口;upload_none 将二进制原子写入本地结果目录,数据库仅保存带 SHA-256 的有界占位符;任务详情、同步响应、异步查询和兼容协议按需校验恢复。补充 24 小时清理、容量上限、历史小批量治理命令及管理端说明。 风险:本地结果超过 TTL、丢失或损坏时分别返回明确的 410/500;空间不足时返回 503 且不重试上游。未自动执行历史治理。 验证:三种真实图片模型同步/异步与幂等重放通过;Go vet/全量测试、前端 111 测试、lint/typecheck/build、OpenAPI、迁移安全、govulncheck、依赖审计、手工发布测试及 Linux amd64 构建通过。
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -235,6 +236,47 @@ func TestCleanupExpiredLocalTempAssetsDeletesExpiredStaticFiles(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCleanupExpiredLocalTempAssetsDeletesNestedBinaryResultsWithIndependentTTL(t *testing.T) {
|
||||
generatedDir := t.TempDir()
|
||||
taskDir := filepath.Join(generatedDir, "results", "task-123")
|
||||
if err := os.MkdirAll(taskDir, 0o750); err != nil {
|
||||
t.Fatalf("create local result fixture dir: %v", err)
|
||||
}
|
||||
oldResult := filepath.Join(taskDir, strings.Repeat("a", 64)+".bin")
|
||||
freshResult := filepath.Join(taskDir, strings.Repeat("b", 64)+".bin")
|
||||
for _, path := range []string{oldResult, freshResult} {
|
||||
if err := os.WriteFile(path, []byte("asset"), 0o640); err != nil {
|
||||
t.Fatalf("write fixture %s: %v", path, err)
|
||||
}
|
||||
}
|
||||
now := time.Now()
|
||||
if err := os.Chtimes(oldResult, now.Add(-25*time.Hour), now.Add(-25*time.Hour)); err != nil {
|
||||
t.Fatalf("age old binary result: %v", err)
|
||||
}
|
||||
if err := os.Chtimes(freshResult, now.Add(-23*time.Hour), now.Add(-23*time.Hour)); err != nil {
|
||||
t.Fatalf("age fresh binary result: %v", err)
|
||||
}
|
||||
server := &Server{
|
||||
cfg: config.Config{
|
||||
LocalGeneratedStorageDir: generatedDir,
|
||||
LocalResultTTLHours: 24,
|
||||
},
|
||||
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
||||
}
|
||||
|
||||
deleted := server.cleanupExpiredLocalTempAssets(context.Background(), now)
|
||||
|
||||
if deleted != 1 {
|
||||
t.Fatalf("expected one expired binary result delete, got %d", deleted)
|
||||
}
|
||||
if _, err := os.Stat(oldResult); !os.IsNotExist(err) {
|
||||
t.Fatalf("old binary result should be deleted, stat err=%v", err)
|
||||
}
|
||||
if _, err := os.Stat(freshResult); err != nil {
|
||||
t.Fatalf("fresh binary result should remain: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestConversationKeyPriority(t *testing.T) {
|
||||
request := httptest.NewRequest(http.MethodPost, "/api/v1/chat/completions", nil)
|
||||
request.Header.Set("X-EasyAI-Conversation-ID", "from-header")
|
||||
|
||||
Reference in New Issue
Block a user