perf: 流式错误判定预过滤、再减一个写索引,加可选明细保留期

- 流式放行前的上游错误判定加廉价子串预过滤("error" / response.failed):
  长思考阶段几十上百条事件不再逐条 json.Unmarshal。
- usage_logs 去掉 idx_channel_created:没有任何查询按 channel_id 过滤该表
  (渠道统计走 created_at 时间窗后在内存分组),复合索引只是每行一次多余的
  B-tree 插入;schema v9 迁移里显式 DROP 旧库索引。至此该表只剩
  (user_id, created_at)、created_at 两个真被用到的索引。
- 新增可选保留期(OT_USAGE_RETENTION_DAYS,默认 0=永久保留):每天分批
  (2000 行/批、批间 50ms)清理过期 usage_logs,避免长期运行后时间窗查询
  与备份随表增长变慢;余额流水 balance_logs 不在清理范围。

参数复测(记账稳态吞吐,3 次取中位):
- batch 64(当前)30.2k rows/s;batch 128 19.0k、batch 256 14.3k(更差,保持 64)
- wal_autocheckpoint(4000)、cache_size(-64MB) 与当前配置无差别,不引入

验证:go vet、go test ./...、-race(usage/proxy/store)全绿;新增保留期三项测试
(只删明细不动流水、未开启不动数据、跨分批删完)。
This commit is contained in:
Sakurasan committed 2026-10-30 16:45:57 +08:00
1 parent 8e5fe16611
commit 531a03c836
9 files changed
+224 -20

No files matched your search

+12
View File
@@ -18,6 +18,7 @@ type Config struct {
JWT JWTConfig
Auth AuthConfig
Proxy ProxyConfig
Usage UsageConfig
RateLimit RateLimitConfig
WebAuthn WebAuthnConfig
Master string // 渠道密钥 AES-GCM 主密钥(来自环境变量)
@@ -32,6 +33,13 @@ type Config struct {
TrustedProxies []string
}
// UsageConfig 用量数据的保留策略。
type UsageConfig struct {
// RetentionDays 用量明细保留天数;0(默认)= 永久保留。
// 只清 usage_logs,不动余额流水。
RetentionDays int
}
// WebAuthnConfig Passkey(WebAuthn)配置。
type WebAuthnConfig struct {
RPID string // Relying Party ID(域名,如 localhost)
@@ -156,6 +164,7 @@ func Load() (*Config, error) {
v.SetDefault("proxy.affinity_ttl", "5m")
v.SetDefault("proxy.channel_acquire_wait", "150ms")
v.SetDefault("proxy.auth_cache_ttl", "2s")
v.SetDefault("usage.retention_days", 0)
v.SetDefault("proxy.health_interval", "60s")
v.SetDefault("proxy.health_fail_threshold", 2)
v.SetDefault("proxy.log_raw", false)
@@ -213,6 +222,9 @@ func Load() (*Config, error) {
HealthFailThreshold: v.GetInt("proxy.health_fail_threshold"),
LogRaw: v.GetBool("proxy.log_raw"),
},
Usage: UsageConfig{
RetentionDays: v.GetInt("usage.retention_days"),
},
RateLimit: RateLimitConfig{
UserRPS: v.GetInt("ratelimit.user_rps"),
LoginIPPerMin: v.GetInt("ratelimit.login_ip_per_min"),