test: throttle history verification
This commit is contained in:
@@ -52,7 +52,7 @@
|
||||
- 群消息设置 `noecho=false`,每条服务端接受的群消息期望 2000 个连接各收到一次。
|
||||
- 实时 drain 结束后,两机先汇总 ACK 台账和“已实时观测但未收到 ACK”的消息台账;等待 `history_settle`(默认 5 分钟)让 Kafka 自然排空,再通过 `{get what=data}` 按 Topic/Seq 回查两类消息。
|
||||
- 每轮建立 WS 后、正式计时前,以 32 个 worker 对本机 1000 个账号做好友关系、P2P Topic 和发布权限预检;任一异常会终止本轮。
|
||||
- 历史回查默认每机使用 1 个 worker、每页 50 条、查询间隔 100ms、总超时 3 分钟,并在超时内持续重试缺失区间。该限速用于避免正确性核验本身制造 Topic/Proxy 洪峰;历史读取吞吐应使用独立场景测试。
|
||||
- 历史回查默认每机使用 1 个 worker、每页 20 条、查询间隔 500ms、总超时 3 分钟,并在超时内持续重试缺失区间。该限速用于避免正确性核验本身制造 Topic/Proxy 洪峰;历史读取吞吐应使用独立场景测试。
|
||||
- publish 错误完整分类为 `403`、`502`、`503`、`ctrl_timeout`、`socket_error` 和 `other`。`publish_rejected` 只统计明确拒绝,超时和 socket 错误单独计入 `publish_outcome_unknown`。
|
||||
- `delivery_local_p95_ms` 使用发送方本机收到 echo 的同机时钟样本,是投递延迟门禁口径;兼容字段 `delivery_p95_ms` 与它保持一致。
|
||||
- `delivery_cross_machine_p95_ms` 是跨机器单向观测值,只有两机时钟同步达标时才有意义;`cross_machine_negative_latency_samples > 0` 明确表示存在时钟偏差。
|
||||
|
||||
@@ -390,7 +390,7 @@ func (r *agentRun) issueHistoryQueries(ctx context.Context, queries []historyQue
|
||||
return
|
||||
}
|
||||
jobs := make(chan historyQuery)
|
||||
interval := 100 * time.Millisecond
|
||||
interval := 500 * time.Millisecond
|
||||
if r.cfg != nil && r.cfg.HistoryInterval != "" {
|
||||
interval = mustDuration(r.cfg.HistoryInterval)
|
||||
}
|
||||
@@ -430,7 +430,7 @@ func (r *agentRun) historyPageSize() int {
|
||||
if r.cfg != nil && r.cfg.HistoryPageSize > 0 {
|
||||
return r.cfg.HistoryPageSize
|
||||
}
|
||||
return 50
|
||||
return 20
|
||||
}
|
||||
|
||||
func missingHistory(recorder *deliveryRecorder, expected map[string]AcceptedMessage) []AcceptedMessage {
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
"preflight_workers": 32,
|
||||
"skip_p2p_preflight": false,
|
||||
"history_workers": 1,
|
||||
"history_page_size": 50,
|
||||
"history_query_interval": "100ms",
|
||||
"history_page_size": 20,
|
||||
"history_query_interval": "500ms",
|
||||
"history_settle": "5m",
|
||||
"history_timeout": "3m",
|
||||
"p2p_messages_per_user": 10,
|
||||
|
||||
@@ -90,13 +90,13 @@ func loadConfig(path string) (*Config, error) {
|
||||
return nil, errors.New("history_workers must not exceed 128")
|
||||
}
|
||||
if cfg.HistoryPageSize <= 0 {
|
||||
cfg.HistoryPageSize = 50
|
||||
cfg.HistoryPageSize = 20
|
||||
}
|
||||
if cfg.HistoryPageSize > 200 {
|
||||
return nil, errors.New("history_page_size must not exceed 200")
|
||||
}
|
||||
if cfg.HistoryInterval == "" {
|
||||
cfg.HistoryInterval = "100ms"
|
||||
cfg.HistoryInterval = "500ms"
|
||||
}
|
||||
if _, err = configDuration(cfg.HistoryInterval); err != nil {
|
||||
return nil, fmt.Errorf("history_query_interval: %w", err)
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestLoadConfigAppliesBoundedHistoryDefaults(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cfg.HistoryWorkers != 1 || cfg.HistoryPageSize != 50 || cfg.HistoryInterval != "100ms" ||
|
||||
if cfg.HistoryWorkers != 1 || cfg.HistoryPageSize != 20 || cfg.HistoryInterval != "500ms" ||
|
||||
cfg.HistorySettle != "5m" || cfg.HistoryTimeout != "3m" ||
|
||||
cfg.PreflightWorkers != 32 || cfg.WSPingInterval != "20s" {
|
||||
t.Fatalf("unexpected defaults: %+v", cfg)
|
||||
|
||||
@@ -344,7 +344,10 @@ func (c *WSClient) removeWait(id string) {
|
||||
}
|
||||
|
||||
func (c *WSClient) readLoop() {
|
||||
defer close(c.done)
|
||||
defer func() {
|
||||
_ = c.conn.Close()
|
||||
close(c.done)
|
||||
}()
|
||||
for {
|
||||
var envelope map[string]json.RawMessage
|
||||
if err := c.conn.ReadJSON(&envelope); err != nil {
|
||||
|
||||
@@ -48,3 +48,39 @@ func TestWSClientKeepaliveSendsPing(t *testing.T) {
|
||||
t.Fatal("websocket keepalive ping was not received")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWSClientReadLoopClosesConnectionAfterDecodeError(t *testing.T) {
|
||||
serverSawClose := make(chan struct{}, 1)
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := (&websocket.Upgrader{}).Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
if err = conn.WriteMessage(websocket.TextMessage, []byte("{")); err != nil {
|
||||
return
|
||||
}
|
||||
if _, _, err = conn.ReadMessage(); err != nil {
|
||||
serverSawClose <- struct{}{}
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
conn, _, err := websocket.DefaultDialer.Dial("ws"+strings.TrimPrefix(server.URL, "http"), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client := &WSClient{conn: conn, waits: make(map[string]chan ctrlPacket), done: make(chan struct{})}
|
||||
go client.readLoop()
|
||||
|
||||
select {
|
||||
case <-client.done:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("read loop did not exit after malformed frame")
|
||||
}
|
||||
select {
|
||||
case <-serverSawClose:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("read loop exited without closing the underlying connection")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user