Sakurasan
|
0e10f0bbde
|
feat: 余额预扣 reserve/capture、冷却兜底重试、Prometheus /metrics
- 预扣:每请求按 body 估算成本上界计入冻结额(1e-8 USD 原子计数),402 判定
改为「余额 - 在途冻结 ≤ 0」,并发的长流式请求不再互相透支;OT_PROXY_RESERVE_MAX_TOKENS
控制估算基数(默认 4096,0=关闭),handler defer 释放,失败/中断/panic 均覆盖
- 调度:全部候选都在运行期冷却时,剩余冷却 ≤ OT_PROXY_COOLDOWN_GRACE 的最早恢复渠道
兜底重试一次而不是 503(429 Retry-After 语义);DB 层不可用的渠道不参与兜底
- 指标:openteam_proxy_requests_total / request_duration / channel_failures /
usage_queue_depth / frozen_balance,/metrics 默认关闭(OT_METRICS_ENABLED 开启)
|
2026-11-09 01:04:40 +08:00 |
|
Sakurasan
|
b2e500ff51
|
feat: 金额 decimal 精确计费、failover 按失败类型封顶、渠道小时桶预聚合、分层守护测试
- 计费:19 个金额字段 float64→decimal.Decimal,DB 列保持 numeric(20,8) 零迁移;
API 金额仍为 JSON number(MarshalJSONWithoutQuotes),前端零改动
- failover:保留时间预算,新增 OT_PROXY_FAILOVER_CAPS_NET/SERVER/RATE/QUOTA
按失败类型封顶换渠道次数(429/配额类瞬时失败时间预算形同虚设)
- 预聚合:新增 usage_channel_hourlies(schema v10),记账事务内同步 upsert,
长窗口渠道统计走桶不再扫明细,spent_last_30d 改扫 usage_daily,v10 一次性回填幂等
- 守护:internal/arch_test.go 用 go/parser 强制分层依赖(fail-closed)
|
2026-11-09 00:41:48 +08:00 |
|
Sakurasan
|
531a03c836
|
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)全绿;新增保留期三项测试
(只删明细不动流水、未开启不动数据、跨分批删完)。
|
2026-10-30 16:45:57 +08:00 |
|
Sakurasan
|
117e03673b
|
perf(sqlite): 写事务改 immediate + synchronous=NORMAL,单 worker 批 64
上一轮发现「多 worker 在 SQLite 上更慢」,这轮把它当成一个可调优问题重新测:
基准(2000 条记账、暖机后跑稳态,同一台机器反复 3 次取分布):
- 默认 deferred 事务 + batch 32:11k–20k rows/s,抖动极大(并发写升级锁失败触发
SQLITE_BUSY 重试,这正是之前 p99 飙到 19–34ms 的原因)
- _txlock=immediate + batch 32:28.2–28.5k rows/s,抖动收敛
- _txlock=immediate + batch 64 + synchronous=NORMAL:29.7–30.1k rows/s,波动 <1%
- 同样配置下 2 个 worker:7.7k–16.7k,仍更差(SQLite 只有一个写者)
因此:
- store.withSQLitePragmas 增加 _txlock=immediate 与 _pragma=synchronous(NORMAL);
immediate 让写事务在 BEGIN 就取写锁,消除升级冲突;NORMAL 是 WAL 下的标准配置,
提交不再逐次 fsync(代价:断电可能丢最后一个事务,已在注释里写明)。
- 记账批大小按驱动区分:SQLite 单 worker 用 64,PG 保持 32。
- 保持 SQLite 单 worker(数据不支持多 worker)。
端到端复测(16 并发、mock 上游):p50 649µs→548µs、p99 2.32ms→1.60ms、
RPS 21.2k→24.9k;对比本轮开始前的 18.7k/2.2ms 亦有改善。
验证:go vet、go test ./...、go test -race(usage/proxy/store)全绿。
|
2026-10-30 16:08:55 +08:00 |
|
Sakurasan
|
f1181150aa
|
perf: 亲和表分片、记账按驱动并发、访问日志与原始体控量
继续上一轮性能改造的剩余项:
- 会话亲和表分 16 片(原来一个全局 mutex,每个请求都要走一遍),
键改为内联 FNV-1a 的 64 位摘要(原先是拼接字符串 + hex,每请求两次分配),
请求路径上零分配;新增键稳定性/无分配/分片并发测试。
- 记账落库按驱动自适应并发:Recorder 按用户分片到多个 worker(同一用户恒定
同片,流水顺序不变)。SQLite 只有一个写者,实测 4 worker 会互相抢锁把
p99 拖到 19–34ms,所以 SQLite 固定 1 个、PostgreSQL 用 4 个。
新增多分片落库与顺序测试(用显式 worker 数在 SQLite 上验证语义)。
- 访问日志:每请求一次 log.Printf 直写 stderr,高 QPS 下是系统调用热点;
改为 64 KiB 缓冲 + 每秒 flush(写/flush 互斥,-race 已验)。
- 原始体控量:LogRaw 落库的请求体截到 256 KiB、响应体截到 1 MiB 并标注原始
体积,避免单条日志几十 MB 拖慢 usage_logs 查询与备份。
- 渠道加权随机选路从 crypto/rand + big.Int 换成 math/rand/v2 的 IntN,
去掉每请求的系统调用与两次分配。
跳过的两项与原因:
- 「PG 下把 model 目录查询合并成一次 JOIN」:目录已缓存 10s,冷路径每模型
10 秒才走一次,合并收益接近 0。
- 「每流一个 heartbeat goroutine」:Go 的 ticker 成本很低,千级并发流仍在
可接受范围,改动收益不抵风险。
实测(16 并发、mock 上游、SQLite):p50 679µs→558µs、p99 2.6ms→2.2ms、
RPS 18.7k→23.9k。验证:go vet、go test ./...、-race(proxy/channel/usage)全绿。
|
2026-10-30 14:25:10 +08:00 |
|
Sakurasan
|
8c12207478
|
perf: 目录缓存与流式正文快路径,请求热路径开销减半
按性能分析逐项落地,实测对比(16 并发、mock 上游零延迟、SQLite):
- 每请求 SQL 10 → 2:模型/渠道目录(模型行、绑定、渠道行)进程内缓存 10s,
运行期状态(冷却、健康)仍每次实时判定;管理端写操作经中间件、
健康状态变化经回调立即失效。新增 TestProxyRequestSQLBudget 钉住预算。
- 端到端 p50 1.3–1.6ms → 0.66–0.70ms,RPS 3.5–8.9k → 16–18k。
- 流式正文累积:字符串 += 改 strings.Builder(原为 O(n²)),
逐行 JSON 提取改字节快路径,并加廉价的子串预过滤、拿到输出 usage 后停累积。
500 块:949µs/7.9MB 分配 → 83µs/136KB。
- 请求体定点改写:模型映射从整包 unmarshal/marshal 改为顶层字段替换
(100KB:585µs → 29µs),stream_options 注入同理;requestText 改为
只在「上游没报用量」时按需提取,亲和键改用原始体前缀。
- 渠道满载背压:新增 Acquire 有界排队(OT_PROXY_CHANNEL_ACQUIRE_WAIT,
默认 150ms);排队仍满时返回 503 + Retry-After 而不是 502。
实测渠道上限 4、16 并发:无排队 81 次 503;排队 150ms 后 400/400 成功。
- 记账按批聚合:同批同用户一次扣款、流水批量插入、日表按 (user,model,日期)
聚合,替代每行 5 条语句;幂等仍靠 ref_id 唯一索引兜底。
- gorm 开启 PrepareStmt(SQL 解析此前占 CPU 15%);tiktoken 编码器按模型复用。
- 管理端用量列表不再返回原始请求/响应体(可能几十 MB),
改为 has_raw 标记 + GET /admin/usage/:id 按需拉取,前端弹窗打开时取。
验证:go vet、go test ./...、go test -race(proxy/channel/usage/tokenizer)、
vue-tsc、eslint、vite build 全绿;本地起 mock 上游实测列表/详情接口与
管理页弹窗按需加载。
|
2026-10-30 14:14:21 +08:00 |
|
Sakurasan
|
a3197e2146
|
fix: 记账同一 trace 防重复扣款
流水 ref_id 有唯一索引,但扣款本身没有:重试/重复 flush 场景下
同一 trace 会被重复扣钱。billUser 扣款前先查 ref_id 是否已存在。
在远端记账重写基础上合并防重检查与测试。
|
2026-10-30 01:21:43 +08:00 |
|
Sakurasan
|
984a31cbc5
|
fix: 记账可靠性——关停排空队列、失败重试、扣费不再静默;余额改 SQL 原子递减
- Recorder.Close 改为停止接收+排空残留,channel 不再 close(杜绝 send-on-closed panic),Close 后的 Record 走同步落库
- flush 瞬时 DB 错误重试 3 次,不再整批丢弃;事务内扣费/流水/日聚合失败一律打日志,明细与扣费不再静默脱节
- 扣费与管理员调余额改 SQL 侧原子加减(SQLite 驱动不支持 FOR UPDATE,读-改-写并发会丢扣费);调余额 ref_id 改纳秒不再撞唯一索引
- 最后一个启用的管理员不可被降级/禁用(管理面锁死后 reset-admin-password 救不回)
|
2026-10-29 02:56:31 +08:00 |
|
 SakurasanandClaude
|
c84fea9ef0
|
记账: Recorder Close 幂等防重复关闭
- main 的 defer Shutdown 与显式 Close 双调用导致停机 panic close of closed channel
- 用 sync.Once 保证只关闭一次
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-08-16 18:55:15 +08:00 |
|
 SakurasanandClaude
|
0ee92d78e7
|
代理: 流式中断按已生成部分计费
- 新增 tokenizer 包(tiktoken-go)按模型估算 token, 未知模型回退 cl100k_base
- passthrough 提取请求输入文本 + SSE 已生成内容, 客户端断开时记 canceled
- usage 计费范围扩展: canceled(流式中断)按已生成部分收费
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-08-16 09:04:18 +08:00 |
|
 SakurasanandClaude
|
ec4de8d913
|
M0-M4: 推倒重来基线(基建+用户/密钥/核心代理+前端+管理后台+三协议互转)
- 后端 Go+Gin+GORM: 配置(OT_ env)/SQLite/Postgres 双驱动、用户体系(argon2id+JWT access/refresh)、
API Key(sk- 48位, 仅存 SHA-256 哈希)
- 代理网关: /v1/chat/completions、/v1/responses、/v1/messages、/v1/models;错误按客户端协议返回
- 三协议互转(convert 包): Chat↔Messages↔Responses 请求/响应 + 流式 SSE 逐事件转换(直通优先)
- 用量计费: 异步批量记账、余额扣减、balance_logs、usage_daily 日聚合
- 管理 API: 用户/渠道 CRUD+测试+模型导入/模型定价+绑定/统计/系统配置
- 前端 Vue3+TS+Tailwind(taste-skill 设计 tokens): Landing/登录注册/控制台/管理后台,
自建组件+Phosphor 图标+自建 SVG 趋势图, 已过 web-design-guidelines 复查
- mock 上游: OpenAI+Anthropic 双协议模拟(含流式)
Co-Authored-By: Claude <noreply@anthropic.com>
|
2026-08-15 15:34:06 +08:00 |
|
Sakurasan
|
360c6b33a6
|
M0+M1: 基建 + 用户/密钥/核心代理
后端 (Go/Gin/GORM):
- 配置(viper+env)、SQLite/Postgres 迁移、argon2id、AES-GCM 渠道密钥、JWT+refresh cookie
- 用户注册/登录/刷新/登出、API Key CRUD(仅存哈希、明文一次展示)
- 代理网关: /v1/chat/completions、/v1/responses、/v1/models 直通 OpenAI 渠道
非流式+流式(SSE 零缓冲转发), 用量捕获(chat 末块/responses completed 嵌套),
OpenAI 错误格式(401/402/404/502), 余额检查
- 异步批量记账 + 余额流水 + 日聚合, admin 用户/余额/配置 API
- 单测: crypto/jwt/apikey/流式 usage 提取
前端 (Vue3+TS+Vite+Tailwind v4):
- taste-skill 设计 tokens: 深色仪表盘, 石墨+信号铜色, Outfit+JetBrains Mono
- Landing/登录/注册, 控制台(仪表盘图表/密钥管理/用量明细)
- 基础组件 Button/Input/Badge/Modal, ECharts 用量图
部署: docker-compose(nginx+api+postgres), 双 Dockerfile, nginx SSE 反代
联调: scripts/mockupstream 本地 mock 上游, 端到端验证通过
|
2026-08-15 13:10:47 +08:00 |
|