fix(tasks): 任务队列 - 限制缓存回源并修复并发租约与过期写入
This commit is contained in:
@@ -174,7 +174,7 @@ func RegisterTaskRoutes(r *gin.RouterGroup, svc *service.Service) {
|
||||
fail(c, http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
initial, err := svc.TaskTextReplay(user.ID, c.Param("id"), after)
|
||||
initial, err := svc.CachedTaskTextReplay(c.Request.Context(), user.ID, c.Param("id"), after)
|
||||
if err != nil {
|
||||
failService(c, err)
|
||||
return
|
||||
@@ -308,7 +308,7 @@ func streamTaskTextEvents(c *gin.Context, svc *service.Service, userID string, t
|
||||
c.Writer.Flush()
|
||||
case <-pollTicker.C:
|
||||
}
|
||||
next, err := svc.TaskTextReplay(userID, taskID, after)
|
||||
next, err := svc.CachedTaskTextReplay(c.Request.Context(), userID, taskID, after)
|
||||
if err != nil {
|
||||
writeTaskTextSSE(c, "error", 0, map[string]string{"message": "任务文本流不可用"})
|
||||
return
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"infinite-canvas/backend/internal/model"
|
||||
"infinite-canvas/backend/internal/repository"
|
||||
"infinite-canvas/backend/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func TestTaskTextEventCursorPrefersQueryAndSupportsLastEventID(t *testing.T) {
|
||||
@@ -32,6 +37,42 @@ func TestTaskTextEventCursorPrefersQueryAndSupportsLastEventID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamTaskTextEventsCachedReplayReachesTerminalWithoutDuplicateDelta(t *testing.T) {
|
||||
t.Setenv("REDIS_URL", "")
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.AutoMigrate(&model.Task{}, &model.TaskTextDelta{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Create(&model.Task{ID: "task", UserID: "user", Status: model.TaskStatusRunning}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Create(&model.TaskTextDelta{ID: "delta", UserID: "user", TaskID: "task", Sequence: 8, Content: "text", ExpiresAt: time.Now().Add(time.Hour)}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
svc := service.New(repository.New(db), t.TempDir())
|
||||
t.Cleanup(func() { _ = svc.Close() })
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
|
||||
defer cancel()
|
||||
initial, err := svc.CachedTaskTextReplay(ctx, "user", "task", 7)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Model(&model.Task{}).Where("id = ?", "task").Updates(map[string]any{"status": model.TaskStatusSucceeded, "progress": 100}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
response := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(response)
|
||||
c.Request = httptest.NewRequest("GET", "/api/tasks/task/text-events", nil).WithContext(ctx)
|
||||
streamTaskTextEvents(c, svc, "user", "task", 7, initial)
|
||||
body := response.Body.String()
|
||||
if strings.Count(body, "id: 8\nevent: delta") != 1 || !strings.Contains(body, "event: terminal") || !strings.Contains(body, `"progress":100`) {
|
||||
t.Fatalf("unexpected cached SSE stream: %q", body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamTaskTextEventsWritesSequenceAndTerminalEvent(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
Reference in New Issue
Block a user