From f91034477b8d02d72c7cdfa1b8c2c472549624eb Mon Sep 17 00:00:00 2001 From: k2safe Date: Wed, 23 Sep 2026 13:46:40 +0800 Subject: [PATCH] docs: record feature module APIs and Linux Redis acceptance --- MODULES.md | 2 + ROADMAP.md | 12 +++ performance/modulesAcceptance.md | 50 ++++++++++ performance/modulesLinux.json | 159 +++++++++++++++++++++++++++++++ performance/modulesLinux.txt | 53 +++++++++++ 5 files changed, 276 insertions(+) create mode 100644 performance/modulesAcceptance.md create mode 100644 performance/modulesLinux.json create mode 100644 performance/modulesLinux.txt diff --git a/MODULES.md b/MODULES.md index 9163c6b..f18bed0 100644 --- a/MODULES.md +++ b/MODULES.md @@ -166,3 +166,5 @@ Linux runner 顺序运行两个容器:全量 race/vet/build/独立生成项目 每次最多一个容器,限制 2 CPU、2 GiB(不额外使用 swap)、256 pids;Redis 测试容器禁外网, 只用内部 loopback,不发布端口。Redis 集成二进制为静态非 race,适配器单测在全量 race 中覆盖。 结束清理本轮容器,并记录提交、源文件摘要、镜像 ID、日志摘要和原有容器是否仍运行。 + +本轮通过结果和原始证据见 [业务模块验收](performance/modulesAcceptance.md)。 diff --git a/ROADMAP.md b/ROADMAP.md index 35d8eea..27b2fb8 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -69,4 +69,16 @@ - 新增 `scripts/verifyProxy.py`,在 Linux Docker 中用 Nginx 反向代理 native TLS 服务,检查真实 TLS、Keep-Alive、上游超时和 native 优雅关闭。 - 自动验收入口:`python3 scripts/verifyPerformance.py`;Linux 固定验收入口:`python3 scripts/verifyLinux.py --load --tls --duration 30m`;代理联调入口:`python3 scripts/verifyProxy.py`。 +## 6. 可复用业务基础模块(第一版) + +- `cache`:有界 LRU 内存缓存、TTL、数据复制、同进程合并回源;`cache/redis` 适配 go-redis 客户端,保留借用连接池的生命周期边界。 +- `httpc`:复用出站连接、每目标主机连接上限、覆盖响应体的总超时、保守重试、requestId 传递、JSON 响应大小限制与安全日志字段。 +- `middleware`:有容量上限的令牌桶限流;按已认证身份隔离的幂等保护,覆盖冲突、处理中记录、失败记录、容量与请求/响应大小上限;协作式请求 deadline。 +- `task`:固定 worker、有界队列、任务超时、panic 隔离、间隔调度、不重叠执行、停止接收与排空/取消。 +- `go-omni add`:向已有项目生成 Handler / Service / Repository / Model 和模块注册入口;目标存在时拒绝覆盖,默认内存 CRUD 可注入持久化 Repository。 +- `examples/modules` 将以上运行时能力串联,使用真实 TLS 在 nethttp、native、实验 HTTP/2 下验证缓存、出站 requestId、认证、幂等、后台任务与退出。 +- 使用文档:[业务基础模块](MODULES.md)。验证入口:`GO_OMNI_SCAFFOLD_INTEGRATION=1 go test -race ./...` 和 `python3 scripts/verifyModules.py`;Linux runner 顺序运行全量检查及独立 Redis 容器,硬限制 2 CPU / 2 GiB / 256 pids。 +- 最终代码已通过 Linux amd64 全仓库 race/vet/build、独立生成项目 CRUD、真实 Redis 7.4.11 与三种网络层 TLS 组合测试;测试容器清理完成、原有 7 个业务容器仍运行。提交、源码摘要与完整输出见 [验收记录](performance/modulesAcceptance.md)。 +- 当前幂等、限流、任务调度和合并回源均为单进程能力;Redis 仅提供缓存存储,不代表已实现分布式去重、任务持久化或跨副本选主。eTax 指定业务模块接入依用户要求暂缓。 + 以上阶段以验收结果判断完成。当前仍是基础框架试用阶段;MySQL、适配层契约和一次业务模块试用已完成,自研网络层已有可运行预览,稳定性与生产规模验证尚未完成。 diff --git a/performance/modulesAcceptance.md b/performance/modulesAcceptance.md new file mode 100644 index 0000000..30e1200 --- /dev/null +++ b/performance/modulesAcceptance.md @@ -0,0 +1,50 @@ +# 业务基础模块验收 + +2026-09-23,第一版功能验收通过。运行时代码提交: +`b9259e628e84a4e1cc3a73801b04dd864c4f3a19`。 +使用方式与限制见 [MODULES.md](../MODULES.md)。 + +| 能力 | 验收内容 | +| --- | --- | +| cache | 字节切片所有权、LRU/容量、TTL 边界、取消/关闭、并发合并加载、独立等待取消、加载容量/超时/panic、存储故障不触发回源 | +| cache/redis | 真实 Redis 二进制值读写、TTL 到期、零 TTL 持久、删除、WRONGTYPE 错误保留、取消写入不落库、关闭客户端错误不被当成 miss | +| httpc | TCP 连接复用、GET 503 重试、POST/不可重放 body 不做应用层重试、总超时覆盖退避与响应体、响应大小、禁止自动重定向、请求 ID、提前拒绝时关闭请求体 | +| middleware | 并发限流额度、补充与过期、429/503、504 和已提交响应;身份隔离、精确响应重放、内容冲突、在途记录不随 TTL 过期、失败/panic/超限记录、容量拒绝与超时组合 | +| task | worker/队列上限、panic 隔离、正常排空、超时取消、队列任务不在强制关闭后启动、间隔任务不重叠、跳过统计 | +| go-omni add | 模块命名/路径校验、符号链接拒绝、已有文件保护、独立消费项目编译和完整 CRUD、认证/校验/取消 | +| 组合运行 | 同一示例在 nethttp/native/实验 HTTP/2 的真实 TLS 下验证:合并缓存、上游请求 ID、幂等重放、认证不可绕过、任务只提交一次和退出 | + +macOS arm64 / Go 1.27.1 完成主体提交的全仓库 race、vet、build 与独立生成项目测试; +最后的 HTTP 客户端请求体关闭修补另行通过 httpc race/vet。 + +Linux amd64 / Go 1.25.14 对上述最终运行时代码提交重新执行并通过: + +```sh +GO_OMNI_SCAFFOLD_INTEGRATION=1 go test -race -count=1 -timeout=240s ./... +go vet ./... +go build ./... +``` + +随后使用 Redis 7.4.11 执行真实集成测试。Redis 测试二进制静态编译、未启用 race, +适配器单元测试包含在前一阶段的全量 race 检查中。生成项目使用本地 replace 验证当前源码, +本轮不包含新版本标签发布或远程 CLI 安装验收。 + +可复现入口: + +```sh +python3 scripts/verifyModules.py --goproxy https://goproxy.cn,direct +``` + +两阶段容器顺序运行,单容器限制 2 CPU / 2 GiB 内存及内存+swap / 256 pids, +总时限 900 秒;本次约 155 秒完成。Redis 容器以非 root 身份运行,禁外网、根文件系统只读, +只使用内部 loopback,未发布端口。结束确认两个测试容器清理完毕,原有 7 个业务容器均仍运行。 + +原始证据:[结构化报告](modulesLinux.json)、[完整日志](modulesLinux.txt)。 +报告记录提交、各源码 SHA-256、镜像 ID、命令、资源上限和日志摘要,已与本地文件逐项核对。 +报告中的工作区状态仅为上一轮生成的两个未跟踪验收文件,没有未提交的运行时代码。 + +测试期间修正了任务池取消回调尚未执行时任务结果误报成功的竞态, +以及幂等缓冲链未提交响应时 deadline 应交给外层 Timeout 返回 504 的组合行为。 + +这是功能与并发正确性验收,不是生产吞吐或跨副本一致性验收。当前限流、幂等、调度与合并回源 +是单进程能力;请求/任务超时依赖业务配合取消。native 和 HTTP/2 的成熟度沿用各自原有定义。 diff --git a/performance/modulesLinux.json b/performance/modulesLinux.json new file mode 100644 index 0000000..584a689 --- /dev/null +++ b/performance/modulesLinux.json @@ -0,0 +1,159 @@ +{ + "status": "passed", + "startedAt": "2026-09-23T05:41:28.082372+00:00", + "testedCommit": "b9259e628e84a4e1cc3a73801b04dd864c4f3a19", + "workingTreeStatus": "?? performance/modulesLinux.json\n?? performance/modulesLinux.txt", + "sourceSha256": { + "cache/cache.go": "4aad0a644c0c9ce6353e61e70059715bea5402f0d7132b50003ee126c3836cad", + "cache/cache_test.go": "c850cd8a90c1e6a8d701a415e283d4147fbd66d12d054002014cbf348863aea2", + "cache/memory.go": "3ac2a710a0d4677ad58dd6817005033ef7cc939be7eb3e40476212ce7f18ad42", + "cache/redis/redis.go": "3f522258da8a0fc6627645573221e9f5ccbdfea0fe8a1450985cfd084e2c18ce", + "cache/redis/redis_test.go": "4948647b2d31803252af1ee4e4b96372b53f9fac46f03ab064b40abe2d5a0aa1", + "cmd/go-omni/main.go": "b778ec1659ca91772a8cd57e04a1190c9bc4f40652e8ec15a1acda6c0c820a96", + "cmd/go-omni/main_test.go": "312e0dea1d46cbbd436acdb4fcac0704d4781746d9e8433f1203f67ef9601065", + "cmd/go-omni/scaffold/addModule.go": "8f43bd68c2b3820cf1d036c4f2c5f9d06d1c7c8e5114f5081597d905f8d8cdcc", + "cmd/go-omni/scaffold/addModule_test.go": "667c45312f628f0fb5b8206ddef7ccc02a75f33b72f73fd6a17b03da0cb62e91", + "cmd/go-omni/scaffold/moduleTemplates/README.md.tmpl": "9d3bfc46cac5618bcff7d8a6b2cc9f123e818aeb3dee9bcdfe0f91db584994bd", + "cmd/go-omni/scaffold/moduleTemplates/handler-handler.go.tmpl": "729cd0f6faba53d5d2c1492935e12ac2adcefde4490748e25c18be7ccb3f7139", + "cmd/go-omni/scaffold/moduleTemplates/model-model.go.tmpl": "a005611f6b455d4d8469e316bc26fa571043b2da1834db2b3dd14a96d66221d7", + "cmd/go-omni/scaffold/moduleTemplates/module.go.tmpl": "fb0be7fd571ba2c0edb01d70d68cdbbfe53561caab98346bf1ac7cd8df07205a", + "cmd/go-omni/scaffold/moduleTemplates/repository-repository.go.tmpl": "902da3d9f12395fb226178c4428ff547e1c2fce3261882dba98d1400d8c90747", + "cmd/go-omni/scaffold/moduleTemplates/service-service.go.tmpl": "49d26044a69a43e983aa5ba4b5b2399b26b77898b23e230751ad1372c2af3178", + "cmd/go-omni/scaffold/scaffold.go": "15baca1b5c4607c8f96fb6f6d4b96a661a86899012f93a6c380bc6d894b18803", + "cmd/go-omni/scaffold/scaffold_test.go": "58aab9251b01e394bbc90aeb538ff035dd32c73c0d4815132ac6cbc086527cda", + "cmd/go-omni/scaffold/templates/README.md.tmpl": "dc5ce58c49b4c0b50a87fdca794513cad38f428975bdfe772e89944e39614bd4", + "cmd/go-omni/scaffold/templates/cmd-server-main.go.tmpl": "07c4f81109ece43fcf15bb807ada5ae6dc7e732babc7293269514a21cd389955", + "cmd/go-omni/scaffold/templates/databaseIntegration_test.go.tmpl": "81c4f7fbc919d74b738d3a7338248cbd83cfa0803b2058e8ee184e3ce51e7e9b", + "cmd/go-omni/scaffold/templates/go.mod.tmpl": "7d3bb15d2292e36939cc1905258d2800aeaf0e37d7d041b2ff8cfdb6fc84c57a", + "cmd/go-omni/scaffold/templates/internal-handler-user.go.tmpl": "7f8dbcd2f9b0129a90160d6059e47d19d4758274060a1a3e5160055d0592d34f", + "cmd/go-omni/scaffold/templates/internal-model-errors.go.tmpl": "f123eace52952321b0665a92a2923514c52c8c3bf13df1065710e9675750f837", + "cmd/go-omni/scaffold/templates/internal-model-user.go.tmpl": "b391143e7386e169b53e7fed25de1146e8c52f3fa287b12728e8d22b33f386de", + "cmd/go-omni/scaffold/templates/internal-repository-memory.go.tmpl": "148abc273c1fab3f28536706792762ae41e8687dfe9986c374372f162600feac", + "cmd/go-omni/scaffold/templates/internal-repository-mysql.go.tmpl": "34bd333e29211a0025ff6ebaea18c7f142f18f1cc451d692f9d360e8c8f6f635", + "cmd/go-omni/scaffold/templates/internal-repository-users.go.tmpl": "6d7dd80f34a9c215daeb4a86bfbe11e845e7ff7e9864c8af43e1c2ec3a8b28ec", + "cmd/go-omni/scaffold/templates/internal-service-user.go.tmpl": "3fde559ac634a6c85d322dbc3487691a192644268e85aa3911dd2561a4116196", + "cmd/go-omni/scaffold/templates/internal-servicecontext-context.go.tmpl": "2504d9750194a543121c9e93328f5f2a535f1db9f8dbd361edf3681cdfa21c39", + "cmd/go-omni/scaffold/templates/schema.sql.tmpl": "7c5108f18fc7d8ff75c97afd96fcaa9f8ed026c79c71c5612f55572d73354abb", + "cmd/go-omni/scaffold/version.go": "4d2d1b0d6d0ea716ff4cb7ff08cc25e6f91e06b1a4585176e3128a22e2f2f4ad", + "examples/modules/main.go": "6b44b981c0f654e241664c94612cd95ba8a394155b589add0814d81397156a26", + "examples/modules/main_test.go": "4ceb869a7e1ab3fb037af5411ec3dec47af66738adb66c4db01498d446aab7fb", + "go.mod": "a839f9bd2501f28fb712969465fcba94c04ad0aa4f80d4a1f189070e4cb1b4b3", + "go.sum": "d2bd32eaf65d25d833c6481fe5af2dadd00dcdc73e2df3fcac2d4dc418d143fe", + "httpc/client.go": "bbec35b6234a554a4aefd0fef97ace3850288642faab2e382dcbe19a6cb95cd8", + "httpc/client_test.go": "972ebd2efab618629e394484c288afe4e675fcfe0c50121d9fa9071680d6e763", + "httpx/context.go": "104434499f074eedf2c54e4d0e90db587feb230ca8bb76803b87f2e42d9a8118", + "middleware/idempotency.go": "9b150d610e188cbd7d6783fc843563996c86d328024fd237e6a245977d30e299", + "middleware/middleware.go": "a1e6cfae40d3e1701238a680009d66121f1bbaed786e81b3f6133eb1198a1cf6", + "middleware/middleware_test.go": "9249aac16f251858bfe7937f768dc628bf4cc392af75f90223ccacd0f13d2cb9", + "middleware/protection_test.go": "4347bb6074078c4c7bd9f65f5fa13a0ac2bc9a8615b297588cc3358aede69a7e", + "middleware/rateLimit.go": "f3448dfbb773e4b91864f99e4afa2feb699f15e6ce100eb82d6fe4a426323a46", + "middleware/request_id.go": "d52bf17bf80245bafe878026b044857442f0af8df689bcaed4d967d006cbd947", + "middleware/timeout.go": "e2f446d992c9f0c986deb75fa71297f19c78f8e43593f174cd19fe93e385e440", + "scripts/verifyModules.py": "e0ff3fa6ac074ceb4fd7e4c2a407df90dd20261c5d93b913b0d4b03bfc121d1b", + "task/task.go": "ef671a9b7d84e7f53cdb2dcb787881e47b00ef0afb29fb6e4698fa8765a7bfe7", + "task/task_test.go": "6e33ecd81345ece6102f48e60050a0d990e1bf75c3840e262d5da2962c743681" + }, + "containerLimits": { + "cpus": 2, + "memoryBytes": 2147483648, + "memoryAndSwapBytes": 2147483648, + "pidsLimit": 256 + }, + "wallTimeoutSeconds": 900, + "imageIds": { + "golang:1.25": "sha256:699337d620559a59b4a2bb298ad59611e535d2ee755a34cf2d2a98f37578dc80", + "redis:7.4-alpine": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499" + }, + "commands": [ + [ + "/usr/bin/docker", + "run", + "--rm", + "--name", + "go-omni-modules-795f2a339ebc-build", + "--cpus", + "2", + "--memory", + "2048m", + "--memory-swap", + "2048m", + "--pids-limit", + "256", + "--log-opt", + "max-size=10m", + "--log-opt", + "max-file=1", + "-v", + "/root/go-omni-validation:/workspace:ro", + "-v", + "/tmp/goOmniModulesjyctce2e:/output", + "-w", + "/workspace", + "-e", + "GOWORK=off", + "-e", + "GOMAXPROCS=2", + "-e", + "GO_OMNI_SCAFFOLD_INTEGRATION=1", + "-e", + "GOPROXY=https://goproxy.cn,direct", + "golang:1.25", + "sh", + "-c", + "go version && go test -race -count=1 -timeout=240s ./... && go vet ./... && go build ./... && CGO_ENABLED=0 go test -c -o /output/redis.test ./cache/redis" + ], + [ + "/usr/bin/docker", + "run", + "--rm", + "--name", + "go-omni-modules-795f2a339ebc-redis", + "--cpus", + "2", + "--memory", + "2048m", + "--memory-swap", + "2048m", + "--pids-limit", + "256", + "--log-opt", + "max-size=10m", + "--log-opt", + "max-file=1", + "--network", + "none", + "--read-only", + "--cap-drop", + "ALL", + "--security-opt", + "no-new-privileges", + "--user", + "999:999", + "--tmpfs", + "/tmp:rw,nosuid,noexec,size=64m,mode=1777", + "-v", + "/tmp/goOmniModulesjyctce2e:/test:ro", + "--entrypoint", + "sh", + "-e", + "GOMAXPROCS=2", + "-e", + "GO_OMNI_REDIS_TEST_ADDR=127.0.0.1:6379", + "redis:7.4-alpine", + "-c", + "set -eu\nredis-server --version\nredis-server --bind 127.0.0.1 --port 6379 --save '' --appendonly no --maxmemory 32mb --daemonize yes --pidfile /tmp/redis.pid --logfile /tmp/redis.log --dir /tmp\ntrap 'redis-cli -h 127.0.0.1 shutdown nosave >/dev/null 2>&1 || true' EXIT\nattempt=0\nuntil redis-cli -h 127.0.0.1 ping >/dev/null 2>&1; do\n attempt=$((attempt + 1))\n if [ \"$attempt\" -gt 50 ]; then cat /tmp/redis.log; exit 1; fi\n sleep 0.1\ndone\n/test/redis.test -test.v -test.run '^TestRedisIntegration$' -test.count=1 -test.timeout=30s\n" + ] + ], + "cleanedContainers": [ + "go-omni-modules-795f2a339ebc-build", + "go-omni-modules-795f2a339ebc-redis" + ], + "notes": [ + "Full repository race/vet/build plus independent generated-module CRUD in the compiler container.", + "Real Redis uses a statically linked non-race test binary, loopback only, network=none and no published ports.", + "At most one validation container; no pre-existing service or database is accessed." + ], + "logSha256": "f069a476609b98ea803d193dc6d8bd49a0a4ed2ccdda9a139bc4413f5565764a", + "finishedAt": "2026-09-23T05:44:03.152464+00:00", + "preExistingContainersStillRunning": 7 +} diff --git a/performance/modulesLinux.txt b/performance/modulesLinux.txt new file mode 100644 index 0000000..359ce29 --- /dev/null +++ b/performance/modulesLinux.txt @@ -0,0 +1,53 @@ +go version go1.25.14 linux/amd64 +go: downloading github.com/redis/go-redis/v9 v9.17.3 +go: downloading golang.org/x/mod v0.17.0 +go: downloading github.com/go-sql-driver/mysql v1.9.3 +go: downloading golang.org/x/net v0.25.0 +go: downloading github.com/go-playground/validator/v10 v10.20.0 +go: downloading filippo.io/edwards25519 v1.1.0 +go: downloading github.com/cespare/xxhash/v2 v2.3.0 +go: downloading github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f +go: downloading github.com/gabriel-vasile/mimetype v1.4.3 +go: downloading github.com/go-playground/universal-translator v0.18.1 +go: downloading github.com/leodido/go-urn v1.4.0 +go: downloading golang.org/x/crypto v0.23.0 +go: downloading golang.org/x/text v0.15.0 +go: downloading github.com/go-playground/locales v0.14.1 +ok git.yuntuo.hk/k2safe/go-omni 1.085s +ok git.yuntuo.hk/k2safe/go-omni/app 1.644s +ok git.yuntuo.hk/k2safe/go-omni/auth 1.036s +ok git.yuntuo.hk/k2safe/go-omni/cache 1.034s +ok git.yuntuo.hk/k2safe/go-omni/cache/redis 1.014s +ok git.yuntuo.hk/k2safe/go-omni/cmd/go-omni 1.186s +ok git.yuntuo.hk/k2safe/go-omni/cmd/go-omni/scaffold 3.732s +ok git.yuntuo.hk/k2safe/go-omni/config 1.036s +ok git.yuntuo.hk/k2safe/go-omni/database 1.014s +? git.yuntuo.hk/k2safe/go-omni/errors [no test files] +? git.yuntuo.hk/k2safe/go-omni/examples/basic [no test files] +ok git.yuntuo.hk/k2safe/go-omni/examples/modules 1.070s +ok git.yuntuo.hk/k2safe/go-omni/examples/reference 1.171s +? git.yuntuo.hk/k2safe/go-omni/examples/reference/handler [no test files] +? git.yuntuo.hk/k2safe/go-omni/examples/reference/model [no test files] +ok git.yuntuo.hk/k2safe/go-omni/examples/reference/repository 1.013s +? git.yuntuo.hk/k2safe/go-omni/examples/reference/service [no test files] +ok git.yuntuo.hk/k2safe/go-omni/health 1.041s +ok git.yuntuo.hk/k2safe/go-omni/httpc 1.104s +ok git.yuntuo.hk/k2safe/go-omni/httpx 1.034s +ok git.yuntuo.hk/k2safe/go-omni/internal/perfcheck 1.047s +? git.yuntuo.hk/k2safe/go-omni/internal/testutil [no test files] +ok git.yuntuo.hk/k2safe/go-omni/logx 1.013s +ok git.yuntuo.hk/k2safe/go-omni/metrics 1.035s +ok git.yuntuo.hk/k2safe/go-omni/middleware 1.060s +? git.yuntuo.hk/k2safe/go-omni/response [no test files] +ok git.yuntuo.hk/k2safe/go-omni/task 1.118s +ok git.yuntuo.hk/k2safe/go-omni/transport 1.069s +ok git.yuntuo.hk/k2safe/go-omni/transport/http2 10.636s +? git.yuntuo.hk/k2safe/go-omni/transport/internal/routing [no test files] +ok git.yuntuo.hk/k2safe/go-omni/transport/native 2.716s +ok git.yuntuo.hk/k2safe/go-omni/transport/nethttp 1.034s +? git.yuntuo.hk/k2safe/go-omni/transport/transporttest [no test files] +? git.yuntuo.hk/k2safe/go-omni/validate [no test files] +Redis server v=7.4.11 sha=00000000:0 malloc=jemalloc-5.3.0 bits=64 build=40ff01a501d8e4b6 +=== RUN TestRedisIntegration +--- PASS: TestRedisIntegration (0.08s) +PASS