feat(gomsg): pymsg/libmsg 协议的 Go 实现,waf logclient 抽取接入
- 新增 gomsg/:帧协议(4B 大端长度 + UTF-8,64MiB 上限)、MsgClient/MsgServer、 RpcClient/RpcServer(_route 分发、错误脱敏、panic 恢复、json.Number 保精度)、 AsyncQueue[T]、LogClient(集中式日志,级别过滤 + fail-safe),线协议与 Python/C++ 字节级兼容 - waf/internal/logclient 改为 gomsg 薄适配层(336→20 行),main/engine/proxy 零改动 - gomsg 26 项单测全绿(-race);waf 编译通过
This commit is contained in:
1 parent
0ed03ac095
commit
c4154d4db9
16 files changed
+1624
-317
No files matched your search
@@ -0,0 +1,120 @@
|
||||
package gomsg
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func collectBatches(batches *[][]int, mu *sync.Mutex) func([]int) {
|
||||
return func(b []int) {
|
||||
mu.Lock()
|
||||
*batches = append(*batches, append([]int(nil), b...))
|
||||
mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsyncQueueFlushOnBatchSize(t *testing.T) {
|
||||
var batches [][]int
|
||||
var mu sync.Mutex
|
||||
q := NewAsyncQueue[int](collectBatches(&batches, &mu), AsyncQueueConfig{
|
||||
BatchSize: 5,
|
||||
FlushInterval: time.Hour, // 只测批量阈值,不测定时
|
||||
})
|
||||
for i := 1; i <= 5; i++ {
|
||||
if !q.Push(i) {
|
||||
t.Fatal("push should succeed")
|
||||
}
|
||||
}
|
||||
deadline := time.Now().Add(2 * time.Second)
|
||||
for {
|
||||
mu.Lock()
|
||||
n := len(batches)
|
||||
mu.Unlock()
|
||||
if n >= 1 {
|
||||
break
|
||||
}
|
||||
if time.Now().After(deadline) {
|
||||
t.Fatal("batch not flushed in time")
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if len(batches[0]) != 5 || batches[0][4] != 5 {
|
||||
t.Fatalf("batch = %v", batches[0])
|
||||
}
|
||||
q.Shutdown()
|
||||
}
|
||||
|
||||
func TestAsyncQueueFlushOnInterval(t *testing.T) {
|
||||
var batches [][]int
|
||||
var mu sync.Mutex
|
||||
q := NewAsyncQueue[int](collectBatches(&batches, &mu), AsyncQueueConfig{
|
||||
BatchSize: 100,
|
||||
FlushInterval: 100 * time.Millisecond,
|
||||
})
|
||||
if !q.Push(42) {
|
||||
t.Fatal("push should succeed")
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
mu.Lock()
|
||||
n := len(batches)
|
||||
mu.Unlock()
|
||||
if n < 1 {
|
||||
t.Fatal("expected interval flush")
|
||||
}
|
||||
q.Shutdown()
|
||||
}
|
||||
|
||||
func TestAsyncQueueShutdownDrains(t *testing.T) {
|
||||
var batches [][]int
|
||||
var mu sync.Mutex
|
||||
q := NewAsyncQueue[int](collectBatches(&batches, &mu), AsyncQueueConfig{
|
||||
BatchSize: 100,
|
||||
FlushInterval: time.Hour,
|
||||
})
|
||||
for i := 1; i <= 3; i++ {
|
||||
q.Push(i)
|
||||
}
|
||||
q.Shutdown()
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if len(batches) != 1 || len(batches[0]) != 3 {
|
||||
t.Fatalf("expected drained batch [1 2 3], got %v", batches)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsyncQueueDropOnFull(t *testing.T) {
|
||||
var batches [][]int
|
||||
var mu sync.Mutex
|
||||
q := NewAsyncQueue[int](collectBatches(&batches, &mu), AsyncQueueConfig{
|
||||
BatchSize: 1000,
|
||||
FlushInterval: time.Hour,
|
||||
MaxQueueSize: 4,
|
||||
})
|
||||
// 后台协程可能已取走部分,填满队列直到 Push 返回 false
|
||||
dropped := false
|
||||
for i := 0; i < 100; i++ {
|
||||
if !q.Push(i) {
|
||||
dropped = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !dropped {
|
||||
t.Fatal("expected drop when queue full")
|
||||
}
|
||||
d, _ := q.Stats()
|
||||
if d == 0 {
|
||||
t.Fatal("dropped counter should be > 0")
|
||||
}
|
||||
q.Shutdown()
|
||||
}
|
||||
|
||||
func TestAsyncQueueRejectAfterShutdown(t *testing.T) {
|
||||
q := NewAsyncQueue[int](func([]int) {}, AsyncQueueConfig{})
|
||||
q.Shutdown()
|
||||
if q.Push(1) {
|
||||
t.Fatal("push after shutdown should fail")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user