fix(tasks): 任务队列 - 限制缓存回源并修复并发租约与过期写入
This commit is contained in:
1 parent
1ff5e5cada
commit
bf0c4454fa
23 files changed
+1098
-59
No files matched your search
@@ -0,0 +1,86 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// 只运行临时 Unix socket Redis,不读取 REDIS_URL,不连接部署环境。
|
||||
func TestRuntimeCoordinatorRedisLeasePreservesLongestTTL(t *testing.T) {
|
||||
binary, err := exec.LookPath("redis-server")
|
||||
if err != nil {
|
||||
t.Skip("redis-server unavailable; real Redis lease integration not run")
|
||||
}
|
||||
// macOS 的 Unix socket 路径有长度限制,不使用包含长测试名的 t.TempDir。
|
||||
dir, err := os.MkdirTemp("", "canvas-redis-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = os.RemoveAll(dir) })
|
||||
socket := filepath.Join(dir, "r.sock")
|
||||
cmd := exec.Command(binary, "--port", "0", "--unixsocket", socket, "--unixsocketperm", "700", "--save", "", "--appendonly", "no", "--dir", dir)
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = cmd.Process.Kill(); _ = cmd.Wait() })
|
||||
client := redis.NewClient(&redis.Options{Network: "unix", Addr: socket, MaxRetries: -1, ContextTimeoutEnabled: true})
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
for client.Ping(ctx).Err() != nil {
|
||||
if ctx.Err() != nil {
|
||||
t.Fatal("temporary Redis did not start")
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
c := &runtimeCoordinator{redis: client, instanceID: "isolated-test"}
|
||||
long, ok, err := c.acquireLease(ctx, "workers", 2, 45*time.Minute)
|
||||
if err != nil || !ok {
|
||||
t.Fatalf("legacy lease: %v %v", ok, err)
|
||||
}
|
||||
defer long.release()
|
||||
short, ok, err := c.acquireLease(ctx, "workers", 2, time.Minute)
|
||||
if err != nil || !ok {
|
||||
t.Fatalf("new lease: %v %v", ok, err)
|
||||
}
|
||||
defer short.release()
|
||||
key := "canvas:slots:workers"
|
||||
assertLongestTTL := func() {
|
||||
t.Helper()
|
||||
ttl, err := client.PTTL(ctx, key).Result()
|
||||
if err != nil || ttl < 44*time.Minute {
|
||||
t.Fatalf("short lease truncated shared key TTL: %s %v", ttl, err)
|
||||
}
|
||||
}
|
||||
assertLongestTTL()
|
||||
if err := short.renew(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertLongestTTL()
|
||||
if _, ok, err := c.acquireLease(ctx, "workers", 2, time.Minute); err != nil || ok {
|
||||
t.Fatalf("global capacity exceeded: %v %v", ok, err)
|
||||
}
|
||||
if err := client.ZAdd(ctx, key, redis.Z{Score: float64(time.Now().Add(-time.Second).UnixMilli()), Member: short.token}).Err(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := short.renew(ctx); err == nil {
|
||||
t.Fatal("expired Redis lease resurrected")
|
||||
}
|
||||
replacement, ok, err := c.acquireLease(ctx, "workers", 2, time.Minute)
|
||||
if err != nil || !ok {
|
||||
t.Fatalf("abandoned slot not reclaimed: %v %v", ok, err)
|
||||
}
|
||||
defer replacement.release()
|
||||
short.release()
|
||||
short.release()
|
||||
if err := replacement.renew(ctx); err != nil {
|
||||
t.Fatalf("old release removed new owner: %v", err)
|
||||
}
|
||||
assertLongestTTL()
|
||||
}
|
||||
Reference in new issue
Block a user