Compare commits

...

1329 Commits

Author SHA1 Message Date
Kevin Wan
5b74b9ab7b feat(mcp): add opt-in request metadata bridge for tool handlers (#5550) 2026-04-25 17:11:04 +08:00
Kevin Wan
4a67261b7b fix(discov): move etcd hosts from URI authority to path for Go 1.26 compatibility (#5548) 2026-04-25 10:48:28 +08:00
dependabot[bot]
22bdae0787 chore(deps): bump codecov/codecov-action from 5 to 6 (#5521) 2026-04-11 15:41:58 +08:00
dependabot[bot]
e8675d6a9a chore(deps): bump google.golang.org/grpc from 1.79.3 to 1.80.0 (#5523) 2026-04-11 15:06:08 +08:00
dependabot[bot]
e441c44975 chore(deps): bump google.golang.org/grpc from 1.79.3 to 1.80.0 in /tools/goctl (#5524) 2026-04-11 10:46:11 +08:00
Kevin Wan
3f91a79a2b chore: update goctl version to v1.10.1 and bump go-zero dependency (#5518) 2026-03-28 23:01:48 +08:00
Name
8c47c01739 fix(rest/httpc): reject request body for HEAD method in buildRequest (#5457)
Co-authored-by: 1911860538 <alxps1911@gmail.com>
2026-03-28 14:16:53 +00:00
dependabot[bot]
f59a1cb0de chore(deps): bump github.com/grafana/pyroscope-go from 1.2.7 to 1.2.8 (#5513)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-28 08:49:57 +08:00
dependabot[bot]
d44ff6ddc8 chore(deps): bump github.com/pelletier/go-toml/v2 from 2.2.4 to 2.3.0 (#5512)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-28 08:35:12 +08:00
Kevin Wan
6ffa9cabec chore: reorder Eval/EvalCtx after Do/DoCtx in redis.go for consistent method ordering (#5502) 2026-03-22 20:39:30 +08:00
Ran丶
0069721586 feat(redis): add Do/DoCtx for generic command execution #5417 (#5442) 2026-03-22 12:26:53 +00:00
Kevin Wan
ba9c275853 chore: upgrade Go version to 1.24 and update dependencies (#5499) 2026-03-22 18:47:43 +08:00
fyyang
9a6447ab5c feat: goctl model Add a new method hasField (#5484) 2026-03-22 06:26:56 +00:00
kesonan
004995f06a feat(goctl/rpc): support external proto imports with cross-package ty… (#5472)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-22 04:01:20 +00:00
Amshith Nair
c12c82b2f6 test(mathx,stringx): add missing edge case tests for CalcEntropy and … (#5471) 2026-03-22 03:24:02 +00:00
Name
85d770d340 perf(core/stringx): replace manual char filter with strings.Map (#5453)
Co-authored-by: 1911860538 <alxps1911@gmail.com>
2026-03-22 03:04:12 +00:00
Name
8cd7f7a2d8 refactor(core): replace TakeOne usage with cmp.Or (#5461)
Co-authored-by: 1911860538 <alxps1911@gmail.com>
2026-03-22 02:50:16 +00:00
Amshith Nair
db3101361b docs(mathx): add godoc comment to Numerical type constraint (#5470) 2026-03-21 15:25:37 +00:00
kesonan
eb2302b71e fix(swagger): add example field to path/form/header parameters (#5497)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-21 15:18:55 +00:00
Amshith Nair
04ed637366 test(hash): add unit tests for Hash, Hash determinism, and Md5Hex edg… (#5469) 2026-03-15 15:02:57 +00:00
Kevin Wan
567087a715 test(goctl): add regression test for per-service type alias filtering (#5481) (#5483)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-03-15 22:51:27 +08:00
kesonan
4d2e64a417 fix #5481 (#5482) 2026-03-15 14:14:12 +00:00
kesonan
b01831b4c5 (goctl)fix file copy permission missed (#5475) 2026-03-15 13:55:27 +00:00
Kevin Wan
d1a014955c fix: critical security fixes in core/codec (S0) (#5479) 2026-03-15 16:40:15 +08:00
Kevin Wan
ec802e25a6 feat: add JSON5 configuration support (#5433)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-03-14 21:19:46 +08:00
dependabot[bot]
8a2e09dfd1 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.36.1 to 2.37.0 (#5444)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-28 20:29:53 +08:00
dependabot[bot]
220d438fe7 chore(deps): bump github.com/modelcontextprotocol/go-sdk from 1.3.0 to 1.3.1 (#5435) 2026-02-21 13:04:51 +08:00
dependabot[bot]
2cd96146fa chore(deps): bump github.com/redis/go-redis/v9 from 9.17.3 to 9.18.0 (#5432)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-17 20:21:10 +08:00
Kevin Wan
7e96317fad chore: update goctl version (#5431) 2026-02-15 20:25:43 +08:00
Kevin Wan
70728ce2e2 chore: update go-zero version (#5430) 2026-02-15 19:29:30 +08:00
dependabot[bot]
6a72a735d4 chore(deps): bump github.com/modelcontextprotocol/go-sdk from 1.2.0 to 1.3.0 (#5413) 2026-02-12 22:42:08 +08:00
Kevin Wan
b139a82c2e fix: resolve data race in service discovery map access (#5408) 2026-02-06 23:16:05 +08:00
Kevin Wan
bdddf1f30c feat(gateway): export WithDialer option for custom gRPC client configuration (#5406) 2026-02-06 21:50:50 +08:00
dependabot[bot]
9b74b7e09e chore(deps): bump github.com/emicklei/proto from 1.14.2 to 1.14.3 in /tools/goctl (#5403)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-06 21:01:11 +08:00
RUGBEN
4d5ed2c45d fix(conf): support equal signs in property values (#5392)
Co-authored-by: liaogs <liaoguoshun@qq.com>
2026-02-01 04:29:16 +00:00
dependabot[bot]
a2310bf9d7 chore(deps): bump go.mongodb.org/mongo-driver/v2 from 2.4.2 to 2.5.0 (#5393) 2026-01-31 07:18:09 +08:00
dependabot[bot]
be846eba01 chore(deps): bump github.com/redis/go-redis/v9 from 9.17.2 to 9.17.3 (#5390) 2026-01-29 12:32:11 +08:00
Kevin Wan
b20f0e3d60 test(conf): add comprehensive validation tests for Load function (#5388) 2026-01-25 07:21:05 +08:00
RUGBEN
e2bb65d43c fix(conf): Remove redundant validation (#5372)
Co-authored-by: liaogs <liaoguoshun@qq.com>
2026-01-24 15:46:40 +00:00
mk0walsk
94e2f5bd12 Refactor routes and harden AddTool (#5375) 2026-01-24 12:13:35 +00:00
Kevin Wan
173f76acf9 feat: add cmdline argument to control whether generate package name from proto filename (#5387) 2026-01-24 19:47:14 +08:00
godLei6
6e1af75635 rpc service use proto.Package.Name by support multi proto file (#5378) 2026-01-24 08:44:44 +00:00
dependabot[bot]
84ff755e61 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.36.0 to 2.36.1 (#5386)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-24 15:06:06 +08:00
dependabot[bot]
4b9d23aef5 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.35.0 to 2.36.0 (#5381) 2026-01-23 21:55:24 +08:00
dependabot[bot]
97b9aebe99 chore(deps): bump go.mongodb.org/mongo-driver/v2 from 2.4.1 to 2.4.2 (#5385) 2026-01-23 07:44:19 +08:00
Kevin Wan
8e7e5695eb feat(mcp): migrate to official go-sdk with simplified API (#5362) 2025-12-26 00:21:45 +08:00
Kevin Wan
4b4751e76c chore: remove jaeger exporter due to official deprecation (#5361)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-12-25 23:03:14 +08:00
Kevin Wan
fcec494ea8 fix: ignore context cancel on triggering breaker of httpc (#5360)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-12-25 21:39:45 +08:00
Kevin Wan
42117c2dcc feat: upgrade go to version 1.23 (#5359) 2025-12-25 21:08:36 +08:00
dependabot[bot]
4b631f3785 chore(deps): bump github.com/zeromicro/go-zero from 1.9.3 to 1.9.4 in /tools/goctl (#5356)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-25 14:15:09 +08:00
Qiu shao
f29c8612e8 fix(zrpc): fix slow threshold priority in stat interceptor (#5310)
Co-authored-by: qiuwenhao <qiushaotest@qq.com>
2025-12-23 14:45:33 +00:00
Kevin Wan
35ba024103 chore: refactor code (#5352) 2025-12-23 22:29:52 +08:00
kesonan
52df1c532a Fix the issue of incorrect values notified in the configuration center (#5348) 2025-12-23 22:06:04 +08:00
Kevin Wan
39729f3756 fix(discov): add retry cooldown to prevent CPU/disk exhaustion on auth errors (#5347) 2025-12-20 22:04:38 +08:00
Kevin Wan
5c9ea81db2 docs: simplify README files while preserving structure (#5338) 2025-12-13 13:01:35 +08:00
Qiu shao
b284664de4 perf(mapping): use strings.EqualFold to optimize bool parsing (#5324)
Co-authored-by: qiuwenhao <qiushaotest@qq.com>
2025-12-12 15:24:10 +00:00
Ran丶
1b76885040 feat(redis): add redis command for getex (#5323) 2025-12-12 15:18:46 +00:00
Kevin Wan
eef217522b chore: simplify readme (#5334) 2025-12-12 22:32:45 +08:00
Kevin Wan
6bd0d169d5 docs: add AI-Native Development section to README (#5333) 2025-12-12 22:28:47 +08:00
soasurs
3d291328d8 feat(zrpc): migrate kube resolver from Endpoints to EndpointSlice API (#4987)
Signed-off-by: soasurs <soasurs@gmail.com>
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2025-12-11 23:09:08 +08:00
dependabot[bot]
858f8ca82e chore(deps): bump go.mongodb.org/mongo-driver/v2 from 2.4.0 to 2.4.1 (#5329)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-11 20:33:49 +08:00
Qiu shao
4ff3975c5a perf(config): optimize getfullname (#5328)
Co-authored-by: qiuwenhao <qiushaotest@qq.com>
2025-12-10 14:46:26 +00:00
Kevin Wan
7b23f73268 fix(timingwheel): add missing Wait() call and improve code clarity (#5315)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-12-07 11:37:56 +08:00
Kevin Wan
918a7be698 docs: enhance copilot instructions with detailed architecture patterns (#5313)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-06 23:39:28 +08:00
dependabot[bot]
0a724447cd chore(deps): bump github.com/spf13/cobra from 1.10.1 to 1.10.2 in /tools/goctl (#5312) 2025-12-05 09:30:53 +08:00
Gregor Fischer
9e425893a7 Fix typos and grammar in comments (#5308) 2025-12-03 14:32:49 +00:00
dependabot[bot]
4de13b6cc8 chore(deps): bump github.com/redis/go-redis/v9 from 9.17.1 to 9.17.2 (#5307)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-03 22:25:56 +08:00
Qiu shao
c6f75532fa Fix/logx test log mismatchtime schedule num (#5305)
Co-authored-by: qiuwenhao <qiushaotest@qq.com>
2025-11-30 13:44:53 +00:00
dependabot[bot]
fdf4ccf057 chore(deps): bump github.com/zeromicro/go-zero from 1.9.2 to 1.9.3 in /tools/goctl (#5289) 2025-11-29 22:16:18 +08:00
dependabot[bot]
b333ed245b chore(deps): bump github.com/redis/go-redis/v9 from 9.17.0 to 9.17.1 (#5301) 2025-11-28 17:02:14 +08:00
dependabot[bot]
8f1576df36 chore(deps): bump actions/checkout from 5 to 6 (#5297) 2025-11-25 23:05:20 +08:00
Gregor Fischer
72dd970969 Fix Grammar and Typo in Comments (#5284) 2025-11-20 21:26:50 +08:00
dependabot[bot]
29b65e12c1 chore(deps): bump github.com/redis/go-redis/v9 from 9.16.0 to 9.17.0 (#5285)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-20 20:57:32 +08:00
Yuntsy
577a611dc3 fix(logx): add missing color for levelSevere in wrapLevelWithColor (#5281) 2025-11-19 14:46:28 +00:00
Kevin Wan
75941aedd4 refactor: simplify getValueInterface function (#5280) 2025-11-16 20:36:49 +08:00
lerity-yao
c7065171d7 fix(orm): properly handle zero value scanning for pointer destinations (#5270) 2025-11-16 11:59:13 +00:00
Gregor Fischer
052de3b552 chore: fix grammar and typos in comments (#5279) 2025-11-16 11:27:17 +00:00
Kevin Wan
866613af8c Update readme-cn.md (#5266) 2025-11-11 22:24:17 +08:00
Kevin Wan
3d4f6a5e16 Add company to the user list (#5264) 2025-11-01 22:00:35 +08:00
dependabot[bot]
d1d47d02d5 chore(deps): bump go.mongodb.org/mongo-driver/v2 from 2.3.1 to 2.4.0 (#5262) 2025-10-29 09:53:57 +08:00
Kevin Wan
d6c876860b feat(zrpc): change NonBlock default to true following gRPC best practices (#5259) 2025-10-26 12:56:34 +00:00
Kevin Wan
98423ca948 fix(goctl): use rest.Serverless for generated integration tests (#5258) 2025-10-25 23:14:54 +08:00
Kevin Wan
4e52d77ad8 fix(trace): use sync.Once to prevent multiple trace initialization (#5244)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-10-25 20:10:15 +08:00
Kevin Wan
1fc2cfb859 fix: gateway trace headers 5248 (#5256)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-25 12:16:31 +08:00
Gregor Fischer
942cdae41d Fix typos in comments and messages (#5254) 2025-10-25 01:28:40 +00:00
dependabot[bot]
e9c3607bc6 chore(deps): bump github.com/redis/go-redis/v9 from 9.14.1 to 9.16.0 (#5255)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-24 21:19:49 +08:00
dependabot[bot]
d1603e9166 chore(deps): bump github.com/redis/go-redis/v9 from 9.14.0 to 9.14.1 (#5251)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-22 21:01:51 +08:00
zhoushuguang
e30317e9c4 feat: consistent hash balancer support (#5246)
Co-authored-by: 周曙光 <zsg@zhoushuguangdeMacBook-Pro.local>
2025-10-19 14:10:30 +00:00
stemlaud
568f9ce007 chore: remove extra spaces in the comment (#5245)
Signed-off-by: stemlaud <stemlaud@outlook.com>
2025-10-19 13:42:10 +00:00
dependabot[bot]
dcb309065a chore(deps): bump github/codeql-action from 3 to 4 (#5243)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-13 23:09:38 +00:00
Kevin Wan
bf8e17a686 test: add unit tests for goctl docker command (PR #4343) (#5241) 2025-10-13 21:55:25 +08:00
Jack001
b2ebbfce62 fix: ensure Dockerfile includes etc directory and correct CMD based on config (#4343)
Co-authored-by: 白少杰macpro <harrellharris68491@gmail.com>
2025-10-13 13:42:15 +00:00
Kevin Wan
2b10a6a223 fix: support PUT, PATCH, DELETE methods for request body definitions in swagger (#5239) 2025-10-12 18:24:11 +08:00
Kevin Wan
80c320b46e chore: remove unused code (#5238) 2025-10-12 11:55:57 +08:00
Kevin Wan
bea9d150a1 fix(goctl): restore API summaries in swagger generation (#5237) 2025-10-12 11:38:58 +08:00
Kevin Wan
3f756a2cbf chore: update goctl version (#5236) 2025-10-11 18:01:18 +08:00
Kevin Wan
bbe5bbb0c0 chore: update go-redis for the retracted versions (#5235) 2025-10-11 16:20:23 +08:00
dependabot[bot]
5ad2278a69 chore(deps): bump go.mongodb.org/mongo-driver/v2 from 2.3.0 to 2.3.1 (#5230)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-10 21:32:04 +08:00
ZH1995
77763fe748 Fix api doc url in readme-cn.md (#5233) 2025-10-10 13:21:46 +00:00
Kevin Wan
538c4fb5c7 fix: issue #5154, not using sse template files (#5220) 2025-10-08 11:56:52 +00:00
Kevin Wan
315fb2fe0a Add company to the list in readme-cn.md (#5222) 2025-10-07 21:04:09 +08:00
Copilot
e382887eb8 docs: Add comprehensive documentation for blocking Redis operations (XReadGroup, Blpop) (#5221)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kevwan <1918356+kevwan@users.noreply.github.com>
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2025-10-07 16:46:10 +08:00
Kevin Wan
cf21cb2b0b chore: refactor to remove duplicated code (#5216) 2025-10-06 22:24:44 +08:00
Copilot
61e8894c31 Fix swagger generation: info block and server tags not included (#5215)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kevwan <1918356+kevwan@users.noreply.github.com>
2025-10-06 22:02:42 +08:00
Copilot
7a6c3c8129 Fix swagger path generation: remove trailing slash for root routes with prefix (#5212) 2025-10-05 12:12:03 +08:00
Rizky Ikwan
875fec3e1a chore: fix typos (#5210) 2025-10-04 02:56:07 +00:00
Kevin Wan
60128c2100 chore: update goctl version (#5205) 2025-10-02 22:48:57 +08:00
Copilot
ce6d0e3ea7 fix(goctl/swagger): correct $ref placement in array definitions when useDefinitions is enabled (#5199)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kevwan <1918356+kevwan@users.noreply.github.com>
2025-10-02 22:11:18 +08:00
Kevin Wan
fa85c84af3 chore: code refactoring (#5204) 2025-10-02 21:48:03 +08:00
Remember
440884105e feat(handler): add sseSlowThreshold (#5196) 2025-10-02 13:34:44 +00:00
Copilot
271f10598f Add complete test scaffolding support with --test flag for API projects (#5176)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kevwan <1918356+kevwan@users.noreply.github.com>
2025-09-27 21:13:13 +08:00
Remember
cf55a88ce3 fix(rest): change SSE SetWriteDeadline error log to debug level (#5162) 2025-09-27 12:48:35 +00:00
dependabot[bot]
c1c786b14a chore(deps): bump github.com/redis/go-redis/v9 from 9.14.0 to 9.15.0 (#5193)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-27 11:51:15 +08:00
Remember
988fb9d9bf fix: SSE handler blocking (#5181)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-26 13:53:42 +00:00
Copilot
d212c81bca Add GitHub Copilot instructions for go-zero project (#5178)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: kevwan <1918356+kevwan@users.noreply.github.com>
2025-09-20 13:40:43 +08:00
Kevin Wan
bc43df2641 optimize: mapreduce panic stacktrace (#5168) 2025-09-14 19:33:09 +08:00
dependabot[bot]
351b8cb37b chore(deps): bump github.com/redis/go-redis/v9 from 9.13.0 to 9.14.0 (#5169)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-11 22:07:13 +08:00
wanwu
0d681a2e29 opt: optimization of machine performance data reading (#5174)
Co-authored-by: sam.yang <sam.yang@yijinin.com>
2025-09-11 13:56:07 +00:00
dependabot[bot]
5ea027c5de chore(deps): bump actions/setup-go from 5 to 6 (#5156)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-09 21:07:44 +08:00
dependabot[bot]
5de6112dcd chore(deps): bump actions/stale from 9 to 10 (#5157)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-09 18:55:45 +08:00
Kevin Wan
4fb51723b7 Add company to the list (#5153) 2025-09-07 22:53:49 +08:00
me-cs
06502d1115 update:optimize slice find and Unquote func (#5108) 2025-09-07 00:41:45 +00:00
kesonan
3854d6dd00 fix array type generation error (#5142) 2025-09-04 13:41:15 +00:00
dependabot[bot]
895854913a chore(deps): bump github.com/spf13/pflag from 1.0.7 to 1.0.10 in /tools/goctl (#5141)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-04 21:35:50 +08:00
dependabot[bot]
ef753b8857 chore(deps): bump github.com/spf13/cobra from 1.9.1 to 1.10.1 in /tools/goctl (#5147)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-04 21:28:40 +08:00
dependabot[bot]
9c16fede73 chore(deps): bump github.com/redis/go-redis/v9 from 9.12.1 to 9.13.0 (#5149)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-04 21:17:33 +08:00
Kevin Wan
ce11adb5e4 feat: add code generation headers in safe to edit files (#5136) 2025-09-01 21:27:30 +08:00
Kevin Wan
894e8b1218 chore: update goctl version (#5138) 2025-08-31 23:37:00 +08:00
Kevin Wan
2ec7e432dd chore: refactor (#5137) 2025-08-31 17:35:52 +08:00
guonaihong
870e8352c1 fix:issue-5110 (#5113) 2025-08-31 09:17:34 +00:00
Qiying Wang
de42f27e03 feat: prefer json.Marshaler over fmt.Stringer for JSON log output whe… (#5117) 2025-08-31 09:06:25 +00:00
Kevin Wan
955b8016aa feat: support goctl --module to set go module (#5135) 2025-08-31 16:40:49 +08:00
dependabot[bot]
d728a3b2d9 chore(deps): bump github.com/stretchr/testify from 1.11.0 to 1.11.1 in /tools/goctl (#5124)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-31 10:51:06 +08:00
dependabot[bot]
0c205a71fc chore(deps): bump github.com/gookit/color from 1.5.4 to 1.6.0 in /tools/goctl (#5132)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-31 10:42:06 +08:00
dependabot[bot]
a8c0199d96 chore(deps): bump github.com/grafana/pyroscope-go from 1.2.4 to 1.2.7 (#5121) 2025-08-28 08:47:40 +08:00
dependabot[bot]
032a266ec4 chore(deps): bump github.com/stretchr/testify from 1.11.0 to 1.11.1 (#5125) 2025-08-28 08:46:29 +08:00
dependabot[bot]
40b75fbb9b chore(deps): bump github.com/stretchr/testify from 1.10.0 to 1.11.0 (#5120) 2025-08-27 00:28:34 +08:00
dependabot[bot]
afad55045b chore(deps): bump github.com/stretchr/testify from 1.10.0 to 1.11.0 in /tools/goctl (#5119) 2025-08-26 21:09:57 +08:00
Kevin Wan
5f54f06ee5 chore: refactor field keys in logx (#5104) 2025-08-20 20:48:47 +08:00
Qiying Wang
20f56ae1d0 feat: support customize of log keys (#5103) 2025-08-20 12:11:45 +00:00
geekeryy
73d6fcfccd feat: Support projectPkg template variables in config, handler, logic, main, and svc template files (#4939) 2025-08-19 12:29:41 +00:00
Kevin Wan
20d20ef861 fix: github release workflow (#5096) 2025-08-17 23:04:32 +08:00
Kevin Wan
a37422b504 fix: release workflows (#5095) 2025-08-17 17:50:38 +08:00
Kevin Wan
a81d898408 chore: update go-zero version (#5093)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-08-17 17:00:15 +08:00
kesonan
a5d42e20d5 Update goctl version to 1.9.0-alpha (#5090) 2025-08-15 12:08:02 +00:00
kesonan
4bdb07f225 (goctl)feature: supported sse generation (#5082)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-15 01:49:50 +00:00
dependabot[bot]
3e6ec9b83d chore(deps): bump go.mongodb.org/mongo-driver/v2 from 2.2.3 to 2.3.0 (#5087) 2025-08-15 05:08:22 +08:00
me-cs
f0a3d213dc update:simplify slice lookup by using slices.Contains() (#5084)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-14 15:33:25 +00:00
Kevin Wan
94562ded74 chore: fix compile errors (#5085)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-08-14 23:09:35 +08:00
guonaihong
d68cf4920c fix: 5076 (#5083)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-14 22:32:09 +08:00
dependabot[bot]
31b749ab67 chore(deps): bump actions/checkout from 4 to 5 (#5080) 2025-08-12 15:15:25 +08:00
dependabot[bot]
3834319278 chore(deps): bump github.com/redis/go-redis/v9 from 9.12.0 to 9.12.1 (#5078) 2025-08-12 12:27:33 +08:00
Kevin Wan
1c9d339361 chore: use uber gomock instead of golang/mock (#5075) 2025-08-10 23:01:08 +08:00
Kevin Wan
b7f601c912 feat: support sse in api files (#5074)
Signed-off-by: Kevin Wan <wanjunfeng@gmail.com>
2025-08-10 22:17:08 +08:00
Kevin Wan
1ebbc6f0c7 chore: refactor mon/monc (#5073) 2025-08-09 23:51:44 +08:00
me-cs
b41b1b00df update:github.com/mongodb/mongo-go-driver v2.0 Migration (#4687) 2025-08-09 13:21:53 +00:00
hoshi
f36e5fed35 fear(model);add uuid:varchar to p2m (#5022)
Co-authored-by: hoshi <zheng.hao1@outlook.com>
2025-08-09 06:24:33 +00:00
Kevin Wan
2583673c8b chore: always ignore unknown fields for gateway requests (#5072)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-08-09 13:07:17 +08:00
guonaihong
00e67b9d20 feat: add option to ignore unknown fields in gateway request parsing (#5058) 2025-08-09 04:40:07 +00:00
Kevin Wan
9fd1f29845 chore: refactor (#5071) 2025-08-09 12:27:43 +08:00
Ioannis Pinakoulakis
130e1ba963 perf: pre-allocate all known length arrays to avoid re-scaling (#5029)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2025-08-08 16:03:25 +00:00
Kevin Wan
a2b98dbcf7 chore: refactor (#5068) 2025-08-08 23:30:03 +08:00
sunhao1296
b46d507a1d fix: resolve concurrent get may lead to empty result in ImmutableResource (#5065)
Co-authored-by: hsun <hsun@apac.freewheel.com>
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-08 23:00:49 +08:00
Kevin Wan
3152581d0d optimize: logging with fields (#5066) 2025-08-08 12:23:10 +00:00
dependabot[bot]
46e466f037 chore(deps): bump github.com/redis/go-redis/v9 from 9.11.0 to 9.12.0 (#5056)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-06 01:18:27 +00:00
Kevin Wan
151b3d1085 chore: fix codecov warning (#5055)
Signed-off-by: Kevin Wan <wanjunfeng@gmail.com>
2025-08-05 22:55:26 +08:00
Kevin Wan
ea53fe41de chore: fix codecov problem (#5053) 2025-08-05 21:37:22 +08:00
queryfast
d9df08b079 chore: fix some minor issues in comments (#5051)
Signed-off-by: queryfast <queryfast@outlook.com>
2025-08-05 04:13:44 +00:00
Kevin Wan
569c00ad09 chore: refactor redis stream (#5048)
Signed-off-by: Kevin Wan <wanjunfeng@gmail.com>
2025-08-03 14:58:09 +08:00
jk2K
9da76fbf04 feat: redis support consumer groups (#4912)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-02 13:36:08 +00:00
Kevin Wan
b69db5e09d chore: refactor etcd discov (#5046) 2025-08-01 23:12:49 +08:00
guonaihong
ee6b7cee79 fix: Issue with etcd key disappearing and unable to auto-re-register (#4960) 2025-08-01 22:54:07 +08:00
Kevin Wan
d150248c52 chore: refactor jsonx.Marshal (#5045)
Signed-off-by: Kevin Wan <wanjunfeng@gmail.com>
2025-08-01 22:05:13 +08:00
Kevin Wan
610a7345dc chore: refactor gateway middlewares (#5042) 2025-08-01 00:09:57 +08:00
Yi Deng
b0b31f3993 feat(gateway): add custom middleware support with onion model (#5035) 2025-07-31 13:33:36 +00:00
Kevin Wan
82a937d517 chore: refactor unit tests (#5041) 2025-07-31 20:10:45 +08:00
Joe Bird
93c11a7eb7 fix(httpx): Resolve HTML escaping issue during JSON serialization (#5032) 2025-07-31 11:53:40 +00:00
Kevin Wan
63ec989376 fix: large memory usage on detail logging post requests (#5039) 2025-07-31 19:09:32 +08:00
Kevin Wan
bf75027889 chore: add more tests (#5038) 2025-07-30 19:45:35 +08:00
Kevin Wan
d505fae979 fix: unmarshal problem on env vars for type env string (#5037)
Signed-off-by: kevin <wanjunfeng@gmail.com>
Signed-off-by: Kevin Wan <wanjunfeng@gmail.com>
2025-07-30 18:09:25 +08:00
Kevin Wan
25f37ca750 chore: add unit test for WithCodeResponseWriter (#5028)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-07-25 21:45:47 +08:00
csbzy
0be63c3625 Fix SSE timeout will affected by http.Server 's WriteTimeout (#5024)
Co-authored-by: csbzy <chenshaobo65@mail.com>
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-23 23:04:45 +08:00
dependabot[bot]
b011a072c7 chore(deps): bump github.com/spf13/pflag from 1.0.6 to 1.0.7 in /tools/goctl (#5021) 2025-07-23 09:53:42 +08:00
Kevin Wan
3c9b6335fb chore: refactor set in collection package (#5016) 2025-07-18 21:25:40 +08:00
Qiu shao
bf6ef5f033 feat: add generic TypedSet with 2x performance boost and compile-time (#4888)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-18 12:57:43 +00:00
Kevin Wan
ff890628b0 feat: optimize ignore fields in orm (#5015) 2025-07-18 20:36:28 +08:00
wiki
cc79e3d842 feat(sqlx): add field tag (-) skip logic in unwrapFields (#5010)
Co-authored-by: wukun30 <wukun30@meituan.com>
2025-07-18 11:58:38 +00:00
Kevin Wan
f11b78ced9 feat: support masking sensitive data in logx (#5003)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-07-18 19:51:22 +08:00
dependabot[bot]
1d2b0d7ab8 chore(deps): bump github.com/grafana/pyroscope-go from 1.2.3 to 1.2.4 (#5014)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-18 10:41:26 +08:00
dependabot[bot]
da987e1270 chore(deps): bump github.com/grafana/pyroscope-go from 1.2.2 to 1.2.3 (#5004)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-15 11:51:22 +08:00
Kevin Wan
12e03c8843 chore: update goctl version (#5002) 2025-07-13 11:18:51 +08:00
Twilikiss
8cf4f95bd7 Fix POST JSON parameter determination logic (goctl api swagger) & Add some unit test. (#4997)
Co-authored-by: XiaobinChen <xiaobin.chen@corerain.com>
2025-07-13 03:07:35 +00:00
anlynn
ba0febf308 fix: Fix PostgreSQL numeric type mapping in goctl model generation (#4992)
Co-authored-by: 李安琳 <anlynn@gmail.com>
2025-07-13 02:40:16 +00:00
Kevin Wan
c9ff6a10d3 feat: support serverless in rest (#5001)
Signed-off-by: kevin <wanjunfeng@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-13 00:00:52 +08:00
Kevin Wan
a71e56de52 fix: context key error in sql read write mode (#5000) 2025-07-12 06:58:08 +08:00
Kevin Wan
bae8d4f4c8 chore: refactoring sql read write mode (#4990)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-07-11 01:05:55 +08:00
zhoushuguang
8c6266f338 sql read write support (#4976)
Co-authored-by: light.zhou <light.zhou@bkyo.io>
2025-07-09 16:04:56 +00:00
Kevin Wan
95d5b81f44 chore: optimize pr 4979 (#4988)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-07-09 23:55:24 +08:00
geekeryy
bca7bbc142 fix: correct duration type comparison in environment variable processing (#4979) 2025-07-09 15:22:27 +00:00
Kevin Wan
df9a52664b fix issue #4986 2025-07-08 13:58:48 +00:00
Kevin Wan
937cf0db96 Update readme-cn.md (#4983) 2025-07-04 11:02:49 +08:00
Kevin Wan
75cebb65f8 fix: timeout 0s not working (#4932)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-07-01 17:01:24 +08:00
dependabot[bot]
410f56e73a chore(deps): bump github.com/redis/go-redis/v9 from 9.10.0 to 9.11.0 (#4969)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-25 18:35:01 +08:00
dependabot[bot]
017909a3ab chore(deps): bump github.com/emicklei/proto from 1.14.1 to 1.14.2 in /tools/goctl (#4961)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-19 15:40:18 +08:00
kesonan
0d31e6c375 (goctl): fix #4943 (#4953) 2025-06-14 15:36:30 +00:00
Kevin Wan
0ba86b1849 chore: add more tests (#4949) 2025-06-13 22:10:08 +08:00
wanwu
4cacc4d9d3 fix: the time.Duration type panics due to numerical values (#4944)
Co-authored-by: sam.yang <sam.yang@yijinin.com>
2025-06-12 15:11:07 +00:00
Eric
a99c14da4a fix: typo of the logic of CpuThreshold in comments (#4942)
Co-authored-by: zhouyy <zhouyy@ickey.cn>
2025-06-12 08:28:44 +00:00
Kevin Wan
985582264a chore: fix warnings (#4940) 2025-06-12 00:04:29 +08:00
Kevin Wan
8364e341e1 chore: update go-zero dep (#4933)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-06-09 18:08:20 +08:00
Kevin Wan
0f2b589d4d Revert "fix: api group set timeout: 0s not working." (#4931) 2025-06-08 23:14:38 +08:00
spectatorMrZ
19fec36d24 fix: api group set timeout: 0s not working. (#4785) 2025-06-08 14:50:21 +00:00
Kevin Wan
f037bf344d chore: add more tests (#4930) 2025-06-08 22:08:04 +08:00
MarkJoyMa
d99cf35b07 Feat/continue profiling (#4867)
Co-authored-by: aiden.ma <Aiden.ma@yijinin.com>
Co-authored-by: aiden.ma <aiden.ma@bkyo.io>
2025-06-07 21:12:31 +08:00
Kevin Wan
f459f1b5ff chore: update goctl version (#4929) 2025-06-07 21:01:35 +08:00
Haiwei Zhang
0140fd417b feat(goctl): generate mongo model with cache prefix (#4907) 2025-06-07 12:54:33 +00:00
jaron
7969e0ca38 fix(goctl): Fix getting swagger consume types (#4903) 2025-06-07 12:46:34 +00:00
Kevin Wan
91c885b5b0 chore: add more unit tests for mcp (#4928) 2025-06-07 20:41:57 +08:00
MarkJoyMa
d4cccca387 Fix the problem that mcp request id is not of int type (#4914) 2025-06-07 10:37:18 +08:00
dependabot[bot]
4b2095ed03 chore(deps): bump github.com/redis/go-redis/v9 from 9.9.0 to 9.10.0 (#4926) 2025-06-07 10:07:26 +08:00
dependabot[bot]
1229eeb2d2 chore(deps): bump go.mongodb.org/mongo-driver from 1.17.3 to 1.17.4 (#4924) 2025-06-06 19:45:26 +08:00
dependabot[bot]
9142b146c5 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.34.0 to 2.35.0 (#4919)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-06 10:09:15 +08:00
Kevin Wan
8a1b2d5aed chore: fix typo (#4920) 2025-06-05 22:51:22 +08:00
Leon cap
da5d39e6ca fix: correct spelling of 'cancellation' in timeout handler comment (#4916)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2025-06-05 22:42:53 +08:00
Leon cap
68c5a17c67 fix: correct spelling of 'underlying' in Header method comment (#4918) 2025-06-05 10:36:21 +00:00
Leon cap
b53f9f5f2d fix: correct spelling of 'TimeoutHandler' in timeout handler comment (#4917) 2025-06-04 15:48:37 +00:00
dependabot[bot]
36d57626b6 chore(deps): bump github.com/redis/go-redis/v9 from 9.8.0 to 9.9.0 (#4905)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-28 11:32:57 +08:00
Kevin Wan
4e36ba832f Update readme.md (#4897) 2025-05-25 22:25:56 +08:00
Kevin Wan
a44954a771 fix: don't set read/write timeout if timeout middleware disabled (#4895)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-05-25 15:07:58 +08:00
kesonan
f3edd4b880 goctl: v1.8.4-beta (#4890) 2025-05-25 05:36:56 +00:00
Kevin Wan
2de3e397ff chore: revert go version to 1.21 (#4893) 2025-05-24 18:17:49 +08:00
Qiu shao
a435eb56f2 perf(hash): optimize Md5Hex encoding performance (#4891) 2025-05-24 08:41:21 +00:00
Kevin Wan
d80761c147 chore: refactor coding style (#4887) 2025-05-22 23:29:40 +08:00
me-cs
e7bd0d8b60 update:To standardize the time format, use the go standard library's own (#4875) 2025-05-22 15:26:53 +00:00
me-cs
b109b3ef4c update:use builtin cmp func (#4879) 2025-05-22 15:19:13 +00:00
Kevin Wan
e3c371ac89 chore: refactor 2025-05-20 12:59:35 +00:00
燕归来
15eb6f4f6d test(hash): modify TestConsistentHashTransferOnFailure to more reasonable test transfer ratio (#4874) 2025-05-20 12:51:50 +00:00
me-cs
4d3681b71c Optimize slicing operations (#4877) 2025-05-20 11:36:02 +00:00
dependabot[bot]
a682bda0bb chore(deps): bump github.com/jackc/pgx/v5 from 5.7.4 to 5.7.5 (#4871)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 10:19:06 +08:00
kesonan
45b27ad93a goctl: 1.8.4-beta (#4869) 2025-05-19 15:30:50 +00:00
Kevin Wan
292a8302a1 chore: optimize mcp (#4866) 2025-05-17 12:28:06 +08:00
kesonan
91ab1f6d2b goctl features of 1.8.4-alpha (#4849) 2025-05-15 13:59:48 +00:00
Kevin Wan
5048c350ae chore: fix test failure in profilecenter_test.go 2025-05-15 13:31:53 +00:00
Kevin Wan
94edc32f3e chore: optimize profile center and remove tablewriter dependency 2025-05-15 13:22:27 +00:00
Kevin Wan
ec989b2e2a chore: for backward compatibility (#4852)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-05-11 20:19:00 +08:00
me-cs
82fe802e81 update:Use the official sync.OnceFunc (#4840) 2025-05-11 12:08:43 +00:00
me-cs
072d68f897 update:Use the official slice operate func (#4841) 2025-05-11 11:48:54 +00:00
Kevin Wan
2e91ba5811 chore: refactor rest file server (#4851)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-05-11 12:44:43 +08:00
shaouai
5564c43197 feat: serve files using embed.FS (#4847) 2025-05-10 15:43:13 +00:00
Kevin Wan
e55158b0f7 chore: update deps in goctl (#4830) 2025-05-04 16:18:02 +08:00
Kevin Wan
69aa7fe346 feat: improve mcp (#4828)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-05-04 15:29:14 +08:00
kesonan
c3820a95c1 release goctl swagger (#4829) 2025-05-04 06:05:41 +00:00
Kevin Wan
493f3bad0f fix: allow special characters like periods in API route paths (#4827) 2025-05-04 11:07:43 +08:00
Kevin Wan
eb0d5ad3a4 Update readme-cn.md (#4826) 2025-05-02 17:31:49 +08:00
wwwfeng
14192050ae fix: goctl api tsgen (#4726) 2025-05-02 09:15:32 +00:00
spectatorMrZ
9193e771e3 fix: pg gen model missing cache prefix (#4788)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2025-05-02 08:52:55 +00:00
hoshi
808b4e496a feat:add cache prefix support to pg (#4741)
Co-authored-by: 郑好 <zheng.hao1@outlook.com>
2025-05-02 08:10:47 +00:00
kesonan
e416d01f8d api format from stdin (#4772) 2025-05-02 07:59:47 +00:00
Joe Bird
789c5de873 fix(marshaler): fix bug when marshal array (#4790) 2025-05-02 07:54:00 +00:00
Rankgice
52078a0c14 Fix the issue of generating swagger @doc "xxx" that fails, and use th… (#4816)
Co-authored-by: lxr <qiyuechuqi@an-idear.com>
2025-05-02 07:06:03 +00:00
Qiu shao
7ef13116a0 feat: add http to http method test case (#4820) 2025-05-02 06:59:36 +00:00
dependabot[bot]
6b8053410a chore(deps): bump github.com/redis/go-redis/v9 from 9.7.3 to 9.8.0 (#4821)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-02 14:48:30 +08:00
dependabot[bot]
81c6928445 chore(deps): bump github.com/emicklei/proto from 1.14.0 to 1.14.1 in /tools/goctl (#4817)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-02 14:29:42 +08:00
Kevin Wan
761c2dd716 chore: update goctl version (#4822) 2025-05-02 14:18:54 +08:00
kesonan
aeceb3cfbe Update goctl version to 1.8.3-beta (#4812) 2025-04-27 15:53:49 +00:00
kesonan
15ea07aad1 goctl: support custom swagger authentication (#4811) 2025-04-27 15:43:37 +00:00
shaouai
98bebbc74f feat(swagger): allow users to specify the generated swagger file name (#4809) 2025-04-27 15:34:52 +00:00
kesonan
eafd11d949 goctl: supported api types group for EXPERIMENTAL(实验性功能:支持 api type 结构体按照分组名称拆分文件) (#4810) 2025-04-27 15:18:33 +00:00
Kevin Wan
b251ce346e feat: mcp server sdk (#4794)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-04-27 23:06:37 +08:00
kesonan
812140ba36 fix: goctl swagger missing security definition and submit json body data error (#4808) 2025-04-25 14:58:45 +00:00
kesonan
44735e949c fix array schmea generation incorrect (#4801) 2025-04-23 23:59:01 +00:00
kesonan
bf313c3c56 fix: swagger separator incorrect in Windows OS (#4799) 2025-04-23 13:51:02 +00:00
kesonan
94e7753262 fix: the parameter "required" in the Swagger document generated for repair is incorrect (#4791) 2025-04-21 04:18:02 +00:00
kesonan
9c478626d2 feature/goctl-api-swagger (#4780) 2025-04-17 14:38:55 +00:00
Kevin Wan
801c283478 Delete issue-translator.yml 2025-04-10 12:01:21 +08:00
Kevin Wan
2a54faf997 chore: coding style (#4771) 2025-04-10 09:28:42 +08:00
Hanggang Z
ecd98f3653 chore: add more orm_test (#4766) 2025-04-09 13:49:00 +00:00
soasurs
61641581eb fix: form fields of request optional (#4755)
Signed-off-by: soasurs <soasurs@gmail.com>
2025-04-08 13:05:21 +00:00
Kevin Wan
6f2730d5ae chore: update goctl version (#4754) 2025-04-06 19:09:02 +08:00
dependabot[bot]
0eff777b62 chore(deps): bump github.com/jackc/pgx/v5 from 5.7.2 to 5.7.4 (#4737)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-26 22:41:55 +08:00
dependabot[bot]
cafbf535f7 chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.5.1 to 4.5.2 (#4734) 2025-03-26 16:28:36 +08:00
Kevin Wan
6edfce63e3 feat: add rest.WithSSE to build SSE route easier (#4729) 2025-03-22 13:38:13 +08:00
dependabot[bot]
cdb0098b18 chore(deps): bump github.com/redis/go-redis/v9 from 9.7.1 to 9.7.3 (#4722) 2025-03-21 09:51:31 +08:00
Kevin Wan
620c7f9693 chore: add more tests (#4718) 2025-03-19 23:54:04 +08:00
Meng Ye
dba444a382 feat: support redis getdel command (#4709) 2025-03-19 23:40:14 +08:00
dependabot[bot]
b24fb3ebf7 chore(deps): bump github.com/fullstorydev/grpcurl from 1.9.2 to 1.9.3 (#4701) 2025-03-12 12:05:28 +08:00
POABOB
967f0926eb fix: fix the bug of the numeric/decimal data type in pg (#4686) 2025-03-07 11:12:02 +00:00
dependabot[bot]
e68c683df9 chore(deps): bump github.com/prometheus/client_golang from 1.21.0 to 1.21.1 (#4683)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-05 11:14:23 +08:00
Kevin Wan
247985a065 chore: refactoring & add more tests (#4677) 2025-03-02 20:11:10 +08:00
anlynn
80573af0d8 feat: Add support for serialization of anonymous fields in HTTP client(httpc) (#4676)
Co-authored-by: 李安琳 <anlynn@gmail.com>
2025-03-02 19:10:50 +08:00
Kevin Wan
c0394b631a chore: fix display problems in version-check workflow (#4675) 2025-03-01 22:18:49 +08:00
Kevin Wan
68d1aba377 chore: upgrade go-zero version in goctl (#4674) 2025-03-01 21:23:23 +08:00
Kevin Wan
3315e60272 chore: performance tunning for stable runner (#4670) 2025-02-26 19:19:24 +08:00
dependabot[bot]
327ef73700 chore(deps): bump go.mongodb.org/mongo-driver from 1.17.2 to 1.17.3 (#4669) 2025-02-26 10:03:43 +08:00
dependabot[bot]
eb11521655 chore(deps): bump github.com/redis/go-redis/v9 from 9.7.0 to 9.7.1 (#4665) 2025-02-22 08:19:28 +08:00
Kevin Wan
4c37545e55 Update readme-cn.md (#4664) 2025-02-20 21:13:33 +08:00
dependabot[bot]
2f47c1fba4 chore(deps): bump github.com/prometheus/client_golang from 1.20.5 to 1.21.0 (#4663) 2025-02-20 11:14:13 +08:00
dependabot[bot]
16d54d0ace chore(deps): bump github.com/go-sql-driver/mysql from 1.8.1 to 1.9.0 in /tools/goctl (#4662)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-19 11:38:41 +08:00
dependabot[bot]
9925bcbf99 chore(deps): bump github.com/go-sql-driver/mysql from 1.8.1 to 1.9.0 (#4661)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-19 11:13:41 +08:00
dependabot[bot]
38a5ecb796 chore(deps): bump github.com/spf13/cobra from 1.8.1 to 1.9.1 in /tools/goctl (#4660)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-18 10:47:42 +08:00
Kevin Wan
af78fc7c5f chore: add more tests (#4656) 2025-02-14 23:43:34 +08:00
Kevin Wan
790302b486 fix: should not ignore slowThreshold (#4655) 2025-02-14 23:14:57 +08:00
Nanosk07
6a0672b801 fix: SlowThreshold configuration not taking effect (#4654) 2025-02-14 14:56:25 +00:00
Kevin Wan
560c61612c chore: refactor (#4652) 2025-02-14 09:41:54 +08:00
Kevin Wan
6a988dc4a9 fix: make test purpose method private (#4649) 2025-02-14 00:31:53 +08:00
Kevin Wan
15842c3c7a Create version-check.yml (#4646) 2025-02-13 19:39:22 +08:00
Rui Chen
f2914a74df fix: update version to match with the release (#4645) 2025-02-13 01:11:09 +00:00
Kevin Wan
f113d512e8 chore: coding style (#4644) 2025-02-12 23:48:39 +08:00
kesonan
7a4818da59 Generate caches that support custom key prefix. (#4643) 2025-02-12 15:31:30 +00:00
dependabot[bot]
48d0709ca6 chore(deps): bump golang.org/x/net from 0.34.0 to 0.35.0 (#4640) 2025-02-11 09:25:16 +08:00
Kevin Wan
f747585518 chore: simplify http query array parsing (#4637) 2025-02-09 01:00:52 +08:00
xuerbujia
507ff96546 feat add tag switch to disable form array of split comma format (#4633)
Co-authored-by: wuhongyu <readboy@DESKTOP-T8INU17>
2025-02-09 00:34:41 +08:00
Kevin Wan
651eabb4c6 chore: refactor gateway http context (#4636) 2025-02-08 21:18:07 +08:00
#Suyghur
e6b4372056 fix(gateway): fixed http gateway context propagation error (#4634) 2025-02-08 09:50:26 +00:00
Kevin Wan
24073969a1 fix: redis username not working in redis v7 (#4632) 2025-02-08 12:21:35 +08:00
Kevin Wan
ca797ed22c chore: add trailing newlines (#4631) 2025-02-07 23:39:02 +08:00
youzipi
e347d3f8f8 fix(goctl): allow duplicate_path_expression under different prefix (#4626) 2025-02-07 08:37:11 +00:00
dependabot[bot]
396393b336 chore(deps): bump google.golang.org/protobuf from 1.36.4 to 1.36.5 (#4627) 2025-02-07 10:05:22 +08:00
dependabot[bot]
1f0531b254 chore(deps): bump google.golang.org/protobuf from 1.36.4 to 1.36.5 in /tools/goctl (#4628) 2025-02-07 08:25:54 +08:00
ningzi
77fb271a06 feat(goctl): support go work (#4332) (#4344) 2025-02-05 14:22:40 +00:00
dependabot[bot]
af7cf79963 chore(deps): bump golang.org/x/time from 0.9.0 to 0.10.0 (#4621)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-05 22:00:54 +08:00
dependabot[bot]
7926d396d7 chore(deps): bump golang.org/x/text from 0.21.0 to 0.22.0 in /tools/goctl (#4620)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-05 10:16:06 +08:00
dependabot[bot]
080cd3df84 chore(deps): bump golang.org/x/sys from 0.29.0 to 0.30.0 (#4622) 2025-02-05 09:27:03 +08:00
Kevin Wan
c4e1a6a2d8 chore: refactor mapreduce (#4619) 2025-02-01 00:12:37 +08:00
Kevin Wan
4e71e95e44 chore: add comments (#4618) 2025-01-31 22:42:46 +08:00
JiChen
84db9bcd15 fix: global fields apply to Third-party log module (#4400) 2025-01-31 13:51:20 +00:00
dependabot[bot]
b28f79ac11 chore(deps): bump github.com/spf13/pflag from 1.0.5 to 1.0.6 in /tools/goctl (#4615) 2025-01-30 16:49:47 +08:00
Kevin Wan
e134e77b2b chore: update go-zero to v1.7.6 for goctl (#4614) 2025-01-29 12:47:23 +08:00
Kevin Wan
f669d84ce8 chore: not using goproxy by default (#4613) 2025-01-29 12:28:47 +08:00
Kevin Wan
9213b8ac27 chore: update go-zero to v1.8.0 for goctl (#4611) 2025-01-29 10:21:40 +08:00
Kevin Wan
ae09d0e56d chore: use logc instead of logx if possible (#4610) 2025-01-29 00:32:21 +08:00
Kevin Wan
0bc4206d08 feat: support freebsd (#4609) 2025-01-28 11:11:49 +08:00
Kevin Wan
39ce17bfd2 feat: auto validate config (#4607) 2025-01-28 09:37:27 +08:00
Kevin Wan
d415ba39e2 feat: support http->http in gateway (#4605) 2025-01-27 20:00:58 +08:00
Kevin Wan
c71829c8de Potential fix for code scanning alert no. 57: Arbitrary file access during archive extraction ("Zip Slip") (#4604)
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-01-27 03:53:35 +00:00
Kevin Wan
a32f6d7642 chore: add more tests for logx/logc (#4603) 2025-01-26 00:07:19 +08:00
Devin
64e8c94198 add Debugfn and Infofn to logx/logc #4595 (#4598) 2025-01-25 22:21:50 +08:00
dependabot[bot]
7d05a4bc93 chore(deps): bump google.golang.org/protobuf from 1.36.3 to 1.36.4 (#4601)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-25 19:06:31 +08:00
Kevin Wan
44504e8df7 fix: different concreate types still cause panic (#4597) 2025-01-25 18:53:32 +08:00
dependabot[bot]
114311e51b chore(deps): bump google.golang.org/protobuf from 1.36.3 to 1.36.4 in /tools/goctl (#4602)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-25 18:19:32 +08:00
Kevin Wan
4307ce45fc chore: add redis test (#4593) 2025-01-23 00:03:49 +08:00
xujb
37b54d1fc7 A new User property has been added to the RedisConf object. (#4559) 2025-01-22 23:28:14 +08:00
Kevin Wan
00e0db5def chore: remove useless code (#4592) 2025-01-22 22:55:52 +08:00
Devin
cbcacf31c1 fix: httpx.ParseJsonBody error when request has []byte field #4450 (#4471) 2025-01-22 21:35:32 +08:00
benben
238c92aaa9 fix: wrong way of Unmarshal (#4397) 2025-01-22 13:25:35 +00:00
Kevin Wan
520d2a2075 chore: only upload metrics on mysql (#4591) 2025-01-22 20:26:24 +08:00
Kevin Wan
1023800b02 fix: health check problem (#4590) 2025-01-22 19:32:28 +08:00
MarkJoyMa
030c859171 feat/sqlx_metric (#4587)
Co-authored-by: aiden.ma <Aiden.ma@yijinin.com>
2025-01-22 07:42:02 +00:00
Kevin Wan
e6d1b47a43 Revert "feat/conf_map_required" (#4580) 2025-01-22 07:10:47 +00:00
Devin
6138f85470 Implement #4442 , goctl generate unit test files for api handler and logic (#4443) 2025-01-22 06:52:49 +00:00
Kevin Wan
bf883101d7 fix: etcd discovery mechanism on grpc with idle manager (#4589) 2025-01-22 14:01:18 +08:00
Nanosk07
33011c7ed1 fix: routinegroup & etcd watch goroutine leak (#4514)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2025-01-22 13:38:56 +08:00
saury
17d98f69e0 fix memory leak of grpc resolver (#4490)
Co-authored-by: nk <kui.niu@akuvox.com>
2025-01-22 13:36:13 +08:00
kesonan
b650c8c425 fix syntax of the key expression (#4586) 2025-01-18 15:46:24 +00:00
dependabot[bot]
3d931d7030 chore(deps): bump google.golang.org/protobuf from 1.36.2 to 1.36.3 (#4574)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-16 14:39:27 +08:00
dependabot[bot]
68da9ed51a chore(deps): bump google.golang.org/protobuf from 1.36.2 to 1.36.3 in /tools/goctl (#4575)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-16 13:32:33 +08:00
Kevin Wan
b25c45b352 chore: code format (#4572) 2025-01-14 23:16:13 +08:00
R1aEnK
f05234a967 opt:Error message for optimizing mapping data method (#4570) 2025-01-14 22:50:49 +08:00
MarkJoyMa
12071d17b4 feat/conf_map_required (#4405)
Co-authored-by: aiden.ma <Aiden.ma@yijinin.com>
2025-01-12 17:13:41 +00:00
Kevin Wan
11c47d23df fix: ignore empty form values in http request (#4542) 2025-01-13 01:03:32 +08:00
dependabot[bot]
024f285f86 chore(deps): bump go.mongodb.org/mongo-driver from 1.17.1 to 1.17.2 (#4561)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-11 16:16:34 +08:00
dependabot[bot]
fa4674611a chore(deps): bump google.golang.org/protobuf from 1.36.1 to 1.36.2 (#4556)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-10 08:50:53 +08:00
dependabot[bot]
730c3c5246 chore(deps): bump golang.org/x/net from 0.33.0 to 0.34.0 (#4554)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-10 08:41:10 +08:00
dependabot[bot]
2c9310ac3a chore(deps): bump google.golang.org/protobuf from 1.36.1 to 1.36.2 in /tools/goctl (#4553)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-09 22:57:51 +08:00
dependabot[bot]
74ba0bcd50 chore(deps): bump golang.org/x/sys from 0.28.0 to 0.29.0 (#4548)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-07 11:50:51 +08:00
dependabot[bot]
5f4190b6c6 chore(deps): bump golang.org/x/time from 0.8.0 to 0.9.0 (#4547)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-07 11:38:26 +08:00
Kevin Wan
e1787b4ccb chore: update go version (#4540)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-01-04 12:17:10 +08:00
Kevin Wan
4ac8b492ef chore: update go-zero version (#4539) 2025-01-02 22:33:19 +08:00
Kevin Wan
cdd068575c fix: goctl compile error on windows (#4538) 2025-01-02 22:12:10 +08:00
Kevin Wan
e89e2d8a75 fix compiling error on windows. 2025-01-02 07:00:42 +00:00
Kevin Wan
acd2b94bd9 fix compiling error for goctl on windows 2025-01-02 06:51:40 +00:00
Kevin Wan
6a0c8047f4 fix compiling error for goctl on windows 2025-01-02 06:22:26 +00:00
Kevin Wan
cfe03ea9e1 chore: update goctl deps (#4535) 2025-01-02 00:26:08 +08:00
Kevin Wan
48d21ef8ad chore: add comments (#4534) 2025-01-01 20:49:45 +08:00
Kevin Wan
28a001c5f9 chore: refactor shutdown config, to prevent setting zero values (#4533)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-01-01 20:46:51 +08:00
Qiying Wang
22a41cacc7 feat: add ProcConf to make SetTimeToForceQuit configurable (#4446) 2025-01-01 11:48:53 +00:00
Kevin Wan
fcc246933c fix: service group not working well when callback takes long time (#4531)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2025-01-01 07:06:50 +00:00
dependabot[bot]
5c3679ffe7 chore(deps): bump google.golang.org/protobuf from 1.36.0 to 1.36.1 in /tools/goctl (#4521) 2024-12-28 12:05:03 +08:00
Kevin Wan
eaa01ccb9f Update readme-cn.md (#4525) 2024-12-28 09:59:00 +08:00
dependabot[bot]
b8206fb46a chore(deps): bump google.golang.org/protobuf from 1.36.0 to 1.36.1 (#4523)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-24 09:58:03 +08:00
kesonan
1c3876810e fix command goctl bug invalid (#4520) 2024-12-23 16:00:50 +00:00
Kevin Wan
1d9159ea39 feat: support form array in three notations (#4498)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2024-12-23 00:56:20 +08:00
Kevin Wan
2159d112c3 chore: format the code (#4518) 2024-12-22 15:10:44 +08:00
不插电
f57874a51f fix: DetailedLog format. (#4467) 2024-12-21 03:48:27 +00:00
Xingchen Wang
8625864d43 bugfix:SetSlowThreshold not effective in function logDetails (#4511)
Co-authored-by: Star Wang <starwang@Star-Wangs-MacBook-Pro.local>
2024-12-21 03:22:54 +00:00
dependabot[bot]
8f9ba3ec11 chore(deps): bump golang.org/x/net from 0.32.0 to 0.33.0 (#4516)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-20 12:04:16 +08:00
dependabot[bot]
a1d9bc08f0 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.33.0 to 2.34.0 (#4509)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-19 18:13:12 +08:00
dependabot[bot]
b9d7f1cc77 chore(deps): bump github.com/emicklei/proto from 1.13.4 to 1.14.0 in /tools/goctl (#4510)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-19 17:54:44 +08:00
kesonan
6700910f64 (goctl)fix: api timeout limited during api generation (#4513) 2024-12-19 08:19:10 +00:00
godLei6
9c4ed394a7 fix: go work duplicate prefix get err (#4487) 2024-12-19 01:17:50 +00:00
dependabot[bot]
fd07a9c6e4 chore(deps): bump google.golang.org/protobuf from 1.35.1 to 1.36.0 in /tools/goctl (#4504)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-18 16:01:36 +08:00
dependabot[bot]
b8c239630c chore(deps): bump github.com/emicklei/proto from 1.13.2 to 1.13.4 in /tools/goctl (#4505)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-18 15:30:04 +08:00
dependabot[bot]
672ea55736 chore(deps): bump github.com/stretchr/testify from 1.9.0 to 1.10.0 in /tools/goctl (#4506)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-18 15:16:00 +08:00
dependabot[bot]
f7097866bf chore(deps): bump github.com/zeromicro/go-zero from 1.7.3 to 1.7.4 in /tools/goctl (#4507)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-18 11:27:55 +08:00
dependabot[bot]
796b2bd1b0 chore(deps): bump golang.org/x/crypto from 0.30.0 to 0.31.0 in the go_modules group (#4501)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-18 00:37:32 +08:00
dependabot[bot]
e1e5fb2071 chore(deps): bump golang.org/x/crypto from 0.28.0 to 0.31.0 in /tools/goctl in the go_modules group (#4502)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-17 17:20:57 +08:00
kesonan
89ecb50005 remove string restriction on atserver (#4499) 2024-12-17 03:59:30 +00:00
dependabot[bot]
dbed1ea042 chore(deps): bump google.golang.org/protobuf from 1.35.2 to 1.36.0 (#4500) 2024-12-17 11:58:22 +08:00
Kevin Wan
ad291daf78 chore: format Dockerfile template (#4496) 2024-12-14 12:38:33 +08:00
lascyb
13746a3706 Update docker.tpl (#4495) 2024-12-13 16:08:38 +00:00
dependabot[bot]
f03b13f632 chore(deps): bump github.com/fullstorydev/grpcurl from 1.9.1 to 1.9.2 (#4473)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-08 10:27:42 +08:00
dependabot[bot]
f6f64b1286 chore(deps): bump golang.org/x/net from 0.31.0 to 0.32.0 (#4481)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-08 10:06:05 +08:00
Kevin Wan
300a415f5d Add go-zero users (#4475) 2024-12-05 00:09:44 +08:00
dependabot[bot]
c5de546f8a chore(deps): bump github.com/stretchr/testify from 1.9.0 to 1.10.0 (#4470)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-26 21:46:28 +08:00
Robin
cad243905f fix ts request cli (#4461)
Co-authored-by: robinzhang <azhangrongbing@163.com>
2024-11-21 21:40:08 +08:00
dependabot[bot]
7c8f41d577 chore(deps): bump codecov/codecov-action from 4 to 5 (#4459)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-21 21:25:53 +08:00
dependabot[bot]
cbd118d55f chore(deps): bump google.golang.org/protobuf from 1.35.1 to 1.35.2 (#4457) 2024-11-15 17:00:10 +08:00
dependabot[bot]
9d2a1b8b0a chore(deps): bump golang.org/x/time from 0.7.0 to 0.8.0 (#4454)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-11 00:24:06 +08:00
dependabot[bot]
f6ada979aa chore(deps): bump golang.org/x/net from 0.30.0 to 0.31.0 (#4452)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-11 00:13:28 +08:00
Kevin Wan
53a74759a5 feat: support json array in request body (#4444) 2024-11-05 14:09:30 +00:00
dependabot[bot]
1940f7bd58 chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.5.0 to 4.5.1 (#4447) 2024-11-05 19:37:32 +08:00
Kevin Wan
18cb3141ba feat: support query array in httpx.Parse (#4440) 2024-11-02 13:55:37 +00:00
dependabot[bot]
f822c9a94f chore(deps): bump github.com/fatih/color from 1.17.0 to 1.18.0 (#4434)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-26 14:49:56 +08:00
Kevin Wan
1a3dc75874 chore: upgrade goctl version (#4429) 2024-10-20 10:38:04 +08:00
yangjinheng
796dd5b6e2 fix the source code directory after the soft link (#4425) 2024-10-19 15:37:44 +00:00
dependabot[bot]
94e476ade7 chore(deps): bump github.com/redis/go-redis/v9 from 9.6.1 to 9.7.0 (#4426) 2024-10-19 08:10:32 +08:00
dependabot[bot]
2a74996e1b chore(deps): bump github.com/prometheus/client_golang from 1.20.4 to 1.20.5 (#4424) 2024-10-18 11:38:41 +08:00
fishJack01
f52af1ebf9 feat:New redis method TxPipeline (#4417)
Co-authored-by: fish <fish@fishdeMac-mini.local>
2024-10-13 05:51:55 +00:00
dependabot[bot]
24450f18bb chore(deps): bump google.golang.org/protobuf from 1.34.2 to 1.35.1 (#4414)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-12 21:13:32 +08:00
dependabot[bot]
f1a45d8a23 chore(deps): bump golang.org/x/time from 0.6.0 to 0.7.0 (#4409)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-05 12:58:06 +08:00
MarkJoyMa
9aebba1566 fix/conf_multi_layer_map (#4407)
Co-authored-by: aiden.ma <Aiden.ma@yijinin.com>
2024-10-05 04:46:32 +00:00
dependabot[bot]
4998479f9a chore(deps): bump golang.org/x/net from 0.29.0 to 0.30.0 (#4410)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-05 12:27:34 +08:00
dependabot[bot]
873d1351ee chore(deps): bump golang.org/x/sys from 0.25.0 to 0.26.0 (#4408)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-05 12:04:39 +08:00
dependabot[bot]
afcbca8f24 chore(deps): bump go.mongodb.org/mongo-driver from 1.17.0 to 1.17.1 (#4402)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-03 11:16:00 +08:00
dependabot[bot]
5b8126c2cf chore(deps): bump go.uber.org/automaxprocs from 1.5.3 to 1.6.0 (#4389) 2024-09-24 09:16:20 +08:00
Kevin Wan
4dfaf35151 fix: goctl k8s autoscaling version upgrade (#4387) 2024-09-20 23:16:38 +08:00
dependabot[bot]
00cd77c92b chore(deps): bump github.com/prometheus/client_golang from 1.20.3 to 1.20.4 (#4380)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 10:26:52 +08:00
dependabot[bot]
2145a7a93c chore(deps): bump go.mongodb.org/mongo-driver from 1.16.1 to 1.17.0 (#4379)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-19 22:10:29 +08:00
dependabot[bot]
d5302f2dbe chore(deps): bump golang.org/x/net from 0.28.0 to 0.29.0 (#4360)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-07 21:13:42 +08:00
dependabot[bot]
6181594bc8 chore(deps): bump github.com/jhump/protoreflect from 1.16.0 to 1.17.0 (#4359)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-06 20:13:29 +08:00
dependabot[bot]
11c10e51ff chore(deps): bump github.com/prometheus/client_golang from 1.20.2 to 1.20.3 (#4361)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-06 18:52:59 +08:00
Kevin Wan
3f03126d27 chore: fix goctl Dockerfile warnings (#4358) 2024-09-05 22:13:01 +08:00
dependabot[bot]
d43adc2823 chore(deps): bump golang.org/x/sys from 0.24.0 to 0.25.0 (#4356)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-05 10:17:27 +08:00
Kevin Wan
656222b572 chore: update goctl version (#4353) 2024-09-03 09:22:04 +08:00
Kevin Wan
077b6072fa chore: coding style (#4352) 2024-09-03 09:06:50 +08:00
jursonmo
0cafb1164b should check if devServer Enabled, then call once.Do() (#4351)
Co-authored-by: william <myname@example.com>
2024-09-03 00:54:43 +00:00
MarkJoyMa
90afa08367 Revert "fix: etcd scheme on grpc resolver" (#4349) 2024-09-03 00:48:08 +00:00
Kevin Wan
c92f788292 chore: update go version in test (#4347) 2024-09-01 16:19:31 +08:00
Kevin Wan
e94be9b302 chore: update go version for building goctl releases (#4346) 2024-09-01 16:02:49 +08:00
Kevin Wan
e713d9013d chore: update goctl deps (#4345) 2024-09-01 15:42:45 +08:00
Kevin Wan
24d6150073 chore: refactor config center (#4339)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2024-08-28 12:02:48 +00:00
MarkJoyMa
44cddec5c3 feat: added configuration center function (#3035)
Co-authored-by: aiden.ma <Aiden.ma@yijinin.com>
2024-08-28 14:47:52 +08:00
Kevin Wan
47d13e5ef8 fix: etcd scheme on grpc resolver (#4121) 2024-08-27 23:13:37 +08:00
Kevin Wan
896e1a2abb chore: refactor logx file time format (#4335) 2024-08-27 22:01:01 +08:00
kui
075817a8dd Add custom log file name date format (#4333) 2024-08-27 20:43:25 +08:00
dependabot[bot]
29400f6814 chore(deps): bump github.com/prometheus/client_golang from 1.20.1 to 1.20.2 (#4334)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-27 19:51:54 +08:00
dependabot[bot]
34f536264f chore(deps): bump github.com/prometheus/client_golang from 1.20.0 to 1.20.1 (#4324)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-21 17:38:54 +08:00
Kevin Wan
9d9c7e0fe0 feat: support build tag to reduce binary size w/o k8s (#4323) 2024-08-20 19:53:20 +08:00
dependabot[bot]
e220d3a4cb chore(deps): bump github.com/prometheus/client_golang from 1.19.1 to 1.20.0 (#4317)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-19 12:22:06 +08:00
dependabot[bot]
193dcf90bc chore(deps): bump golang.org/x/sys from 0.23.0 to 0.24.0 (#4307)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-10 21:15:08 +08:00
featherlight
03756c9166 refactor zrpc server interceptor builder (#4300) 2024-08-08 14:37:19 +00:00
kesonan
c1f12c5784 (goctl): fix map conversion (#4306) 2024-08-08 14:19:14 +00:00
dependabot[bot]
2883111af5 chore(deps): bump go.mongodb.org/mongo-driver from 1.16.0 to 1.16.1 (#4305)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-08 22:07:18 +08:00
dependabot[bot]
2758c4e842 chore(deps): bump golang.org/x/net from 0.27.0 to 0.28.0 (#4301)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-07 20:19:47 +08:00
Kevin Wan
4196ddb3e3 Update readme-cn.md (#4294) 2024-08-06 17:54:38 +08:00
dependabot[bot]
e24d797226 chore(deps): bump golang.org/x/time from 0.5.0 to 0.6.0 (#4299)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-06 17:54:01 +08:00
dependabot[bot]
d4349fa958 chore(deps): bump golang.org/x/sys from 0.22.0 to 0.23.0 (#4298)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-06 16:39:57 +08:00
kesonan
da2c14d45f (goctl): fix quickstart error while reading go module info (#4297) 2024-08-05 15:29:06 +00:00
kesonan
64e3aeda55 Add goctl version to code header (#4293) 2024-08-03 14:22:51 +00:00
Kevin Wan
dedba17219 refactor: simplify BatchError (#4292) 2024-08-03 13:57:41 +08:00
kesonan
c6348b9855 refactor goctl-compare (#4290) 2024-08-03 04:26:24 +00:00
chentong
8689a6247e refactor(core/errorx): use errors.Join simplify error handle (#4289) 2024-08-03 03:00:59 +00:00
Rui Chen
ff6ee25d23 ci: update workflows to use go.mod instead of specifying go version (#4286)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2024-08-03 02:12:28 +00:00
Kevin Wan
5213243bbb Update readme.md (#4287) 2024-08-02 18:26:54 +08:00
Kevin Wan
2588a36555 feat: support rest.WithCorsHeaders to customize cors headers (#4284) 2024-07-30 17:29:44 +08:00
Krystian Kulas
c2421beb25 fix: readme remove duplicate text (#4280) 2024-07-29 01:06:29 +00:00
kesonan
dfe8a81c76 Upgrade goctl version to 1.7.1 (#4282) 2024-07-29 00:54:21 +00:00
kesonan
ee643a945e (goctl): fix nested struct generation (#4281) 2024-07-28 15:40:25 +00:00
Kevin Wan
eeda6efae7 chore: upgrade go-zero version (#4277) 2024-07-27 17:31:32 +08:00
Kevin Wan
caf0e64beb chore: optimize lock in discov.etcd (#4275) 2024-07-27 16:27:05 +08:00
dependabot[bot]
0e61303cb0 chore(deps): bump github.com/redis/go-redis/v9 from 9.6.0 to 9.6.1 (#4274)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-26 23:17:39 +08:00
dependabot[bot]
f651d7cf6c chore(deps): bump go.etcd.io/etcd/client/v3 from 3.5.14 to 3.5.15 (#4267)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-25 19:24:07 +08:00
tsinghuacoder
05da2c560b chore: fix some comments (#4270)
Signed-off-by: tsinghuacoder <tsinghuacoder@icloud.com>
2024-07-25 19:11:56 +08:00
Kevin Wan
8ae0f287d6 chore: optimize lock in discov.etcd (#4272) 2024-07-25 17:24:05 +08:00
Kevin Wan
8f7aff558f chore: refactor BuildTypes in tsgen. (#4266) 2024-07-22 21:17:59 +08:00
jaron
6e08d478fe feat(tsgen): tsgen export buildTypes function (#4197) 2024-07-22 12:11:06 +00:00
dependabot[bot]
944ac383d2 chore(deps): bump github.com/redis/go-redis/v9 from 9.5.4 to 9.6.0 (#4262)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-20 23:18:19 +08:00
Kevin Wan
0eec33f14b chore: optimize file reading (#4264) 2024-07-20 22:44:13 +08:00
JiChen
9de04ee035 fix: handle with read the empty file (#4258) 2024-07-20 12:01:13 +00:00
kesonan
cf5b080fbe (goctl): Use .goctl as home if not exists (#4260) 2024-07-19 05:54:24 +00:00
Kevin Wan
4a14164be1 feat: handle using root as the path of file server (#4255) 2024-07-18 15:15:03 +00:00
Kevin Wan
5dd6f2a43a feat: support embed file system to serve files in rest (#4253) 2024-07-17 16:21:08 +08:00
Kevin Wan
a00c956776 chore: upgrade go version (#4248)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2024-07-16 11:43:25 +08:00
yonwoo9
c02fb3acab chore: initialize some slice type variables (#4249) 2024-07-15 15:50:42 +00:00
Kevin Wan
9f8455ddb3 chore: fix typo (#4246) 2024-07-14 10:52:47 +08:00
guonaihong
775b105ab2 added code comments (#4219) 2024-07-13 12:09:58 +00:00
Kevin Wan
ec86f22cd6 feat: support file server in rest (#4244) 2024-07-13 19:58:35 +08:00
dependabot[bot]
e776b5d8ab chore(deps): bump github.com/redis/go-redis/v9 from 9.5.3 to 9.5.4 (#4245)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-13 16:31:57 +08:00
Kevin Wan
2026d4410b fix: should not trigger breaker on duplicate key with mongodb (#4238) 2024-07-08 23:41:02 +08:00
MarkJoyMa
f8437e6364 feat/sqlc_partial (#4237) 2024-07-07 15:54:18 +00:00
Kevin Wan
bd2033eb35 feat: support adding more writer, easy to write to console additionally (#4234)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2024-07-07 23:31:27 +08:00
MarkJoyMa
fed835bc25 feat/redis_hook (#4233) 2024-07-07 04:50:30 +00:00
dependabot[bot]
c9cbd74bf3 chore(deps): bump golang.org/x/net from 0.26.0 to 0.27.0 (#4231)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-07 10:54:37 +08:00
dependabot[bot]
27ea106293 chore(deps): bump golang.org/x/sys from 0.21.0 to 0.22.0 (#4229)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-05 19:20:54 +08:00
Kevin Wan
657923b9d5 Update readme-cn.md (#4228) 2024-07-04 11:05:23 +08:00
dependabot[bot]
8dbec6a800 chore(deps): bump google.golang.org/grpc from 1.64.0 to 1.65.0 (#4227)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-03 21:03:23 +08:00
Kevin Wan
490559434a chore: remove unnecessary return (#4226) 2024-07-02 23:45:18 +08:00
kesonan
4a62d084a9 fix: disable array request body (#4220) 2024-07-02 03:55:01 +00:00
kesonan
2f9b6cf8ec disable nested struct for array and map type (#4222) 2024-06-29 05:44:46 +00:00
Kevin Wan
01bbc78bac Update FUNDING.yml (#4216) 2024-06-27 11:15:42 +08:00
kesonan
a012a9138f (goctl): support nested struct (#4211) 2024-06-25 15:18:15 +00:00
Kevin Wan
4ec9cac82b chore: update readme for goctl installation (#4206) 2024-06-23 11:47:17 +08:00
苏蓝
8d9746e794 Update readme-cn.md (#4205) 2024-06-23 03:32:57 +00:00
Kevin Wan
8f83705199 Update version.go (#4204) 2024-06-21 20:09:36 +08:00
Kevin Wan
f1ed7bd75d Update readme-cn.md (#4195) 2024-06-17 22:28:24 +08:00
Kevin Wan
7a20608756 chore: add trending badge (#4194) 2024-06-17 22:26:08 +08:00
dependabot[bot]
5cfff95e95 chore(deps): bump google.golang.org/protobuf from 1.34.1 to 1.34.2 (#4185)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 14:26:26 +08:00
dependabot[bot]
1e1cc1a0d9 chore(deps): bump github.com/redis/go-redis/v9 from 9.5.2 to 9.5.3 (#4183)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-08 09:28:45 +08:00
dependabot[bot]
0a1440a839 chore(deps): bump golang.org/x/net from 0.25.0 to 0.26.0 (#4180) 2024-06-05 08:01:46 +08:00
kesonan
23980d29c3 fix no such dir if not create goctl home (#4177) 2024-06-04 10:55:56 +00:00
jiz4oh
424119d796 chore: fix the confused log level in comment (#4175) 2024-06-04 10:43:26 +00:00
kesonan
97c7835d9e fix #4161 (#4176) 2024-06-04 10:26:34 +00:00
kesonan
7954ad3759 fix: fix readme (#4174) 2024-06-02 15:22:33 +00:00
dependabot[bot]
e8c9b0ddf8 chore(deps): bump go.etcd.io/etcd/client/v3 from 3.5.13 to 3.5.14 (#4169)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-02 11:17:55 +08:00
dependabot[bot]
70112e59cb chore(deps): bump github.com/redis/go-redis/v9 from 9.4.0 to 9.5.2 (#4172)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-01 13:12:33 +08:00
dependabot[bot]
7ba5ced2d9 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.32.1 to 2.33.0 (#4168)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-30 11:15:59 +08:00
Kevin Wan
962b36d745 fix: log concurrency problems after calling WithXXX methods (#4164) 2024-05-26 12:52:05 +08:00
dependabot[bot]
57060cc6d7 chore(deps): bump google.golang.org/grpc from 1.63.2 to 1.64.0 (#4155) 2024-05-16 07:47:19 +08:00
Kevin Wan
e0c16059d9 optimize: simplify breaker algorithm (#4151) 2024-05-14 17:02:21 +08:00
dependabot[bot]
a0d954dfab chore(deps): bump github.com/fatih/color from 1.16.0 to 1.17.0 (#4150)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-14 10:37:25 +08:00
Alex Last
a5ece25c07 feat: add secure option for sending traces via otlphttp (#3973) 2024-05-12 17:00:54 +00:00
Kevin Wan
0cac41a38b chore: refactor mapping unmarshaler (#4145) 2024-05-12 14:37:36 +08:00
Kevin Wan
f10084a3f5 chore: refactor and coding style (#4144) 2024-05-11 23:06:59 +08:00
Leo
040fee5669 feat: httpx.Parse supports parsing structures that implement the Unmarshaler interface (#4143) 2024-05-11 22:25:10 +08:00
Kevin Wan
42b3bae65a optimize: improve breaker algorithm on recovery time (#4141) 2024-05-11 21:44:26 +08:00
guangwu
7c730b97d8 fix: make: command: Command not found (#4132)
Signed-off-by: guoguangwu <guoguangwug@gmail.com>
2024-05-10 13:33:03 +00:00
Kevin Wan
057bae92ab fix: log panic on Error() or String() panics (#4136) 2024-05-10 12:49:34 +08:00
Kevin Wan
74331a45c9 fix: log panic when use nil error or stringer with Field method (#4130) 2024-05-10 00:31:36 +08:00
chen quan
9d551d507f chore(api/maxconnshandler): add tracing information to the log (#4126) 2024-05-08 05:25:35 +00:00
Kevin Wan
02dd81c05c Update FUNDING.yml (#4128) 2024-05-08 12:52:42 +08:00
dependabot[bot]
3095ba2b1f chore(deps): bump golang.org/x/net from 0.24.0 to 0.25.0 (#4124)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-08 12:52:13 +08:00
dependabot[bot]
2afa60132c chore(deps): bump google.golang.org/protobuf from 1.34.0 to 1.34.1 (#4123)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-08 12:45:10 +08:00
Kevin Wan
e71ed7294b Update FUNDING.yml (#4127) 2024-05-08 12:26:41 +08:00
dependabot[bot]
95822281bf chore(deps): bump golang.org/x/sys from 0.19.0 to 0.20.0 (#4122)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-08 12:22:06 +08:00
Kevin Wan
588e10daef chore: refactor and coding style (#4120) 2024-05-06 18:16:56 +08:00
soasurs
62ba01120e fix: zrpc kube resolver builder (#4119)
Signed-off-by: soasurs <soasurs@gmail.com>
2024-05-06 14:50:35 +08:00
dependabot[bot]
527de1c50e chore(deps): bump google.golang.org/protobuf from 1.33.1-0.20240408130810-98873a205002 to 1.34.0 (#4115)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-04 23:59:47 +08:00
dependabot[bot]
abfe62a2d7 chore(deps): bump github.com/pelletier/go-toml/v2 from 2.2.1 to 2.2.2 (#4116)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-04 23:39:25 +08:00
Kevin Wan
36f4cf97ff Update FUNDING.yml (#4114) 2024-04-30 22:58:51 +08:00
Kevin Wan
b3cd8a32ed feat: trigger breaker on underlying service timeout (#4112) 2024-04-30 19:01:20 +08:00
kesonan
a9d27cda8a (goctl): fix prefix syntax (#4113) 2024-04-30 09:11:08 +00:00
kesonan
04116f647d chore(goctl): change goctl version to 1.6.5 (#4111) 2024-04-30 04:25:47 +00:00
Kevin Wan
a8ccda0c06 feat: add fx.ParallelErr (#4107) 2024-04-29 00:18:30 +08:00
Kevin Wan
bfddb9dae4 feat: add errorx.In to facility error checking (#4105) 2024-04-27 20:43:45 +08:00
Kevin Wan
b337ae36e5 Update readme-cn.md 2024-04-20 10:00:10 +08:00
Kevin Wan
5e5123caa3 chore: add more tests (#4094) 2024-04-19 11:13:23 +08:00
Kevin Wan
d371ab5479 feat: use breaker with ctx to prevent deadline exceeded (#4091)
Signed-off-by: kevin <wanjunfeng@gmail.com>
2024-04-18 23:18:49 +08:00
jaron
1b9b61f505 fix(goctl): GOPROXY env should set by ourself (#4087) 2024-04-18 22:50:30 +08:00
suyhuai
e1f15efb3b add customized.tpl for model template (#4086)
Co-authored-by: sudaoxyz <sudaoxyz@gmail.com>
2024-04-18 14:40:54 +00:00
Kevin Wan
1540bdc4c9 optimize: improve breaker algorithm on recovery time (#4077) 2024-04-18 22:33:25 +08:00
Kevin Wan
95b32b5779 chore: add code coverage (#4090) 2024-04-18 20:58:36 +08:00
Kevin Wan
815a4f7eed feat: support context in breaker methods (#4088) 2024-04-18 18:00:17 +08:00
dependabot[bot]
4b0bacc9c6 chore(deps): bump k8s.io/apimachinery from 0.29.3 to 0.29.4 (#4084)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-18 11:22:49 +08:00
Kevin Wan
e9dc96af17 chore: coding style (#4082) 2024-04-17 23:37:35 +08:00
fearlessfei
62c88a84d1 feat: migrate lua script to lua file (#4069) 2024-04-17 15:20:10 +00:00
Kevin Wan
36088ea0d4 fix: avoid duplicate in logx plain mode (#4080) 2024-04-17 17:43:22 +08:00
dependabot[bot]
164f5aa86c chore(deps): bump github.com/pelletier/go-toml/v2 from 2.2.0 to 2.2.1 (#4073)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-13 12:32:56 +08:00
dependabot[bot]
07d07cdd23 chore(deps): bump github.com/fullstorydev/grpcurl from 1.8.9 to 1.9.1 (#4065)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 13:02:52 +08:00
dependabot[bot]
0efe99af66 chore(deps): bump github.com/jhump/protoreflect from 1.15.6 to 1.16.0 (#4064)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 12:39:00 +08:00
Kevin Wan
927f8bc821 fix: fix ignored context.DeadlineExceeded (#4066) 2024-04-11 11:14:20 +08:00
kesonan
2a7ada993b (goctl)feature/model config (#4062)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2024-04-10 15:01:59 +00:00
Kevin Wan
682460c1c8 fix: fix ignored scanner.Err() (#4063) 2024-04-10 17:28:52 +08:00
Kevin Wan
a66ae0d4c4 fix: timeout on query should return context.DeadlineExceeded (#4060) 2024-04-10 04:17:39 +00:00
dependabot[bot]
d1f24ab70f chore(deps): bump google.golang.org/grpc from 1.63.0 to 1.63.2 (#4058)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-10 10:23:33 +08:00
dependabot[bot]
d0983948b5 chore(deps): bump google.golang.org/grpc from 1.63.0 to 1.63.2 in /tools/goctl (#4059)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-10 10:14:15 +08:00
Kevin Wan
3343fc2cdb chore: update goctl version to 1.6.4 (#4057) 2024-04-09 22:59:33 +08:00
Kevin Wan
3866b5741a feat: support http stream response (#4055) 2024-04-09 20:46:44 +08:00
Kevin Wan
5fbe8ff5c4 chore: coding style (#4054) 2024-04-09 17:19:47 +08:00
jaron
6f763f71f9 chore(goctl): update readme (#4053) 2024-04-09 08:30:25 +00:00
dependabot[bot]
80377f18e7 chore(deps): bump codecov/codecov-action from 3 to 4 (#4051)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-09 15:24:24 +08:00
dependabot[bot]
8690859c7d chore(deps): bump golang.org/x/net from 0.23.0 to 0.24.0 (#4048)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-06 10:03:06 +08:00
dependabot[bot]
d744038198 chore(deps): bump google.golang.org/grpc from 1.62.1 to 1.63.0 (#4045)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-05 19:43:17 +08:00
dependabot[bot]
58ad8cac8a chore(deps): bump golang.org/x/sys from 0.18.0 to 0.19.0 (#4046)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-05 19:28:54 +08:00
dependabot[bot]
74886a151e chore(deps): bump google.golang.org/grpc from 1.62.1 to 1.63.0 in /tools/goctl (#4047)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-05 18:45:49 +08:00
Kevin Wan
c5eda1f155 chore: fix codecov (#4044) 2024-04-05 00:53:13 +08:00
Kevin Wan
b5b7c054ca chore: fix codecov (#4043) 2024-04-05 00:43:38 +08:00
Kevin Wan
6c8073b691 chore: add more tests (#4042) 2024-04-05 00:13:42 +08:00
Kevin Wan
64d430d424 fix: bug on form data with slices (#4040) 2024-04-04 20:28:54 +08:00
Jayson Wang
f138cc792e fix(goctl): multi imports the api cause redeclared error in types.go (#3988)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2024-04-04 11:39:24 +00:00
dependabot[bot]
b20ec8aedb chore(deps): bump golang.org/x/net from 0.22.0 to 0.23.0 (#4039)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-04 10:48:52 +08:00
Kevin Wan
a53254fa91 chore: update codecov config (#4038) 2024-04-03 23:58:02 +08:00
Kevin Wan
08563482e5 chore: coding style (#4037) 2024-04-03 22:55:52 +08:00
fearlessfei
968727412d add custom health response information (#4034)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2024-04-03 14:33:55 +00:00
linden-in-China
6f3d094eba opton to option (#4035) 2024-04-03 14:15:21 +00:00
kesonan
2d3ebb9b62 (goctl) fix #4027 (#4032) 2024-04-01 15:22:29 +00:00
chentong
8c0bb27136 feat: add gen api @doc comment to logic handler routes (#3790)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2024-03-30 11:09:54 +00:00
ak5w
cf987295df fix the usage datasource url of postgresql (#4029) (#4030) 2024-03-30 05:51:54 +00:00
dependabot[bot]
8c92b3af7d chore(deps): bump go.etcd.io/etcd/client/v3 from 3.5.12 to 3.5.13 (#4028)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-30 13:40:24 +08:00
Kevin Wan
5dd9342703 chore: fix test failure (#4031) 2024-03-30 13:29:58 +08:00
shyandsy
3ef59f6a71 fix(httpx): support array field for request dto (#4026)
Co-authored-by: yshi3 <yshi3@tesla.com>
2024-03-30 12:10:56 +08:00
dependabot[bot]
f12802abc7 chore(deps): bump github.com/go-sql-driver/mysql from 1.8.0 to 1.8.1 (#4022)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-27 18:42:35 +08:00
dependabot[bot]
6f0fe67804 chore(deps): bump github.com/go-sql-driver/mysql from 1.8.0 to 1.8.1 in /tools/goctl (#4023)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-27 18:07:00 +08:00
dependabot[bot]
f44f0e7e62 chore(deps): bump github.com/pelletier/go-toml/v2 from 2.1.1 to 2.2.0 (#4017)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-20 23:34:06 +08:00
dependabot[bot]
cdd95296db chore(deps): bump k8s.io/client-go from 0.29.2 to 0.29.3 (#4012)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-19 15:41:20 +08:00
kesonan
3e794cf991 (goctl)fix code_ql (#4009) 2024-03-17 02:21:36 +00:00
Kevin Wan
bbce95e7e1 fix: didn't count failure in allow method with breaker algorithm (#4008) 2024-03-16 22:19:36 +08:00
dependabot[bot]
0449450c64 chore(deps): bump github.com/jackc/pgx/v5 from 5.5.3 to 5.5.4 in /tools/goctl (#4007)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-15 12:55:57 +08:00
dependabot[bot]
9f9a12ea57 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.31.1 to 2.32.1 (#4003)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-14 11:19:44 +08:00
Kevin Wan
cc2a7e97f9 chore: coding style, add code for prometheus (#4002) 2024-03-13 20:00:35 +08:00
dependabot[bot]
09d7af76af chore(deps): bump github.com/go-sql-driver/mysql from 1.7.1 to 1.8.0 in /tools/goctl (#3997)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-13 13:28:27 +08:00
dependabot[bot]
c233a66601 chore(deps): bump github.com/go-sql-driver/mysql from 1.7.1 to 1.8.0 (#3998)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-13 12:56:41 +08:00
dependabot[bot]
94fa12560c chore(deps): bump github.com/jackc/pgx/v5 from 5.5.4 to 5.5.5 (#3999)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-12 12:28:28 +08:00
MarkJoyMa
7d90f906f5 feat: migrate redis breaker into hook (#3982) 2024-03-12 04:21:33 +00:00
Viktor Patchev
f372b98d96 Add: Optimize the error log to be more specific (#3994) 2024-03-11 13:06:50 +08:00
mongobaba
459d3025c5 optimize: change err == xx to errors.Is(err, xx) (#3991) 2024-03-09 12:49:16 +00:00
Kevin Wan
e9e55125a9 chore: fix warnings (#3990) 2024-03-09 13:48:11 +08:00
Kevin Wan
159ecb7386 chore: fix warnings (#3989) 2024-03-08 22:35:17 +08:00
ansoda
69bb746a1d fix: StopAgent panics when trace agent disabled (#3981)
Co-authored-by: ansoda <ansoda@gmail.com>
2024-03-08 10:28:23 +00:00
Kevin Wan
d184f96b13 chore: coding style (#3987) 2024-03-08 16:11:28 +08:00
MarkJoyMa
c7dacb0146 fix: mysql WithAcceptable bug (#3986) 2024-03-08 04:23:41 +00:00
dependabot[bot]
2207477b60 chore(deps): bump google.golang.org/protobuf from 1.32.0 to 1.33.0 in /tools/goctl (#3978)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-07 11:15:48 +08:00
dependabot[bot]
105ab590ff chore(deps): bump google.golang.org/grpc from 1.62.0 to 1.62.1 in /tools/goctl (#3977)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-07 11:01:14 +08:00
dependabot[bot]
2f4c58ed73 chore(deps): bump google.golang.org/grpc from 1.62.0 to 1.62.1 (#3976)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-07 10:45:19 +08:00
dependabot[bot]
1631aa02ad chore(deps): bump github.com/golang/protobuf from 1.5.3 to 1.5.4 (#3984)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-07 10:24:51 +08:00
dependabot[bot]
4df10eef5d chore(deps): bump golang.org/x/net from 0.21.0 to 0.22.0 (#3975)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-06 23:31:02 +08:00
dependabot[bot]
3d552ea7a8 chore(deps): bump google.golang.org/protobuf from 1.32.0 to 1.33.0 (#3974)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-06 21:33:45 +08:00
Kevin Wan
74b87ac9fd chore: coding style (#3972) 2024-03-05 14:40:10 +08:00
Alex Last
ba1d6e3664 fix: only add log middleware to not found handler when enabled (#3969) 2024-03-05 04:14:54 +00:00
dependabot[bot]
2096cd5749 chore(deps): bump github.com/jackc/pgx/v5 from 5.5.3 to 5.5.4 (#3970)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-05 12:09:18 +08:00
dependabot[bot]
2eb2fa26f6 chore(deps): bump golang.org/x/sys from 0.17.0 to 0.18.0 (#3971)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-05 12:03:06 +08:00
Kevin Wan
bc4187ca90 Create SECURITY.md (#3968) 2024-03-04 23:07:54 +08:00
Kevin Wan
b7be25b98b Update readme-cn.md (#3966) 2024-03-03 14:18:27 +08:00
Kevin Wan
dd01695d45 chore: update goctl version to 1.6.3 (#3965) 2024-03-03 13:36:35 +08:00
Kevin Wan
25821bdee6 chore: coding style (#3960) 2024-03-03 00:17:38 +08:00
dependabot[bot]
b624b966f0 chore(deps): bump actions/stale from 8 to 9 (#3963)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-03 00:11:56 +08:00
dependabot[bot]
df96262235 chore(deps): bump codecov/codecov-action from 3 to 4 (#3962)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-03 00:06:41 +08:00
dependabot[bot]
2629636f64 chore(deps): bump actions/setup-go from 4 to 5 (#3964)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-03 00:01:14 +08:00
dependabot[bot]
708ad207d7 chore(deps): bump github/codeql-action from 2 to 3 (#3961)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-02 23:54:37 +08:00
Rene Leonhardt
b53ba76a99 feat: Improve Docker build (#3682) 2024-03-02 15:40:31 +00:00
POABOB
be7f93924a feature: add a mongo registry option to convert type easier. (#3780)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2024-03-02 15:13:44 +00:00
Kevin Wan
45be48a4ee chore: coding style (#3959) 2024-03-02 22:45:24 +08:00
kesonan
e08ba2fee8 (goctl)fix parser issues (#3930) 2024-03-02 14:27:39 +00:00
Kevin Wan
a5d2b971a1 chore: add more tests (#3958) 2024-03-02 21:58:13 +08:00
Qiu shao
9763c8b143 feat:add redis mset func (#3820) 2024-03-02 12:00:25 +00:00
Kevin Wan
4e3f1776dc chore: coding style (#3957) 2024-03-02 19:09:14 +08:00
fearlessfei
e38036cea2 feat: retry ignore specified errors (#3808) 2024-03-02 18:53:20 +08:00
Kevin Wan
8e97c5819f chore: add more tests (#3954) 2024-03-02 12:22:55 +08:00
#Suyghur
0ee44c7064 feat(redis): added and impl ZADDNX command (#3944) 2024-03-02 10:15:10 +08:00
Kevin Wan
a1bacd3fc8 feat: a concurrent runner with messages taken in pushing order (#3941) 2024-03-02 10:03:58 +08:00
Kevin Wan
c98d5fdaf4 chore: simplify linux nocgroup logic (#3953) 2024-03-02 07:13:41 +08:00
dependabot[bot]
2ee43b41b8 chore(deps): bump github.com/stretchr/testify from 1.8.4 to 1.9.0 in /tools/goctl (#3952)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-02 06:50:33 +08:00
dependabot[bot]
8367af3416 chore(deps): bump github.com/stretchr/testify from 1.8.4 to 1.9.0 (#3951)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-02 06:42:25 +08:00
Kevin Wan
03b6e377d7 chore: add lock for batcherror (#3950) 2024-03-02 00:59:15 +08:00
chentong
ec41880476 fix: BatchError.Add() non thread safe (#3946) 2024-03-01 16:32:39 +00:00
Kevin Wan
5263805b3b chore: simplify linux nocgroup logic (#3949) 2024-03-02 00:23:52 +08:00
Alex Last
a7363f0c21 feat: add nocgroup build tag for systems without cgroup (#3948) 2024-03-01 15:52:20 +00:00
mongobaba
52e5d85221 feat: add break metrics for sqlx.statement (#3947) 2024-03-01 14:55:32 +00:00
MarkJoyMa
88aab8f635 fix: mapping FillDefault is optional! bug (#3940) 2024-02-27 16:23:47 +00:00
Kevin Wan
1f63cbe9c6 Update readme-cn.md (#3939) 2024-02-26 17:24:52 +08:00
Kevin Wan
0dfaf135dd feat: support breaker with sql statements (#3936) 2024-02-25 11:24:44 +08:00
Kevin Wan
914bcdcf2b chore: add tests (#3931) 2024-02-23 23:00:35 +08:00
fffreedom
e38cb0118d when the Unmarshaler parsing value by fillSliceFromString, if the val… (#3927)
Co-authored-by: danahan <danahan@tencent.com>
2024-02-23 14:00:58 +00:00
dependabot[bot]
cb8161c799 chore(deps): bump google.golang.org/grpc from 1.61.1 to 1.62.0 in /tools/goctl (#3929) 2024-02-23 10:26:41 +08:00
dependabot[bot]
c4dac2095f chore(deps): bump google.golang.org/grpc from 1.61.1 to 1.62.0 (#3928) 2024-02-23 03:34:23 +08:00
Kevin Wan
25a807afb2 chore: add tests (#3921) 2024-02-20 10:11:43 +08:00
Kevin Wan
6be37ad533 chore: optimize coding style and add unit tests (#3917) 2024-02-17 15:50:07 +08:00
chen quan
28cb2c5804 feat: support sse ignore timeout (#2041)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2024-02-17 07:06:45 +00:00
Kevin Wan
0f1d4c6bca optimize: improve performance on log disabled (#3916) 2024-02-17 14:55:48 +08:00
dependabot[bot]
bfe8335cb2 chore(deps): bump k8s.io/client-go from 0.29.1 to 0.29.2 (#3913)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-16 10:43:40 +08:00
dependabot[bot]
3c10ce0115 chore(deps): bump k8s.io/apimachinery from 0.29.1 to 0.29.2 (#3912)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-16 10:33:54 +08:00
Kevin Wan
1303e0fe6f feat: optimize circuit breaker algorithm (#3897) 2024-02-15 20:29:24 +08:00
Kevin Wan
9c17499757 optimize: shedding algorithm performance (#3908) 2024-02-15 20:22:22 +08:00
dependabot[bot]
8ceb2885db chore(deps): bump google.golang.org/grpc from 1.61.0 to 1.61.1 in /tools/goctl (#3911)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-15 20:09:38 +08:00
dependabot[bot]
00944894b4 chore(deps): bump google.golang.org/grpc from 1.61.0 to 1.61.1 (#3910)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-15 17:31:10 +08:00
dependabot[bot]
609fb3d59e chore(deps): bump golang.org/x/net from 0.20.0 to 0.21.0 (#3907)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-10 11:58:12 +08:00
dependabot[bot]
01c330abe7 chore(deps): bump golang.org/x/sys from 0.16.0 to 0.17.0 (#3902)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-08 22:37:11 +08:00
Kevin Wan
2ccef5bb4f feat: support ScheduleImmediately in TaskRunner (#3896) 2024-02-06 06:26:22 +00:00
dependabot[bot]
10f1d93e2a chore(deps): bump github.com/jackc/pgx/v5 from 5.5.2 to 5.5.3 (#3895) 2024-02-06 09:42:05 +08:00
Kevin Wan
dd518c8eac chore: update goctl version to 1.6.2 (#3890) 2024-02-03 21:31:11 +08:00
Kevin Wan
97cf2421de chore: add more tests (#3888) 2024-02-03 20:33:20 +08:00
Kevin Wan
786a80131e chore: fix test failure (#3883) 2024-02-01 22:41:05 +08:00
dependabot[bot]
93d257f9f5 chore(deps): bump go.etcd.io/etcd/client/v3 from 3.5.11 to 3.5.12 (#3887)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-01 10:34:46 +08:00
dependabot[bot]
f79535057f chore(deps): bump github.com/jhump/protoreflect from 1.15.5 to 1.15.6 (#3886)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-31 10:37:30 +08:00
dependabot[bot]
a905f4c20c chore(deps): bump github.com/emicklei/proto from 1.13.0 to 1.13.2 in /tools/goctl (#3882)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-30 15:02:02 +08:00
kesonan
3331954a78 (goctl): fix unresolved type if linked api imported (#3881) 2024-01-29 13:05:08 +00:00
dependabot[bot]
f54c2e384f chore(deps): bump google.golang.org/grpc from 1.60.1 to 1.61.0 (#3879)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-27 19:15:05 +08:00
dependabot[bot]
4b83f2ebd0 chore(deps): bump k8s.io/client-go from 0.29.0 to 0.29.1 (#3861)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-26 21:42:55 +08:00
dependabot[bot]
1c572ee16b chore(deps): bump github.com/jhump/protoreflect from 1.15.4 to 1.15.5 (#3875)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-26 13:44:15 +08:00
dependabot[bot]
b3402430e8 chore(deps): bump github.com/google/uuid from 1.5.0 to 1.6.0 (#3876)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-26 13:02:09 +08:00
dependabot[bot]
076f5de7d9 chore(deps): bump google.golang.org/grpc from 1.60.1 to 1.61.0 in /tools/goctl (#3870)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-25 22:57:17 +08:00
dependabot[bot]
303a74559a chore(deps): bump k8s.io/apimachinery from 0.29.0 to 0.29.1 (#3864)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-25 22:14:22 +08:00
Kevin Wan
c08e741d7a fix: cpu stat in cgroup v2 (#3857) 2024-01-17 23:35:42 +08:00
dependabot[bot]
06d2c07fce chore(deps): bump github.com/jackc/pgx/v5 from 5.5.1 to 5.5.2 (#3850)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-17 22:55:53 +08:00
Kevin Wan
b6f00a5789 Update readme-cn.md (#3849) 2024-01-15 18:42:28 +08:00
MarkJoyMa
dace520654 fix: revert sqlx metric namespace (#3847) 2024-01-14 02:38:10 +00:00
Kevin Wan
44d347d48a fix: issue #3840 (#3846) 2024-01-14 09:37:01 +08:00
Kevin Wan
408827d876 fix: issue 3840 (#3845) 2024-01-13 23:48:50 +08:00
Kevin Wan
9e33b557b1 chore: refactor redis (#3844) 2024-01-13 23:02:19 +08:00
Kevin Wan
368caa7608 feat: upgrade go-redis to v9 (#3088)
Co-authored-by: cong <zhangcong1992@gmail.com>
2024-01-13 22:40:58 +08:00
Kevin Wan
7822a4c1cb chore: refactor mapping errors (#3843) 2024-01-13 22:11:19 +08:00
Remember
0441f84606 fix(mapping): call fillSliceValue panic if the value is nil (#3839) 2024-01-13 13:48:43 +00:00
Kevin Wan
81d72b5010 chore: make cpu usage more smooth (#3842) 2024-01-13 19:36:25 +08:00
kesonan
7ba8adfc74 fix(goctl)/new parser (#3834)
Co-authored-by: keson <keson@kesondeMacBook-Pro.local>
2024-01-11 15:50:53 +00:00
dependabot[bot]
ffd2a78623 chore(deps): bump github.com/DATA-DOG/go-sqlmock from 1.5.1 to 1.5.2 (#3830)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-11 11:42:47 +08:00
Kevin Wan
1b9b3cada7 Update readme-cn.md (#3836) 2024-01-10 20:08:48 +08:00
dependabot[bot]
38c8f9cf21 chore(deps): bump golang.org/x/net from 0.19.0 to 0.20.0 (#3831)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-09 23:37:04 +08:00
dependabot[bot]
54dbb05bb9 chore(deps): bump github.com/DATA-DOG/go-sqlmock from 1.5.1 to 1.5.2 in /tools/goctl (#3832)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-09 21:45:06 +08:00
kesonan
9a671f6059 fix #3825 (#3828) 2024-01-06 14:45:46 +00:00
dependabot[bot]
80aab0b3f8 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.31.0 to 2.31.1 (#3826)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-06 21:10:12 +08:00
dependabot[bot]
2d0286646f chore(deps): bump golang.org/x/sys from 0.15.0 to 0.16.0 (#3829)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-06 21:01:00 +08:00
dependabot[bot]
d012fe97b1 chore(deps): bump github.com/prometheus/client_golang from 1.17.0 to 1.18.0 (#3822)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-30 10:25:20 +08:00
dependabot[bot]
7ca13bc25e chore(deps): bump google.golang.org/protobuf from 1.31.1-0.20231027082548-f4a6c1f6e5c1 to 1.32.0 (#3809)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-23 23:12:33 +08:00
dependabot[bot]
9c20f10743 chore(deps): bump google.golang.org/protobuf from 1.31.1-0.20231027082548-f4a6c1f6e5c1 to 1.32.0 in /tools/goctl (#3810)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-23 22:57:37 +08:00
Kimjin-gd
6ec38ec056 fix: negative float32 overflow when unmarshalling (#3811)
Co-authored-by: kim1.jin <kim1.jin@bkyo.io>
2023-12-23 14:47:11 +00:00
dependabot[bot]
28c742a1e1 chore(deps): bump k8s.io/client-go from 0.28.4 to 0.29.0 (#3800)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-22 23:46:19 +08:00
dependabot[bot]
b3b6cfe947 chore(deps): bump github.com/pelletier/go-toml/v2 from 2.1.0 to 2.1.1 (#3801)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-21 19:18:55 +08:00
dependabot[bot]
a8ef7b51eb chore(deps): bump k8s.io/apimachinery from 0.28.4 to 0.29.0 (#3802)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-21 18:59:55 +08:00
dependabot[bot]
124968114a chore(deps): bump google.golang.org/grpc from 1.60.0 to 1.60.1 (#3806)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-21 18:21:21 +08:00
dependabot[bot]
04ed821b65 chore(deps): bump google.golang.org/grpc from 1.60.0 to 1.60.1 in /tools/goctl (#3807)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-21 17:47:30 +08:00
dependabot[bot]
15599ac0a0 chore(deps): bump github.com/google/uuid from 1.4.0 to 1.5.0 (#3799) 2023-12-20 00:48:09 +08:00
dependabot[bot]
0cf6971664 chore(deps): bump golang.org/x/crypto from 0.16.0 to 0.17.0 (#3803) 2023-12-20 00:35:43 +08:00
dependabot[bot]
47c4f2831c chore(deps): bump golang.org/x/crypto from 0.16.0 to 0.17.0 in /tools/goctl (#3804)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 23:44:50 +08:00
Kevin Wan
2b18dd1764 chore: update goctl deps (#3797) 2023-12-17 14:06:32 +08:00
Kevin Wan
27c4908342 chore: coding style (#3796) 2023-12-17 13:44:55 +08:00
李登富
48625fa381 fix endless loop caused by ErrCompacted (#3774)
Co-authored-by: lidengfu <lidengfu@excean.com>
2023-12-17 05:28:19 +00:00
Kevin Wan
83a776a190 chore: upgrade otel, removed ut temporarily because of otel API changes (#3795) 2023-12-17 12:26:48 +08:00
Qiu shao
431f9af43e feat:add redis ExistsMany method (#3769) 2023-12-16 14:49:16 +08:00
gongluck
8c2f4c1899 Fixed #3771 (#3788) 2023-12-16 06:37:35 +00:00
Alex Last
919477ffe4 fix(servicegroup): use logx for shutdown message (#3719) 2023-12-16 06:25:02 +00:00
Summer-lights
400386459c fix(redis): redis ttl -1 and -2 (#3783) 2023-12-16 05:46:53 +00:00
dependabot[bot]
ebe0801d2f chore(deps): bump github.com/emicklei/proto from 1.12.2 to 1.13.0 in /tools/goctl (#3782)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-16 13:39:59 +08:00
dependabot[bot]
b76d85f204 chore(deps): bump google.golang.org/grpc from 1.59.0 to 1.60.0 in /tools/goctl (#3787)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-16 13:25:31 +08:00
dependabot[bot]
28ba57afb3 chore(deps): bump github.com/jhump/protoreflect from 1.15.3 to 1.15.4 (#3794)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-16 13:18:36 +08:00
dependabot[bot]
d6873047ce chore(deps): bump google.golang.org/grpc from 1.59.0 to 1.60.0 (#3791) 2023-12-16 00:44:30 +08:00
dependabot[bot]
54c0f2e5cf chore(deps): bump github.com/DATA-DOG/go-sqlmock from 1.5.0 to 1.5.1 in /tools/goctl (#3781) 2023-12-15 12:13:12 +08:00
dependabot[bot]
7795231cc6 chore(deps): bump github.com/DATA-DOG/go-sqlmock from 1.5.0 to 1.5.1 (#3779)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-15 00:24:28 +08:00
zzZZzzz888
4835e4fe51 fix: coredump: goctl model mysql ddl --src user_base.sql --dir . area… (#3777)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-12-12 16:09:15 +00:00
dependabot[bot]
daef970091 chore(deps): bump github.com/jackc/pgx/v5 from 5.5.0 to 5.5.1 (#3778)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-12 23:53:29 +08:00
guangwu
05020a92e8 fix: primary key unique key simultaneously exist cacheIdPrefix duplicate (#3763)
Signed-off-by: guoguangwu <guoguangwu@magic-shield.com>
2023-12-11 23:13:35 +08:00
POABOB
a1bbac3c6c fix: prevent a crash if there is a unique key constraint with a nil field. (#3770) 2023-12-11 13:29:05 +00:00
Kevin Wan
22c98beb24 feat: add dbtest to facility db test (#3768) 2023-12-09 22:52:06 +08:00
dependabot[bot]
8fd710d5e7 chore(deps): bump go.mongodb.org/mongo-driver from 1.13.0 to 1.13.1 (#3767) 2023-12-09 18:47:08 +08:00
dependabot[bot]
91a735ae47 chore(deps): bump go.etcd.io/etcd/client/v3 from 3.5.10 to 3.5.11 (#3764)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-08 17:01:12 +08:00
Kevin Wan
39c662eece Update readme-cn.md (#3755) 2023-12-04 21:02:30 +08:00
kesonan
5e63002cf8 (goctl:) fix circle import in case new parser (#3750)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-11-29 11:13:39 +00:00
dependabot[bot]
c46bcf7e1b chore(deps): bump golang.org/x/net from 0.18.0 to 0.19.0 (#3749)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-29 19:00:37 +08:00
dependabot[bot]
3c65bdbb66 chore(deps): bump golang.org/x/sys from 0.14.0 to 0.15.0 (#3747)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-28 15:37:52 +08:00
null
5630bce286 Fix incorrect description in documentation (#3745) 2023-11-28 06:58:08 +00:00
dependabot[bot]
75524da21e chore(deps): bump golang.org/x/time from 0.4.0 to 0.5.0 (#3746)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-28 14:15:58 +08:00
Kevin Wan
ede7e683fd feat: auto stop profiling after one minute (#3742) 2023-11-24 21:27:05 +08:00
Kevin Wan
eb14d1347e chore: refactor ring (#3739) 2023-11-23 23:57:26 +08:00
POABOB
c220b5d886 fix: prevent ring index overflow (#3738) 2023-11-23 15:44:33 +00:00
dependabot[bot]
5e8e21b257 chore(deps): bump k8s.io/client-go from 0.28.3 to 0.28.4 (#3737)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-23 23:13:16 +08:00
dependabot[bot]
0635a4ac96 chore(deps): bump k8s.io/apimachinery from 0.28.3 to 0.28.4 (#3733)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-22 23:37:24 +08:00
MarkJoyMa
c71b753c78 fix: goctl FindOne error (#3731) 2023-11-21 03:52:51 +00:00
Kevin Wan
2f8cffc699 chore: update mongo driver (#3727) 2023-11-19 16:35:25 +08:00
Kevin Wan
9c1aa6da3d chore: refact dart code generation (#3726) 2023-11-18 22:41:26 +08:00
anstns
da67ea2300 add map type (#3704) 2023-11-18 13:55:13 +00:00
kesonan
72dd2736f5 change command-line arg 'table' from string to slice type (#3707) 2023-11-13 11:46:17 +00:00
dependabot[bot]
24695bba09 chore(deps): bump golang.org/x/net from 0.17.0 to 0.18.0 (#3709)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-10 10:55:38 +08:00
dependabot[bot]
c7c43062c5 chore(deps): bump golang.org/x/text from 0.13.0 to 0.14.0 in /tools/goctl (#3701)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-07 20:25:33 +08:00
dependabot[bot]
97e1ea0633 chore(deps): bump github.com/spf13/cobra from 1.7.0 to 1.8.0 in /tools/goctl (#3700)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-07 20:11:03 +08:00
dependabot[bot]
04b9737a61 chore(deps): bump github.com/fatih/color from 1.15.0 to 1.16.0 (#3698)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-07 19:31:41 +08:00
dependabot[bot]
b0fb246693 chore(deps): bump golang.org/x/sys from 0.13.0 to 0.14.0 (#3699)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-07 18:03:49 +08:00
dependabot[bot]
41140ac78c chore(deps): bump golang.org/x/time from 0.3.0 to 0.4.0 (#3697)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-07 17:44:25 +08:00
dependabot[bot]
1281904572 chore(deps): bump github.com/jackc/pgx/v5 from 5.4.3 to 5.5.0 (#3696)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-07 11:32:10 +08:00
kesonan
c8a8ff7cad Feat/default new api parser (#3683) 2023-11-04 14:48:44 +00:00
zhaolei
df2799fff1 fix import error if generate multiple proto (#3694) 2023-11-04 12:42:25 +00:00
dependabot[bot]
fd8ee0b851 chore(deps): bump github.com/emicklei/proto from 1.12.1 to 1.12.2 in /tools/goctl (#3690)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-03 10:24:21 +08:00
Ash V
6ecc5e7b73 Enhanced the CODE_OF_CONDUCT Guidelines (#3680) 2023-10-29 07:07:02 +00:00
Kevin Wan
52963c2ebf chore: update go-zero version to v1.6.0 in goctl (#3679) 2023-10-28 21:42:16 +08:00
Kevin Wan
07e3e14c0e chore: remove go build version in fuzz test (#3678) 2023-10-28 20:53:03 +08:00
Rene Leonhardt
34c5f6616c chore: upgrade go to 1.19 (#3677) 2023-10-28 12:12:04 +00:00
MarkJoyMa
32600f2619 fix: adjust log encode output mode (#3676) 2023-10-28 11:46:52 +00:00
dependabot[bot]
b07df1c344 chore(deps): bump go.etcd.io/etcd/client/v3 from 3.5.9 to 3.5.10 (#3675)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-28 13:16:25 +08:00
Kevin Wan
a1fca3a1da chore: upgrade go dependencies (#3657) 2023-10-28 00:19:22 +08:00
Kevin Wan
9394e59597 chore: update goctl version to 1.6.0 (#3674) 2023-10-27 21:59:35 +08:00
dependabot[bot]
f8adc71529 chore(deps): bump github.com/google/uuid from 1.3.1 to 1.4.0 (#3673)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-27 21:34:27 +08:00
MarkJoyMa
c05e03bb5a feat: add metrics (#3624) 2023-10-26 15:51:28 +00:00
Kevin Wan
199e86050e chore: simplify prometheus check (#3672) 2023-10-26 20:32:13 +08:00
#Suyghur
1e2a12b3d6 feat(metric): added Dec() and Sub() in GaugeVec interface (#3666) 2023-10-26 20:13:42 +08:00
Kevin Wan
922efbfc2d chore: refactor zrpc timeout (#3671) 2023-10-26 08:55:26 +08:00
vankillua
842c4d81cc feat: support the specified timeout of rpc methods (#2742)
Co-authored-by: hanzijian <hanzijian@52tt.com>
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-10-25 13:01:57 +00:00
dependabot[bot]
2a335c7608 chore(deps): bump github.com/fullstorydev/grpcurl from 1.8.8 to 1.8.9 (#3668)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-25 15:15:30 +08:00
Bhargav Shirin Nalamati
35edd6b19d fixed typo: reds to redis (#3664) 2023-10-24 02:54:15 +00:00
Kevin Wan
36bbc6a2e2 chore: add error handling on registering event handlers to k8s (#3663) 2023-10-23 21:57:09 +08:00
唐小鸭
e20ccdd011 Support for resource injection (#3383)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-10-23 13:22:16 +00:00
Kevin Wan
c2ff00883a chore: update restful/grpc servers shutdown stages (#3662) 2023-10-23 13:03:05 +00:00
MarkJoyMa
00db97fcc1 feat: model add withSession (#3658) 2023-10-23 04:43:05 +00:00
7134g
117c3a9069 fix: multiple files import the same api file (#3642) 2023-10-23 04:04:52 +00:00
Kevin Wan
172ff407f3 chore: refactor mongo logs (#3660) 2023-10-23 11:03:55 +08:00
shenbaise9527
a242fec5e1 feat: support for disable mon logs like sqlx (#3606)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-10-22 15:00:02 +00:00
Kevin Wan
6286941ebf chore: add go-zero users (#3659) 2023-10-22 22:23:24 +08:00
Kevin Wan
42e0a6f90c chore: refactor errors to use errors.Is (#3654) 2023-10-21 00:00:57 +08:00
cary
81ae7d36b5 Support for adding ignore_columns parameters to the goctl model pg (#3427) 2023-10-20 08:58:18 +00:00
Kevin Wan
944e76edb9 chore: refactor errors (#3651) 2023-10-20 14:58:38 +08:00
MarkJoyMa
151768ef82 feat: optimize logx print error (#3649) 2023-10-19 13:46:52 +00:00
Surav Shrestha
50581c7f5c docs fix typo in core/logx/readme.md (#3650) 2023-10-19 13:33:25 +00:00
dependabot[bot]
54041ef9e4 chore(deps): bump google.golang.org/grpc from 1.58.2 to 1.59.0 in /tools/goctl (#3645) 2023-10-19 12:38:08 +08:00
dependabot[bot]
5a9ae5ef02 chore(deps): bump google.golang.org/grpc from 1.58.2 to 1.59.0 (#3647) 2023-10-19 12:26:36 +08:00
guonaihong
19de13bb04 Upgrade grpc-go,fix 0day problem. (#3623) 2023-10-19 03:45:59 +00:00
Kevin Wan
3ab4e82168 chore: upgrade go to 1.19 (#3648) 2023-10-19 11:30:37 +08:00
Armaan
619e838513 updated CONTRIBUTING.md with emojified , fun, precise and engaging text (#3643) 2023-10-19 03:06:38 +00:00
kesonan
423597a01c feat: export devserver.Config (#3638) 2023-10-17 15:38:21 +00:00
kesonan
d84dfe1b20 fix: goctl unit test (#3636) 2023-10-17 11:15:32 +00:00
Soham Tembhurne
87b7a1120d Update documenation section #background (#3634) 2023-10-16 00:11:17 +00:00
Kevin Wan
528af8a99d chore: update readme for Mac install instructions (#3633) 2023-10-16 08:08:42 +08:00
Soham Tembhurne
17fc68ac5a Update readme.md (#3630) 2023-10-15 23:45:25 +08:00
Kevin Wan
804a56bd14 fix: optimize logx for less GC objects (#3627) 2023-10-15 23:37:45 +08:00
Kevin Wan
88f60d7736 chore: refactor signal sigterm and sigint (#3632) 2023-10-15 23:24:17 +08:00
#Suyghur
95b7a3d3ce feat: add the SIGINT signal in signals.go to subscribe the user input ctrl+c to exit the application operation (#3611) 2023-10-15 22:58:15 +08:00
Kevin Wan
d71c0da7b7 chore: refactor error comparison (#3629) 2023-10-15 13:41:06 +00:00
Kevin Wan
fd070fec91 feat: retry with ctx deadline (#3626) 2023-10-15 13:39:44 +00:00
Kevin Wan
4f22034342 fix: unmarshal from number to string with incorrect error message (#3625) 2023-10-15 02:06:00 +00:00
Ikko Eltociear Ashimine
b731aa38af refactor: update builder.go (#3620) 2023-10-13 07:03:15 +00:00
dependabot[bot]
bf996a1812 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.30.5 to 2.31.0 (#3616)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-12 11:44:55 +08:00
dependabot[bot]
af7ce65244 chore(deps): bump golang.org/x/net from 0.15.0 to 0.17.0 in /tools/goctl (#3618)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-12 11:37:06 +08:00
dependabot[bot]
952db71835 chore(deps): bump golang.org/x/net from 0.16.0 to 0.17.0 (#3612)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-11 17:37:14 +08:00
Kevin Wan
abd1fa96a9 fix: UpdateStmt doesn't update the statement correctly in sqlx/bulkinserter.go (#3607) 2023-10-09 21:57:26 +08:00
Kevin Wan
5aedd9c076 chore: simplify parsing numbers with overflow (#3610) 2023-10-09 13:00:09 +00:00
Kevin Wan
ff230c4b1d chore: refactor goctl api (#3605) 2023-10-07 22:58:29 +08:00
kesonan
02c95108b9 optimize: fix experimental api (#3604) 2023-10-07 19:48:41 +08:00
dependabot[bot]
1ff541afe4 chore(deps): bump golang.org/x/net from 0.15.0 to 0.16.0 (#3603)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-07 10:50:49 +08:00
Kevin Wan
11a8cbc1e5 chore: rename noOpBreaker to nopBreaker (#3602) 2023-10-06 23:41:09 +08:00
dependabot[bot]
c063976822 chore(deps): bump golang.org/x/sys from 0.12.0 to 0.13.0 (#3601)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-06 22:04:14 +08:00
dependabot[bot]
cb707034ce chore(deps): bump github.com/jhump/protoreflect from 1.15.2 to 1.15.3 (#3600)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-05 15:26:21 +08:00
dependabot[bot]
f10db27efd chore(deps): bump github.com/prometheus/client_golang from 1.16.0 to 1.17.0 (#3594)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-28 11:38:20 +08:00
Kevin Wan
4878f90546 chore: update goctl version to 1.5.6 (#3593) 2023-09-27 22:55:50 +08:00
Kevin Wan
421e6617b1 chore: add more tests (#3592) 2023-09-27 22:33:27 +08:00
Kevin Wan
0ee7a271d3 fix: avoid float overflow in mapping.Unmarshal (#3590) 2023-09-26 13:46:34 +00:00
dependabot[bot]
af022b9655 chore(deps): bump google.golang.org/grpc from 1.58.1 to 1.58.2 in /tools/goctl (#3584) 2023-09-25 16:23:29 +08:00
dependabot[bot]
98d46261d9 chore(deps): bump google.golang.org/grpc from 1.58.1 to 1.58.2 (#3585)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-24 23:27:02 +08:00
Kevin Wan
4222fd97bc chore: add test for logging rotate size (#3587) 2023-09-24 22:28:03 +08:00
dependabot[bot]
814852f0b8 chore(deps): bump github.com/fullstorydev/grpcurl from 1.8.7 to 1.8.8 (#3586)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-24 19:04:34 +08:00
Kevin Wan
ded2888759 fix: avoid integer overflow in mapping.Unmarshal (#3582) 2023-09-21 14:22:33 +00:00
Kevin Wan
18d66a795d chore: add more tests (#3578) 2023-09-20 23:52:10 +08:00
Kevin Wan
4211672bfd chore: add more tests (#3577) 2023-09-20 00:01:26 +08:00
Kevin Wan
68df0c3620 chore: add more tests (#3575) 2023-09-18 11:01:46 +08:00
xt-inking
5e435b6a76 fix: avoid losing logs before closing (#3573) 2023-09-17 11:38:53 +00:00
Kevin Wan
0dcede6457 chore: refactor log limit in rest (#3572) 2023-09-16 22:33:30 +08:00
Awadabang
cc21f5fae2 update: limit logBrief http body size (#3498)
Co-authored-by: 常公征 <changgz@yealink.com>
2023-09-16 11:58:21 +00:00
dependabot[bot]
b22ad50d59 chore(deps): bump google.golang.org/grpc from 1.58.0 to 1.58.1 in /tools/goctl (#3568)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-16 16:52:01 +08:00
Kevin Wan
974252980c chore: upgrade grpc (#3570) 2023-09-15 23:21:22 +08:00
dependabot[bot]
8d83986d27 chore(deps): bump google.golang.org/grpc from 1.57.0 to 1.58.0 in /tools/goctl (#3546) 2023-09-12 20:00:52 +08:00
Kevin Wan
6821b0a7dd chore: upgrade grpc (#3558) 2023-09-12 10:30:26 +08:00
Kevin Wan
1ba1724c65 chore: refactor (#3545) 2023-09-06 22:36:43 +08:00
Xinwei Xiong
ca5a7df5b0 feat: Optimize Encoding Functions and Add Descriptive Comments (#3543) 2023-09-06 14:19:50 +00:00
dependabot[bot]
69a3024853 chore(deps): bump golang.org/x/net from 0.14.0 to 0.15.0 (#3544)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-06 20:28:03 +08:00
dependabot[bot]
fd3abf3717 chore(deps): bump golang.org/x/text from 0.12.0 to 0.13.0 in /tools/goctl (#3542)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-09-06 20:20:12 +08:00
dependabot[bot]
99b3750d10 chore(deps): bump golang.org/x/sys from 0.11.0 to 0.12.0 (#3541)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-06 00:02:00 +08:00
POABOB
33f6d7ebb8 fix: goctl pg gen will extract all fields when the same table name exists in different schemas (#3496) (#3517) 2023-09-04 20:48:26 +08:00
kesonan
c4ef9ceb68 Add api version (#3536) 2023-09-02 01:45:48 +00:00
dependabot[bot]
e95861f28a chore(deps): bump github.com/pelletier/go-toml/v2 from 2.0.9 to 2.1.0 (#3532)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-31 22:52:23 +08:00
Kevin Wan
d3cd7b17c0 Revert "add:func() QueryRowsPartial,QueryRowPartial into cachedsql.go" (#3523) 2023-08-27 21:36:14 +08:00
liumin-go
a50515496c add:func() QueryRowsPartial,QueryRowPartial into cachedsql.go (#3512)
Co-authored-by: 刘敏 <liumin@liumindeMac-mini.local>
2023-08-27 08:05:02 +00:00
Kevin Wan
0423313d9b feat: support json:"-" in mapping (#3521) 2023-08-27 16:04:38 +08:00
dependabot[bot]
7bbe7de05f chore(deps): bump github.com/google/uuid from 1.3.0 to 1.3.1 (#3511)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-24 14:59:49 +08:00
dependabot[bot]
83a451f2f4 chore(deps): bump github.com/jhump/protoreflect from 1.15.1 to 1.15.2 (#3518)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-24 14:39:50 +08:00
Kevin Wan
d2a874f21d chore: upgrade go-zero, and update goctl version (#3509) 2023-08-21 09:09:51 +08:00
Kevin Wan
fd85b24b25 Update readme-cn.md 2023-08-20 23:38:39 +08:00
Kevin Wan
14fcbd7658 fix #3499 (#3508) 2023-08-19 22:17:24 +08:00
Kevin Wan
cb3ffc76a3 fix: #3478 (#3493) 2023-08-14 14:22:22 +00:00
dependabot[bot]
45fbd7dc35 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.30.4 to 2.30.5 (#3490)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-10 19:53:17 +08:00
dependabot[bot]
af821cf794 chore(deps): bump golang.org/x/net from 0.13.0 to 0.14.0 (#3484)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-09 10:39:32 +08:00
dependabot[bot]
ec69950153 chore(deps): bump github.com/jackc/pgx/v5 from 5.4.2 to 5.4.3 (#3483)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-09 10:18:27 +08:00
Kevin Wan
ce5e78db53 chore: use jsonTagKey to replace json literals (#3479) 2023-08-06 22:00:24 +08:00
dependabot[bot]
ed75802eaa chore(deps): bump golang.org/x/text from 0.11.0 to 0.12.0 in /tools/goctl (#3477)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-06 09:57:19 +08:00
dependabot[bot]
76c92b571d chore(deps): bump golang.org/x/sys from 0.10.0 to 0.11.0 (#3476)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-06 00:19:00 +08:00
dependabot[bot]
a2e703c53e chore(deps): bump golang.org/x/net from 0.12.0 to 0.13.0 (#3463)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-08-04 20:39:17 +08:00
dependabot[bot]
ca698deb2a chore(deps): bump go.mongodb.org/mongo-driver from 1.12.0 to 1.12.1 (#3472)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-04 19:13:16 +08:00
guangwu
a9f4aab86b fix: "EXPRIMENTAL" is a misspelling of "EXPERIMENTAL" (#3462) 2023-08-02 23:59:37 +08:00
Kevin Wan
c3f57e9b0a chore: fix potential nil pointer errors (#3454) 2023-07-30 21:37:41 +08:00
Kevin Wan
ad4cce959d chore: add more tests (#3453) 2023-07-29 22:34:16 +08:00
Shyunn
279123f4a7 feat: add prometheus summary metrics (#3440)
Co-authored-by: chen quan <chenquan.dev@gmail.com>
2023-07-29 16:51:43 +08:00
dependabot[bot]
457eb1961b chore(deps): bump google.golang.org/grpc from 1.56.2 to 1.57.0 (#3445)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-28 22:18:14 +08:00
dependabot[bot]
63df384a4b chore(deps): bump google.golang.org/grpc from 1.56.2 to 1.57.0 in /tools/goctl (#3446)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-28 22:17:23 +08:00
MarkJoyMa
42bfa26e2b fix: remove mapping redundant error (#3439) 2023-07-24 00:10:50 +08:00
Kevin Wan
ff04356704 fix: format error should not trigger circuit breaker in sqlx (#3437) 2023-07-23 20:40:03 +08:00
MarkJoyMa
05db706c62 feat: optimize mapping error (#3438) 2023-07-23 12:10:41 +00:00
dependabot[bot]
ef2e0d859d chore(deps): bump go.uber.org/automaxprocs from 1.5.2 to 1.5.3 (#3435)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-22 00:03:07 +08:00
dependabot[bot]
05ec16ae9d chore(deps): bump github.com/gookit/color from 1.5.3 to 1.5.4 in /tools/goctl (#3433)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-21 23:27:49 +08:00
dependabot[bot]
13e685e0db chore(deps): bump github.com/emicklei/proto from 1.12.0 to 1.12.1 in /tools/goctl (#3431)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-21 23:12:06 +08:00
dependabot[bot]
c10f44b74e chore(deps): bump github.com/emicklei/proto from 1.11.2 to 1.12.0 in /tools/goctl (#3429) 2023-07-18 09:41:13 +08:00
Kevin Wan
57644420ed chore: update go-zero for goctl (#3426) 2023-07-14 21:38:42 +08:00
dependabot[bot]
b245159417 chore(deps): bump github.com/pelletier/go-toml/v2 from 2.0.8 to 2.0.9 (#3423)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-14 19:57:36 +08:00
dependabot[bot]
c26ea17669 chore(deps): bump github.com/iancoleman/strcase from 0.2.0 to 0.3.0 in /tools/goctl (#3424) 2023-07-14 13:20:06 +08:00
Kevin Wan
a7daff3587 chore: make servicegroup panic as demand (#3422) 2023-07-13 14:08:35 +00:00
dependabot[bot]
6719d06146 chore(deps): bump github.com/jackc/pgx/v5 from 5.4.1 to 5.4.2 (#3417)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-13 10:56:18 +08:00
Kevin Wan
0c6eaeda9f chore: coding style (#3413) 2023-07-12 01:08:09 +08:00
Xinyan Lu
b9c0c0f8b5 feat: add detail type mismatch info in number fields check (#3386) (#3387) 2023-07-11 16:29:42 +00:00
Kevin Wan
77da459165 chore: make test stable (#3412) 2023-07-11 16:20:41 +00:00
Kevin Wan
13cdbdc98b chore: avoid nested WithCodeResponseWriter (#3406) 2023-07-11 15:59:43 +00:00
guangwu
e8c1e6e09b fix: log format error (#3409) 2023-07-11 05:28:53 +00:00
guangwu
f1171e01f2 chore: slice replace loop (#3410) 2023-07-11 05:27:46 +00:00
cong
61e562d0c7 refactor(rest): keep rest log collector context key private (#3407) 2023-07-10 01:52:26 +00:00
chen quan
b71453985c feat(sqlx): support for custom Acceptable function (#3405) 2023-07-10 01:16:45 +00:00
Kevin Wan
31b9ba19a2 chore: refactor httpx.TimeoutHandler (#3400) 2023-07-09 07:04:59 +00:00
dependabot[bot]
3170afd57b chore(deps): bump google.golang.org/grpc from 1.56.1 to 1.56.2 in /tools/goctl (#3403)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-08 16:21:50 +08:00
dependabot[bot]
03e365a5d8 chore(deps): bump google.golang.org/grpc from 1.56.1 to 1.56.2 (#3402)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-08 15:34:45 +08:00
dependabot[bot]
7d4fce9588 chore(deps): bump golang.org/x/net from 0.11.0 to 0.12.0 (#3401)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-06 14:54:13 +08:00
扶桑花间
916cea858f 1. Fix w. (http. Flusher). Flush() error (#3388) 2023-07-05 15:27:15 +00:00
dependabot[bot]
a86942d532 chore(deps): bump golang.org/x/sys from 0.9.0 to 0.10.0 (#3396)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-05 22:49:07 +08:00
dependabot[bot]
f76c70ea9a chore(deps): bump golang.org/x/text from 0.10.0 to 0.11.0 in /tools/goctl (#3397)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-05 22:19:04 +08:00
MarkJoyMa
4cbfdb3d74 feat: optimize must log add stack (#3384) 2023-06-30 01:11:03 +00:00
dependabot[bot]
aefa6dfb50 chore(deps): bump google.golang.org/protobuf from 1.30.0 to 1.31.0 (#3376) 2023-06-30 09:06:20 +08:00
dependabot[bot]
9047029475 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.30.3 to 2.30.4 (#3381) 2023-06-30 09:05:05 +08:00
dependabot[bot]
f296c182f7 chore(deps): bump google.golang.org/protobuf from 1.30.0 to 1.31.0 in /tools/goctl (#3377)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-27 22:45:00 +08:00
Kevin Wan
40e7a4cd07 chore: refactor httpx.SetOkHandler (#3373) 2023-06-26 00:27:26 +08:00
Kevin Wan
92e5819e91 opt: improve logx performance (#3371) 2023-06-25 15:41:28 +08:00
2822132073
8d23ab158b fix In goctl new api, occur error invalid character 'A' looking for beginning of value (#3357) 2023-06-25 07:26:21 +00:00
唐小鸭
bcccfab824 [fix] The directory is not recognized when it is in a soft link (#3337) 2023-06-25 05:06:27 +00:00
dependabot[bot]
f7e701a634 chore(deps): bump google.golang.org/grpc from 1.56.0 to 1.56.1 (#3367) 2023-06-25 13:04:13 +08:00
dependabot[bot]
7c2d8e5cc2 chore(deps): bump google.golang.org/grpc from 1.56.0 to 1.56.1 in /tools/goctl (#3369) 2023-06-25 12:57:03 +08:00
dependabot[bot]
5b622d6265 chore(deps): bump go.mongodb.org/mongo-driver from 1.11.7 to 1.12.0 (#3370)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-23 14:50:48 +08:00
dependabot[bot]
c5510a4e1b chore(deps): bump github.com/jackc/pgx/v5 from 5.4.0 to 5.4.1 (#3363)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-21 15:16:06 +08:00
Kevin Wan
2a33b74b35 chore: coding style (#3362) 2023-06-17 22:59:00 +08:00
anqiansong
45bb547a81 (goctl)fix: #3328 (#3348)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-06-17 13:23:57 +00:00
Mikael
f5f5261556 add whether to generate rpc client option (#3361)
Co-authored-by: admin <admin@admindeMacBook-Pro.local>
2023-06-17 12:52:49 +00:00
Kevin Wan
b176d5d434 chore: add more tests (#3359) 2023-06-17 20:51:33 +08:00
MarkJoyMa
92f6c48349 fix: NewClientWithTarget miss default config (#3358) 2023-06-16 23:29:52 +08:00
dependabot[bot]
71e8230e65 chore(deps): bump google.golang.org/grpc from 1.55.0 to 1.56.0 (#3354) 2023-06-16 07:46:33 +08:00
dependabot[bot]
018fa8e0a0 chore(deps): bump google.golang.org/grpc from 1.55.0 to 1.56.0 in /tools/goctl (#3355) 2023-06-16 07:31:48 +08:00
dependabot[bot]
979fe9718a chore(deps): bump github.com/prometheus/client_golang from 1.15.1 to 1.16.0 (#3352) 2023-06-16 07:29:38 +08:00
Kevin Wan
f998803131 chore: refactor and add more tests (#3351) 2023-06-16 01:04:58 +08:00
TaoYu
1262266ac2 feat: httpx add common handler (#3269) 2023-06-15 15:31:15 +00:00
dependabot[bot]
9c32bf8478 chore(deps): bump github.com/zeromicro/ddl-parser from 1.0.4 to 1.0.5 in /tools/goctl (#3350) 2023-06-15 07:34:19 +08:00
dependabot[bot]
37ec7f6443 chore(deps): bump github.com/jackc/pgx/v5 from 5.3.1 to 5.4.0 (#3349) 2023-06-15 07:31:16 +08:00
dependabot[bot]
2fdc4dfc0f chore(deps): bump golang.org/x/net from 0.10.0 to 0.11.0 (#3346) 2023-06-14 07:05:24 +08:00
dependabot[bot]
4b2a6ba3de chore(deps): bump golang.org/x/text from 0.9.0 to 0.10.0 in /tools/goctl (#3342) 2023-06-13 07:37:52 +08:00
dependabot[bot]
7fa3f10f22 chore(deps): bump golang.org/x/sys from 0.8.0 to 0.9.0 (#3341) 2023-06-13 07:37:30 +08:00
elza
4a29a0b642 fix: fixed goctl api go --home parameter error when loading non-exist… (#3319)
Co-authored-by: yuanyou <yuanyou@kezaihui.com>
2023-06-12 16:00:41 +00:00
Kevin Wan
a62745a152 Update readme-cn.md 2023-06-12 23:35:33 +08:00
Kevin Wan
28314326e7 chore: more tests (#3340) 2023-06-12 23:29:23 +08:00
Kevin Wan
f6bdb6e1de chore: add more tests (#3338) 2023-06-12 01:22:20 +08:00
Kevin Wan
efa6940001 chore: improve logx gzip (#3332) 2023-06-09 22:50:59 +08:00
Ron_haur
da81d8f774 Fix: logx with Compress auto delete old logs (#3329)
Co-authored-by: haoran.ren <haoran.ren@mihoyo.com>
2023-06-08 11:08:04 +00:00
dependabot[bot]
fd84b27bdc chore(deps): bump go.mongodb.org/mongo-driver from 1.11.6 to 1.11.7 (#3325) 2023-06-08 07:21:49 +08:00
Kevin Wan
6b4d0d89c0 chore: add more tests (#3324) 2023-06-07 00:46:43 +08:00
Kevin Wan
d61a55f779 chore: update readme to remove upgrade parts. (#3318) 2023-06-04 23:34:38 +08:00
Kevin Wan
8ef4164209 chore: make test stable (#3317) 2023-06-04 23:20:58 +08:00
Kevin Wan
50e29e2075 chore: update go-zero for goctl (#3316) 2023-06-04 17:28:27 +08:00
Kevin Wan
452c9dbcaf chore: add more tests (#3315) 2023-06-01 21:08:44 +08:00
dependabot[bot]
3564e36a35 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.30.2 to 2.30.3 (#3309)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-01 10:42:26 +08:00
dependabot[bot]
e479e47634 chore(deps): bump github.com/stretchr/testify from 1.8.3 to 1.8.4 in /tools/goctl (#3306)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-31 11:28:15 +08:00
dependabot[bot]
ad921a6419 chore(deps): bump github.com/stretchr/testify from 1.8.3 to 1.8.4 (#3305)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-31 11:20:12 +08:00
Kevin Wan
44c8d6f269 chore: add more tests (#3304) 2023-05-30 23:27:27 +08:00
Kevin Wan
8a4cc4f98d chore: add more tests (#3299) 2023-05-29 23:44:36 +08:00
kangqi
e751736516 trace exporter: add new type file (#3298)
Co-authored-by: zhaikangqi <794556486@qq.com>
2023-05-29 18:24:59 +08:00
Kevin Wan
032f2419a2 Update readme-cn.md 2023-05-29 17:36:10 +08:00
Kevin Wan
84adc054bc chore: add more tests (#3296) 2023-05-29 07:39:41 +08:00
Kevin Wan
b92e706ce1 chore: refactor (#3295) 2023-05-28 21:31:36 +08:00
MiNG
1b5946346e feat: support optional otel global initialization for #3284 (#3292) 2023-05-28 11:41:48 +00:00
Kevin Wan
28d3905731 chore: add more tests (#3294) 2023-05-28 19:26:45 +08:00
hc
3726851c7f feat: sqlc add SetCacheWithExpire method (#3249)
Co-authored-by: luohancai <luohancai@taqu.cn>
2023-05-28 12:27:30 +08:00
Kevin Wan
2f2ddd373b chore: refactor retry (#3291) 2023-05-28 12:11:55 +08:00
Xiaoju Jiang
8d48e34eed update: expand the retry method to support timeout and interval control (#3283) 2023-05-28 10:17:50 +08:00
Kevin Wan
32f78668db chore: add more tests (#3290) 2023-05-27 23:57:33 +08:00
Kevin Wan
cd0f3726ed chore: add more tests (#3288) 2023-05-27 21:49:11 +08:00
me-cs
0217044900 update:Use the Milliseconds method of duration to get the number of milliseconds (#3285)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-05-26 14:32:46 +00:00
Kevin Wan
8b4382dcec chore: add more tests (#3286) 2023-05-26 22:30:03 +08:00
Kevin Wan
fa33329a44 chore: add more tests (#3282) 2023-05-26 00:21:47 +08:00
dependabot[bot]
d76a39ac26 chore(deps): bump github.com/pelletier/go-toml/v2 from 2.0.7 to 2.0.8 (#3280)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-25 16:50:36 +08:00
guangwu
76a7a17e57 typo (#3281) 2023-05-25 15:58:11 +08:00
Kevin Wan
4a2a8d9e45 chore: add more tests (#3279) 2023-05-24 23:58:45 +08:00
guangwu
ef26b39b4c misspelling (#3248) 2023-05-24 08:15:27 +00:00
anqiansong
3ca40001b4 feat(goctl): Add with session for model tpl (#3272) 2023-05-24 07:34:26 +00:00
Toby
278ae3d26a feat: add OtlpHttpPath config support for ZincObserve Telemetry (#3271)
Signed-off-by: Toby Yan <me@tobyan.com>
Co-authored-by: cong <zhangcong1992@gmail.com>
2023-05-23 03:11:58 +00:00
dependabot[bot]
fa1d6d50a8 chore(deps): bump github.com/stretchr/testify from 1.8.2 to 1.8.3 (#3267) 2023-05-22 09:41:17 +08:00
dependabot[bot]
0f4973be06 chore(deps): bump github.com/stretchr/testify from 1.8.2 to 1.8.3 in /tools/goctl (#3268) 2023-05-21 21:43:24 +08:00
Kevin Wan
a9aac7e420 chore: add more tests (#3265) 2023-05-19 23:29:30 +08:00
Kevin Wan
925cf8d3d1 chore: add more tests (#3261) 2023-05-19 12:15:43 +08:00
Kevin Wan
99ce24e2ab chore: add more tests (#3260) 2023-05-19 00:56:50 +08:00
Kevin Wan
701bb31ed2 chore: add more tests (#3259) 2023-05-18 23:43:50 +08:00
Kevin Wan
55e2c7ee83 chore: add more tests (#3258) 2023-05-18 23:11:32 +08:00
Kevin Wan
90839965fa chore: remove directive for tests (#3257) 2023-05-18 21:34:33 +08:00
Kevin Wan
f7228e9af1 chore: add more tests (#3256) 2023-05-18 12:24:04 +08:00
Kevin Wan
f95adae3c1 Update readme-cn.md 2023-05-18 11:00:03 +08:00
Kevin Wan
bff5b81ad9 feat: support using session to execute statements in transaction (#3252) 2023-05-17 14:15:24 +00:00
guangwu
f0bdfb928f fix call error func (#3245) 2023-05-12 04:34:08 +00:00
Kevin Wan
e4a1b7bb39 chore: format the code (#3243) 2023-05-12 10:38:05 +08:00
dependabot[bot]
b6906b5d21 chore(deps): bump go.etcd.io/etcd/client/v3 from 3.5.8 to 3.5.9 (#3242)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-12 08:27:46 +08:00
guangwu
116da96178 add ignore file (#3240) 2023-05-11 08:53:13 +00:00
lchjczw
9fa98c2bd3 api imports take the form of relative paths (#3201)
Co-authored-by: 李春华 <lichunhua@threesoft.cn>
2023-05-10 03:40:07 +00:00
Kevin Wan
b1c4c4736f chore: better comments (#3232) 2023-05-09 22:58:40 +08:00
Thirteen
ef410e8083 fix: generate client directory for goctl (#3166) 2023-05-08 21:03:48 +00:00
fondoger
c22bc1c8ea [dart-gen] Fix lists containing atomic types (#3210) 2023-05-08 21:00:46 +00:00
Toby
1853428011 feat: add otlptracegrpc otlptracehttp headers support for Uptrace (#3219)
Signed-off-by: Toby Yan <me@tobyan.com>
Co-authored-by: cong <zhangcong1992@gmail.com>
2023-05-08 20:58:29 +00:00
dependabot[bot]
3637e10815 chore(deps): bump golang.org/x/net from 0.9.0 to 0.10.0 (#3230) 2023-05-09 04:53:33 +08:00
Kevin Wan
93124329ac chore: add more tests (#3229) 2023-05-08 23:49:13 +08:00
yangtao
851a72f1cc Add RunSafe with context (#3224)
Co-authored-by: yangtao <mrynag8614@163.com>
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-05-08 23:08:31 +08:00
SleeplessBot
a93c24ce84 Add method label for prometheus middleware metrics (#3226)
Co-authored-by: 蓝益尤 <lan.yiyou@intellif.com>
2023-05-08 12:59:20 +00:00
Kevin Wan
9f42eda9ff fix: timeout handler not implementing http.Flusher (#3225) 2023-05-08 18:07:02 +08:00
soasurs
8762a3b7ba fix: Errorv should generate JSON Object for content field in log (#3222)
Signed-off-by: soasurs <soasurs@gmail.com>
2023-05-08 09:16:44 +00:00
Kevin Wan
2684a157ff chore: remove fgprof, use pprof directly (#3220) 2023-05-07 18:21:10 +08:00
guangwu
63368d8b0c io/ioutil deprecated (#3217) 2023-05-06 09:50:54 +00:00
guangwu
4f13fe8188 io/ioutil deprecated (#3215) 2023-05-06 16:37:43 +08:00
Kevin Wan
9fc7874336 chore: optimize stat calculation (#3213) 2023-05-06 15:31:56 +08:00
dependabot[bot]
e6518521eb chore(deps): bump google.golang.org/grpc from 1.54.0 to 1.55.0 in /tools/goctl (#3209)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-06 14:29:51 +08:00
Kevin Wan
8f5a0a2de7 fix: remove etcd pings to avoid too-many-pings error (#3212) 2023-05-06 12:39:19 +08:00
ALMAS
774e8d1d08 feat: replaced color package to support Windows (#3207) 2023-05-05 13:09:54 +00:00
cong
8ad0668612 fix(zrpc): remove default keepalive params for NewClientWithTarget (#3208) 2023-05-05 13:00:59 +00:00
dependabot[bot]
8a043d2443 chore(deps): bump golang.org/x/sys from 0.7.0 to 0.8.0 (#3204)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-05 11:03:18 +08:00
dependabot[bot]
0e2ee97a02 chore(deps): bump go.mongodb.org/mongo-driver from 1.11.4 to 1.11.6 (#3205)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-05 10:41:23 +08:00
Kevin Wan
42300a7d83 chore: add more tests (#3203) 2023-05-04 23:43:34 +08:00
dependabot[bot]
fe97fab274 chore(deps): bump github.com/prometheus/client_golang from 1.15.0 to 1.15.1 (#3200)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-04 11:00:51 +08:00
dependabot[bot]
f93e752f98 chore(deps): bump github.com/emicklei/proto from 1.11.1 to 1.11.2 in /tools/goctl (#3199)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-04 10:24:24 +08:00
fondoger
3a66fc038f [dart-gen] Fix nullable list item issue (#3192) 2023-05-01 15:01:28 +08:00
Kevin Wan
b028ed058d chore: change port to 6060 by default in devserver (#3191) 2023-05-01 11:18:15 +08:00
guonaihong
1fd0c3992b fix panic (#3176) 2023-04-30 14:58:30 +00:00
Kevin Wan
1aebb3e5e4 chore: update readme (#3190) 2023-04-30 11:21:28 +08:00
Kevin Wan
8ffe4c01d1 chore: use logx.Must instead of log.Fatal (#3189) 2023-04-29 23:46:04 +08:00
Kevin Wan
a31256b327 chore: add more tests (#3187) 2023-04-29 22:59:07 +08:00
Kevin Wan
14caf5c799 chore: simplify tests with logtest (#3184) 2023-04-29 20:36:29 +08:00
chensy
c0f8a58ed7 no \t to space line (#3177)
Co-authored-by: chenjieping <chenjieping@kezaihui.com>
2023-04-29 08:35:04 +00:00
dependabot[bot]
3189ec7be6 chore(deps): bump github.com/go-sql-driver/mysql from 1.7.0 to 1.7.1 (#3174)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-28 23:27:01 +08:00
dependabot[bot]
f51e9f0ea7 chore(deps): bump github.com/go-sql-driver/mysql from 1.7.0 to 1.7.1 in /tools/goctl (#3175)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-27 23:37:28 +08:00
cong
ba9d510cdb fix(metrics): enable prometheus global switch when user enable devsever metrics (#3169) 2023-04-24 20:02:04 +08:00
Kevin Wan
8c9b619199 chore: update go-zero for goctl (#3165) 2023-04-24 13:02:26 +08:00
Kevin Wan
49f73265b9 chore: refactor (#3164) 2023-04-24 12:34:52 +08:00
Kevin Wan
7568674b2b chore: use %q instead of %s for unmarshaling (#3163) 2023-04-24 12:11:12 +08:00
Kevin Wan
3da740b7fc chore: remove unnecessary code (#3161) 2023-04-23 22:58:22 +08:00
chen quan
ce4eb6ed61 fix: fixed #2945 (#2953)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-04-23 14:22:03 +00:00
MarkJoyMa
9970ff55cd feat: refactor gateway code (#3160) 2023-04-23 22:05:10 +08:00
MarkJoyMa
d10740f871 feat: inheritance rewrite error prompt is more friendly (#3156) 2023-04-23 12:56:54 +00:00
Kevin Wan
027193dc99 chore: refactor gateway (#3157) 2023-04-22 23:25:51 +08:00
Kevin Wan
de1e0f2410 Update readme-cn.md 2023-04-22 22:36:55 +08:00
Kevin Wan
062073ce58 chore: update codecov to ignore mock files (#3155) 2023-04-22 14:07:15 +08:00
MarkJoyMa
e20b02f311 gateway: open timeout function cabinet (#3047)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-04-22 05:23:47 +00:00
Kevin Wan
02357d2616 chore: make error message more readable (#3154) 2023-04-22 13:03:59 +08:00
guangwu
489d69f779 Add debug message on unmarshal errors (#3153) 2023-04-21 13:12:02 +00:00
Kevin Wan
117611a170 fix: default value for keepalive set to 0 (#3152) 2023-04-21 03:15:05 +00:00
Kevin Wan
0a46ad7ac1 chore: fix go 1.18 dep problem (#3149) 2023-04-21 01:01:17 +08:00
Liam Hao
bf905eaff3 fix default grpc-gateway connect timeout (#3142) 2023-04-20 15:13:13 +00:00
dependabot[bot]
88cb35e3d5 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.30.1 to 2.30.2 (#3144)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-20 22:59:57 +08:00
fondoger
078825b4eb [dart-gen] Support Null-safe and omitempty json tag (#3134) 2023-04-17 05:06:52 +00:00
Kevin Wan
bbfce6abe9 chore: refactor max/min in fx (#3135) 2023-04-16 23:40:30 +08:00
suplalalala
0d11ce03a8 feature: two evaluate operate func addtion in package stream (#3129)
Co-authored-by: Riven <Riven.chen@hairobotics.com>
2023-04-16 15:14:25 +00:00
dependabot[bot]
757ed19dc5 chore(deps): bump k8s.io/apimachinery from 0.26.3 to 0.27.1 (#3131)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-15 22:29:46 +08:00
dependabot[bot]
c5fd074aac chore(deps): bump github.com/prometheus/client_golang from 1.14.0 to 1.15.0 (#3122)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-15 22:01:02 +08:00
LiKe
8fa0bd1f1c fix(goctl): test field (#3114) 2023-04-15 21:46:37 +08:00
dependabot[bot]
ede19a89ec chore(deps): bump go.etcd.io/etcd/client/v3 from 3.5.7 to 3.5.8 (#3123)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-15 21:24:11 +08:00
anqiansong
73664b92f0 fix(goctl): missing rpc --style flag (#3112) 2023-04-10 09:48:48 +00:00
Kevin Wan
8d9c2fa22a chore: update go-zero version in goctl (#3109) 2023-04-08 23:24:29 +08:00
cong
22fad4bb9c feat(trace): add trace test helpers (#3108) 2023-04-08 14:52:25 +00:00
lchjczw
189e9bd9da Fix the problem of package name for generated kt code. (#3082)
Co-authored-by: 李春华 <lichunhua@threesoft.cn>
2023-04-08 14:46:34 +00:00
Kevin Wan
98c9b5928a refactor: simplify zrpc stat config (#3107) 2023-04-08 14:45:05 +00:00
Kevin Wan
e13fd62d38 chore: coding style (#3106)
Co-authored-by: cong <zhangcong1992@gmail.com>
2023-04-08 21:37:53 +08:00
李登富
ffacae89eb fix:multipule flag client go package name (#3104)
Co-authored-by: lidengfu <lidengfu@excean.com>
2023-04-08 21:36:45 +08:00
cong
49135fe25e refactor(zrpc): prefer static config for zrpc server statinterceptor (#3105) 2023-04-08 13:03:32 +00:00
MarkJoyMa
2e6402f4b5 fix: fillDefault, handling of nested structs (#3072) 2023-04-08 11:43:13 +00:00
Kevin Wan
07f03ebd0c fix: should not conflict on lower members (#3095) 2023-04-08 14:47:57 +08:00
dependabot[bot]
92f2676afc chore(deps): bump golang.org/x/text from 0.8.0 to 0.9.0 in /tools/goctl (#3101) 2023-04-08 13:28:01 +08:00
dependabot[bot]
1807305e6d chore(deps): bump golang.org/x/net from 0.8.0 to 0.9.0 (#3100) 2023-04-08 13:27:14 +08:00
dependabot[bot]
38a97d4531 chore(deps): bump github.com/spf13/cobra from 1.6.1 to 1.7.0 in /tools/goctl (#3098)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-07 20:41:03 +08:00
dependabot[bot]
b9f98ecc4a chore(deps): bump go.mongodb.org/mongo-driver from 1.11.3 to 1.11.4 (#3097)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-07 20:22:51 +08:00
dependabot[bot]
1dc222f4b2 chore(deps): bump golang.org/x/sys from 0.6.0 to 0.7.0 (#3096)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-05 22:33:40 +08:00
cong
a79b8de24d feat(bloom): bloom support Ctx API (#3089) 2023-04-03 14:37:04 +00:00
cong
5da8a93c75 feat(redis): add ScriptRun API and migrate EvalCtx to ScriptRun for limit, lock and bloom (#3087) 2023-04-02 11:28:25 +08:00
cong
b49fc81618 refactor(redistest): simplify redistest.CreateRedis API (#3086) 2023-04-01 05:02:21 +00:00
cong
6a692453dc chore(ci): fail lint ci when gofmt or tidy check failed (#3085) 2023-04-01 03:41:17 +00:00
Kevin Wan
8d0cceb80c chore: add more tests (#3084) 2023-03-31 22:33:43 +08:00
heyehang
e06abf4f6f fixbug:superfluous response.WriteHeader (#3083) 2023-03-31 13:05:29 +00:00
Kevin Wan
ee555a85da chore: coding style (#3080) 2023-03-31 02:13:48 +00:00
anqiansong
1904af2323 feat(goctl): Support gateway sample generation (#3049) 2023-03-29 09:06:23 +00:00
cong
95b85336d6 refactor(redis): add NonBlock config, disable redis ping by default (#3073) 2023-03-29 02:28:12 +00:00
guoguangwu
ca4ce7bce8 fix : misspelled word (#3075) 2023-03-29 02:25:26 +00:00
Kevin Wan
9065eb90d9 chore: coding style (#3074) 2023-03-28 16:34:22 +00:00
anqiansong
50bc361430 feat(goctl): Add api parser (#2585) 2023-03-28 15:45:26 +00:00
Kevin Wan
455a6c8f97 Update readme-cn.md, add company users. 2023-03-27 12:22:32 +08:00
Kevin Wan
04434646eb chore: refactor zrpc setup (#3064) 2023-03-25 20:40:21 +08:00
dependabot[bot]
992a56e90b chore(deps): bump google.golang.org/grpc from 1.53.0 to 1.54.0 in /tools/goctl (#3061)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-24 16:14:44 +08:00
dependabot[bot]
ed4d5e5813 chore(deps): bump google.golang.org/grpc from 1.53.0 to 1.54.0 (#3060)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-24 11:44:26 +08:00
dependabot[bot]
fe85e7cb42 chore(deps): bump k8s.io/client-go from 0.26.2 to 0.26.3 (#3052)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-24 10:16:45 +08:00
Kevin Wan
9c6b516bb8 fix: #3058 (#3059) 2023-03-23 23:45:57 +08:00
dependabot[bot]
2e9063a9a1 chore(deps): bump go.mongodb.org/mongo-driver from 1.11.2 to 1.11.3 (#3054)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-23 22:46:50 +08:00
dependabot[bot]
c3648be533 chore(deps): bump go.uber.org/automaxprocs from 1.5.1 to 1.5.2 (#3051)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-23 22:23:47 +08:00
Kevin Wan
0ab06f62ca chore: add more tests (#3045) 2023-03-19 23:56:36 +08:00
Kevin Wan
6170d7b790 feat: rest validation on http requests (#3041) 2023-03-19 12:04:18 +00:00
cong
18d163c4f7 fix(executors): periodicalexecutor should handle crash correctly (#3043) 2023-03-18 15:04:15 +00:00
Snake
a561048d59 Fix bug: replace int and float with num type in dart (#3042)
Co-authored-by: zhoumingji <zhoumingji@cmsr.chinamobile.com>
2023-03-18 14:49:22 +00:00
dependabot[bot]
7a647ca40c chore(deps): bump google.golang.org/protobuf from 1.29.1 to 1.30.0 in /tools/goctl (#3037)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-17 20:40:36 +08:00
dependabot[bot]
3f6f14f976 chore(deps): bump google.golang.org/protobuf from 1.29.1 to 1.30.0 (#3036)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-17 20:25:43 +08:00
dependabot[bot]
a78d57bebd chore(deps): bump google.golang.org/protobuf from 1.28.2-0.20220831092852-f930b1dc76e8 to 1.29.1 in /tools/goctl (#3029)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-16 00:20:18 +08:00
dependabot[bot]
74452eb7b5 chore(deps): bump golang.org/x/text from 0.7.0 to 0.8.0 in /tools/goctl (#3028)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 23:56:58 +08:00
dependabot[bot]
a9e364a01a chore(deps): bump golang.org/x/net from 0.7.0 to 0.8.0 (#3027)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 23:32:23 +08:00
chen quan
29c2e20b41 chore(action): upgrade go 1.18 version in release (#3032) 2023-03-15 12:53:04 +00:00
dependabot[bot]
42c146bcbd chore(deps): bump google.golang.org/protobuf from 1.29.0 to 1.29.1 (#3026)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 11:30:19 +08:00
dependabot[bot]
b61e364458 chore(deps): bump github.com/zeromicro/go-zero from 1.4.4 to 1.5.0 in /tools/goctl (#3023) 2023-03-14 08:48:42 +08:00
dependabot[bot]
18a4dcb79f chore(deps): bump github.com/fatih/color from 1.14.1 to 1.15.0 (#3022) 2023-03-14 08:47:44 +08:00
Kevin Wan
60a13f1e53 chore: add more tests (#3018) 2023-03-12 20:42:50 +08:00
sniperwzq
3e093bf34e defines the method to customize http server (#2171) 2023-03-11 23:15:00 +08:00
Kevin Wan
211b9498ef chore: add more tests (#3016) 2023-03-11 22:22:39 +08:00
Kevin Wan
cca45be3c5 chore: refactor orm code (#3015) 2023-03-11 18:03:20 +08:00
YK.xiong
e735915d89 fix QueryRowsPartial getTaggedFieldValueMap func (#2884)
Co-authored-by: yongkun.xiong <weilone@vip.qq.com>
2023-03-11 07:28:09 +00:00
Kevin Wan
f77e2c9cfa chore: add more tests (#3014) 2023-03-11 14:57:56 +08:00
Shyunn
544aa7c432 Added zrpc server custom serverID for custom registration Key when the service is registered on ETCD. (#3008) 2023-03-11 14:34:28 +08:00
Kevin Wan
4cef2b412c fix: avoid unmarshal panic with incorrect map keys #3002 (#3013) 2023-03-11 07:53:57 +08:00
Kevin Wan
123c61ad12 chore: update readme (#3011) 2023-03-10 22:18:48 +08:00
Kevin Wan
fbf129d535 chore: add more tests (#3010) 2023-03-10 21:56:19 +08:00
Kevin Wan
c8a17a97be chore: add more tests (#3009) 2023-03-10 20:48:10 +08:00
Kevin Wan
3a493cd6a6 chore: add more tests (#3006) 2023-03-10 17:36:39 +08:00
Kevin Wan
7a0c04bc21 feat: unique redis addrs and trim spaces (#3004) 2023-03-10 16:09:07 +08:00
iyyzh
3c9fe0b381 init postgresql err (#3003) 2023-03-10 05:58:07 +00:00
文豆芽
f8b2dc8c9f reids cluster bug (#2986)
Co-authored-by: shaocongcong <shao.congcong@yalla.live>
2023-03-09 21:37:34 +08:00
taobig
37cb00d789 Export cache.Option param to NewXXXModel() (#2995) 2023-03-09 13:34:58 +00:00
dependabot[bot]
e3e7bc736b chore(deps): bump google.golang.org/protobuf from 1.28.2-0.20230222093303-bc1253ad3743 to 1.29.0 (#2993)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 19:56:14 +08:00
dependabot[bot]
fafbee24b8 chore(deps): bump github.com/golang/protobuf from 1.5.2 to 1.5.3 (#2992)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 16:24:37 +08:00
dependabot[bot]
8ec29d29ce chore(deps): bump golang.org/x/sys from 0.5.0 to 0.6.0 (#2983)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 15:23:02 +08:00
fabio
cb7f3e8a17 feat(redis):add LpopCount,RpopCount (#2990) 2023-03-09 05:45:45 +00:00
dependabot[bot]
03391b48ca chore(deps): bump github.com/alicebob/miniredis/v2 from 2.30.0 to 2.30.1 (#2991)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 13:34:48 +08:00
dependabot[bot]
d0dedb0624 chore(deps): bump github.com/jhump/protoreflect from 1.15.0 to 1.15.1 (#2982)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-08 23:55:43 +08:00
Snake
e136deb3a7 Update goclt dart gen: Add scheme config and content-type header (#2987)
Co-authored-by: zhoumingji <zhoumingji@cmsr.chinamobile.com>
2023-03-08 15:40:55 +00:00
anqiansong
a2592a17e9 format code 2023-03-08 15:32:08 +00:00
anqiansong
05abf4a2ff fix typo 2023-03-08 15:32:08 +00:00
anqiansong
d40000d4b9 fix typo 2023-03-08 15:32:08 +00:00
anqiansong
4620924105 Fix typo 2023-03-08 15:32:08 +00:00
Kevin Wan
a05fe7bf0a chore: remove optional in redis config (#2979) 2023-03-07 14:58:04 +08:00
kevin
dd347e96b0 chore: add comments 2023-03-07 01:27:41 +00:00
kevin
a972f400c6 fix: test failure 2023-03-07 01:27:41 +00:00
kevin.wan
fb7664a764 fix: config map with json tag 2023-03-07 01:27:41 +00:00
Kevin Wan
7d5d7d9085 chore: clear errors on conf conflict keys (#2972)
Co-authored-by: kevin.wan <kevin.wan@yijinin.com>
2023-03-06 12:45:17 +08:00
Kevin Wan
9911c11e9c Update FUNDING.yml 2023-03-05 22:47:17 +08:00
Kevin Wan
0d5a68869d fix: gateway conf doesn't work (#2968) 2023-03-05 22:19:58 +08:00
Kevin Wan
d9d79e930d Merge pull request from GHSA-fgxv-gw55-r5fq
* fix: Authorization Bypass Through User-Controlled Key

* chore: add not safe domain test
2023-03-04 23:34:11 +08:00
Kevin Wan
d953675085 chore: add tests (#2960) 2023-03-04 20:34:15 +08:00
yangjinheng
dbc8f9faca timeout writer add hijack 2023-03-04 11:50:04 +00:00
yangjinheng
96998ae570 Update timeouthandler.go 2023-03-04 11:50:04 +00:00
MarkJoyMa
7086fb6dda x 2023-03-04 11:44:47 +00:00
MarkJoyMa
1ad7809fde x 2023-03-04 11:44:47 +00:00
MarkJoyMa
142c46228b x 2023-03-04 11:44:47 +00:00
MarkJoyMa
ba771f8ff1 add ut 2023-03-04 11:44:47 +00:00
MarkJoyMa
f3cf891d4f feat: conf add FillDefault func 2023-03-04 11:44:47 +00:00
zhoumingji
ba71964b16 Fix bug in dartgen: The property 'isEmpty' can't be unconditionally accessed because the receiver can be 'null' 2023-03-04 11:37:38 +00:00
zhoumingji
97ada59175 Fix bug in dartgen: Increase the processing logic when route.RequestType is empty 2023-03-04 04:31:27 +00:00
cui fliter
b41ccc5992 fix functiom name
Signed-off-by: cui fliter <imcusg@gmail.com>
2023-03-04 04:29:19 +00:00
dependabot[bot]
e23f421976 chore(deps): bump k8s.io/client-go from 0.26.1 to 0.26.2 (#2955)
Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.26.1 to 0.26.2.
- [Release notes](https://github.com/kubernetes/client-go/releases)
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.26.1...v0.26.2)

---
updated-dependencies:
- dependency-name: k8s.io/client-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-04 12:28:25 +08:00
Kevin Wan
dc5b8dd716 feat: support grpc client keepalive config (#2950) 2023-03-03 23:40:17 +08:00
tanglihao
a40d8b0684 fix code format style use const config.DefaultFormat 2023-03-03 14:58:18 +00:00
tanglihao
cb39b5836f fix log name conflict 2023-03-03 14:58:18 +00:00
dependabot[bot]
4988f2a4da chore(deps): bump k8s.io/apimachinery from 0.26.1 to 0.26.2 (#2954)
Bumps [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) from 0.26.1 to 0.26.2.
- [Release notes](https://github.com/kubernetes/apimachinery/releases)
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.26.1...v0.26.2)

---
updated-dependencies:
- dependency-name: k8s.io/apimachinery
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-03 22:56:07 +08:00
anqiansong
7ca89a85ab format code 2023-03-03 07:11:13 +00:00
anqiansong
1ac2384750 remove unused code 2023-03-03 07:11:13 +00:00
anqiansong
0e040ec5b4 remove unused code 2023-03-03 07:11:13 +00:00
anqiansong
4bc1b78a91 Fix #2879 2023-03-03 07:11:13 +00:00
qiying.wang
148afcf1a7 chore: remove redundant prefix of "error: " in error creation 2023-03-02 12:46:19 +00:00
Kevin Wan
1cd1b17f70 fix: security #9 (#2949)
* fix: security #9

* fix: missing packages

* chore: update deps
2023-03-02 16:31:39 +08:00
dependabot[bot]
59c110688d chore(deps): bump github.com/pelletier/go-toml/v2 from 2.0.6 to 2.0.7 (#2943)
Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.0.6 to 2.0.7.
- [Release notes](https://github.com/pelletier/go-toml/releases)
- [Changelog](https://github.com/pelletier/go-toml/blob/v2/.goreleaser.yaml)
- [Commits](https://github.com/pelletier/go-toml/compare/v2.0.6...v2.0.7)

---
updated-dependencies:
- dependency-name: github.com/pelletier/go-toml/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 14:22:00 +08:00
dependabot[bot]
6a25323467 chore(deps): bump go.opentelemetry.io/otel/exporters/jaeger (#2942)
Bumps [go.opentelemetry.io/otel/exporters/jaeger](https://github.com/open-telemetry/opentelemetry-go) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/jaeger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 13:51:17 +08:00
dependabot[bot]
6aeb3dfb1c chore(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp (#2941)
Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp](https://github.com/open-telemetry/opentelemetry-go) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 12:55:25 +08:00
qiying.wang
0cb61b9a9c chore: add tests for logc debug 2023-03-02 04:38:54 +00:00
qiying.wang
10d263395c feat: add debug log for logc 2023-03-02 04:38:54 +00:00
kevin
d65801f258 chore: add comments 2023-03-02 04:32:24 +00:00
kevin
eaac0ba8de chore: add more tests 2023-03-02 04:32:24 +00:00
kevin
b449f2f39e chore: add more tests 2023-03-02 04:32:24 +00:00
kevin
c57b0b8f90 feat: check key overwritten 2023-03-02 04:32:24 +00:00
dependabot[bot]
696406b887 chore(deps): bump go.opentelemetry.io/otel/exporters/zipkin (#2940)
Bumps [go.opentelemetry.io/otel/exporters/zipkin](https://github.com/open-telemetry/opentelemetry-go) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/zipkin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 12:26:58 +08:00
dependabot[bot]
cc1779936e chore(deps): bump github.com/jackc/pgx/v5 from 5.2.0 to 5.3.1 (#2939)
Bumps [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.2.0 to 5.3.1.
- [Release notes](https://github.com/jackc/pgx/releases)
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.2.0...v5.3.1)

---
updated-dependencies:
- dependency-name: github.com/jackc/pgx/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 12:11:53 +08:00
Kevin Wan
de4924a274 fix: config map cannot handle case-insensitive keys. (#2932)
* fix: #2922

* chore: rename const

* feat: support anonymous map field

* feat: support anonymous map field
2023-02-28 23:32:08 +08:00
kevin
2b08e0510c chore: go mod tidy and update deps 2023-02-28 04:38:56 +00:00
kevin
afac48a8ea chore: go mod tidy and update deps 2023-02-28 04:38:56 +00:00
dependabot[bot]
a50b604dc9 chore(deps): bump github.com/jhump/protoreflect from 1.14.1 to 1.15.0
Bumps [github.com/jhump/protoreflect](https://github.com/jhump/protoreflect) from 1.14.1 to 1.15.0.
- [Release notes](https://github.com/jhump/protoreflect/releases)
- [Commits](https://github.com/jhump/protoreflect/compare/v1.14.1...v1.15.0)

---
updated-dependencies:
- dependency-name: github.com/jhump/protoreflect
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-28 03:19:53 +00:00
dependabot[bot]
eda44b6ae8 chore(deps): bump github.com/stretchr/testify from 1.8.1 to 1.8.2
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-28 03:19:04 +00:00
Kevin Wan
284331b7b1 fix: #2899, using autoscaling/v2beta2 instead of v2beta1 (#2900)
* fix: #2899, using autoscaling/v2 instead of v2beta1

* chore: change hpa definition

---------

Co-authored-by: kevin.wan <kevin.wan@yijinin.com>
2023-02-26 22:32:27 +08:00
Kevin Wan
66be213346 chore: refine rest validator (#2928)
* chore: refine rest validator

* chore: add more tests

* chore: reformat code

* chore: add comments
2023-02-26 21:58:58 +08:00
Qiying Wang
92c8899f47 feat: add configurable validator for httpx.Parse (#2923)
Co-authored-by: qiying.wang <qiying.wang@highlight.mobi>
2023-02-26 20:40:22 +08:00
Kevin Wan
238c830f17 fix: timeout not working if greater than global rest timeout (#2926) 2023-02-26 20:22:20 +08:00
raymonder jin
ace125f189 del unnecessary blank 2023-02-26 04:59:23 +00:00
Kevin Wan
a5e5f04bcf chore: reformat code (#2925) 2023-02-26 09:35:59 +08:00
chenquan
3bc40d9eaf fix: fixed the bug that old trace instances may be fetched 2023-02-22 13:01:22 +00:00
yiwu
133c40ac1c Pgx (#2902)
* change postgres driver  to  pgx

* modified:   go.mod
	modified:   go.sum

* chore: tidy go.sum

---------

Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-02-22 14:11:49 +08:00
xiandong
eaaf87cdeb rm parseErr when kindJaeger 2023-02-22 05:41:07 +00:00
xiandong
6dbcfb5e5d rm kindJaegerUdp 2023-02-22 05:41:07 +00:00
xiandong
16a5f30b0c add parseEndpoint 2023-02-22 05:41:07 +00:00
xiandong
4e6d800877 add parseEndpoint 2023-02-22 05:41:07 +00:00
xiandong
af19addf47 add test for Endpoint of kindJaegerUdp 2023-02-22 05:41:07 +00:00
xiandong
ebc425b797 add test for Endpoint of kindJaegerUdp 2023-02-22 05:41:07 +00:00
xiandong
b6bedcd522 add kindJaegerUdp 2023-02-22 05:41:07 +00:00
xiandong
12060c9c0c opentelemetry support AgentHost, AgentPort 2023-02-22 05:41:07 +00:00
xiandong
e575bf8317 add jaeger.WithAgentEndpoint 2023-02-22 05:41:07 +00:00
strong
0fe84b225c fix(goctl): unsupported database type: bpchar (#2840) 2023-02-21 19:40:29 +08:00
dependabot[bot]
33af0745a0 chore(deps): bump golang.org/x/net from 0.5.0 to 0.7.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.5.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.5.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-21 06:19:21 +00:00
dependabot[bot]
1d0265a77e chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.4.3 to 4.5.0 (#2904)
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.4.3 to 4.5.0.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.4.3...v4.5.0)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 14:16:47 +08:00
Kevin Wan
03fe036204 chore: reformat code (#2903) 2023-02-20 23:37:05 +08:00
anqiansong
03d073a884 feat: Add request.ts (#2901)
* Add request.ts

* Update comments

* Refactor request filename
2023-02-20 22:54:25 +08:00
cong
64ab00e8e3 refactor: simplify sqlx fail fast ping and simplify miniredis setup in test (#2897)
* chore(redistest): simplify miniredis setup in test

* refactor(sqlx): simplify sqlx fail fast ping

* chore: close connection if not available
2023-02-19 17:18:19 +08:00
Kevin Wan
d113e1352c Update readme-cn.md 2023-02-17 14:48:41 +08:00
fondoger
32e3116ee3 Fix bug in dart api gen: path parameter is not replaced 2023-02-16 15:24:51 +00:00
Kevin Wan
1dd18e2329 Update readme-cn.md 2023-02-16 21:51:17 +08:00
dependabot[bot]
44b2389f9c chore(deps): bump go.uber.org/goleak from 1.2.0 to 1.2.1 (#2883)
Bumps [go.uber.org/goleak](https://github.com/uber-go/goleak) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/uber-go/goleak/releases)
- [Changelog](https://github.com/uber-go/goleak/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uber-go/goleak/compare/v1.2.0...v1.2.1)

---
updated-dependencies:
- dependency-name: go.uber.org/goleak
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-16 21:18:00 +08:00
Kevin Wan
8bc34c58f4 fix: test failures (#2892)
Co-authored-by: kevin.wan <kevin.wan@yijinin.com>
2023-02-16 19:45:34 +08:00
fondoger
5756627904 Fix Dart API generation bugs; Add ability to generate API for path parameters (#2887)
* Fix bug in dartgen: Import path should match the generated api filename

* Use Route.HandlerName as generated dart API function name

Reasons:
- There is bug when using url path name as function name, because it may have invalid characters such as ":"
- Switching to HandlerName aligns with other languages such as typescript generation

* [DartGen] Add ability to generate api for url path parameters such as /path/:param
2023-02-16 15:19:46 +08:00
Kevin Wan
cddf3875cf refactor: simplify stringx.Replacer, and avoid potential infinite loops (#2877)
* simplify replace

* backup

* refactor: simplify stringx.Replacer

* chore: add comments and const

* chore: add more tests

* chore: rename variable
2023-02-14 10:18:02 +08:00
dependabot[bot]
9be17a2d28 chore(deps): bump go.mongodb.org/mongo-driver from 1.11.1 to 1.11.2
Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.11.1 to 1.11.2.
- [Release notes](https://github.com/mongodb/mongo-go-driver/releases)
- [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.11.1...v1.11.2)

---
updated-dependencies:
- dependency-name: go.mongodb.org/mongo-driver
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 23:39:41 +00:00
Kevin Wan
b8a86e2135 Update readme-cn.md 2023-02-13 22:03:22 +08:00
Kevin Wan
072db116c3 chore: refactor (#2875) 2023-02-12 22:20:36 +08:00
dahaihu
cacd5dc91a fix Replacer suffix match, and add test case (#2867)
* fix: replace shoud replace the longest match

* feat: revert bytes.Buffer to strings.Builder

* fix: loop reset nextStart

* feat: add node longest match test

* feat: add replacer suffix match test case

* feat: multiple match

* fix: partial match ends

* fix: replace look back upon error

* feat: rm unnecessary branch

---------

Co-authored-by: hudahai <hscxrzs@gmail.com>
Co-authored-by: hushichang <hushichang@sensetime.com>
2023-02-12 21:04:35 +08:00
Kevin Wan
3736dacf1e chore: add more tests (#2873) 2023-02-12 20:32:56 +08:00
Kevin Wan
434973c206 fix: test failure (#2874) 2023-02-12 20:08:02 +08:00
Mikael
84f9863b63 only unmashal public variables (#2872)
* only unmashal public variables

* only unmashal public variables

* only unmashal public variables

* only unmashal public variables
2023-02-12 19:53:20 +08:00
Kevin Wan
99a7e6600d feat: use dependabot for goctl (#2869)
* feat: use dependabot for goctl

* chore: add more tests

* chore: remove temp file
2023-02-11 16:32:28 +08:00
chen quan
ea7dab3d26 feat(sqlx): error log print traceId and spanId (#2845) 2023-02-11 16:12:14 +08:00
Kevin Wan
d7d6eccce6 chore: remove clickhouse, added to zero-contrib (#2848) 2023-02-11 15:28:59 +08:00
Kevin Wan
0a5a26385d chore: add more tests (#2866)
* chore: add more tests

* chore: add more tests

* chore: fix test failure
2023-02-11 14:21:39 +08:00
hudahai
62e59837c6 fix: loop reset nextStart 2023-02-10 04:26:50 +00:00
hudahai
981d7dab13 feat: revert bytes.Buffer to strings.Builder 2023-02-10 04:26:50 +00:00
hudahai
d9a732a273 fix: replace shoud replace the longest match 2023-02-10 04:26:50 +00:00
dependabot[bot]
b6f1bce695 chore(deps): bump go.opentelemetry.io/otel/exporters/jaeger (#2858)
Bumps [go.opentelemetry.io/otel/exporters/jaeger](https://github.com/open-telemetry/opentelemetry-go) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/jaeger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-10 10:41:35 +08:00
dependabot[bot]
0988c4148f chore(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp](https://github.com/open-telemetry/opentelemetry-go) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-10 01:49:56 +00:00
dependabot[bot]
165133b91b chore(deps): bump go.opentelemetry.io/otel/exporters/zipkin (#2857)
Bumps [go.opentelemetry.io/otel/exporters/zipkin](https://github.com/open-telemetry/opentelemetry-go) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/zipkin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-09 22:23:04 +08:00
dependabot[bot]
cd8081c567 chore(deps): bump go.opentelemetry.io/otel/trace from 1.12.0 to 1.13.0 (#2860)
Bumps [go.opentelemetry.io/otel/trace](https://github.com/open-telemetry/opentelemetry-go) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/trace
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-09 20:23:04 +08:00
dependabot[bot]
e40a089086 chore(deps): bump google.golang.org/grpc from 1.52.3 to 1.53.0 (#2856)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.52.3 to 1.53.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.52.3...v1.53.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-09 13:38:23 +08:00
Kevin Wan
d9780fb2a6 Update readme-cn.md 2023-02-08 12:49:33 +08:00
dependabot[bot]
2c8ae994cf chore(deps): bump golang.org/x/sys from 0.4.0 to 0.5.0 (#2854)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/golang/sys/releases)
- [Commits](https://github.com/golang/sys/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-08 12:38:11 +08:00
dependabot[bot]
67a046b554 chore(deps): bump github.com/ClickHouse/clickhouse-go/v2 (#2838)
Bumps [github.com/ClickHouse/clickhouse-go/v2](https://github.com/ClickHouse/clickhouse-go) from 2.5.1 to 2.6.0.
- [Release notes](https://github.com/ClickHouse/clickhouse-go/releases)
- [Changelog](https://github.com/ClickHouse/clickhouse-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ClickHouse/clickhouse-go/compare/v2.5.1...2.6.0)

---
updated-dependencies:
- dependency-name: github.com/ClickHouse/clickhouse-go/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-05 14:55:26 +08:00
Kevin Wan
a019a1f59f fix: conf anonymous overlay problem (#2847) 2023-02-05 14:40:57 +08:00
Kevin Wan
aed312f3c0 Update readme-cn.md 2023-01-31 23:12:17 +08:00
Kevin Wan
58138fd56c Chore/rewire (#2836)
* fix: problem on name overlaping in config (#2820)

* chore: fix missing funcs on windows (#2825)

* chore: add more tests (#2812)

* chore: add more tests

* chore: add more tests

* chore: add more tests (#2814)

* chore: add more tests (#2815)

* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests

* feat: upgrade go to v1.18 (#2817)

* feat: upgrade go to v1.18

* feat: upgrade go to v1.18

* chore: change interface{} to any (#2818)

* chore: change interface{} to any

* chore: update goctl version to 1.5.0

* chore: update goctl deps

* chore: update goctl interface{} to any (#2819)

* chore: update goctl interface{} to any

* chore: update goctl interface{} to any

* chore(deps): bump google.golang.org/grpc from 1.52.0 to 1.52.3 (#2823)

* support custom maxBytes in API file (#2822)

* feat: mapreduce generic version (#2827)

* feat: mapreduce generic version

* fix: gateway mr type issue

---------

Co-authored-by: kevin.wan <kevin.wan@yijinin.com>

* feat: add MustNewRedis (#2824)

* feat: add MustNewRedis

* feat: add MustNewRedis

* feat: add MustNewRedis

* x

* x

* fix ut

* x

* x

* x

* x

* x

* chore: improve codecov (#2828)

* feat: converge grpc interceptor processing (#2830)

* feat: converge grpc interceptor processing

* x

* x

* chore(deps): bump go.opentelemetry.io/otel/exporters/zipkin (#2831)

* chore(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp (#2833)

Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp](https://github.com/open-telemetry/opentelemetry-go) from 1.11.2 to 1.12.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.11.2...v1.12.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump go.opentelemetry.io/otel/exporters/jaeger (#2832)

Bumps [go.opentelemetry.io/otel/exporters/jaeger](https://github.com/open-telemetry/opentelemetry-go) from 1.11.2 to 1.12.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.11.2...v1.12.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/jaeger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Xiaoju Jiang <44432198+jiang4869@users.noreply.github.com>
Co-authored-by: kevin.wan <kevin.wan@yijinin.com>
Co-authored-by: MarkJoyMa <64180138+MarkJoyMa@users.noreply.github.com>
2023-01-31 13:58:22 +08:00
dependabot[bot]
f2588b238f chore(deps): bump go.opentelemetry.io/otel/exporters/jaeger (#2832)
Bumps [go.opentelemetry.io/otel/exporters/jaeger](https://github.com/open-telemetry/opentelemetry-go) from 1.11.2 to 1.12.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.11.2...v1.12.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/jaeger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-31 11:04:39 +08:00
dependabot[bot]
cc5ae722a2 chore(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp (#2833)
Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp](https://github.com/open-telemetry/opentelemetry-go) from 1.11.2 to 1.12.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.11.2...v1.12.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-31 09:21:08 +08:00
dependabot[bot]
1ee61709d9 chore(deps): bump go.opentelemetry.io/otel/exporters/zipkin (#2831) 2023-01-31 09:06:24 +08:00
MarkJoyMa
dd117ce9cf feat: converge grpc interceptor processing (#2830)
* feat: converge grpc interceptor processing

* x

* x
2023-01-30 15:48:50 +08:00
Kevin Wan
3c0dc8435e chore: improve codecov (#2828) 2023-01-29 21:39:54 +08:00
MarkJoyMa
fde05ccb28 feat: add MustNewRedis (#2824)
* feat: add MustNewRedis

* feat: add MustNewRedis

* feat: add MustNewRedis

* x

* x

* fix ut

* x

* x

* x

* x

* x
2023-01-29 18:03:05 +08:00
Kevin Wan
464ed51728 feat: mapreduce generic version (#2827)
* feat: mapreduce generic version

* fix: gateway mr type issue

---------

Co-authored-by: kevin.wan <kevin.wan@yijinin.com>
2023-01-29 18:01:23 +08:00
Xiaoju Jiang
413ee919e6 support custom maxBytes in API file (#2822) 2023-01-29 10:43:00 +08:00
Kevin Wan
35b9568657 chore: fix missing funcs on windows (#2825) 2023-01-28 22:45:36 +08:00
dependabot[bot]
167d76b46d chore(deps): bump google.golang.org/grpc from 1.52.0 to 1.52.3 (#2823) 2023-01-28 16:48:30 +08:00
Kevin Wan
ab9eeff500 fix: problem on name overlaping in config (#2820) 2023-01-24 21:14:48 +08:00
Kevin Wan
eab904af64 chore: update goctl interface{} to any (#2819)
* chore: update goctl interface{} to any

* chore: update goctl interface{} to any
2023-01-24 17:51:03 +08:00
Kevin Wan
ae87114282 chore: change interface{} to any (#2818)
* chore: change interface{} to any

* chore: update goctl version to 1.5.0

* chore: update goctl deps
2023-01-24 16:32:02 +08:00
Kevin Wan
7e0ac77139 feat: upgrade go to v1.18 (#2817)
* feat: upgrade go to v1.18

* feat: upgrade go to v1.18
2023-01-24 15:47:01 +08:00
Kevin Wan
696da4efee chore: add more tests (#2815)
* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests
2023-01-24 13:43:13 +08:00
Kevin Wan
ceab564429 chore: add more tests (#2814) 2023-01-24 12:03:05 +08:00
Kevin Wan
4bd8025c5b chore: add more tests (#2812)
* chore: add more tests

* chore: add more tests
2023-01-23 23:27:47 +08:00
Kevin Wan
f3369f8e81 chore: update goctl version to 1.4.4 (#2811) 2023-01-21 21:45:25 +08:00
Kevin Wan
c9b05ae07e fix: mapping optional dep not canonicaled (#2807) 2023-01-20 23:57:49 +08:00
Kevin Wan
32a59dbc27 chore: refactor func name (#2804)
* chore: refactor func name

* chore: make plain log clearer
2023-01-18 17:20:45 +08:00
Kevin Wan
ba0dff2d61 chore: add more tests (#2803)
* chore: add more tests

* chore: add more tests
2023-01-18 13:15:41 +08:00
Kevin Wan
10da5e0424 chore: add more tests (#2801) 2023-01-17 21:55:36 +08:00
Kevin Wan
4bed34090f chore: add more tests (#2800) 2023-01-17 09:59:42 +08:00
Kevin Wan
2bfecf9354 chore: remove mgo related packages (#2799) 2023-01-16 23:13:59 +08:00
Kevin Wan
6d129e0264 chore: add more tests (#2797)
* chore: add more tests

* chore: add more tests

* chore: add more tests
2023-01-16 22:33:39 +08:00
foliet
a2df1bb164 fix: modify the generated update function and add return values for update and delete functions (#2793) 2023-01-15 22:11:08 +08:00
Kevin Wan
5f02e623f5 chore: add more tests (#2795)
* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests
2023-01-15 21:32:41 +08:00
Kevin Wan
963b52fb1b chore: add more tests (#2794) 2023-01-15 15:28:27 +08:00
Kevin Wan
02265d0bfe chore: add more tests (#2792)
* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests
2023-01-15 00:16:12 +08:00
Kevin Wan
2e57e91826 Update readme-cn.md 2023-01-13 23:03:18 +08:00
Ofey Chan
82c642d3f4 feat: expose NewTimingWheelWithClock (#2787) 2023-01-13 17:46:40 +08:00
Kevin Wan
b2571883ca chore: refactor (#2785)
* chore: refactor

* chore: refactor
2023-01-13 14:04:37 +08:00
Alonexy
00ff50c2cc add zset withsocre float (#2689)
* add zset withsocre float

* update

* add IncrbyFloat,HincrbyFloat

Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-01-12 22:37:14 +08:00
Kevin Wan
4d7fa08b0b feat: support **struct in mapping (#2784)
* feat: support **struct in mapping

* chore: fix test failure
2023-01-12 20:45:32 +08:00
Kevin Wan
367afb544c feat: support ptr of ptr of ... in mapping (#2779)
* feat: support ptr of ptr of ... in mapping

* feat: support ptr of ptr of time.Duration in mapping

* feat: support ptr of ptr of json.Number in mapping

* chore: improve setting in mapping

* feat: support ptr of ptr encoding.TextUnmarshaler in mapping

* chore: add more tests

* fix: string ptr

* chore: update tests
2023-01-12 15:56:51 +08:00
cong
43b8c7f641 chore(trace): improve rest tracinghandler (#2783) 2023-01-12 12:50:57 +08:00
dependabot[bot]
a2dcb0079a chore(deps): bump github.com/jhump/protoreflect from 1.14.0 to 1.14.1 (#2782) 2023-01-12 09:41:03 +08:00
cong
f9619328f2 refactor(rest): use static config for trace ignore paths. (#2773) 2023-01-12 09:40:18 +08:00
Kevin Wan
bae061a67e chore: add tests (#2778) 2023-01-11 15:21:39 +08:00
Kevin Wan
0b176e17ac fix: #2576 (#2776) 2023-01-11 00:45:11 +08:00
Kevin Wan
6340e24c17 chore: add tests (#2774) 2023-01-09 23:48:31 +08:00
Kevin Wan
74e0676617 feat: add config to truncate long log content (#2767) 2023-01-09 09:39:30 +08:00
MarkJoyMa
0defb7522f feat: replace NewBetchInserter function name (#2769) 2023-01-09 09:38:57 +08:00
Kevin Wan
0c786ca849 chore: remove simple methods, inlined (#2768) 2023-01-09 00:55:13 +08:00
Kevin Wan
26c541b9cb feat: add middlewares config for zrpc (#2766)
* feat: add middlewares config for zrpc

* chore: add tests

* chore: improve codecov

* chore: improve codecov
2023-01-08 19:34:05 +08:00
Kevin Wan
ade6f9ee46 feat: add middlewares config for rest (#2765)
* feat: add middlewares config for rest

* chore: disable logs in tests

* chore: enable verbose in tests
2023-01-08 16:41:53 +08:00
Kevin Wan
f4502171ea Update readme-cn.md 2023-01-08 12:42:27 +08:00
chensy
8157e2118d fix: replace goctl ExactValidArgs to MatchAll (#2759)
Co-authored-by: chenjieping <chenjieping@kezaihui.com>
2023-01-07 17:07:40 +08:00
Kevin Wan
e52dace416 chore: refactor (#2764) 2023-01-07 14:13:44 +08:00
chen quan
dc260f196a refactor: simplify the code (#2763)
* refactor: simplify the code

* fix: fix data race

* refactor: simplify the code

* refactor: simplify the code
2023-01-07 13:32:56 +08:00
dependabot[bot]
559726112c chore(deps): bump github.com/alicebob/miniredis/v2 from 2.23.1 to 2.30.0 (#2762)
Bumps [github.com/alicebob/miniredis/v2](https://github.com/alicebob/miniredis) from 2.23.1 to 2.30.0.
- [Release notes](https://github.com/alicebob/miniredis/releases)
- [Changelog](https://github.com/alicebob/miniredis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/alicebob/miniredis/compare/v2.23.1...v2.30.0)

---
updated-dependencies:
- dependency-name: github.com/alicebob/miniredis/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-07 12:15:22 +08:00
MarkJoyMa
a5fcf24c04 feat: add batch inserter (#2755) 2023-01-06 23:30:50 +08:00
chen quan
fc9b3ffdc1 refactor: use opentelemetry's standard api to track http status code (#2760) 2023-01-06 23:27:54 +08:00
MarkJoyMa
e71c505e94 feat: add mongo options (#2753)
* feat: add mongo options

* feat: add mongo options

* feat: add mongo options

* feat: add mongo options

* feat: add mongo options

* feat: add mongo options
2023-01-05 22:14:50 +08:00
chen quan
21c49009c0 chore: remove unnecessary code (#2754) 2023-01-05 22:12:07 +08:00
#Suyghur
69d355eb4b feat(redis): add zscan command implementation (#2729) (#2751) 2023-01-04 13:44:17 +08:00
Kevin Wan
83f88d177f chore: improve codecov (#2752) 2023-01-04 13:42:20 +08:00
xiang
641ebf1667 feat: trace http.status_code (#2708)
* feat: trace http.status_code

* feat: implements http.Flusher & http.Hijacker for traceResponseWriter

* test: delete notTracingSpans after test

* feat: trace http.status_code

* feat: implements http.Flusher & http.Hijacker for traceResponseWriter

* test: delete notTracingSpans after test

* refactor: update trace handler span message

* fix: code conflict
2023-01-04 10:21:57 +08:00
Kevin Wan
cf435bfcc1 chore: remove roadmap file, not updating (#2749) 2023-01-03 23:38:14 +08:00
Kevin Wan
28f1b15b8e Update readme.md (#2748) 2023-01-03 23:14:39 +08:00
cong
42413dc294 feat(trace): support otlp http exporter (#2746)
* feat(trace): support otlp http exporter

* chore: use otlptracehttp v1.10.0 not upgrade grpc version prevent other modules break

* refactor(trace): rename exporter kind grpc to otlpgrpc.

BREAKING CHANGE: trace Config.Batcher should use otlpgrpc instead of grpc now.
2023-01-03 22:49:30 +08:00
Kevin Wan
ec7ac43948 chore: reorg imports (#2745)
* chore: reorg imports

* chore: format code
2023-01-03 22:26:45 +08:00
cong
deefc1a8eb fix(trace): grpc exporter should use nonblock option (#2744)
* fix(trace): grpc exporter should use nonblock option

* chore: sort imports
2023-01-03 18:15:09 +08:00
Kevin Wan
036328f1ea chore: update tests (#2741)
* chore: update tests

* chore: codecov on comments

* chore: codecov on comments
2023-01-03 18:02:35 +08:00
wojiukankan
85057a623d 🐛 debug grpc export (#2379) (#2719)
* 🐛 debug grpc export (#2379) 

#2379 Fixed the issue that the GRPC exporter did not establish an RPC link
原文使用的 otlptracegrpc.NewUnstarted创建的是一个未建立rpc连接的导出器,无法正常使用;改为otlptracegrpc.New才妥

* Update agent_test.go

修复单元测试失败
2023-01-03 17:04:35 +08:00
Xargin
1c544a26be use stat instead of disableStat (#2740) 2023-01-03 11:29:24 +08:00
chainlife
20a61ce43e logx conf add DisableStat (#2434)
Co-authored-by: sunsoft <sunsoft@qq.com>
2023-01-02 23:22:13 +08:00
Kevin Wan
dd294e8cd6 fix: #2700, timeout not enough for writing responses (#2738)
* fix: #2700, timeout not enough for writing responses

* fix: test fail

* chore: add comments
2023-01-02 13:51:15 +08:00
JackSon_tm.m
3e9d0161bc add ServeHTTP to Server/Engin for doing Httptest (#2704) 2023-01-02 00:24:58 +08:00
Kevin Wan
cf6c349118 fix: #2735 (#2736)
* fix: #2735

* chore: make error consistent
2023-01-01 12:21:53 +08:00
Kevin Wan
c7a0ec428c fix: key like TLSConfig not working (#2730)
* fix: key like TLSConfig not working

* fix: remove unnecessary code

* chore: rename variable
2022-12-29 14:50:53 +08:00
chowyu12
ce1c02f4f9 Feat: ignorecolums add sort (#2648)
* add go-grpc_opt and go_opt for grpc new command

* feat: remove log when disable log

* feat: add sort

Co-authored-by: zhouyy <zhouyy@ickey.cn>
2022-12-28 14:53:22 +08:00
Kevin Wan
c3756a8f1c fix: etcd publisher reconnecting problem (#2710)
* fix: etcd publisher reconnecting problem

* chore: fix wrong call
2022-12-27 20:03:03 +08:00
Archer
f4fd735aee Use read-write lock instead of mutex (#2727) 2022-12-26 15:00:47 +08:00
Archer
683d793719 RawFieldNames should ignore the field whose name is start with a dash (#2725) 2022-12-24 21:27:32 +08:00
Kevin Wan
affbcb5698 fix: camel cased key of map item in config (#2715)
* fix: camel cased key of map item in config

* fix: mapping anonymous problem

* fix: mapping anonymous problem

* chore: refactor

* chore: add more tests

* chore: refactor
2022-12-24 21:26:33 +08:00
Kevin Wan
f0d1722bbd chore: pass by value for config in dev server (#2712) 2022-12-24 11:41:23 +08:00
chowyu12
c4f8eca459 Feat update rootpkg (#2718)
* add go-grpc_opt and go_opt for grpc new command

* feat: remove log when disable log

* feat: remove repeat code

Co-authored-by: zhouyy <zhouyy@ickey.cn>
2022-12-23 23:57:56 +08:00
Kevin Wan
251c071418 Update readme.md 2022-12-22 23:21:41 +08:00
Kevin Wan
6652c4e445 Update readme-cn.md 2022-12-16 00:08:12 +08:00
Kevin Wan
f73613dff0 Update readme.md 2022-12-16 00:07:50 +08:00
Kevin Wan
7a75dce465 refactor: remove duplicated code (#2705) 2022-12-14 23:36:56 +08:00
anqiansong
801f1adf71 Remove useless file (#2699) 2022-12-14 23:22:01 +08:00
Kevin Wan
f76b976262 fix: #2684 (#2693) 2022-12-13 00:16:31 +08:00
anqiansong
a49f9060c2 Add more test (#2692) 2022-12-12 22:45:18 +08:00
fyyang
ebe28882eb fix: Fix string.title (#2687)
* fix: unsignedTypeMap type error

* fix: string.Title

* style: string.Title test
2022-12-11 23:44:19 +08:00
Kevin Wan
fdc57d07d7 fix: #2672 (#2681)
* fix: #2672

* chore: fix more cases

* chore: update deps

* chore: update deps

* chore: refactor

* chore: refactor

* chore: refactor
2022-12-11 00:41:50 +08:00
re-dylan
ef22042f4d feat: add dev server and health (#2665)
* feat: add dev server and health

* fix: fix ci

* fix: fix comment.

* feat: add enabled

* remove no need test

* feat: mv devServer to internal

* feat: default enable pprof

Co-authored-by: dylan.wang <dylan.wang@yijinin.com>
2022-12-10 20:40:23 +08:00
Tim Xiao
944193ce25 fix:Remove duplicate code (#2686) 2022-12-10 20:13:37 +08:00
Kevin Wan
dcfc9b79f1 feat: accept camelcase for config keys (#2651)
* feat: accept camelcase for config keys

* chore: refactor

* chore: refactor

* chore: add more tests

* chore: refactor

* fix: map elements of array
2022-12-08 22:01:36 +08:00
Kevin Wan
b7052854bb chore: tidy go.sum (#2675) 2022-12-08 06:50:42 +08:00
Kevin Wan
4729a16142 chore: update deps (#2674) 2022-12-07 23:33:30 +08:00
benqi
3604659027 fix: fix client side in zeromicro#2109 (zeromicro#2116) (#2659)
* fix: fix client side in zeromicro#2109 (zeromicro#2116)

* fix: fix client side in zeromicro#2109 (zeromicro#2116)

* fix: fix client side in zeromicro#2109 (zeromicro#2116)
2022-12-04 00:25:52 +08:00
Kevin Wan
9f7f94b673 chore: upgrade dependencies (#2658) 2022-12-03 22:06:45 +08:00
bensonfx
0b3629b636 Fixes #2603 bump goctl cobra version to macos completion help bug (#2656)
Co-authored-by: Benson Yan <yanyong@axera-tech.com>
2022-12-03 19:03:38 +08:00
heyehang
a644ec7edd feature : responses whit context (#2637) 2022-12-03 18:48:02 +08:00
Kevin Wan
9941055eaa feat: add trace.SpanIDFromContext and trace.TraceIDFromContext (#2654) 2022-12-02 11:00:44 +08:00
EinfachePhy
10fd9131a1 replace strings.Title to cases.Title (#2650) 2022-12-02 00:15:51 +08:00
bigrocs
90828a0d4a The default port is used when there is no port number for k8s (#2598)
* k8s 没有端口号时使用默认端口

* Modify the not port test
2022-11-27 09:00:11 +08:00
edieruby
b1c3c21c81 fix: log currentSize should not be 0 when file exists and size is not 0 (#2639) 2022-11-25 23:48:32 +08:00
chen quan
97a8b3ade5 fix(rest): fix issues#2628 (#2629) 2022-11-23 22:50:08 +08:00
Kevin Wan
95a5f64493 feat: add stringx.ToCamelCase (#2622) 2022-11-20 17:41:39 +08:00
chensy
20e659749a fix: fix conflict with the import package name (#2610)
Co-authored-by: chenjieping <chenjieping@kezaihui.com>
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2022-11-20 15:23:25 +08:00
Kevin Wan
94708cc78f chore: update deps (#2621) 2022-11-19 22:28:40 +08:00
Kevin Wan
06fafd2153 feat: validate value in options for mapping (#2616) 2022-11-18 19:46:23 +08:00
Kevin Wan
79de932646 chore: update dependencies (#2594)
* chore: update deps

* chore: update deps

* chore: update deps

* chore: update deps
2022-11-12 18:04:39 +08:00
Kevin Wan
b562e940e7 feat: support bool for env tag (#2593) 2022-11-12 13:58:35 +08:00
Kevin Wan
69068cdaf0 feat: support env tag in config (#2577)
* feat: support env tag in config

* chore: add more tests

* chore: add more tests, add stringx.Join

* fix: test fail

* chore: remove print code

* chore: rename variable
2022-11-11 23:17:09 +08:00
dependabot[bot]
f25788ebea chore(deps): bump go.mongodb.org/mongo-driver from 1.10.3 to 1.11.0 (#2588) 2022-11-11 07:01:54 +08:00
dependabot[bot]
1293c4321b chore(deps): bump github.com/alicebob/miniredis/v2 from 2.23.0 to 2.23.1 (#2587) 2022-11-11 06:59:13 +08:00
Kevin Wan
e3e08a7396 fix: inherit issue when parent after inherits (#2586)
* fix: inherit issue when parent after inherits

* chore: add more tests
2022-11-10 22:13:05 +08:00
dependabot[bot]
4b071f4c33 chore(deps): bump github.com/jhump/protoreflect from 1.13.0 to 1.14.0 (#2579)
Bumps [github.com/jhump/protoreflect](https://github.com/jhump/protoreflect) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/jhump/protoreflect/releases)
- [Commits](https://github.com/jhump/protoreflect/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: github.com/jhump/protoreflect
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-10 14:42:53 +08:00
骑着毛驴背单词
81831b60a9 fix(change model template file type): All model template variables ar… (#2573)
* fix(change model template file type): All model template variables are stored in tpl format files with the same name as the template generated using the template init command

* fix(change model template file type): All model template variables are stored in tpl format files with the same name as the template generated using the template init command

Co-authored-by: qilvge <qilvge@.qilvge.com>
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2022-11-10 13:38:35 +08:00
Kevin Wan
1677a4dceb feat: conf inherit (#2568)
* feat: add ValuerWithParent

* feat: make etcd config inherit from parents

* chore: add more tests

* chore: add more tests

* chore: add more comments

* chore: refactor

* chore: add more comments

* fix: fix duplicated code and refactor

* fix: remove unnecessary code

* fix: fix test case for removing print

* feat: support partial inherit
2022-11-08 15:27:48 +08:00
王哈哈
dac3600b53 Modify comment syntax error (#2572) 2022-11-04 21:55:17 +08:00
anqiansong
3db64c7d47 fix(goctl): Fix #2561 (#2562)
* Fix #2561

* format code
2022-10-29 22:40:56 +08:00
Kevin Wan
7eb6aae949 fix: potential slice append issue (#2560) 2022-10-28 08:14:03 +08:00
foliet
07128213d6 chore: update "DO NOT EDIT" format (#2559)
* chore: update "DO NOT EDIT" format

* Update readme.md

* Update head.go
2022-10-27 19:41:24 +08:00
anqiansong
9504d30049 fix(goctl): fix redundant import (#2551) 2022-10-25 06:58:51 +08:00
chen quan
ce73b9a85c chore(action): enable cache dependency (#2549) 2022-10-24 22:50:24 +08:00
re-dylan
4d2a146733 typo(mapping): fix typo for key (#2548) 2022-10-24 21:43:53 +08:00
Kevin Wan
46e236fef7 chore: add more tests (#2547) 2022-10-23 10:54:41 +08:00
1099 changed files with 84077 additions and 19763 deletions

View File

@@ -1,3 +0,0 @@
comment: false
ignore:
- "tools"

View File

@@ -1 +1,7 @@
**/.git **/.git
.dockerignore
Dockerfile
goctl
Makefile
readme.md
readme-cn.md

12
.github/FUNDING.yml vendored
View File

@@ -1,13 +1,3 @@
# These are supported funding model platforms # These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] github: [zeromicro]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # https://gitee.com/kevwan/static/raw/master/images/sponsor.jpg
ethereum: 0x5052b7f6B937B02563996D23feb69b38D06Ca150 | kevwan

344
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,344 @@
# GitHub Copilot Instructions for go-zero
This document provides guidelines for GitHub Copilot when assisting with development in the go-zero project.
## Project Overview
go-zero is a web and RPC framework with lots of built-in engineering practices designed to ensure the stability of busy services with resilience design. It has been serving sites with tens of millions of users for years.
### Key Architecture Components
- **REST API framework** (`rest/`) - HTTP service framework with middleware chain support
- **RPC framework** (`zrpc/`) - gRPC-based RPC framework with etcd service discovery and p2c_ewma load balancing
- **Gateway** (`gateway/`) - API gateway supporting both HTTP and gRPC upstreams with proto-based routing
- **MCP Server** (`mcp/`) - Model Context Protocol server for AI agent integration via SSE
- **Core utilities** (`core/`) - Production-grade components:
- Resilience: circuit breakers (`breaker/`), rate limiters (`limit/`), adaptive load shedding (`load/`)
- Storage: SQL with cache (`stores/sqlc/`), Redis (`stores/redis/`), MongoDB (`stores/mongo/`)
- Concurrency: MapReduce (`mr/`), worker pools (`executors/`), sync primitives (`syncx/`)
- Observability: metrics (`metric/`), tracing (`trace/`), structured logging (`logx/`)
- **Code generation tool** (`tools/goctl/`) - CLI tool for generating Go code from `.api` and `.proto` files
## Coding Standards and Conventions
### Code Style
1. **Follow Go conventions**: Use `gofmt` for formatting, follow effective Go practices
2. **Package naming**: Use lowercase, single-word package names when possible
3. **Error handling**: Always handle errors explicitly, use `errorx.BatchError` for multiple errors
4. **Context propagation**: Always pass `context.Context` as the first parameter for functions that may block
5. **Configuration structures**: Use struct tags with JSON annotations, defaults, and validation
**Pattern**: All service configs embed `service.ServiceConf` for common fields (Name, Log, Mode, Telemetry)
```go
type Config struct {
service.ServiceConf // Always embed for services
Host string `json:",default=0.0.0.0"`
Port int // Required field (no default)
Timeout int64 `json:",default=3000"` // Timeouts in milliseconds
Optional string `json:",optional"` // Optional field
Mode string `json:",default=pro,options=dev|test|rt|pre|pro"` // Validated options
}
```
**Service modes**: `dev`/`test`/`rt` disable load shedding and stats; `pre`/`pro` enable all resilience features
### Interface Design
1. **Small interfaces**: Follow Go's preference for small, focused interfaces
2. **Context methods**: Provide both context and non-context versions of methods
3. **Options pattern**: Use functional options for complex configuration
Example:
```go
func (c *Client) Get(key string, val any) error {
return c.GetCtx(context.Background(), key, val)
}
func (c *Client) GetCtx(ctx context.Context, key string, val any) error {
// implementation
}
```
### Testing Patterns
1. **Test file naming**: Use `*_test.go` suffix
2. **Test function naming**: Use `TestFunctionName` pattern
3. **Use testify/assert**: Prefer `assert` package for assertions
4. **Table-driven tests**: Use table-driven tests for multiple scenarios
5. **Mock interfaces**: Use `go.uber.org/mock` for mocking
6. **Test helpers**: Use `redistest`, `mongtest` helpers for database testing
Example test pattern:
```go
func TestSomething(t *testing.T) {
tests := []struct {
name string
input string
expected string
wantErr bool
}{
{"valid case", "input", "output", false},
{"error case", "bad", "", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := SomeFunction(tt.input)
if tt.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.expected, result)
})
}
}
```
## Framework-Specific Guidelines
### REST API Development
1. **API Definition**: Use `.api` files to define REST APIs with goctl codegen
2. **Handler pattern**: Separate business logic into logic packages (handlers call logic layer)
3. **Middleware chain**: Middlewares wrap via `chain.Chain` interface - use `Append()` or `Prepend()` to control order
- Built-in middlewares (all in `rest/handler/`): tracing, logging, metrics, recovery, breaker, shedding, timeout, maxconns, maxbytes, gunzip
- Custom middleware: `func(http.Handler) http.Handler` - call `next.ServeHTTP(w, r)` to continue chain
4. **Response handling**: Use `httpx.WriteJson(w, code, v)` for JSON responses
5. **Error handling**: Use `httpx.Error(w, err)` or `httpx.ErrorCtx(ctx, w, err)` for HTTP error responses
6. **Route registration**: Routes defined with `Method`, `Path`, and `Handler` - wildcards use `:param` syntax
### RPC Development
1. **Protocol Buffers**: Use protobuf for service definitions, generate code with goctl
2. **Service discovery**: Use etcd for dynamic service registration/discovery, or direct endpoints for static routing
3. **Load balancing**: Default is `p2c_ewma` (power of 2 choices with EWMA), configurable via `BalancerName`
4. **Client configuration**: Support `Etcd`, `Endpoints`, or `Target` - use `BuildTarget()` to construct connection string
5. **Interceptors**: Implement gRPC interceptors for cross-cutting concerns (auth, logging, metrics)
6. **Health checks**: gRPC health checks enabled by default (`Health: true`)
### Database Operations
1. **SQL operations**: Use `sqlx.SqlConn` interface - methods always end with `Ctx` for context support
2. **Caching pattern**: `stores/sqlc` provides `CachedConn` for automatic cache-aside pattern
- `QueryRowCtx`: Query with cache key, auto-populate on cache miss
- `ExecCtx`: Execute and delete cache keys
3. **Transactions**: Use `sqlx.SqlConn.TransactCtx()` to get transaction session
4. **Connection pooling**: Managed automatically (64 max idle/open, 1min lifetime)
5. **Test helpers**: Use `redistest.CreateRedis(t)` for Redis, SQL mocks for DB testing
Example cache pattern:
```go
err := c.QueryRowCtx(ctx, &dest, key, func(ctx context.Context, conn sqlx.SqlConn) error {
return conn.QueryRowCtx(ctx, &dest, query, args...)
})
```
### Configuration Management
1. **YAML configuration**: Use YAML for configuration files
2. **Environment variables**: Support environment variable overrides
3. **Validation**: Include proper validation for configuration parameters
4. **Sensible defaults**: Provide reasonable default values
## Error Handling Best Practices
1. **Wrap errors**: Use `fmt.Errorf` with `%w` verb to wrap errors
2. **Custom errors**: Define custom error types when needed
3. **Error logging**: Log errors appropriately with context
4. **Graceful degradation**: Implement fallback mechanisms
## Performance Considerations
1. **Resource pools**: Use connection pools and worker pools
2. **Circuit breakers**: Implement circuit breaker patterns for external calls
3. **Rate limiting**: Apply rate limiting to protect services
4. **Load shedding**: Implement adaptive load shedding
5. **Metrics**: Add appropriate metrics and monitoring
## Security Guidelines
1. **Input validation**: Validate all input parameters
2. **SQL injection prevention**: Use parameterized queries
3. **Authentication**: Implement proper JWT token handling
4. **HTTPS**: Support TLS/HTTPS configurations
5. **CORS**: Configure CORS appropriately for web APIs
## Documentation Standards
1. **Package documentation**: Include package-level documentation
2. **Function documentation**: Document exported functions with examples
3. **API documentation**: Maintain API documentation in sync
4. **README updates**: Update README for significant changes
## GitHub Issue Management
### Understanding and Categorizing Issues
When analyzing GitHub issues, consider these common categories:
1. **Bug Reports**: Stack traces, version info, reproduction steps
2. **Feature Requests**: Use case, proposed solution, alternatives
3. **Questions**: Usage, configuration, or architecture
4. **Documentation Issues**: Missing, unclear, or incorrect docs
5. **Performance Issues**: Benchmarks, profiling data, resource usage
### Issue Analysis Checklist
- Identify affected component (REST, RPC, Gateway, MCP, Core utilities, goctl)
- Check versions (go-zero, Go)
- Look for reproduction steps or code examples
- Review code snippets, logs, or stack traces
- Check if related to resilience features (breaker, load shedding, rate limiting)
- Determine production impact
### Responding to Issues
Be helpful and professional. Ask clarifying questions when needed. Reference relevant documentation and code files. Provide code examples following project conventions. Suggest workarounds when applicable.
### Chinese to English Translation
go-zero has an international user base. When encountering issues or comments written in Chinese, translate them to English to ensure all contributors can participate in discussions.
#### Translation Guidelines
1. **Update issue titles**: Edit the issue title to include English translation only
2. **Translate comments in place**: Add a comment with the English translation, followed by the original Chinese text
3. **Keep original Chinese**: After translating, include the original Chinese text in a blockquote for verification
4. **Encourage English communication**: Politely suggest users write in English for better collaboration
5. **Maintain technical accuracy**: Preserve technical terms, component names, and code exactly
6. **Translate naturally**: Avoid literal word-by-word translation; use idiomatic English
7. **Preserve formatting**: Keep markdown formatting, code blocks, and links intact
8. **Keep URLs unchanged**: Don't translate URLs or file paths
#### Common Technical Terms (Chinese → English)
- 框架 → **Framework** | 中间件 → **Middleware** | 负载均衡 → **Load Balancing**
- 熔断器 → **Circuit Breaker** | 限流 → **Rate Limiting** | 降载/过载保护 → **Load Shedding**
- 服务发现 → **Service Discovery** | 配置 → **Configuration** | 弹性/容错 → **Resilience** | 微服务 → **Microservices**
#### Translation Example
**Original Chinese Title:** `goctl 执行环境问题`
**Updated Title:** `goctl Execution Environment Issue`
**Original Chinese Comment:** `我在项目中遇到熔断器配置问题`
**Translation in Comment:**
```markdown
I encountered a circuit breaker configuration issue in my project.
> Original (原文): 我在项目中遇到熔断器配置问题
```
### Common Issue Patterns and Solutions
#### Configuration Issues
- Check `service.ServiceConf` embedding and struct tags
- Verify YAML syntax, defaults, and validation rules
- Reference: [rest/config.go](rest/config.go), [zrpc/config.go](zrpc/config.go)
#### Code Generation (goctl) Issues
- Verify `.api` or `.proto` file syntax and goctl version
- Reference: `tools/goctl/` directory
#### RPC Connection Issues
- Check etcd configuration, service discovery, and endpoints
- Verify load balancing settings (p2c_ewma)
#### Database/Cache Issues
- Verify `sqlx.SqlConn` usage with context
- Check cache key generation, invalidation, and connection pools
- Use test helpers (`redistest`, `mongtest`)
#### Performance Issues
- Check if load shedding is enabled (mode: `pre`/`pro`)
- Review circuit breaker thresholds, rate limiting, and context timeouts
### Referencing Codebase
When explaining issues, reference specific files and patterns:
- REST API: `rest/`, `rest/handler/`, `rest/httpx/`
- RPC: `zrpc/`, `zrpc/internal/`
- Core utilities: `core/breaker/`, `core/limit/`, `core/load/`, etc.
- Gateway: `gateway/`
- MCP: `mcp/`
- Code generation: `tools/goctl/`
- Examples: `adhoc/` directory contains various examples
### Encouraging Best Practices
When responding to issues, gently guide users toward:
- Proper error handling with context
- Using resilience features (breakers, rate limiters)
- Following testing patterns with table-driven tests
- Implementing proper resource cleanup
- Reading existing documentation in `docs/` and `readme.md`
## Common Patterns to Follow
### Service Configuration
```go
type ServiceConf struct {
Name string
Log logx.LogConf
Mode string `json:",default=pro,options=[dev,test,pre,pro]"`
// ... other common fields
}
```
### Middleware Implementation
```go
func SomeMiddleware() rest.Middleware {
return func(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Pre-processing
next.ServeHTTP(w, r)
// Post-processing
}
}
}
```
### Resource Management
Always implement proper resource cleanup using defer and context cancellation.
## Build and Test Commands
- Build: `go build ./...`
- Test: `go test ./...`
- Test with race detection: `go test -race ./...`
- Format: `gofmt -w .`
- Code generation:
- REST API: `goctl api go -api *.api -dir .`
- RPC: `goctl rpc protoc *.proto --go_out=. --go-grpc_out=. --zrpc_out=.`
- Model from SQL: `goctl model mysql datasource -url="user:pass@tcp(host:port)/db" -table="*" -dir="./model"`
## Critical Architecture Patterns
### Resilience Design Philosophy
go-zero implements defense-in-depth with multiple protection layers:
1. **Circuit Breaker** (`core/breaker`): Google SRE breaker - tracks success/failure, opens on error threshold
2. **Adaptive Load Shedding** (`core/load`): CPU-based auto-rejection when system overloaded (disabled in dev/test/rt modes)
3. **Rate Limiting** (`core/limit`): Token bucket (Redis-based) and period limiters
4. **Timeout Control**: Cascading timeouts via context - set at multiple levels (client, server, handler)
### Middleware Chain Architecture
`rest/chain` provides middleware composition:
```go
// Middleware signature
type Middleware func(http.Handler) http.Handler
// Chain operations
chain := chain.New(m1, m2)
chain.Append(m3) // Adds to end: m1 -> m2 -> m3
chain.Prepend(m0) // Adds to start: m0 -> m1 -> m2 -> m3
handler := chain.Then(finalHandler)
```
### Concurrency Patterns
- **MapReduce** (`core/mr`): Parallel processing with worker pools - use for batch operations
- **Executors** (`core/executors`): Bulk/period executors for batching operations
- **SingleFlight** (`core/syncx`): Deduplicates concurrent identical requests
Remember to run tests and ensure all checks pass before submitting changes. The project emphasizes high quality, performance, and reliability, so these should be primary considerations in all development work.

View File

@@ -5,7 +5,19 @@
version: 2 version: 2
updates: updates:
- package-ecosystem: "docker" # Update image tags in Dockerfile
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions" # Update GitHub Actions
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "gomod" # See documentation for possible values - package-ecosystem: "gomod" # See documentation for possible values
directory: "/" # Location of package manifests directory: "/" # Location of package manifests
schedule: schedule:
interval: "daily" interval: "daily"
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/tools/goctl" # Location of package manifests
schedule:
interval: "daily"

View File

@@ -35,11 +35,11 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v4
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@@ -50,7 +50,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v2 uses: github/codeql-action/autobuild@v4
# Command-line programs to run using the OS shell. # Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl # 📚 https://git.io/JvXDl
@@ -64,4 +64,4 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@v4

View File

@@ -11,14 +11,16 @@ jobs:
name: Linux name: Linux
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Set up Go 1.x
uses: actions/setup-go@v3
with:
go-version: ^1.16
id: go
- name: Check out code into the Go module directory - name: Check out code into the Go module directory
uses: actions/checkout@v3 uses: actions/checkout@v6
- name: Set up Go 1.x
uses: actions/setup-go@v6
with:
go-version-file: go.mod
check-latest: true
cache: true
id: go
- name: Get dependencies - name: Get dependencies
run: | run: |
@@ -27,30 +29,42 @@ jobs:
- name: Lint - name: Lint
run: | run: |
go vet -stdmethods=false $(go list ./...) go vet -stdmethods=false $(go list ./...)
go install mvdan.cc/gofumpt@latest
test -z "$(gofumpt -l -extra .)" || echo "Please run 'gofumpt -l -w -extra .'" go mod tidy
if ! test -z "$(git status --porcelain)"; then
echo "Please run 'go mod tidy'"
exit 1
fi
- name: Test - name: Test
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./... run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
- name: Codecov - name: Codecov
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v6
with:
files: ./coverage.txt
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
test-win: test-win:
name: Windows name: Windows
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- name: Set up Go 1.x
uses: actions/setup-go@v3
with:
go-version: ^1.16
- name: Checkout codebase - name: Checkout codebase
uses: actions/checkout@v3 uses: actions/checkout@v6
- name: Set up Go 1.x
uses: actions/setup-go@v6
with:
# make sure Go version compatible with go-zero
go-version-file: go.mod
check-latest: true
cache: true
- name: Test - name: Test
run: | run: |
go mod verify go mod verify
go mod download go mod download
go test -v -race ./... go test ./...
cd tools/goctl && go build -v goctl.go cd tools/goctl && go build -v goctl.go

View File

@@ -1,18 +0,0 @@
name: 'issue-translator'
on:
issue_comment:
types: [created]
issues:
types: [opened]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: usthe/issues-translate-action@v2.7
with:
IS_MODIFY_TITLE: true
# not require, default false, . Decide whether to modify the issue title
# if true, the robot account @Issues-translate-bot must have modification permissions, invite @Issues-translate-bot to your project or use your custom bot.
CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿
# not require. Customize the translation robot prefix message.

View File

@@ -7,7 +7,7 @@ jobs:
close-issues: close-issues:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v6 - uses: actions/stale@v10
with: with:
days-before-issue-stale: 365 days-before-issue-stale: 365
days-before-issue-close: 90 days-before-issue-close: 90

View File

@@ -16,13 +16,13 @@ jobs:
- goarch: "386" - goarch: "386"
goos: darwin goos: darwin
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v6
- uses: zeromicro/go-zero-release-action@master - uses: zeromicro/go-zero-release-action@master
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }} goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }} goarch: ${{ matrix.goarch }}
goversion: "https://dl.google.com/go/go1.17.5.linux-amd64.tar.gz" goversion: "https://dl.google.com/go/go1.21.13.linux-amd64.tar.gz"
project_path: "tools/goctl" project_path: "tools/goctl"
binary_name: "goctl" binary_name: "goctl"
extra_files: tools/goctl/readme.md tools/goctl/readme-cn.md extra_files: tools/goctl/readme.md tools/goctl/readme-cn.md

View File

@@ -5,7 +5,12 @@ jobs:
name: runner / staticcheck name: runner / staticcheck
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
check-latest: true
cache: true
- uses: reviewdog/action-staticcheck@v1 - uses: reviewdog/action-staticcheck@v1
with: with:
github_token: ${{ secrets.github_token }} github_token: ${{ secrets.github_token }}
@@ -14,6 +19,6 @@ jobs:
# Report all results. # Report all results.
filter_mode: nofilter filter_mode: nofilter
# Exit with 1 when it find at least one finding. # Exit with 1 when it find at least one finding.
fail_on_error: true fail_level: any
# Set staticcheck flags # Set staticcheck flags
staticcheck_flags: -checks=inherit,-SA1019,-SA1029,-SA5008 staticcheck_flags: -checks=inherit,-SA1019,-SA1029,-SA5008

42
.github/workflows/version-check.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: Release Version Check
on:
push:
tags:
- 'tools/goctl/v*'
workflow_dispatch:
jobs:
version-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: '1.21'
- name: Extract tag version
id: get_version
run: |
# Extract version from tools/goctl/v* format
VERSION="${GITHUB_REF#refs/tags/tools/goctl/v}"
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "Extracted version: $VERSION"
- name: Check version in goctl source code
run: |
# Change to goctl directory
cd tools/goctl
# Check version in BuildVersion constant
VERSION_IN_CODE=$(grep -r "const BuildVersion =" . | grep -o '".*"' | tr -d '"')
echo "Version in code: $VERSION_IN_CODE"
echo "Expected version: $VERSION"
if [ "$VERSION_IN_CODE" != "$VERSION" ]; then
echo "Version mismatch: Version in code ($VERSION_IN_CODE) doesn't match tag version ($VERSION)"
exit 1
fi
echo "✅ Version check passed!"

7
.gitignore vendored
View File

@@ -11,12 +11,15 @@
!api !api
# ignore # ignore
.idea **/.idea
**/.vscode
**/.DS_Store **/.DS_Store
**/logs **/logs
**/adhoc
**/coverage.txt
**/WARP.md
# for test purpose # for test purpose
**/adhoc
go.work go.work
go.work.sum go.work.sum

View File

@@ -1,102 +1,76 @@
# Contributing # 🚀 Contributing to go-zero
Welcome to go-zero! Welcome to the go-zero community! We're thrilled to have you here. Contributing to our project is a fantastic way to be a part of the go-zero journey. Let's make this guide exciting and fun!
- [Before you get started](#before-you-get-started) ## 📜 Before You Dive In
- [Code of Conduct](#code-of-conduct)
- [Community Expectations](#community-expectations)
- [Getting started](#getting-started)
- [Your First Contribution](#your-first-contribution)
- [Find something to work on](#find-something-to-work-on)
- [Find a good first topic](#find-a-good-first-topic)
- [Work on an Issue](#work-on-an-issue)
- [File an Issue](#file-an-issue)
- [Contributor Workflow](#contributor-workflow)
- [Creating Pull Requests](#creating-pull-requests)
- [Code Review](#code-review)
- [Testing](#testing)
# Before you get started ### 🤝 Code of Conduct
## Code of Conduct Let's start on the right foot. Please take a moment to read and embrace our [Code of Conduct](/code-of-conduct.md). We're all about creating a welcoming and respectful environment.
Please make sure to read and observe our [Code of Conduct](/code-of-conduct.md). ### 🌟 Community Expectations
## Community Expectations At go-zero, we're like a close-knit family, and we believe in creating a healthy, friendly, and productive atmosphere. It's all about sharing knowledge and building amazing things together.
go-zero is a community project driven by its community which strives to promote a healthy, friendly and productive environment. ## 🚀 Getting Started
go-zero is a web and rpc framework written in Go. It's born to ensure the stability of the busy sites with resilient design. Builtin goctl greatly improves the development productivity.
# Getting started Get your adventure rolling! Here's how to begin:
- Fork the repository on GitHub. 1. 🍴 **Fork the Repository**: Head over to the GitHub repository and fork it to your own space.
- Make your changes on your fork repository.
- Submit a PR.
2. 🛠️ **Make Your Magic**: Work your magic in your forked repository. Create new features, squash bugs, or improve documentation - it's your world to conquer!
# Your First Contribution 3. 🚀 **Submit a PR (Pull Request)**: When you're ready to unveil your creation, submit a Pull Request. We can't wait to see your awesome work!
We will help you to contribute in different areas like filing issues, developing features, fixing critical bugs and ## 🌟 Your First Contribution
getting your work reviewed and merged.
If you have questions about the development process, We're here to guide you on your quest to become a go-zero contributor. Whether you want to file issues, develop features, or tame some critical bugs, we've got you covered.
feel free to [file an issue](https://github.com/zeromicro/go-zero/issues/new/choose).
## Find something to work on If you have questions or need guidance at any stage, don't hesitate to [open an issue](https://github.com/zeromicro/go-zero/issues/new/choose).
We are always in need of help, be it fixing documentation, reporting bugs or writing some code. ## 🔍 Find Something to Work On
Look at places where you feel best coding practices aren't followed, code refactoring is needed or tests are missing.
Here is how you get started.
### Find a good first topic Ready to dive into the action? There are several ways to contribute:
[go-zero](https://github.com/zeromicro/go-zero) has beginner-friendly issues that provide a good first issue. ### 💼 Find a Good First Topic
For example, [go-zero](https://github.com/zeromicro/go-zero) has
[help wanted](https://github.com/zeromicro/go-zero/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) and
[good first issue](https://github.com/zeromicro/go-zero/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
labels for issues that should not need deep knowledge of the system.
We can help new contributors who wish to work on such issues.
Another good way to contribute is to find a documentation improvement, such as a missing/broken link. Discover easy-entry issues labeled as [help wanted](https://github.com/zeromicro/go-zero/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) or [good first issue](https://github.com/zeromicro/go-zero/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). These issues are perfect for newcomers and don't require deep knowledge of the system. We're here to assist you with these tasks.
Please see [Contributing](#contributing) below for the workflow.
#### Work on an issue ### 🪄 Work on an Issue
When you are willing to take on an issue, just reply on the issue. The maintainer will assign it to you. Once you've picked an issue that excites you, let us know by commenting on it. Our maintainers will assign it to you, and you can embark on your mission!
### File an Issue ### 📢 File an Issue
While we encourage everyone to contribute code, it is also appreciated when someone reports an issue. Reporting an issue is just as valuable as code contributions. If you discover a problem, don't hesitate to [open an issue](https://github.com/zeromicro/go-zero/issues/new/choose). Be sure to follow our guidelines when submitting an issue.
Please follow the prompted submission guidelines while opening an issue. ## 🎯 Contributor Workflow
# Contributor Workflow Here's a rough guide to your contributor journey:
Please do not ever hesitate to ask a question or send a pull request. 1. 🌱 Create a New Branch: Start by creating a topic branch, usually based on the 'master' branch. This is where your contribution will grow.
This is a rough outline of what a contributor's workflow looks like: 2. 💡 Make Commits: Commit your work in logical units. Each commit should tell a story.
- Create a topic branch from where to base the contribution. This is usually master. 3. 🚀 Push Changes: Push the changes in your topic branch to your personal fork of the repository.
- Make commits of logical units.
- Push changes in a topic branch to a personal fork of the repository.
- Submit a pull request to [go-zero](https://github.com/zeromicro/go-zero).
## Creating Pull Requests 4. 📦 Submit a Pull Request: When your creation is complete, submit a Pull Request to the [go-zero repository](https://github.com/zeromicro/go-zero).
Pull requests are often called simply "PR". ## 🌠 Creating Pull Requests
go-zero generally follows the standard [github pull request](https://help.github.com/articles/about-pull-requests/) process.
To submit a proposed change, please develop the code/fix and add new test cases.
After that, run these local verifications before submitting pull request to predict the pass or
fail of continuous integration.
* Format the code with `gofmt` Pull Requests (PRs) are your way of making a grand entrance with your contribution. Here's how to do it:
* Run the test with data race enabled `go test -race ./...`
## Code Review - 💼 Format Your Code: Ensure your code is beautifully formatted with `gofmt`.
- 🏃 Run Tests: Verify that your changes pass all the tests, including data race tests. Run `go test -race ./...` for the ultimate validation.
To make it easier for your PR to receive reviews, consider the reviewers will need you to: ## 👁️‍🗨️ Code Review
* follow [good coding guidelines](https://github.com/golang/go/wiki/CodeReviewComments). Getting your PR reviewed is the final step before your contribution becomes part of go-zero's magical world. To make the process smooth, keep these things in mind:
* write [good commit messages](https://chris.beams.io/posts/git-commit/).
* break large changes into a logical series of smaller patches which individually make easily understandable changes, and in aggregate solve a broader issue.
- 🧙‍♀️ Follow Good Coding Practices: Stick to [good coding guidelines](https://github.com/golang/go/wiki/CodeReviewComments).
- 📝 Write Awesome Commit Messages: Craft [impressive commit messages](https://chris.beams.io/posts/git-commit/) - they're like spells in the wizard's book!
- 🔍 Break It Down: For larger changes, consider breaking them into a series of smaller, logical patches. Each patch should make an understandable and meaningful improvement.
Congratulations on your contribution journey! We're thrilled to have you as part of our go-zero community. Let's make amazing things together! 🌟
Now, go out there and start your adventure! If you have any more magical ideas to enhance this guide, please share them. 🔥

View File

@@ -1,28 +0,0 @@
# go-zero Roadmap
This document defines a high level roadmap for go-zero development and upcoming releases.
Community and contributor involvement is vital for successfully implementing all desired items for each release.
We hope that the items listed below will inspire further engagement from the community to keep go-zero progressing and shipping exciting and valuable features.
## 2021 Q2
- [x] Support service discovery through K8S client api
- [x] Log full sql statements for easier sql problem solving
## 2021 Q3
- [x] Support `goctl model pg` to support PostgreSQL code generation
- [x] Adapt builtin tracing mechanism to opentracing solutions
## 2021 Q4
- [x] Support `username/password` authentication in ETCD
- [x] Support `SSL/TLS` in ETCD
- [x] Support `SSL/TLS` in `zRPC`
- [x] Support `TLS` in redis connections
- [x] Support `goctl bug` to report bugs conveniently
## 2022
- [x] Support `context` in redis related methods for timeout and tracing
- [x] Support `context` in sql related methods for timeout and tracing
- [x] Support `context` in mongodb related methods for timeout and tracing
- [x] Add `httpc.Do` with HTTP call governance, like circuit breaker etc.
- [ ] Support `goctl doctor` command to report potential issues for given service
- [ ] Support `goctl mock` command to start a mocking server with given `.api` file

16
SECURITY.md Normal file
View File

@@ -0,0 +1,16 @@
# Security Policy
## Supported Versions
We publish releases monthly.
| Version | Supported |
| ------- | ------------------ |
| >= 1.4.4 | :white_check_mark: |
| < 1.4.4 | :x: |
## Reporting a Vulnerability
https://github.com/zeromicro/go-zero/security/advisories
Accepted vulnerabilities are expected to be fixed within a month.

View File

@@ -1,76 +1,127 @@
# Contributor Covenant Code of Conduct # Contributor Covenant Code of Conduct
## Our Pledge ## Our Pledge
In the interest of fostering an open and welcoming environment, we as We as members, contributors, and leaders pledge to make participation in our
contributors and maintainers pledge to make participation in our project and community a harassment-free experience for everyone, regardless of age, body
our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender
size, disability, ethnicity, sex characteristics, gender identity and expression, identity and expression, level of experience, education, socio-economic status,
level of experience, education, socio-economic status, nationality, personal nationality, personal appearance, race, caste, color, religion, or sexual
appearance, race, religion, or sexual identity and orientation. identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards ## Our Standards
Examples of behavior that contributes to creating a positive environment Examples of behavior that contributes to a positive environment for our
include: community include:
* Using welcoming and inclusive language * Demonstrating empathy and kindness toward other people
* Being respectful of differing viewpoints and experiences * Being respectful of differing opinions, viewpoints, and experiences
* Gracefully accepting constructive criticism * Giving and gracefully accepting constructive feedback
* Focusing on what is best for the community * Accepting responsibility and apologizing to those affected by our mistakes,
* Showing empathy towards other community members and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior by participants include: Examples of unacceptable behavior include:
* The use of sexualized language or imagery and unwelcome sexual attention or * The use of sexualized language or imagery, and sexual attention or advances of
advances any kind
* Trolling, insulting/derogatory comments, and personal or political attacks * Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment * Public or private harassment
* Publishing others' private information, such as a physical or electronic * Publishing others' private information, such as a physical or email address,
address, without explicit permission without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a * Other conduct which could reasonably be considered inappropriate in a
professional setting professional setting
## Our Responsibilities ## Enforcement Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable Community leaders are responsible for clarifying and enforcing our standards of
behavior and are expected to take appropriate and fair corrective action in acceptable behavior and will take appropriate and fair corrective action in
response to any instances of unacceptable behavior. response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Project maintainers have the right and responsibility to remove, edit, or Community leaders have the right and responsibility to remove, edit, or reject
reject comments, commits, code, wiki edits, issues, and other contributions comments, commits, code, wiki edits, issues, and other contributions that are
that are not aligned to this Code of Conduct, or to ban temporarily or not aligned to this Code of Conduct, and will communicate reasons for moderation
permanently any contributor for other behaviors that they deem inappropriate, decisions when appropriate.
threatening, offensive, or harmful.
## Scope ## Scope
This Code of Conduct applies within all project spaces, and it also applies when This Code of Conduct applies within all community spaces, and also applies when
an individual is representing the project or its community in public spaces. an individual is officially representing the community in public spaces.
Examples of representing a project or community include using an official Examples of representing our community include using an official e-mail address,
project e-mail address, posting via an official social media account, or acting posting via an official social media account, or acting as an appointed
as an appointed representative at an online or offline event. Representation of representative at an online or offline event.
a project may be further defined and clarified by project maintainers.
## Enforcement ## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All reported to the community leaders responsible for enforcement at
complaints will be reviewed and investigated and will result in a response that [INSERT CONTACT METHOD].
is deemed necessary and appropriate to the circumstances. The project team is All complaints will be reviewed and investigated promptly and fairly.
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good All community leaders are obligated to respect the privacy and security of the
faith may face temporary or permanent repercussions as determined by other reporter of any incident.
members of the project's leadership.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution ## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, This Code of Conduct is adapted from the [Contributor Covenant][homepage],
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
[homepage]: https://www.contributor-covenant.org Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].

View File

@@ -1,6 +1,8 @@
package bloom package bloom
import ( import (
"context"
_ "embed"
"errors" "errors"
"strconv" "strconv"
@@ -8,27 +10,22 @@ import (
"github.com/zeromicro/go-zero/core/stores/redis" "github.com/zeromicro/go-zero/core/stores/redis"
) )
const ( // for detailed error rate table, see http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html
// for detailed error rate table, see http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html // maps as k in the error rate table
// maps as k in the error rate table const maps = 14
maps = 14
setScript = `
for _, offset in ipairs(ARGV) do
redis.call("setbit", KEYS[1], offset, 1)
end
`
testScript = `
for _, offset in ipairs(ARGV) do
if tonumber(redis.call("getbit", KEYS[1], offset)) == 0 then
return false
end
end
return true
`
)
// ErrTooLargeOffset indicates the offset is too large in bitset. var (
var ErrTooLargeOffset = errors.New("too large offset") // ErrTooLargeOffset indicates the offset is too large in bitset.
ErrTooLargeOffset = errors.New("too large offset")
//go:embed setscript.lua
setLuaScript string
setScript = redis.NewScript(setLuaScript)
//go:embed testscript.lua
testLuaScript string
testScript = redis.NewScript(testLuaScript)
)
type ( type (
// A Filter is a bloom filter. // A Filter is a bloom filter.
@@ -38,12 +35,12 @@ type (
} }
bitSetProvider interface { bitSetProvider interface {
check([]uint) (bool, error) check(ctx context.Context, offsets []uint) (bool, error)
set([]uint) error set(ctx context.Context, offsets []uint) error
} }
) )
// New create a Filter, store is the backed redis, key is the key for the bloom filter, // New creates a Filter, store is the backed redis, key is the key for the bloom filter,
// bits is how many bits will be used, maps is how many hashes for each addition. // bits is how many bits will be used, maps is how many hashes for each addition.
// best practices: // best practices:
// elements - means how many actual elements // elements - means how many actual elements
@@ -58,14 +55,24 @@ func New(store *redis.Redis, key string, bits uint) *Filter {
// Add adds data into f. // Add adds data into f.
func (f *Filter) Add(data []byte) error { func (f *Filter) Add(data []byte) error {
return f.AddCtx(context.Background(), data)
}
// AddCtx adds data into f with context.
func (f *Filter) AddCtx(ctx context.Context, data []byte) error {
locations := f.getLocations(data) locations := f.getLocations(data)
return f.bitSet.set(locations) return f.bitSet.set(ctx, locations)
} }
// Exists checks if data is in f. // Exists checks if data is in f.
func (f *Filter) Exists(data []byte) (bool, error) { func (f *Filter) Exists(data []byte) (bool, error) {
return f.ExistsCtx(context.Background(), data)
}
// ExistsCtx checks if data is in f with context.
func (f *Filter) ExistsCtx(ctx context.Context, data []byte) (bool, error) {
locations := f.getLocations(data) locations := f.getLocations(data)
isSet, err := f.bitSet.check(locations) isSet, err := f.bitSet.check(ctx, locations)
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -98,7 +105,7 @@ func newRedisBitSet(store *redis.Redis, key string, bits uint) *redisBitSet {
} }
func (r *redisBitSet) buildOffsetArgs(offsets []uint) ([]string, error) { func (r *redisBitSet) buildOffsetArgs(offsets []uint) ([]string, error) {
var args []string args := make([]string, 0, len(offsets))
for _, offset := range offsets { for _, offset := range offsets {
if offset >= r.bits { if offset >= r.bits {
@@ -111,14 +118,14 @@ func (r *redisBitSet) buildOffsetArgs(offsets []uint) ([]string, error) {
return args, nil return args, nil
} }
func (r *redisBitSet) check(offsets []uint) (bool, error) { func (r *redisBitSet) check(ctx context.Context, offsets []uint) (bool, error) {
args, err := r.buildOffsetArgs(offsets) args, err := r.buildOffsetArgs(offsets)
if err != nil { if err != nil {
return false, err return false, err
} }
resp, err := r.store.Eval(testScript, []string{r.key}, args) resp, err := r.store.ScriptRunCtx(ctx, testScript, []string{r.key}, args)
if err == redis.Nil { if errors.Is(err, redis.Nil) {
return false, nil return false, nil
} else if err != nil { } else if err != nil {
return false, err return false, err
@@ -132,23 +139,25 @@ func (r *redisBitSet) check(offsets []uint) (bool, error) {
return exists == 1, nil return exists == 1, nil
} }
// del only use for testing.
func (r *redisBitSet) del() error { func (r *redisBitSet) del() error {
_, err := r.store.Del(r.key) _, err := r.store.Del(r.key)
return err return err
} }
// expire only use for testing.
func (r *redisBitSet) expire(seconds int) error { func (r *redisBitSet) expire(seconds int) error {
return r.store.Expire(r.key, seconds) return r.store.Expire(r.key, seconds)
} }
func (r *redisBitSet) set(offsets []uint) error { func (r *redisBitSet) set(ctx context.Context, offsets []uint) error {
args, err := r.buildOffsetArgs(offsets) args, err := r.buildOffsetArgs(offsets)
if err != nil { if err != nil {
return err return err
} }
_, err = r.store.Eval(setScript, []string{r.key}, args) _, err = r.store.ScriptRunCtx(ctx, setScript, []string{r.key}, args)
if err == redis.Nil { if errors.Is(err, redis.Nil) {
return nil return nil
} }

View File

@@ -1,30 +1,31 @@
package bloom package bloom
import ( import (
"context"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/redis/redistest" "github.com/zeromicro/go-zero/core/stores/redis/redistest"
) )
func TestRedisBitSet_New_Set_Test(t *testing.T) { func TestRedisBitSet_New_Set_Test(t *testing.T) {
store, clean, err := redistest.CreateRedis() store := redistest.CreateRedis(t)
assert.Nil(t, err) ctx := context.Background()
defer clean()
bitSet := newRedisBitSet(store, "test_key", 1024) bitSet := newRedisBitSet(store, "test_key", 1024)
isSetBefore, err := bitSet.check([]uint{0}) isSetBefore, err := bitSet.check(ctx, []uint{0})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if isSetBefore { if isSetBefore {
t.Fatal("Bit should not be set") t.Fatal("Bit should not be set")
} }
err = bitSet.set([]uint{512}) err = bitSet.set(ctx, []uint{512})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
isSetAfter, err := bitSet.check([]uint{512}) isSetAfter, err := bitSet.check(ctx, []uint{512})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -42,9 +43,7 @@ func TestRedisBitSet_New_Set_Test(t *testing.T) {
} }
func TestRedisBitSet_Add(t *testing.T) { func TestRedisBitSet_Add(t *testing.T) {
store, clean, err := redistest.CreateRedis() store := redistest.CreateRedis(t)
assert.Nil(t, err)
defer clean()
filter := New(store, "test_key", 64) filter := New(store, "test_key", 64)
assert.Nil(t, filter.Add([]byte("hello"))) assert.Nil(t, filter.Add([]byte("hello")))
@@ -53,3 +52,51 @@ func TestRedisBitSet_Add(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, ok) assert.True(t, ok)
} }
func TestFilter_Exists(t *testing.T) {
store, clean := redistest.CreateRedisWithClean(t)
rbs := New(store, "test", 64)
_, err := rbs.Exists([]byte{0, 1, 2})
assert.NoError(t, err)
clean()
rbs = New(store, "test", 64)
_, err = rbs.Exists([]byte{0, 1, 2})
assert.Error(t, err)
}
func TestRedisBitSet_check(t *testing.T) {
store, clean := redistest.CreateRedisWithClean(t)
ctx := context.Background()
rbs := newRedisBitSet(store, "test", 0)
assert.Error(t, rbs.set(ctx, []uint{0, 1, 2}))
_, err := rbs.check(ctx, []uint{0, 1, 2})
assert.Error(t, err)
rbs = newRedisBitSet(store, "test", 64)
_, err = rbs.check(ctx, []uint{0, 1, 2})
assert.NoError(t, err)
clean()
rbs = newRedisBitSet(store, "test", 64)
_, err = rbs.check(ctx, []uint{0, 1, 2})
assert.Error(t, err)
}
func TestRedisBitSet_set(t *testing.T) {
logx.Disable()
store, clean := redistest.CreateRedisWithClean(t)
ctx := context.Background()
rbs := newRedisBitSet(store, "test", 0)
assert.Error(t, rbs.set(ctx, []uint{0, 1, 2}))
rbs = newRedisBitSet(store, "test", 64)
assert.NoError(t, rbs.set(ctx, []uint{0, 1, 2}))
clean()
rbs = newRedisBitSet(store, "test", 64)
assert.Error(t, rbs.set(ctx, []uint{0, 1, 2}))
}

3
core/bloom/setscript.lua Normal file
View File

@@ -0,0 +1,3 @@
for _, offset in ipairs(ARGV) do
redis.call("setbit", KEYS[1], offset, 1)
end

View File

@@ -0,0 +1,6 @@
for _, offset in ipairs(ARGV) do
if tonumber(redis.call("getbit", KEYS[1], offset)) == 0 then
return false
end
end
return true

View File

@@ -1,22 +1,19 @@
package breaker package breaker
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/zeromicro/go-zero/core/mathx"
"github.com/zeromicro/go-zero/core/proc" "github.com/zeromicro/go-zero/core/proc"
"github.com/zeromicro/go-zero/core/stat" "github.com/zeromicro/go-zero/core/stat"
"github.com/zeromicro/go-zero/core/stringx" "github.com/zeromicro/go-zero/core/stringx"
) )
const ( const numHistoryReasons = 5
numHistoryReasons = 5
timeFormat = "15:04:05"
)
// ErrServiceUnavailable is returned when the Breaker state is open. // ErrServiceUnavailable is returned when the Breaker state is open.
var ErrServiceUnavailable = errors.New("circuit breaker is open") var ErrServiceUnavailable = errors.New("circuit breaker is open")
@@ -31,38 +28,53 @@ type (
Name() string Name() string
// Allow checks if the request is allowed. // Allow checks if the request is allowed.
// If allowed, a promise will be returned, the caller needs to call promise.Accept() // If allowed, a promise will be returned,
// on success, or call promise.Reject() on failure. // otherwise ErrServiceUnavailable will be returned as the error.
// If not allow, ErrServiceUnavailable will be returned. // The caller needs to call promise.Accept() on success,
// or call promise.Reject() on failure.
Allow() (Promise, error) Allow() (Promise, error)
// AllowCtx checks if the request is allowed when ctx isn't done.
AllowCtx(ctx context.Context) (Promise, error)
// Do runs the given request if the Breaker accepts it. // Do runs the given request if the Breaker accepts it.
// Do returns an error instantly if the Breaker rejects the request. // Do returns an error instantly if the Breaker rejects the request.
// If a panic occurs in the request, the Breaker handles it as an error // If a panic occurs in the request, the Breaker handles it as an error
// and causes the same panic again. // and causes the same panic again.
Do(req func() error) error Do(req func() error) error
// DoCtx runs the given request if the Breaker accepts it when ctx isn't done.
DoCtx(ctx context.Context, req func() error) error
// DoWithAcceptable runs the given request if the Breaker accepts it. // DoWithAcceptable runs the given request if the Breaker accepts it.
// DoWithAcceptable returns an error instantly if the Breaker rejects the request. // DoWithAcceptable returns an error instantly if the Breaker rejects the request.
// If a panic occurs in the request, the Breaker handles it as an error // If a panic occurs in the request, the Breaker handles it as an error
// and causes the same panic again. // and causes the same panic again.
// acceptable checks if it's a successful call, even if the err is not nil. // acceptable checks if it's a successful call, even if the error is not nil.
DoWithAcceptable(req func() error, acceptable Acceptable) error DoWithAcceptable(req func() error, acceptable Acceptable) error
// DoWithAcceptableCtx runs the given request if the Breaker accepts it when ctx isn't done.
DoWithAcceptableCtx(ctx context.Context, req func() error, acceptable Acceptable) error
// DoWithFallback runs the given request if the Breaker accepts it. // DoWithFallback runs the given request if the Breaker accepts it.
// DoWithFallback runs the fallback if the Breaker rejects the request. // DoWithFallback runs the fallback if the Breaker rejects the request.
// If a panic occurs in the request, the Breaker handles it as an error // If a panic occurs in the request, the Breaker handles it as an error
// and causes the same panic again. // and causes the same panic again.
DoWithFallback(req func() error, fallback func(err error) error) error DoWithFallback(req func() error, fallback Fallback) error
// DoWithFallbackCtx runs the given request if the Breaker accepts it when ctx isn't done.
DoWithFallbackCtx(ctx context.Context, req func() error, fallback Fallback) error
// DoWithFallbackAcceptable runs the given request if the Breaker accepts it. // DoWithFallbackAcceptable runs the given request if the Breaker accepts it.
// DoWithFallbackAcceptable runs the fallback if the Breaker rejects the request. // DoWithFallbackAcceptable runs the fallback if the Breaker rejects the request.
// If a panic occurs in the request, the Breaker handles it as an error // If a panic occurs in the request, the Breaker handles it as an error
// and causes the same panic again. // and causes the same panic again.
// acceptable checks if it's a successful call, even if the err is not nil. // acceptable checks if it's a successful call, even if the error is not nil.
DoWithFallbackAcceptable(req func() error, fallback func(err error) error, acceptable Acceptable) error DoWithFallbackAcceptable(req func() error, fallback Fallback, acceptable Acceptable) error
// DoWithFallbackAcceptableCtx runs the given request if the Breaker accepts it when ctx isn't done.
DoWithFallbackAcceptableCtx(ctx context.Context, req func() error, fallback Fallback,
acceptable Acceptable) error
} }
// Fallback is the func to be called if the request is rejected.
Fallback func(err error) error
// Option defines the method to customize a Breaker. // Option defines the method to customize a Breaker.
Option func(breaker *circuitBreaker) Option func(breaker *circuitBreaker)
@@ -86,12 +98,12 @@ type (
internalThrottle interface { internalThrottle interface {
allow() (internalPromise, error) allow() (internalPromise, error)
doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error doReq(req func() error, fallback Fallback, acceptable Acceptable) error
} }
throttle interface { throttle interface {
allow() (Promise, error) allow() (Promise, error)
doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error doReq(req func() error, fallback Fallback, acceptable Acceptable) error
} }
) )
@@ -114,23 +126,71 @@ func (cb *circuitBreaker) Allow() (Promise, error) {
return cb.throttle.allow() return cb.throttle.allow()
} }
func (cb *circuitBreaker) AllowCtx(ctx context.Context) (Promise, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
return cb.Allow()
}
}
func (cb *circuitBreaker) Do(req func() error) error { func (cb *circuitBreaker) Do(req func() error) error {
return cb.throttle.doReq(req, nil, defaultAcceptable) return cb.throttle.doReq(req, nil, defaultAcceptable)
} }
func (cb *circuitBreaker) DoCtx(ctx context.Context, req func() error) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
return cb.Do(req)
}
}
func (cb *circuitBreaker) DoWithAcceptable(req func() error, acceptable Acceptable) error { func (cb *circuitBreaker) DoWithAcceptable(req func() error, acceptable Acceptable) error {
return cb.throttle.doReq(req, nil, acceptable) return cb.throttle.doReq(req, nil, acceptable)
} }
func (cb *circuitBreaker) DoWithFallback(req func() error, fallback func(err error) error) error { func (cb *circuitBreaker) DoWithAcceptableCtx(ctx context.Context, req func() error,
acceptable Acceptable) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
return cb.DoWithAcceptable(req, acceptable)
}
}
func (cb *circuitBreaker) DoWithFallback(req func() error, fallback Fallback) error {
return cb.throttle.doReq(req, fallback, defaultAcceptable) return cb.throttle.doReq(req, fallback, defaultAcceptable)
} }
func (cb *circuitBreaker) DoWithFallbackAcceptable(req func() error, fallback func(err error) error, func (cb *circuitBreaker) DoWithFallbackCtx(ctx context.Context, req func() error,
fallback Fallback) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
return cb.DoWithFallback(req, fallback)
}
}
func (cb *circuitBreaker) DoWithFallbackAcceptable(req func() error, fallback Fallback,
acceptable Acceptable) error { acceptable Acceptable) error {
return cb.throttle.doReq(req, fallback, acceptable) return cb.throttle.doReq(req, fallback, acceptable)
} }
func (cb *circuitBreaker) DoWithFallbackAcceptableCtx(ctx context.Context, req func() error,
fallback Fallback, acceptable Acceptable) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
return cb.DoWithFallbackAcceptable(req, fallback, acceptable)
}
}
func (cb *circuitBreaker) Name() string { func (cb *circuitBreaker) Name() string {
return cb.name return cb.name
} }
@@ -168,7 +228,7 @@ func (lt loggedThrottle) allow() (Promise, error) {
}, lt.logError(err) }, lt.logError(err)
} }
func (lt loggedThrottle) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { func (lt loggedThrottle) doReq(req func() error, fallback Fallback, acceptable Acceptable) error {
return lt.logError(lt.internalThrottle.doReq(req, fallback, func(err error) bool { return lt.logError(lt.internalThrottle.doReq(req, fallback, func(err error) bool {
accept := acceptable(err) accept := acceptable(err)
if !accept && err != nil { if !accept && err != nil {
@@ -179,7 +239,7 @@ func (lt loggedThrottle) doReq(req func() error, fallback func(err error) error,
} }
func (lt loggedThrottle) logError(err error) error { func (lt loggedThrottle) logError(err error) error {
if err == ErrServiceUnavailable { if errors.Is(err, ErrServiceUnavailable) {
// if circuit open, not possible to have empty error window // if circuit open, not possible to have empty error window
stat.Report(fmt.Sprintf( stat.Report(fmt.Sprintf(
"proc(%s/%d), callee: %s, breaker is open and requests dropped\nlast errors:\n%s", "proc(%s/%d), callee: %s, breaker is open and requests dropped\nlast errors:\n%s",
@@ -198,14 +258,14 @@ type errorWindow struct {
func (ew *errorWindow) add(reason string) { func (ew *errorWindow) add(reason string) {
ew.lock.Lock() ew.lock.Lock()
ew.reasons[ew.index] = fmt.Sprintf("%s %s", time.Now().Format(timeFormat), reason) ew.reasons[ew.index] = fmt.Sprintf("%s %s", time.Now().Format(time.TimeOnly), reason)
ew.index = (ew.index + 1) % numHistoryReasons ew.index = (ew.index + 1) % numHistoryReasons
ew.count = mathx.MinInt(ew.count+1, numHistoryReasons) ew.count = min(ew.count+1, numHistoryReasons)
ew.lock.Unlock() ew.lock.Unlock()
} }
func (ew *errorWindow) String() string { func (ew *errorWindow) String() string {
var reasons []string reasons := make([]string, 0, ew.count)
ew.lock.Lock() ew.lock.Lock()
// reverse order // reverse order

View File

@@ -1,11 +1,13 @@
package breaker package breaker
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
"time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/stat" "github.com/zeromicro/go-zero/core/stat"
@@ -16,10 +18,274 @@ func init() {
} }
func TestCircuitBreaker_Allow(t *testing.T) { func TestCircuitBreaker_Allow(t *testing.T) {
t.Run("allow", func(t *testing.T) {
b := NewBreaker() b := NewBreaker()
assert.True(t, len(b.Name()) > 0) assert.True(t, len(b.Name()) > 0)
_, err := b.Allow() _, err := b.Allow()
assert.Nil(t, err) assert.Nil(t, err)
})
t.Run("allow with ctx", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
_, err := b.AllowCtx(context.Background())
assert.Nil(t, err)
})
t.Run("allow with ctx timeout", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
ctx, cancel := context.WithTimeout(context.Background(), time.Microsecond)
defer cancel()
time.Sleep(time.Millisecond)
_, err := b.AllowCtx(ctx)
assert.ErrorIs(t, err, context.DeadlineExceeded)
})
t.Run("allow with ctx cancel", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
for i := 0; i < 100; i++ {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
cancel()
_, err := b.AllowCtx(ctx)
assert.ErrorIs(t, err, context.Canceled)
}
_, err := b.AllowCtx(context.Background())
assert.NoError(t, err)
})
}
func TestCircuitBreaker_Do(t *testing.T) {
t.Run("do", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
err := b.Do(func() error {
return nil
})
assert.Nil(t, err)
})
t.Run("do with ctx", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
err := b.DoCtx(context.Background(), func() error {
return nil
})
assert.Nil(t, err)
})
t.Run("do with ctx timeout", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
ctx, cancel := context.WithTimeout(context.Background(), time.Microsecond)
defer cancel()
time.Sleep(time.Millisecond)
err := b.DoCtx(ctx, func() error {
return nil
})
assert.ErrorIs(t, err, context.DeadlineExceeded)
})
t.Run("do with ctx cancel", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
for i := 0; i < 100; i++ {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
cancel()
err := b.DoCtx(ctx, func() error {
return nil
})
assert.ErrorIs(t, err, context.Canceled)
}
assert.NoError(t, b.DoCtx(context.Background(), func() error {
return nil
}))
})
}
func TestCircuitBreaker_DoWithAcceptable(t *testing.T) {
t.Run("doWithAcceptable", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
err := b.DoWithAcceptable(func() error {
return nil
}, func(err error) bool {
return true
})
assert.Nil(t, err)
})
t.Run("doWithAcceptable with ctx", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
err := b.DoWithAcceptableCtx(context.Background(), func() error {
return nil
}, func(err error) bool {
return true
})
assert.Nil(t, err)
})
t.Run("doWithAcceptable with ctx timeout", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
ctx, cancel := context.WithTimeout(context.Background(), time.Microsecond)
defer cancel()
time.Sleep(time.Millisecond)
err := b.DoWithAcceptableCtx(ctx, func() error {
return nil
}, func(err error) bool {
return true
})
assert.ErrorIs(t, err, context.DeadlineExceeded)
})
t.Run("doWithAcceptable with ctx cancel", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
for i := 0; i < 100; i++ {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
cancel()
err := b.DoWithAcceptableCtx(ctx, func() error {
return nil
}, func(err error) bool {
return true
})
assert.ErrorIs(t, err, context.Canceled)
}
assert.NoError(t, b.DoWithAcceptableCtx(context.Background(), func() error {
return nil
}, func(err error) bool {
return true
}))
})
}
func TestCircuitBreaker_DoWithFallback(t *testing.T) {
t.Run("doWithFallback", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
err := b.DoWithFallback(func() error {
return nil
}, func(err error) error {
return err
})
assert.Nil(t, err)
})
t.Run("doWithFallback with ctx", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
err := b.DoWithFallbackCtx(context.Background(), func() error {
return nil
}, func(err error) error {
return err
})
assert.Nil(t, err)
})
t.Run("doWithFallback with ctx timeout", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
ctx, cancel := context.WithTimeout(context.Background(), time.Microsecond)
defer cancel()
time.Sleep(time.Millisecond)
err := b.DoWithFallbackCtx(ctx, func() error {
return nil
}, func(err error) error {
return err
})
assert.ErrorIs(t, err, context.DeadlineExceeded)
})
t.Run("doWithFallback with ctx cancel", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
for i := 0; i < 100; i++ {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
cancel()
err := b.DoWithFallbackCtx(ctx, func() error {
return nil
}, func(err error) error {
return err
})
assert.ErrorIs(t, err, context.Canceled)
}
assert.NoError(t, b.DoWithFallbackCtx(context.Background(), func() error {
return nil
}, func(err error) error {
return err
}))
})
}
func TestCircuitBreaker_DoWithFallbackAcceptable(t *testing.T) {
t.Run("doWithFallbackAcceptable", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
err := b.DoWithFallbackAcceptable(func() error {
return nil
}, func(err error) error {
return err
}, func(err error) bool {
return true
})
assert.Nil(t, err)
})
t.Run("doWithFallbackAcceptable with ctx", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
err := b.DoWithFallbackAcceptableCtx(context.Background(), func() error {
return nil
}, func(err error) error {
return err
}, func(err error) bool {
return true
})
assert.Nil(t, err)
})
t.Run("doWithFallbackAcceptable with ctx timeout", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
ctx, cancel := context.WithTimeout(context.Background(), time.Microsecond)
defer cancel()
time.Sleep(time.Millisecond)
err := b.DoWithFallbackAcceptableCtx(ctx, func() error {
return nil
}, func(err error) error {
return err
}, func(err error) bool {
return true
})
assert.ErrorIs(t, err, context.DeadlineExceeded)
})
t.Run("doWithFallbackAcceptable with ctx cancel", func(t *testing.T) {
b := NewBreaker()
assert.True(t, len(b.Name()) > 0)
for i := 0; i < 100; i++ {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
cancel()
err := b.DoWithFallbackAcceptableCtx(ctx, func() error {
return nil
}, func(err error) error {
return err
}, func(err error) bool {
return true
})
assert.ErrorIs(t, err, context.Canceled)
}
assert.NoError(t, b.DoWithFallbackAcceptableCtx(context.Background(), func() error {
return nil
}, func(err error) error {
return err
}, func(err error) bool {
return true
}))
})
} }
func TestLogReason(t *testing.T) { func TestLogReason(t *testing.T) {

View File

@@ -1,6 +1,9 @@
package breaker package breaker
import "sync" import (
"context"
"sync"
)
var ( var (
lock sync.RWMutex lock sync.RWMutex
@@ -14,6 +17,13 @@ func Do(name string, req func() error) error {
}) })
} }
// DoCtx calls Breaker.DoCtx on the Breaker with given name.
func DoCtx(ctx context.Context, name string, req func() error) error {
return do(name, func(b Breaker) error {
return b.DoCtx(ctx, req)
})
}
// DoWithAcceptable calls Breaker.DoWithAcceptable on the Breaker with given name. // DoWithAcceptable calls Breaker.DoWithAcceptable on the Breaker with given name.
func DoWithAcceptable(name string, req func() error, acceptable Acceptable) error { func DoWithAcceptable(name string, req func() error, acceptable Acceptable) error {
return do(name, func(b Breaker) error { return do(name, func(b Breaker) error {
@@ -21,21 +31,44 @@ func DoWithAcceptable(name string, req func() error, acceptable Acceptable) erro
}) })
} }
// DoWithAcceptableCtx calls Breaker.DoWithAcceptableCtx on the Breaker with given name.
func DoWithAcceptableCtx(ctx context.Context, name string, req func() error,
acceptable Acceptable) error {
return do(name, func(b Breaker) error {
return b.DoWithAcceptableCtx(ctx, req, acceptable)
})
}
// DoWithFallback calls Breaker.DoWithFallback on the Breaker with given name. // DoWithFallback calls Breaker.DoWithFallback on the Breaker with given name.
func DoWithFallback(name string, req func() error, fallback func(err error) error) error { func DoWithFallback(name string, req func() error, fallback Fallback) error {
return do(name, func(b Breaker) error { return do(name, func(b Breaker) error {
return b.DoWithFallback(req, fallback) return b.DoWithFallback(req, fallback)
}) })
} }
// DoWithFallbackCtx calls Breaker.DoWithFallbackCtx on the Breaker with given name.
func DoWithFallbackCtx(ctx context.Context, name string, req func() error, fallback Fallback) error {
return do(name, func(b Breaker) error {
return b.DoWithFallbackCtx(ctx, req, fallback)
})
}
// DoWithFallbackAcceptable calls Breaker.DoWithFallbackAcceptable on the Breaker with given name. // DoWithFallbackAcceptable calls Breaker.DoWithFallbackAcceptable on the Breaker with given name.
func DoWithFallbackAcceptable(name string, req func() error, fallback func(err error) error, func DoWithFallbackAcceptable(name string, req func() error, fallback Fallback,
acceptable Acceptable) error { acceptable Acceptable) error {
return do(name, func(b Breaker) error { return do(name, func(b Breaker) error {
return b.DoWithFallbackAcceptable(req, fallback, acceptable) return b.DoWithFallbackAcceptable(req, fallback, acceptable)
}) })
} }
// DoWithFallbackAcceptableCtx calls Breaker.DoWithFallbackAcceptableCtx on the Breaker with given name.
func DoWithFallbackAcceptableCtx(ctx context.Context, name string, req func() error,
fallback Fallback, acceptable Acceptable) error {
return do(name, func(b Breaker) error {
return b.DoWithFallbackAcceptableCtx(ctx, req, fallback, acceptable)
})
}
// GetBreaker returns the Breaker with the given name. // GetBreaker returns the Breaker with the given name.
func GetBreaker(name string) Breaker { func GetBreaker(name string) Breaker {
lock.RLock() lock.RLock()
@@ -59,7 +92,7 @@ func GetBreaker(name string) Breaker {
// NoBreakerFor disables the circuit breaker for the given name. // NoBreakerFor disables the circuit breaker for the given name.
func NoBreakerFor(name string) { func NoBreakerFor(name string) {
lock.Lock() lock.Lock()
breakers[name] = newNoOpBreaker() breakers[name] = NopBreaker()
lock.Unlock() lock.Unlock()
} }

View File

@@ -1,6 +1,7 @@
package breaker package breaker
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"testing" "testing"
@@ -22,6 +23,9 @@ func TestBreakersDo(t *testing.T) {
assert.Equal(t, errDummy, Do("any", func() error { assert.Equal(t, errDummy, Do("any", func() error {
return errDummy return errDummy
})) }))
assert.Equal(t, errDummy, DoCtx(context.Background(), "any", func() error {
return errDummy
}))
} }
func TestBreakersDoWithAcceptable(t *testing.T) { func TestBreakersDoWithAcceptable(t *testing.T) {
@@ -30,7 +34,7 @@ func TestBreakersDoWithAcceptable(t *testing.T) {
assert.Equal(t, errDummy, GetBreaker("anyone").DoWithAcceptable(func() error { assert.Equal(t, errDummy, GetBreaker("anyone").DoWithAcceptable(func() error {
return errDummy return errDummy
}, func(err error) bool { }, func(err error) bool {
return err == nil || err == errDummy return err == nil || errors.Is(err, errDummy)
})) }))
} }
verify(t, func() bool { verify(t, func() bool {
@@ -38,6 +42,13 @@ func TestBreakersDoWithAcceptable(t *testing.T) {
return nil return nil
}) == nil }) == nil
}) })
verify(t, func() bool {
return DoWithAcceptableCtx(context.Background(), "anyone", func() error {
return nil
}, func(err error) bool {
return true
}) == nil
})
for i := 0; i < 10000; i++ { for i := 0; i < 10000; i++ {
err := DoWithAcceptable("another", func() error { err := DoWithAcceptable("another", func() error {
@@ -45,12 +56,12 @@ func TestBreakersDoWithAcceptable(t *testing.T) {
}, func(err error) bool { }, func(err error) bool {
return err == nil return err == nil
}) })
assert.True(t, err == errDummy || err == ErrServiceUnavailable) assert.True(t, errors.Is(err, errDummy) || errors.Is(err, ErrServiceUnavailable))
} }
verify(t, func() bool { verify(t, func() bool {
return ErrServiceUnavailable == Do("another", func() error { return errors.Is(Do("another", func() error {
return nil return nil
}) }), ErrServiceUnavailable)
}) })
} }
@@ -75,18 +86,24 @@ func TestBreakersFallback(t *testing.T) {
}, func(err error) error { }, func(err error) error {
return nil return nil
}) })
assert.True(t, err == nil || err == errDummy) assert.True(t, err == nil || errors.Is(err, errDummy))
} err = DoWithFallbackCtx(context.Background(), "fallback", func() error {
verify(t, func() bool { return errDummy
return ErrServiceUnavailable == Do("fallback", func() error { }, func(err error) error {
return nil return nil
}) })
assert.True(t, err == nil || errors.Is(err, errDummy))
}
verify(t, func() bool {
return errors.Is(Do("fallback", func() error {
return nil
}), ErrServiceUnavailable)
}) })
} }
func TestBreakersAcceptableFallback(t *testing.T) { func TestBreakersAcceptableFallback(t *testing.T) {
errDummy := errors.New("any") errDummy := errors.New("any")
for i := 0; i < 10000; i++ { for i := 0; i < 5000; i++ {
err := DoWithFallbackAcceptable("acceptablefallback", func() error { err := DoWithFallbackAcceptable("acceptablefallback", func() error {
return errDummy return errDummy
}, func(err error) error { }, func(err error) error {
@@ -94,12 +111,20 @@ func TestBreakersAcceptableFallback(t *testing.T) {
}, func(err error) bool { }, func(err error) bool {
return err == nil return err == nil
}) })
assert.True(t, err == nil || err == errDummy) assert.True(t, err == nil || errors.Is(err, errDummy))
err = DoWithFallbackAcceptableCtx(context.Background(), "acceptablefallback", func() error {
return errDummy
}, func(err error) error {
return nil
}, func(err error) bool {
return err == nil
})
assert.True(t, err == nil || errors.Is(err, errDummy))
} }
verify(t, func() bool { verify(t, func() bool {
return ErrServiceUnavailable == Do("acceptablefallback", func() error { return errors.Is(Do("acceptablefallback", func() error {
return nil return nil
}) }), ErrServiceUnavailable)
}) })
} }
@@ -110,5 +135,5 @@ func verify(t *testing.T, fn func() bool) {
count++ count++
} }
} }
assert.True(t, count >= 80, fmt.Sprintf("should be greater than 80, actual %d", count)) assert.True(t, count >= 75, fmt.Sprintf("should be greater than 75, actual %d", count))
} }

48
core/breaker/bucket.go Normal file
View File

@@ -0,0 +1,48 @@
package breaker
const (
success = iota
fail
drop
)
// bucket defines the bucket that holds sum and num of additions.
type bucket struct {
Sum int64
Success int64
Failure int64
Drop int64
}
func (b *bucket) Add(v int64) {
switch v {
case fail:
b.fail()
case drop:
b.drop()
default:
b.succeed()
}
}
func (b *bucket) Reset() {
b.Sum = 0
b.Success = 0
b.Failure = 0
b.Drop = 0
}
func (b *bucket) drop() {
b.Sum++
b.Drop++
}
func (b *bucket) fail() {
b.Sum++
b.Failure++
}
func (b *bucket) succeed() {
b.Sum++
b.Success++
}

View File

@@ -0,0 +1,43 @@
package breaker
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestBucketAdd(t *testing.T) {
b := &bucket{}
// Test succeed
b.Add(0) // Using 0 for success
assert.Equal(t, int64(1), b.Sum, "Sum should be incremented")
assert.Equal(t, int64(1), b.Success, "Success should be incremented")
assert.Equal(t, int64(0), b.Failure, "Failure should not be incremented")
assert.Equal(t, int64(0), b.Drop, "Drop should not be incremented")
// Test failure
b.Add(fail)
assert.Equal(t, int64(2), b.Sum, "Sum should be incremented")
assert.Equal(t, int64(1), b.Failure, "Failure should be incremented")
assert.Equal(t, int64(0), b.Drop, "Drop should not be incremented")
// Test drop
b.Add(drop)
assert.Equal(t, int64(3), b.Sum, "Sum should be incremented")
assert.Equal(t, int64(1), b.Drop, "Drop should be incremented")
}
func TestBucketReset(t *testing.T) {
b := &bucket{
Sum: 3,
Success: 1,
Failure: 1,
Drop: 1,
}
b.Reset()
assert.Equal(t, int64(0), b.Sum, "Sum should be reset to 0")
assert.Equal(t, int64(0), b.Success, "Success should be reset to 0")
assert.Equal(t, int64(0), b.Failure, "Failure should be reset to 0")
assert.Equal(t, int64(0), b.Drop, "Drop should be reset to 0")
}

View File

@@ -1,57 +1,87 @@
package breaker package breaker
import ( import (
"math"
"time" "time"
"github.com/zeromicro/go-zero/core/collection" "github.com/zeromicro/go-zero/core/collection"
"github.com/zeromicro/go-zero/core/mathx" "github.com/zeromicro/go-zero/core/mathx"
"github.com/zeromicro/go-zero/core/syncx"
"github.com/zeromicro/go-zero/core/timex"
) )
const ( const (
// 250ms for bucket duration // 250ms for bucket duration
window = time.Second * 10 window = time.Second * 10
buckets = 40 buckets = 40
forcePassDuration = time.Second
k = 1.5 k = 1.5
minK = 1.1
protection = 5 protection = 5
) )
// googleBreaker is a netflixBreaker pattern from google. // googleBreaker is a netflixBreaker pattern from google.
// see Client-Side Throttling section in https://landing.google.com/sre/sre-book/chapters/handling-overload/ // see Client-Side Throttling section in https://landing.google.com/sre/sre-book/chapters/handling-overload/
type googleBreaker struct { type (
googleBreaker struct {
k float64 k float64
stat *collection.RollingWindow stat *collection.RollingWindow[int64, *bucket]
proba *mathx.Proba proba *mathx.Proba
} lastPass *syncx.AtomicDuration
}
windowResult struct {
accepts int64
total int64
failingBuckets int64
workingBuckets int64
}
)
func newGoogleBreaker() *googleBreaker { func newGoogleBreaker() *googleBreaker {
bucketDuration := time.Duration(int64(window) / int64(buckets)) bucketDuration := time.Duration(int64(window) / int64(buckets))
st := collection.NewRollingWindow(buckets, bucketDuration) st := collection.NewRollingWindow[int64, *bucket](func() *bucket {
return new(bucket)
}, buckets, bucketDuration)
return &googleBreaker{ return &googleBreaker{
stat: st, stat: st,
k: k, k: k,
proba: mathx.NewProba(), proba: mathx.NewProba(),
lastPass: syncx.NewAtomicDuration(),
} }
} }
func (b *googleBreaker) accept() error { func (b *googleBreaker) accept() error {
accepts, total := b.history() var w float64
weightedAccepts := b.k * float64(accepts) history := b.history()
w = b.k - (b.k-minK)*float64(history.failingBuckets)/buckets
weightedAccepts := mathx.AtLeast(w, minK) * float64(history.accepts)
// https://landing.google.com/sre/sre-book/chapters/handling-overload/#eq2101 // https://landing.google.com/sre/sre-book/chapters/handling-overload/#eq2101
dropRatio := math.Max(0, (float64(total-protection)-weightedAccepts)/float64(total+1)) // for better performance, no need to care about the negative ratio
dropRatio := (float64(history.total-protection) - weightedAccepts) / float64(history.total+1)
if dropRatio <= 0 { if dropRatio <= 0 {
return nil return nil
} }
lastPass := b.lastPass.Load()
if lastPass > 0 && timex.Since(lastPass) > forcePassDuration {
b.lastPass.Set(timex.Now())
return nil
}
dropRatio *= float64(buckets-history.workingBuckets) / buckets
if b.proba.TrueOnProba(dropRatio) { if b.proba.TrueOnProba(dropRatio) {
return ErrServiceUnavailable return ErrServiceUnavailable
} }
b.lastPass.Set(timex.Now())
return nil return nil
} }
func (b *googleBreaker) allow() (internalPromise, error) { func (b *googleBreaker) allow() (internalPromise, error) {
if err := b.accept(); err != nil { if err := b.accept(); err != nil {
b.markDrop()
return nil, err return nil, err
} }
@@ -60,8 +90,9 @@ func (b *googleBreaker) allow() (internalPromise, error) {
}, nil }, nil
} }
func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { func (b *googleBreaker) doReq(req func() error, fallback Fallback, acceptable Acceptable) error {
if err := b.accept(); err != nil { if err := b.accept(); err != nil {
b.markDrop()
if fallback != nil { if fallback != nil {
return fallback(err) return fallback(err)
} }
@@ -69,38 +100,55 @@ func (b *googleBreaker) doReq(req func() error, fallback func(err error) error,
return err return err
} }
var succ bool
defer func() { defer func() {
if e := recover(); e != nil { // if req() panic, success is false, mark as failure
if succ {
b.markSuccess()
} else {
b.markFailure() b.markFailure()
panic(e)
} }
}() }()
err := req() err := req()
if acceptable(err) { if acceptable(err) {
b.markSuccess() succ = true
} else {
b.markFailure()
} }
return err return err
} }
func (b *googleBreaker) markSuccess() { func (b *googleBreaker) markDrop() {
b.stat.Add(1) b.stat.Add(drop)
} }
func (b *googleBreaker) markFailure() { func (b *googleBreaker) markFailure() {
b.stat.Add(0) b.stat.Add(fail)
} }
func (b *googleBreaker) history() (accepts, total int64) { func (b *googleBreaker) markSuccess() {
b.stat.Reduce(func(b *collection.Bucket) { b.stat.Add(success)
accepts += int64(b.Sum) }
total += b.Count
func (b *googleBreaker) history() windowResult {
var result windowResult
b.stat.Reduce(func(b *bucket) {
result.accepts += b.Success
result.total += b.Sum
if b.Failure > 0 {
result.workingBuckets = 0
} else if b.Success > 0 {
result.workingBuckets++
}
if b.Success > 0 {
result.failingBuckets = 0
} else if b.Failure > 0 {
result.failingBuckets++
}
}) })
return return result
} }
type googlePromise struct { type googlePromise struct {

View File

@@ -10,6 +10,7 @@ import (
"github.com/zeromicro/go-zero/core/collection" "github.com/zeromicro/go-zero/core/collection"
"github.com/zeromicro/go-zero/core/mathx" "github.com/zeromicro/go-zero/core/mathx"
"github.com/zeromicro/go-zero/core/stat" "github.com/zeromicro/go-zero/core/stat"
"github.com/zeromicro/go-zero/core/syncx"
) )
const ( const (
@@ -22,11 +23,14 @@ func init() {
} }
func getGoogleBreaker() *googleBreaker { func getGoogleBreaker() *googleBreaker {
st := collection.NewRollingWindow(testBuckets, testInterval) st := collection.NewRollingWindow[int64, *bucket](func() *bucket {
return new(bucket)
}, testBuckets, testInterval)
return &googleBreaker{ return &googleBreaker{
stat: st, stat: st,
k: 5, k: 5,
proba: mathx.NewProba(), proba: mathx.NewProba(),
lastPass: syncx.NewAtomicDuration(),
} }
} }
@@ -63,6 +67,33 @@ func TestGoogleBreakerOpen(t *testing.T) {
}) })
} }
func TestGoogleBreakerRecover(t *testing.T) {
st := collection.NewRollingWindow[int64, *bucket](func() *bucket {
return new(bucket)
}, testBuckets*2, testInterval)
b := &googleBreaker{
stat: st,
k: k,
proba: mathx.NewProba(),
lastPass: syncx.NewAtomicDuration(),
}
for i := 0; i < testBuckets; i++ {
for j := 0; j < 100; j++ {
b.stat.Add(1)
}
time.Sleep(testInterval)
}
for i := 0; i < testBuckets; i++ {
for j := 0; j < 100; j++ {
b.stat.Add(0)
}
time.Sleep(testInterval)
}
verify(t, func() bool {
return b.accept() == nil
})
}
func TestGoogleBreakerFallback(t *testing.T) { func TestGoogleBreakerFallback(t *testing.T) {
b := getGoogleBreaker() b := getGoogleBreaker()
markSuccess(b, 1) markSuccess(b, 1)
@@ -89,13 +120,50 @@ func TestGoogleBreakerReject(t *testing.T) {
}, nil, defaultAcceptable)) }, nil, defaultAcceptable))
} }
func TestGoogleBreakerMoreFallingBuckets(t *testing.T) {
t.Parallel()
t.Run("more falling buckets", func(t *testing.T) {
b := getGoogleBreaker()
func() {
stopChan := time.After(testInterval * 6)
for {
time.Sleep(time.Millisecond)
select {
case <-stopChan:
return
default:
assert.Error(t, b.doReq(func() error {
return errors.New("foo")
}, func(err error) error {
return err
}, func(err error) bool {
return err == nil
}))
}
}
}()
var count int
for i := 0; i < 100; i++ {
if errors.Is(b.doReq(func() error {
return ErrServiceUnavailable
}, nil, defaultAcceptable), ErrServiceUnavailable) {
count++
}
}
assert.True(t, count > 90)
})
}
func TestGoogleBreakerAcceptable(t *testing.T) { func TestGoogleBreakerAcceptable(t *testing.T) {
b := getGoogleBreaker() b := getGoogleBreaker()
errAcceptable := errors.New("any") errAcceptable := errors.New("any")
assert.Equal(t, errAcceptable, b.doReq(func() error { assert.Equal(t, errAcceptable, b.doReq(func() error {
return errAcceptable return errAcceptable
}, nil, func(err error) bool { }, nil, func(err error) bool {
return err == errAcceptable return errors.Is(err, errAcceptable)
})) }))
} }
@@ -105,7 +173,7 @@ func TestGoogleBreakerNotAcceptable(t *testing.T) {
assert.Equal(t, errAcceptable, b.doReq(func() error { assert.Equal(t, errAcceptable, b.doReq(func() error {
return errAcceptable return errAcceptable
}, nil, func(err error) bool { }, nil, func(err error) bool {
return err != errAcceptable return !errors.Is(err, errAcceptable)
})) }))
} }
@@ -164,41 +232,38 @@ func TestGoogleBreakerSelfProtection(t *testing.T) {
} }
func TestGoogleBreakerHistory(t *testing.T) { func TestGoogleBreakerHistory(t *testing.T) {
var b *googleBreaker
var accepts, total int64
sleep := testInterval sleep := testInterval
t.Run("accepts == total", func(t *testing.T) { t.Run("accepts == total", func(t *testing.T) {
b = getGoogleBreaker() b := getGoogleBreaker()
markSuccessWithDuration(b, 10, sleep/2) markSuccessWithDuration(b, 10, sleep/2)
accepts, total = b.history() result := b.history()
assert.Equal(t, int64(10), accepts) assert.Equal(t, int64(10), result.accepts)
assert.Equal(t, int64(10), total) assert.Equal(t, int64(10), result.total)
}) })
t.Run("fail == total", func(t *testing.T) { t.Run("fail == total", func(t *testing.T) {
b = getGoogleBreaker() b := getGoogleBreaker()
markFailedWithDuration(b, 10, sleep/2) markFailedWithDuration(b, 10, sleep/2)
accepts, total = b.history() result := b.history()
assert.Equal(t, int64(0), accepts) assert.Equal(t, int64(0), result.accepts)
assert.Equal(t, int64(10), total) assert.Equal(t, int64(10), result.total)
}) })
t.Run("accepts = 1/2 * total, fail = 1/2 * total", func(t *testing.T) { t.Run("accepts = 1/2 * total, fail = 1/2 * total", func(t *testing.T) {
b = getGoogleBreaker() b := getGoogleBreaker()
markFailedWithDuration(b, 5, sleep/2) markFailedWithDuration(b, 5, sleep/2)
markSuccessWithDuration(b, 5, sleep/2) markSuccessWithDuration(b, 5, sleep/2)
accepts, total = b.history() result := b.history()
assert.Equal(t, int64(5), accepts) assert.Equal(t, int64(5), result.accepts)
assert.Equal(t, int64(10), total) assert.Equal(t, int64(10), result.total)
}) })
t.Run("auto reset rolling counter", func(t *testing.T) { t.Run("auto reset rolling counter", func(t *testing.T) {
b = getGoogleBreaker() b := getGoogleBreaker()
time.Sleep(testInterval * testBuckets) time.Sleep(testInterval * testBuckets)
accepts, total = b.history() result := b.history()
assert.Equal(t, int64(0), accepts) assert.Equal(t, int64(0), result.accepts)
assert.Equal(t, int64(0), total) assert.Equal(t, int64(0), result.total)
}) })
} }
@@ -206,7 +271,7 @@ func BenchmarkGoogleBreakerAllow(b *testing.B) {
breaker := getGoogleBreaker() breaker := getGoogleBreaker()
b.ResetTimer() b.ResetTimer()
for i := 0; i <= b.N; i++ { for i := 0; i <= b.N; i++ {
breaker.accept() _ = breaker.accept()
if i%2 == 0 { if i%2 == 0 {
breaker.markSuccess() breaker.markSuccess()
} else { } else {
@@ -215,6 +280,16 @@ func BenchmarkGoogleBreakerAllow(b *testing.B) {
} }
} }
func BenchmarkGoogleBreakerDoReq(b *testing.B) {
breaker := getGoogleBreaker()
b.ResetTimer()
for i := 0; i <= b.N; i++ {
_ = breaker.doReq(func() error {
return nil
}, nil, defaultAcceptable)
}
}
func markSuccess(b *googleBreaker, count int) { func markSuccess(b *googleBreaker, count int) {
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
p, err := b.allow() p, err := b.allow()

View File

@@ -1,35 +1,58 @@
package breaker package breaker
const noOpBreakerName = "nopBreaker" import "context"
type noOpBreaker struct{} const nopBreakerName = "nopBreaker"
func newNoOpBreaker() Breaker { type nopBreaker struct{}
return noOpBreaker{}
// NopBreaker returns a breaker that never trigger breaker circuit.
func NopBreaker() Breaker {
return nopBreaker{}
} }
func (b noOpBreaker) Name() string { func (b nopBreaker) Name() string {
return noOpBreakerName return nopBreakerName
} }
func (b noOpBreaker) Allow() (Promise, error) { func (b nopBreaker) Allow() (Promise, error) {
return nopPromise{}, nil return nopPromise{}, nil
} }
func (b noOpBreaker) Do(req func() error) error { func (b nopBreaker) AllowCtx(_ context.Context) (Promise, error) {
return nopPromise{}, nil
}
func (b nopBreaker) Do(req func() error) error {
return req() return req()
} }
func (b noOpBreaker) DoWithAcceptable(req func() error, acceptable Acceptable) error { func (b nopBreaker) DoCtx(_ context.Context, req func() error) error {
return req() return req()
} }
func (b noOpBreaker) DoWithFallback(req func() error, fallback func(err error) error) error { func (b nopBreaker) DoWithAcceptable(req func() error, _ Acceptable) error {
return req() return req()
} }
func (b noOpBreaker) DoWithFallbackAcceptable(req func() error, fallback func(err error) error, func (b nopBreaker) DoWithAcceptableCtx(_ context.Context, req func() error, _ Acceptable) error {
acceptable Acceptable) error { return req()
}
func (b nopBreaker) DoWithFallback(req func() error, _ Fallback) error {
return req()
}
func (b nopBreaker) DoWithFallbackCtx(_ context.Context, req func() error, _ Fallback) error {
return req()
}
func (b nopBreaker) DoWithFallbackAcceptable(req func() error, _ Fallback, _ Acceptable) error {
return req()
}
func (b nopBreaker) DoWithFallbackAcceptableCtx(_ context.Context, req func() error,
_ Fallback, _ Acceptable) error {
return req() return req()
} }
@@ -38,5 +61,5 @@ type nopPromise struct{}
func (p nopPromise) Accept() { func (p nopPromise) Accept() {
} }
func (p nopPromise) Reject(reason string) { func (p nopPromise) Reject(_ string) {
} }

View File

@@ -1,6 +1,7 @@
package breaker package breaker
import ( import (
"context"
"errors" "errors"
"testing" "testing"
@@ -8,9 +9,11 @@ import (
) )
func TestNopBreaker(t *testing.T) { func TestNopBreaker(t *testing.T) {
b := newNoOpBreaker() b := NopBreaker()
assert.Equal(t, noOpBreakerName, b.Name()) assert.Equal(t, nopBreakerName, b.Name())
p, err := b.Allow() _, err := b.Allow()
assert.Nil(t, err)
p, err := b.AllowCtx(context.Background())
assert.Nil(t, err) assert.Nil(t, err)
p.Accept() p.Accept()
for i := 0; i < 1000; i++ { for i := 0; i < 1000; i++ {
@@ -21,18 +24,34 @@ func TestNopBreaker(t *testing.T) {
assert.Nil(t, b.Do(func() error { assert.Nil(t, b.Do(func() error {
return nil return nil
})) }))
assert.Nil(t, b.DoCtx(context.Background(), func() error {
return nil
}))
assert.Nil(t, b.DoWithAcceptable(func() error { assert.Nil(t, b.DoWithAcceptable(func() error {
return nil return nil
}, defaultAcceptable)) }, defaultAcceptable))
assert.Nil(t, b.DoWithAcceptableCtx(context.Background(), func() error {
return nil
}, defaultAcceptable))
errDummy := errors.New("any") errDummy := errors.New("any")
assert.Equal(t, errDummy, b.DoWithFallback(func() error { assert.Equal(t, errDummy, b.DoWithFallback(func() error {
return errDummy return errDummy
}, func(err error) error { }, func(err error) error {
return nil return nil
})) }))
assert.Equal(t, errDummy, b.DoWithFallbackCtx(context.Background(), func() error {
return errDummy
}, func(err error) error {
return nil
}))
assert.Equal(t, errDummy, b.DoWithFallbackAcceptable(func() error { assert.Equal(t, errDummy, b.DoWithFallbackAcceptable(func() error {
return errDummy return errDummy
}, func(err error) error { }, func(err error) error {
return nil return nil
}, defaultAcceptable)) }, defaultAcceptable))
assert.Equal(t, errDummy, b.DoWithFallbackAcceptableCtx(context.Background(), func() error {
return errDummy
}, func(err error) error {
return nil
}, defaultAcceptable))
} }

View File

@@ -6,8 +6,6 @@ import (
"crypto/cipher" "crypto/cipher"
"encoding/base64" "encoding/base64"
"errors" "errors"
"github.com/zeromicro/go-zero/core/logx"
) )
// ErrPaddingSize indicates bad padding size. // ErrPaddingSize indicates bad padding size.
@@ -27,22 +25,23 @@ func newECB(b cipher.Block) *ecb {
type ecbEncrypter ecb type ecbEncrypter ecb
// NewECBEncrypter returns an ECB encrypter. // Deprecated: NewECBEncrypter returns an ECB encrypter.
// ECB mode is insecure for multi-block data. Use AES-GCM instead.
func NewECBEncrypter(b cipher.Block) cipher.BlockMode { func NewECBEncrypter(b cipher.Block) cipher.BlockMode {
return (*ecbEncrypter)(newECB(b)) return (*ecbEncrypter)(newECB(b))
} }
// BlockSize returns the mode's block size.
func (x *ecbEncrypter) BlockSize() int { return x.blockSize } func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
// why we don't return error is because cipher.BlockMode doesn't allow this // CryptBlocks encrypts a number of blocks. The length of src must be a multiple of
// the block size. Dst and src must overlap entirely or not at all.
func (x *ecbEncrypter) CryptBlocks(dst, src []byte) { func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
if len(src)%x.blockSize != 0 { if len(src)%x.blockSize != 0 {
logx.Error("crypto/cipher: input not full blocks") panic("crypto/cipher: input not full blocks")
return
} }
if len(dst) < len(src) { if len(dst) < len(src) {
logx.Error("crypto/cipher: output smaller than input") panic("crypto/cipher: output smaller than input")
return
} }
for len(src) > 0 { for len(src) > 0 {
@@ -54,24 +53,25 @@ func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
type ecbDecrypter ecb type ecbDecrypter ecb
// NewECBDecrypter returns an ECB decrypter. // Deprecated: NewECBDecrypter returns an ECB decrypter.
// ECB mode is insecure for multi-block data. Use AES-GCM instead.
func NewECBDecrypter(b cipher.Block) cipher.BlockMode { func NewECBDecrypter(b cipher.Block) cipher.BlockMode {
return (*ecbDecrypter)(newECB(b)) return (*ecbDecrypter)(newECB(b))
} }
// BlockSize returns the mode's block size.
func (x *ecbDecrypter) BlockSize() int { func (x *ecbDecrypter) BlockSize() int {
return x.blockSize return x.blockSize
} }
// why we don't return error is because cipher.BlockMode doesn't allow this // CryptBlocks decrypts a number of blocks. The length of src must be a multiple of
// the block size. Dst and src must overlap entirely or not at all.
func (x *ecbDecrypter) CryptBlocks(dst, src []byte) { func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
if len(src)%x.blockSize != 0 { if len(src)%x.blockSize != 0 {
logx.Error("crypto/cipher: input not full blocks") panic("crypto/cipher: input not full blocks")
return
} }
if len(dst) < len(src) { if len(dst) < len(src) {
logx.Error("crypto/cipher: output smaller than input") panic("crypto/cipher: output smaller than input")
return
} }
for len(src) > 0 { for len(src) > 0 {
@@ -81,14 +81,18 @@ func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
} }
} }
// EcbDecrypt decrypts src with the given key. // Deprecated: EcbDecrypt decrypts src with the given key.
// ECB mode is insecure for multi-block data. Use AES-GCM instead.
func EcbDecrypt(key, src []byte) ([]byte, error) { func EcbDecrypt(key, src []byte) ([]byte, error) {
block, err := aes.NewCipher(key) block, err := aes.NewCipher(key)
if err != nil { if err != nil {
logx.Errorf("Decrypt key error: % x", key)
return nil, err return nil, err
} }
if len(src)%block.BlockSize() != 0 {
return nil, ErrPaddingSize
}
decrypter := NewECBDecrypter(block) decrypter := NewECBDecrypter(block)
decrypted := make([]byte, len(src)) decrypted := make([]byte, len(src))
decrypter.CryptBlocks(decrypted, src) decrypter.CryptBlocks(decrypted, src)
@@ -96,8 +100,9 @@ func EcbDecrypt(key, src []byte) ([]byte, error) {
return pkcs5Unpadding(decrypted, decrypter.BlockSize()) return pkcs5Unpadding(decrypted, decrypter.BlockSize())
} }
// EcbDecryptBase64 decrypts base64 encoded src with the given base64 encoded key. // Deprecated: EcbDecryptBase64 decrypts base64 encoded src with the given base64 encoded key.
// The returned string is also base64 encoded. // The returned string is also base64 encoded.
// ECB mode is insecure for multi-block data. Use AES-GCM instead.
func EcbDecryptBase64(key, src string) (string, error) { func EcbDecryptBase64(key, src string) (string, error) {
keyBytes, err := getKeyBytes(key) keyBytes, err := getKeyBytes(key)
if err != nil { if err != nil {
@@ -117,11 +122,11 @@ func EcbDecryptBase64(key, src string) (string, error) {
return base64.StdEncoding.EncodeToString(decryptedBytes), nil return base64.StdEncoding.EncodeToString(decryptedBytes), nil
} }
// EcbEncrypt encrypts src with the given key. // Deprecated: EcbEncrypt encrypts src with the given key.
// ECB mode is insecure for multi-block data. Use AES-GCM instead.
func EcbEncrypt(key, src []byte) ([]byte, error) { func EcbEncrypt(key, src []byte) ([]byte, error) {
block, err := aes.NewCipher(key) block, err := aes.NewCipher(key)
if err != nil { if err != nil {
logx.Errorf("Encrypt key error: % x", key)
return nil, err return nil, err
} }
@@ -133,8 +138,9 @@ func EcbEncrypt(key, src []byte) ([]byte, error) {
return crypted, nil return crypted, nil
} }
// EcbEncryptBase64 encrypts base64 encoded src with the given base64 encoded key. // Deprecated: EcbEncryptBase64 encrypts base64 encoded src with the given base64 encoded key.
// The returned string is also base64 encoded. // The returned string is also base64 encoded.
// ECB mode is insecure for multi-block data. Use AES-GCM instead.
func EcbEncryptBase64(key, src string) (string, error) { func EcbEncryptBase64(key, src string) (string, error) {
keyBytes, err := getKeyBytes(key) keyBytes, err := getKeyBytes(key)
if err != nil { if err != nil {
@@ -175,10 +181,20 @@ func pkcs5Padding(ciphertext []byte, blockSize int) []byte {
func pkcs5Unpadding(src []byte, blockSize int) ([]byte, error) { func pkcs5Unpadding(src []byte, blockSize int) ([]byte, error) {
length := len(src) length := len(src)
unpadding := int(src[length-1]) if length == 0 {
if unpadding >= length || unpadding > blockSize {
return nil, ErrPaddingSize return nil, ErrPaddingSize
} }
unpadding := int(src[length-1])
if unpadding < 1 || unpadding > blockSize || unpadding > length {
return nil, ErrPaddingSize
}
for _, b := range src[length-unpadding:] {
if int(b) != unpadding {
return nil, ErrPaddingSize
}
}
return src[:length-unpadding], nil return src[:length-unpadding], nil
} }

View File

@@ -1,6 +1,7 @@
package codec package codec
import ( import (
"crypto/aes"
"encoding/base64" "encoding/base64"
"testing" "testing"
@@ -10,7 +11,8 @@ import (
func TestAesEcb(t *testing.T) { func TestAesEcb(t *testing.T) {
var ( var (
key = []byte("q4t7w!z%C*F-JaNdRgUjXn2r5u8x/A?D") key = []byte("q4t7w!z%C*F-JaNdRgUjXn2r5u8x/A?D")
val = []byte("hello") val = []byte("helloworld")
valLong = []byte("helloworldlong..")
badKey1 = []byte("aaaaaaaaa") badKey1 = []byte("aaaaaaaaa")
// more than 32 chars // more than 32 chars
badKey2 = []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") badKey2 = []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
@@ -26,13 +28,41 @@ func TestAesEcb(t *testing.T) {
_, err = EcbDecrypt(badKey2, dst) _, err = EcbDecrypt(badKey2, dst)
assert.NotNil(t, err) assert.NotNil(t, err)
_, err = EcbDecrypt(key, val) _, err = EcbDecrypt(key, val)
// not enough block, just nil // not a multiple of block size
assert.Nil(t, err) assert.NotNil(t, err)
src, err := EcbDecrypt(key, dst) src, err := EcbDecrypt(key, dst)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, val, src) assert.Equal(t, val, src)
} block, err := aes.NewCipher(key)
assert.NoError(t, err)
encrypter := NewECBEncrypter(block)
assert.Equal(t, 16, encrypter.BlockSize())
decrypter := NewECBDecrypter(block)
assert.Equal(t, 16, decrypter.BlockSize())
dst = make([]byte, 8)
assert.Panics(t, func() {
encrypter.CryptBlocks(dst, val)
})
dst = make([]byte, 8)
assert.Panics(t, func() {
encrypter.CryptBlocks(dst, valLong)
})
dst = make([]byte, 8)
assert.Panics(t, func() {
decrypter.CryptBlocks(dst, val)
})
dst = make([]byte, 8)
assert.Panics(t, func() {
decrypter.CryptBlocks(dst, valLong)
})
_, err = EcbEncryptBase64("cTR0N3dDKkYtSmFOZFJnVWpYbjJyNXU4eC9BP0QK", "aGVsbG93b3JsZGxvbmcuLgo=")
assert.Error(t, err)
}
func TestAesEcbBase64(t *testing.T) { func TestAesEcbBase64(t *testing.T) {
const ( const (
val = "hello" val = "hello"
@@ -63,3 +93,44 @@ func TestAesEcbBase64(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, val, string(b)) assert.Equal(t, val, string(b))
} }
func TestPkcs5UnpaddingEmptyInput(t *testing.T) {
_, err := pkcs5Unpadding([]byte{}, 16)
assert.Equal(t, ErrPaddingSize, err)
}
func TestPkcs5UnpaddingMalformedPadding(t *testing.T) {
// Valid PKCS5 padding of 3: last 3 bytes should all be 0x03
// Here we corrupt one padding byte
malformed := []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x02, 0x03, 0x03}
_, err := pkcs5Unpadding(malformed, 16)
assert.Equal(t, ErrPaddingSize, err)
// All padding bytes correct
valid := []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x03, 0x03, 0x03}
result, err := pkcs5Unpadding(valid, 16)
assert.NoError(t, err)
assert.Equal(t, valid[:13], result)
}
func TestPkcs5UnpaddingInvalidPaddingValue(t *testing.T) {
// padding value = 0 (< 1)
_, err := pkcs5Unpadding([]byte{0x41, 0x00}, 16)
assert.Equal(t, ErrPaddingSize, err)
// padding value > blockSize
_, err = pkcs5Unpadding([]byte{0x41, 0x41, 0x41, 0x41, 17}, 4)
assert.Equal(t, ErrPaddingSize, err)
// padding value > length
_, err = pkcs5Unpadding([]byte{0x41, 0x03}, 16)
assert.Equal(t, ErrPaddingSize, err)
}
func TestEcbDecryptEmptyInput(t *testing.T) {
key := []byte("q4t7w!z%C*F-JaNdRgUjXn2r5u8x/A?D")
_, err := EcbDecrypt(key, []byte{})
assert.Equal(t, ErrPaddingSize, err)
}

View File

@@ -23,7 +23,7 @@ var (
zero = big.NewInt(0) zero = big.NewInt(0)
) )
// DhKey defines the Diffie Hellman key. // DhKey defines the Diffie-Hellman key.
type DhKey struct { type DhKey struct {
PriKey *big.Int PriKey *big.Int
PubKey *big.Int PubKey *big.Int
@@ -35,7 +35,7 @@ func ComputeKey(pubKey, priKey *big.Int) (*big.Int, error) {
return nil, ErrInvalidPubKey return nil, ErrInvalidPubKey
} }
if pubKey.Sign() <= 0 && p.Cmp(pubKey) <= 0 { if pubKey.Sign() <= 0 || p.Cmp(pubKey) <= 0 {
return nil, ErrPubKeyOutOfBound return nil, ErrPubKeyOutOfBound
} }
@@ -46,7 +46,7 @@ func ComputeKey(pubKey, priKey *big.Int) (*big.Int, error) {
return new(big.Int).Exp(pubKey, priKey, p), nil return new(big.Int).Exp(pubKey, priKey, p), nil
} }
// GenerateKey returns a Diffie Hellman key. // GenerateKey returns a Diffie-Hellman key.
func GenerateKey() (*DhKey, error) { func GenerateKey() (*DhKey, error) {
var err error var err error
var x *big.Int var x *big.Int

View File

@@ -80,3 +80,46 @@ func TestKeyBytes(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, len(key.Bytes()) > 0) assert.True(t, len(key.Bytes()) > 0)
} }
func TestDHOnErrors(t *testing.T) {
key, err := GenerateKey()
assert.Nil(t, err)
assert.NotEmpty(t, key.Bytes())
_, err = ComputeKey(key.PubKey, key.PriKey)
assert.NoError(t, err)
_, err = ComputeKey(nil, key.PriKey)
assert.Error(t, err)
_, err = ComputeKey(key.PubKey, nil)
assert.Error(t, err)
assert.NotNil(t, NewPublicKey([]byte("")))
}
func TestDHPubKeyBoundary(t *testing.T) {
key, err := GenerateKey()
assert.Nil(t, err)
// pubKey = 0 should be rejected
_, err = ComputeKey(big.NewInt(0), key.PriKey)
assert.ErrorIs(t, err, ErrPubKeyOutOfBound)
// pubKey = -1 should be rejected
_, err = ComputeKey(big.NewInt(-1), key.PriKey)
assert.ErrorIs(t, err, ErrPubKeyOutOfBound)
// pubKey = p should be rejected
_, err = ComputeKey(new(big.Int).Set(p), key.PriKey)
assert.ErrorIs(t, err, ErrPubKeyOutOfBound)
// pubKey = p+1 should be rejected
_, err = ComputeKey(new(big.Int).Add(p, big.NewInt(1)), key.PriKey)
assert.ErrorIs(t, err, ErrPubKeyOutOfBound)
// pubKey = 1 should be accepted
_, err = ComputeKey(big.NewInt(1), key.PriKey)
assert.NoError(t, err)
// pubKey = p-1 should be accepted
_, err = ComputeKey(new(big.Int).Sub(p, big.NewInt(1)), key.PriKey)
assert.NoError(t, err)
}

View File

@@ -2,6 +2,8 @@ package codec
import ( import (
"bytes" "bytes"
"compress/gzip"
"errors"
"fmt" "fmt"
"testing" "testing"
@@ -21,3 +23,45 @@ func TestGzip(t *testing.T) {
assert.True(t, len(bs) < buf.Len()) assert.True(t, len(bs) < buf.Len())
assert.Equal(t, buf.Bytes(), actual) assert.Equal(t, buf.Bytes(), actual)
} }
func TestGunzip(t *testing.T) {
tests := []struct {
name string
input []byte
expected []byte
expectedErr error
}{
{
name: "valid input",
input: func() []byte {
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
gz.Write([]byte("hello"))
gz.Close()
return buf.Bytes()
}(),
expected: []byte("hello"),
expectedErr: nil,
},
{
name: "invalid input",
input: []byte("invalid input"),
expected: nil,
expectedErr: gzip.ErrHeader,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result, err := Gunzip(test.input)
if !bytes.Equal(result, test.expected) {
t.Errorf("unexpected result: %v", result)
}
if !errors.Is(err, test.expectedErr) {
t.Errorf("unexpected error: %v", err)
}
})
}
}

View File

@@ -3,6 +3,7 @@ package codec
import ( import (
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/pem" "encoding/pem"
@@ -46,7 +47,9 @@ type (
} }
) )
// NewRsaDecrypter returns a RsaDecrypter with the given file. // Deprecated: NewRsaDecrypter returns a RsaDecrypter with the given file.
// PKCS#1 v1.5 padding is vulnerable to padding oracle attacks.
// Use NewRsaOAEPDecrypter instead.
func NewRsaDecrypter(file string) (RsaDecrypter, error) { func NewRsaDecrypter(file string) (RsaDecrypter, error) {
content, err := os.ReadFile(file) content, err := os.ReadFile(file)
if err != nil { if err != nil {
@@ -90,7 +93,9 @@ func (r *rsaDecrypter) DecryptBase64(input string) ([]byte, error) {
return r.Decrypt(base64Decoded) return r.Decrypt(base64Decoded)
} }
// NewRsaEncrypter returns a RsaEncrypter with the given key. // Deprecated: NewRsaEncrypter returns a RsaEncrypter with the given key.
// PKCS#1 v1.5 padding is vulnerable to padding oracle attacks.
// Use NewRsaOAEPEncrypter instead.
func NewRsaEncrypter(key []byte) (RsaEncrypter, error) { func NewRsaEncrypter(key []byte) (RsaEncrypter, error) {
block, _ := pem.Decode(key) block, _ := pem.Decode(key)
if block == nil { if block == nil {
@@ -154,3 +159,90 @@ func rsaDecryptBlock(privateKey *rsa.PrivateKey, block []byte) ([]byte, error) {
func rsaEncryptBlock(publicKey *rsa.PublicKey, msg []byte) ([]byte, error) { func rsaEncryptBlock(publicKey *rsa.PublicKey, msg []byte) ([]byte, error) {
return rsa.EncryptPKCS1v15(rand.Reader, publicKey, msg) return rsa.EncryptPKCS1v15(rand.Reader, publicKey, msg)
} }
// NewRsaOAEPDecrypter returns a RsaDecrypter using OAEP with SHA-256.
func NewRsaOAEPDecrypter(file string) (RsaDecrypter, error) {
content, err := os.ReadFile(file)
if err != nil {
return nil, err
}
block, _ := pem.Decode(content)
if block == nil {
return nil, ErrPrivateKey
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return &rsaOAEPDecrypter{
rsaBase: rsaBase{
bytesLimit: privateKey.N.BitLen() >> 3,
},
privateKey: privateKey,
}, nil
}
// NewRsaOAEPEncrypter returns a RsaEncrypter using OAEP with SHA-256.
func NewRsaOAEPEncrypter(key []byte) (RsaEncrypter, error) {
block, _ := pem.Decode(key)
if block == nil {
return nil, ErrPublicKey
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
switch pubKey := pub.(type) {
case *rsa.PublicKey:
// OAEP overhead: 2*hash_size + 2
hashSize := sha256.New().Size()
return &rsaOAEPEncrypter{
rsaBase: rsaBase{
bytesLimit: (pubKey.N.BitLen() >> 3) - 2*hashSize - 2,
},
publicKey: pubKey,
}, nil
default:
return nil, ErrNotRsaKey
}
}
type rsaOAEPDecrypter struct {
rsaBase
privateKey *rsa.PrivateKey
}
func (r *rsaOAEPDecrypter) Decrypt(input []byte) ([]byte, error) {
return r.crypt(input, func(block []byte) ([]byte, error) {
return rsa.DecryptOAEP(sha256.New(), rand.Reader, r.privateKey, block, nil)
})
}
func (r *rsaOAEPDecrypter) DecryptBase64(input string) ([]byte, error) {
if len(input) == 0 {
return nil, nil
}
base64Decoded, err := base64.StdEncoding.DecodeString(input)
if err != nil {
return nil, err
}
return r.Decrypt(base64Decoded)
}
type rsaOAEPEncrypter struct {
rsaBase
publicKey *rsa.PublicKey
}
func (r *rsaOAEPEncrypter) Encrypt(input []byte) ([]byte, error) {
return r.crypt(input, func(block []byte) ([]byte, error) {
return rsa.EncryptOAEP(sha256.New(), rand.Reader, r.publicKey, block, nil)
})
}

View File

@@ -1,7 +1,13 @@
package codec package codec
import ( import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/pem"
"os"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -41,6 +47,7 @@ func TestCryption(t *testing.T) {
file, err := fs.TempFilenameWithText(priKey) file, err := fs.TempFilenameWithText(priKey)
assert.Nil(t, err) assert.Nil(t, err)
defer os.Remove(file)
dec, err := NewRsaDecrypter(file) dec, err := NewRsaDecrypter(file)
assert.Nil(t, err) assert.Nil(t, err)
actual, err := dec.Decrypt(ret) actual, err := dec.Decrypt(ret)
@@ -56,3 +63,78 @@ func TestBadPubKey(t *testing.T) {
_, err := NewRsaEncrypter([]byte("foo")) _, err := NewRsaEncrypter([]byte("foo"))
assert.Equal(t, ErrPublicKey, err) assert.Equal(t, ErrPublicKey, err)
} }
func TestOAEPCryption(t *testing.T) {
enc, err := NewRsaOAEPEncrypter([]byte(pubKey))
assert.Nil(t, err)
ret, err := enc.Encrypt([]byte(testBody))
assert.Nil(t, err)
file, err := fs.TempFilenameWithText(priKey)
assert.Nil(t, err)
defer os.Remove(file)
dec, err := NewRsaOAEPDecrypter(file)
assert.Nil(t, err)
actual, err := dec.Decrypt(ret)
assert.Nil(t, err)
assert.Equal(t, testBody, string(actual))
actual, err = dec.DecryptBase64(base64.StdEncoding.EncodeToString(ret))
assert.Nil(t, err)
assert.Equal(t, testBody, string(actual))
// empty input
actual, err = dec.DecryptBase64("")
assert.Nil(t, err)
assert.Nil(t, actual)
}
func TestOAEPBadKeys(t *testing.T) {
_, err := NewRsaOAEPEncrypter([]byte("bad"))
assert.Equal(t, ErrPublicKey, err)
_, err = NewRsaOAEPDecrypter("nonexistent")
assert.Error(t, err)
// valid PEM but invalid private key content
badPem, err := fs.TempFilenameWithText("-----BEGIN RSA PRIVATE KEY-----\nYmFk\n-----END RSA PRIVATE KEY-----")
assert.Nil(t, err)
defer os.Remove(badPem)
_, err = NewRsaOAEPDecrypter(badPem)
assert.Error(t, err)
// not PEM content at all
notPem, err := fs.TempFilenameWithText("not a pem file")
assert.Nil(t, err)
defer os.Remove(notPem)
_, err = NewRsaOAEPDecrypter(notPem)
assert.Equal(t, ErrPrivateKey, err)
}
func TestOAEPEncrypterParseError(t *testing.T) {
// valid PEM block but invalid public key content
badPub := []byte("-----BEGIN PUBLIC KEY-----\nYmFk\n-----END PUBLIC KEY-----")
_, err := NewRsaOAEPEncrypter(badPub)
assert.Error(t, err)
}
func TestOAEPEncrypterNonRsaKey(t *testing.T) {
ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
assert.Nil(t, err)
derBytes, err := x509.MarshalPKIXPublicKey(&ecKey.PublicKey)
assert.Nil(t, err)
ecPem := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: derBytes})
_, err = NewRsaOAEPEncrypter(ecPem)
assert.Equal(t, ErrNotRsaKey, err)
}
func TestOAEPDecryptBase64Error(t *testing.T) {
file, err := fs.TempFilenameWithText(priKey)
assert.Nil(t, err)
defer os.Remove(file)
dec, err := NewRsaOAEPDecrypter(file)
assert.Nil(t, err)
_, err = dec.DecryptBase64("not-valid-base64!!!")
assert.Error(t, err)
}

View File

@@ -30,7 +30,7 @@ type (
Cache struct { Cache struct {
name string name string
lock sync.Mutex lock sync.Mutex
data map[string]interface{} data map[string]any
expire time.Duration expire time.Duration
timingWheel *TimingWheel timingWheel *TimingWheel
lruCache lru lruCache lru
@@ -43,7 +43,7 @@ type (
// NewCache returns a Cache with given expire. // NewCache returns a Cache with given expire.
func NewCache(expire time.Duration, opts ...CacheOption) (*Cache, error) { func NewCache(expire time.Duration, opts ...CacheOption) (*Cache, error) {
cache := &Cache{ cache := &Cache{
data: make(map[string]interface{}), data: make(map[string]any),
expire: expire, expire: expire,
lruCache: emptyLruCache, lruCache: emptyLruCache,
barrier: syncx.NewSingleFlight(), barrier: syncx.NewSingleFlight(),
@@ -59,7 +59,7 @@ func NewCache(expire time.Duration, opts ...CacheOption) (*Cache, error) {
} }
cache.stats = newCacheStat(cache.name, cache.size) cache.stats = newCacheStat(cache.name, cache.size)
timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v interface{}) { timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v any) {
key, ok := k.(string) key, ok := k.(string)
if !ok { if !ok {
return return
@@ -81,11 +81,15 @@ func (c *Cache) Del(key string) {
delete(c.data, key) delete(c.data, key)
c.lruCache.remove(key) c.lruCache.remove(key)
c.lock.Unlock() c.lock.Unlock()
// RemoveTimer is called outside the lock to avoid performance impact from this
// potentially time-consuming operation. Data integrity is maintained by lruCache,
// which will eventually evict any remaining entries when capacity is exceeded.
c.timingWheel.RemoveTimer(key) c.timingWheel.RemoveTimer(key)
} }
// Get returns the item with the given key from c. // Get returns the item with the given key from c.
func (c *Cache) Get(key string) (interface{}, bool) { func (c *Cache) Get(key string) (any, bool) {
value, ok := c.doGet(key) value, ok := c.doGet(key)
if ok { if ok {
c.stats.IncrementHit() c.stats.IncrementHit()
@@ -97,12 +101,12 @@ func (c *Cache) Get(key string) (interface{}, bool) {
} }
// Set sets value into c with key. // Set sets value into c with key.
func (c *Cache) Set(key string, value interface{}) { func (c *Cache) Set(key string, value any) {
c.SetWithExpire(key, value, c.expire) c.SetWithExpire(key, value, c.expire)
} }
// SetWithExpire sets value into c with key and expire with the given value. // SetWithExpire sets value into c with key and expire with the given value.
func (c *Cache) SetWithExpire(key string, value interface{}, expire time.Duration) { func (c *Cache) SetWithExpire(key string, value any, expire time.Duration) {
c.lock.Lock() c.lock.Lock()
_, ok := c.data[key] _, ok := c.data[key]
c.data[key] = value c.data[key] = value
@@ -120,16 +124,16 @@ func (c *Cache) SetWithExpire(key string, value interface{}, expire time.Duratio
// Take returns the item with the given key. // Take returns the item with the given key.
// If the item is in c, return it directly. // If the item is in c, return it directly.
// If not, use fetch method to get the item, set into c and return it. // If not, use fetch method to get the item, set into c and return it.
func (c *Cache) Take(key string, fetch func() (interface{}, error)) (interface{}, error) { func (c *Cache) Take(key string, fetch func() (any, error)) (any, error) {
if val, ok := c.doGet(key); ok { if val, ok := c.doGet(key); ok {
c.stats.IncrementHit() c.stats.IncrementHit()
return val, nil return val, nil
} }
var fresh bool var fresh bool
val, err := c.barrier.Do(key, func() (interface{}, error) { val, err := c.barrier.Do(key, func() (any, error) {
// because O(1) on map search in memory, and fetch is an IO query // because O(1) on map search in memory, and fetch is an IO query,
// so we do double check, cache might be taken by another call // so we do double-check, cache might be taken by another call
if val, ok := c.doGet(key); ok { if val, ok := c.doGet(key); ok {
return val, nil return val, nil
} }
@@ -157,7 +161,7 @@ func (c *Cache) Take(key string, fetch func() (interface{}, error)) (interface{}
return val, nil return val, nil
} }
func (c *Cache) doGet(key string) (interface{}, bool) { func (c *Cache) doGet(key string) (any, bool) {
c.lock.Lock() c.lock.Lock()
defer c.lock.Unlock() defer c.lock.Unlock()

View File

@@ -52,7 +52,7 @@ func TestCacheTake(t *testing.T) {
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
wg.Add(1) wg.Add(1)
go func() { go func() {
cache.Take("first", func() (interface{}, error) { cache.Take("first", func() (any, error) {
atomic.AddInt32(&count, 1) atomic.AddInt32(&count, 1)
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
return "first element", nil return "first element", nil
@@ -76,7 +76,7 @@ func TestCacheTakeExists(t *testing.T) {
wg.Add(1) wg.Add(1)
go func() { go func() {
cache.Set("first", "first element") cache.Set("first", "first element")
cache.Take("first", func() (interface{}, error) { cache.Take("first", func() (any, error) {
atomic.AddInt32(&count, 1) atomic.AddInt32(&count, 1)
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
return "first element", nil return "first element", nil
@@ -99,7 +99,7 @@ func TestCacheTakeError(t *testing.T) {
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
wg.Add(1) wg.Add(1)
go func() { go func() {
_, err := cache.Take("first", func() (interface{}, error) { _, err := cache.Take("first", func() (any, error) {
atomic.AddInt32(&count, 1) atomic.AddInt32(&count, 1)
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
return "", errDummy return "", errDummy

View File

@@ -5,7 +5,7 @@ import "sync"
// A Queue is a FIFO queue. // A Queue is a FIFO queue.
type Queue struct { type Queue struct {
lock sync.Mutex lock sync.Mutex
elements []interface{} elements []any
size int size int
head int head int
tail int tail int
@@ -15,7 +15,7 @@ type Queue struct {
// NewQueue returns a Queue object. // NewQueue returns a Queue object.
func NewQueue(size int) *Queue { func NewQueue(size int) *Queue {
return &Queue{ return &Queue{
elements: make([]interface{}, size), elements: make([]any, size),
size: size, size: size,
} }
} }
@@ -30,12 +30,12 @@ func (q *Queue) Empty() bool {
} }
// Put puts element into q at the last position. // Put puts element into q at the last position.
func (q *Queue) Put(element interface{}) { func (q *Queue) Put(element any) {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
if q.head == q.tail && q.count > 0 { if q.head == q.tail && q.count > 0 {
nodes := make([]interface{}, len(q.elements)+q.size) nodes := make([]any, len(q.elements)+q.size)
copy(nodes, q.elements[q.head:]) copy(nodes, q.elements[q.head:])
copy(nodes[len(q.elements)-q.head:], q.elements[:q.head]) copy(nodes[len(q.elements)-q.head:], q.elements[:q.head])
q.head = 0 q.head = 0
@@ -49,7 +49,7 @@ func (q *Queue) Put(element interface{}) {
} }
// Take takes the first element out of q if not empty. // Take takes the first element out of q if not empty.
func (q *Queue) Take() (interface{}, bool) { func (q *Queue) Take() (any, bool) {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()

View File

@@ -4,9 +4,9 @@ import "sync"
// A Ring can be used as fixed size ring. // A Ring can be used as fixed size ring.
type Ring struct { type Ring struct {
elements []interface{} elements []any
index int index int
lock sync.Mutex lock sync.RWMutex
} }
// NewRing returns a Ring object with the given size n. // NewRing returns a Ring object with the given size n.
@@ -16,36 +16,44 @@ func NewRing(n int) *Ring {
} }
return &Ring{ return &Ring{
elements: make([]interface{}, n), elements: make([]any, n),
} }
} }
// Add adds v into r. // Add adds v into r.
func (r *Ring) Add(v interface{}) { func (r *Ring) Add(v any) {
r.lock.Lock() r.lock.Lock()
defer r.lock.Unlock() defer r.lock.Unlock()
r.elements[r.index%len(r.elements)] = v rlen := len(r.elements)
r.elements[r.index%rlen] = v
r.index++ r.index++
// prevent ring index overflow
if r.index >= rlen<<1 {
r.index -= rlen
}
} }
// Take takes all items from r. // Take takes all items from r.
func (r *Ring) Take() []interface{} { func (r *Ring) Take() []any {
r.lock.Lock() r.lock.RLock()
defer r.lock.Unlock() defer r.lock.RUnlock()
var size int var size int
var start int var start int
if r.index > len(r.elements) { rlen := len(r.elements)
size = len(r.elements)
start = r.index % len(r.elements) if r.index > rlen {
size = rlen
start = r.index % rlen
} else { } else {
size = r.index size = r.index
} }
elements := make([]interface{}, size) elements := make([]any, size)
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
elements[i] = r.elements[(start+i)%len(r.elements)] elements[i] = r.elements[(start+i)%rlen]
} }
return elements return elements

View File

@@ -19,7 +19,7 @@ func TestRingLess(t *testing.T) {
ring.Add(i) ring.Add(i)
} }
elements := ring.Take() elements := ring.Take()
assert.ElementsMatch(t, []interface{}{0, 1, 2}, elements) assert.ElementsMatch(t, []any{0, 1, 2}, elements)
} }
func TestRingMore(t *testing.T) { func TestRingMore(t *testing.T) {
@@ -28,7 +28,7 @@ func TestRingMore(t *testing.T) {
ring.Add(i) ring.Add(i)
} }
elements := ring.Take() elements := ring.Take()
assert.ElementsMatch(t, []interface{}{6, 7, 8, 9, 10}, elements) assert.ElementsMatch(t, []any{6, 7, 8, 9, 10}, elements)
} }
func TestRingAdd(t *testing.T) { func TestRingAdd(t *testing.T) {

View File

@@ -4,18 +4,28 @@ import (
"sync" "sync"
"time" "time"
"github.com/zeromicro/go-zero/core/mathx"
"github.com/zeromicro/go-zero/core/timex" "github.com/zeromicro/go-zero/core/timex"
) )
type ( type (
// RollingWindowOption let callers customize the RollingWindow. // BucketInterface is the interface that defines the buckets.
RollingWindowOption func(rollingWindow *RollingWindow) BucketInterface[T Numerical] interface {
Add(v T)
Reset()
}
// RollingWindow defines a rolling window to calculate the events in buckets with time interval. // Numerical is the interface that restricts the numerical type.
RollingWindow struct { Numerical = mathx.Numerical
// RollingWindowOption let callers customize the RollingWindow.
RollingWindowOption[T Numerical, B BucketInterface[T]] func(rollingWindow *RollingWindow[T, B])
// RollingWindow defines a rolling window to calculate the events in buckets with the time interval.
RollingWindow[T Numerical, B BucketInterface[T]] struct {
lock sync.RWMutex lock sync.RWMutex
size int size int
win *window win *window[T, B]
interval time.Duration interval time.Duration
offset int offset int
ignoreCurrent bool ignoreCurrent bool
@@ -25,14 +35,15 @@ type (
// NewRollingWindow returns a RollingWindow that with size buckets and time interval, // NewRollingWindow returns a RollingWindow that with size buckets and time interval,
// use opts to customize the RollingWindow. // use opts to customize the RollingWindow.
func NewRollingWindow(size int, interval time.Duration, opts ...RollingWindowOption) *RollingWindow { func NewRollingWindow[T Numerical, B BucketInterface[T]](newBucket func() B, size int,
interval time.Duration, opts ...RollingWindowOption[T, B]) *RollingWindow[T, B] {
if size < 1 { if size < 1 {
panic("size must be greater than 0") panic("size must be greater than 0")
} }
w := &RollingWindow{ w := &RollingWindow[T, B]{
size: size, size: size,
win: newWindow(size), win: newWindow[T, B](newBucket, size),
interval: interval, interval: interval,
lastTime: timex.Now(), lastTime: timex.Now(),
} }
@@ -43,7 +54,7 @@ func NewRollingWindow(size int, interval time.Duration, opts ...RollingWindowOpt
} }
// Add adds value to current bucket. // Add adds value to current bucket.
func (rw *RollingWindow) Add(v float64) { func (rw *RollingWindow[T, B]) Add(v T) {
rw.lock.Lock() rw.lock.Lock()
defer rw.lock.Unlock() defer rw.lock.Unlock()
rw.updateOffset() rw.updateOffset()
@@ -51,13 +62,13 @@ func (rw *RollingWindow) Add(v float64) {
} }
// Reduce runs fn on all buckets, ignore current bucket if ignoreCurrent was set. // Reduce runs fn on all buckets, ignore current bucket if ignoreCurrent was set.
func (rw *RollingWindow) Reduce(fn func(b *Bucket)) { func (rw *RollingWindow[T, B]) Reduce(fn func(b B)) {
rw.lock.RLock() rw.lock.RLock()
defer rw.lock.RUnlock() defer rw.lock.RUnlock()
var diff int var diff int
span := rw.span() span := rw.span()
// ignore current bucket, because of partial data // ignore the current bucket, because of partial data
if span == 0 && rw.ignoreCurrent { if span == 0 && rw.ignoreCurrent {
diff = rw.size - 1 diff = rw.size - 1
} else { } else {
@@ -69,7 +80,7 @@ func (rw *RollingWindow) Reduce(fn func(b *Bucket)) {
} }
} }
func (rw *RollingWindow) span() int { func (rw *RollingWindow[T, B]) span() int {
offset := int(timex.Since(rw.lastTime) / rw.interval) offset := int(timex.Since(rw.lastTime) / rw.interval)
if 0 <= offset && offset < rw.size { if 0 <= offset && offset < rw.size {
return offset return offset
@@ -78,7 +89,7 @@ func (rw *RollingWindow) span() int {
return rw.size return rw.size
} }
func (rw *RollingWindow) updateOffset() { func (rw *RollingWindow[T, B]) updateOffset() {
span := rw.span() span := rw.span()
if span <= 0 { if span <= 0 {
return return
@@ -97,54 +108,54 @@ func (rw *RollingWindow) updateOffset() {
} }
// Bucket defines the bucket that holds sum and num of additions. // Bucket defines the bucket that holds sum and num of additions.
type Bucket struct { type Bucket[T Numerical] struct {
Sum float64 Sum T
Count int64 Count int64
} }
func (b *Bucket) add(v float64) { func (b *Bucket[T]) Add(v T) {
b.Sum += v b.Sum += v
b.Count++ b.Count++
} }
func (b *Bucket) reset() { func (b *Bucket[T]) Reset() {
b.Sum = 0 b.Sum = 0
b.Count = 0 b.Count = 0
} }
type window struct { type window[T Numerical, B BucketInterface[T]] struct {
buckets []*Bucket buckets []B
size int size int
} }
func newWindow(size int) *window { func newWindow[T Numerical, B BucketInterface[T]](newBucket func() B, size int) *window[T, B] {
buckets := make([]*Bucket, size) buckets := make([]B, size)
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
buckets[i] = new(Bucket) buckets[i] = newBucket()
} }
return &window{ return &window[T, B]{
buckets: buckets, buckets: buckets,
size: size, size: size,
} }
} }
func (w *window) add(offset int, v float64) { func (w *window[T, B]) add(offset int, v T) {
w.buckets[offset%w.size].add(v) w.buckets[offset%w.size].Add(v)
} }
func (w *window) reduce(start, count int, fn func(b *Bucket)) { func (w *window[T, B]) reduce(start, count int, fn func(b B)) {
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
fn(w.buckets[(start+i)%w.size]) fn(w.buckets[(start+i)%w.size])
} }
} }
func (w *window) resetBucket(offset int) { func (w *window[T, B]) resetBucket(offset int) {
w.buckets[offset%w.size].reset() w.buckets[offset%w.size].Reset()
} }
// IgnoreCurrentBucket lets the Reduce call ignore current bucket. // IgnoreCurrentBucket lets the Reduce call ignore current bucket.
func IgnoreCurrentBucket() RollingWindowOption { func IgnoreCurrentBucket[T Numerical, B BucketInterface[T]]() RollingWindowOption[T, B] {
return func(w *RollingWindow) { return func(w *RollingWindow[T, B]) {
w.ignoreCurrent = true w.ignoreCurrent = true
} }
} }

View File

@@ -12,18 +12,24 @@ import (
const duration = time.Millisecond * 50 const duration = time.Millisecond * 50
func TestNewRollingWindow(t *testing.T) { func TestNewRollingWindow(t *testing.T) {
assert.NotNil(t, NewRollingWindow(10, time.Second)) assert.NotNil(t, NewRollingWindow[int64, *Bucket[int64]](func() *Bucket[int64] {
return new(Bucket[int64])
}, 10, time.Second))
assert.Panics(t, func() { assert.Panics(t, func() {
NewRollingWindow(0, time.Second) NewRollingWindow[int64, *Bucket[int64]](func() *Bucket[int64] {
return new(Bucket[int64])
}, 0, time.Second)
}) })
} }
func TestRollingWindowAdd(t *testing.T) { func TestRollingWindowAdd(t *testing.T) {
const size = 3 const size = 3
r := NewRollingWindow(size, duration) r := NewRollingWindow[float64, *Bucket[float64]](func() *Bucket[float64] {
return new(Bucket[float64])
}, size, duration)
listBuckets := func() []float64 { listBuckets := func() []float64 {
var buckets []float64 var buckets []float64
r.Reduce(func(b *Bucket) { r.Reduce(func(b *Bucket[float64]) {
buckets = append(buckets, b.Sum) buckets = append(buckets, b.Sum)
}) })
return buckets return buckets
@@ -47,10 +53,12 @@ func TestRollingWindowAdd(t *testing.T) {
func TestRollingWindowReset(t *testing.T) { func TestRollingWindowReset(t *testing.T) {
const size = 3 const size = 3
r := NewRollingWindow(size, duration, IgnoreCurrentBucket()) r := NewRollingWindow[float64, *Bucket[float64]](func() *Bucket[float64] {
return new(Bucket[float64])
}, size, duration, IgnoreCurrentBucket[float64, *Bucket[float64]]())
listBuckets := func() []float64 { listBuckets := func() []float64 {
var buckets []float64 var buckets []float64
r.Reduce(func(b *Bucket) { r.Reduce(func(b *Bucket[float64]) {
buckets = append(buckets, b.Sum) buckets = append(buckets, b.Sum)
}) })
return buckets return buckets
@@ -72,15 +80,19 @@ func TestRollingWindowReset(t *testing.T) {
func TestRollingWindowReduce(t *testing.T) { func TestRollingWindowReduce(t *testing.T) {
const size = 4 const size = 4
tests := []struct { tests := []struct {
win *RollingWindow win *RollingWindow[float64, *Bucket[float64]]
expect float64 expect float64
}{ }{
{ {
win: NewRollingWindow(size, duration), win: NewRollingWindow[float64, *Bucket[float64]](func() *Bucket[float64] {
return new(Bucket[float64])
}, size, duration),
expect: 10, expect: 10,
}, },
{ {
win: NewRollingWindow(size, duration, IgnoreCurrentBucket()), win: NewRollingWindow[float64, *Bucket[float64]](func() *Bucket[float64] {
return new(Bucket[float64])
}, size, duration, IgnoreCurrentBucket[float64, *Bucket[float64]]()),
expect: 4, expect: 4,
}, },
} }
@@ -97,7 +109,7 @@ func TestRollingWindowReduce(t *testing.T) {
} }
} }
var result float64 var result float64
r.Reduce(func(b *Bucket) { r.Reduce(func(b *Bucket[float64]) {
result += b.Sum result += b.Sum
}) })
assert.Equal(t, test.expect, result) assert.Equal(t, test.expect, result)
@@ -108,10 +120,12 @@ func TestRollingWindowReduce(t *testing.T) {
func TestRollingWindowBucketTimeBoundary(t *testing.T) { func TestRollingWindowBucketTimeBoundary(t *testing.T) {
const size = 3 const size = 3
interval := time.Millisecond * 30 interval := time.Millisecond * 30
r := NewRollingWindow(size, interval) r := NewRollingWindow[float64, *Bucket[float64]](func() *Bucket[float64] {
return new(Bucket[float64])
}, size, interval)
listBuckets := func() []float64 { listBuckets := func() []float64 {
var buckets []float64 var buckets []float64
r.Reduce(func(b *Bucket) { r.Reduce(func(b *Bucket[float64]) {
buckets = append(buckets, b.Sum) buckets = append(buckets, b.Sum)
}) })
return buckets return buckets
@@ -138,7 +152,9 @@ func TestRollingWindowBucketTimeBoundary(t *testing.T) {
func TestRollingWindowDataRace(t *testing.T) { func TestRollingWindowDataRace(t *testing.T) {
const size = 3 const size = 3
r := NewRollingWindow(size, duration) r := NewRollingWindow[float64, *Bucket[float64]](func() *Bucket[float64] {
return new(Bucket[float64])
}, size, duration)
stop := make(chan bool) stop := make(chan bool)
go func() { go func() {
for { for {
@@ -157,7 +173,7 @@ func TestRollingWindowDataRace(t *testing.T) {
case <-stop: case <-stop:
return return
default: default:
r.Reduce(func(b *Bucket) {}) r.Reduce(func(b *Bucket[float64]) {})
} }
} }
}() }()

View File

@@ -14,21 +14,23 @@ type SafeMap struct {
lock sync.RWMutex lock sync.RWMutex
deletionOld int deletionOld int
deletionNew int deletionNew int
dirtyOld map[interface{}]interface{} dirtyOld map[any]any
dirtyNew map[interface{}]interface{} dirtyNew map[any]any
} }
// NewSafeMap returns a SafeMap. // NewSafeMap returns a SafeMap.
func NewSafeMap() *SafeMap { func NewSafeMap() *SafeMap {
return &SafeMap{ return &SafeMap{
dirtyOld: make(map[interface{}]interface{}), dirtyOld: make(map[any]any),
dirtyNew: make(map[interface{}]interface{}), dirtyNew: make(map[any]any),
} }
} }
// Del deletes the value with the given key from m. // Del deletes the value with the given key from m.
func (m *SafeMap) Del(key interface{}) { func (m *SafeMap) Del(key any) {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock()
if _, ok := m.dirtyOld[key]; ok { if _, ok := m.dirtyOld[key]; ok {
delete(m.dirtyOld, key) delete(m.dirtyOld, key)
m.deletionOld++ m.deletionOld++
@@ -42,21 +44,20 @@ func (m *SafeMap) Del(key interface{}) {
} }
m.dirtyOld = m.dirtyNew m.dirtyOld = m.dirtyNew
m.deletionOld = m.deletionNew m.deletionOld = m.deletionNew
m.dirtyNew = make(map[interface{}]interface{}) m.dirtyNew = make(map[any]any)
m.deletionNew = 0 m.deletionNew = 0
} }
if m.deletionNew >= maxDeletion && len(m.dirtyNew) < copyThreshold { if m.deletionNew >= maxDeletion && len(m.dirtyNew) < copyThreshold {
for k, v := range m.dirtyNew { for k, v := range m.dirtyNew {
m.dirtyOld[k] = v m.dirtyOld[k] = v
} }
m.dirtyNew = make(map[interface{}]interface{}) m.dirtyNew = make(map[any]any)
m.deletionNew = 0 m.deletionNew = 0
} }
m.lock.Unlock()
} }
// Get gets the value with the given key from m. // Get gets the value with the given key from m.
func (m *SafeMap) Get(key interface{}) (interface{}, bool) { func (m *SafeMap) Get(key any) (any, bool) {
m.lock.RLock() m.lock.RLock()
defer m.lock.RUnlock() defer m.lock.RUnlock()
@@ -70,7 +71,7 @@ func (m *SafeMap) Get(key interface{}) (interface{}, bool) {
// Range calls f sequentially for each key and value present in the map. // Range calls f sequentially for each key and value present in the map.
// If f returns false, range stops the iteration. // If f returns false, range stops the iteration.
func (m *SafeMap) Range(f func(key, val interface{}) bool) { func (m *SafeMap) Range(f func(key, val any) bool) {
m.lock.RLock() m.lock.RLock()
defer m.lock.RUnlock() defer m.lock.RUnlock()
@@ -87,8 +88,10 @@ func (m *SafeMap) Range(f func(key, val interface{}) bool) {
} }
// Set sets the value into m with the given key. // Set sets the value into m with the given key.
func (m *SafeMap) Set(key, value interface{}) { func (m *SafeMap) Set(key, value any) {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock()
if m.deletionOld <= maxDeletion { if m.deletionOld <= maxDeletion {
if _, ok := m.dirtyNew[key]; ok { if _, ok := m.dirtyNew[key]; ok {
delete(m.dirtyNew, key) delete(m.dirtyNew, key)
@@ -102,7 +105,6 @@ func (m *SafeMap) Set(key, value interface{}) {
} }
m.dirtyNew[key] = value m.dirtyNew[key] = value
} }
m.lock.Unlock()
} }
// Size returns the size of m. // Size returns the size of m.

View File

@@ -138,7 +138,7 @@ func TestSafeMap_Range(t *testing.T) {
} }
var count int32 var count int32
m.Range(func(k, v interface{}) bool { m.Range(func(k, v any) bool {
atomic.AddInt32(&count, 1) atomic.AddInt32(&count, 1)
newMap.Set(k, v) newMap.Set(k, v)
return true return true
@@ -147,3 +147,65 @@ func TestSafeMap_Range(t *testing.T) {
assert.Equal(t, m.dirtyNew, newMap.dirtyNew) assert.Equal(t, m.dirtyNew, newMap.dirtyNew)
assert.Equal(t, m.dirtyOld, newMap.dirtyOld) assert.Equal(t, m.dirtyOld, newMap.dirtyOld)
} }
func TestSetManyTimes(t *testing.T) {
const iteration = maxDeletion * 2
m := NewSafeMap()
for i := 0; i < iteration; i++ {
m.Set(i, i)
if i%3 == 0 {
m.Del(i / 2)
}
}
var count int
m.Range(func(k, v any) bool {
count++
return count < maxDeletion/2
})
assert.Equal(t, maxDeletion/2, count)
for i := 0; i < iteration; i++ {
m.Set(i, i)
if i%3 == 0 {
m.Del(i / 2)
}
}
for i := 0; i < iteration; i++ {
m.Set(i, i)
if i%3 == 0 {
m.Del(i / 2)
}
}
for i := 0; i < iteration; i++ {
m.Set(i, i)
if i%3 == 0 {
m.Del(i / 2)
}
}
count = 0
m.Range(func(k, v any) bool {
count++
return count < maxDeletion
})
assert.Equal(t, maxDeletion, count)
}
func TestSetManyTimesNew(t *testing.T) {
m := NewSafeMap()
for i := 0; i < maxDeletion*3; i++ {
m.Set(i, i)
}
for i := 0; i < maxDeletion*2; i++ {
m.Del(i)
}
for i := 0; i < maxDeletion*3; i++ {
m.Set(i+maxDeletion*3, i+maxDeletion*3)
}
for i := 0; i < maxDeletion*2; i++ {
m.Del(i + maxDeletion*2)
}
for i := 0; i < maxDeletion-copyThreshold+1; i++ {
m.Del(i + maxDeletion*2)
}
assert.Equal(t, 0, len(m.dirtyNew))
}

View File

@@ -1,235 +1,53 @@
package collection package collection
import ( import "github.com/zeromicro/go-zero/core/lang"
"github.com/zeromicro/go-zero/core/lang"
"github.com/zeromicro/go-zero/core/logx"
)
const ( // Set is a type-safe generic set collection.
unmanaged = iota // It's not thread-safe, use with synchronization for concurrent access.
untyped type Set[T comparable] struct {
intType data map[T]lang.PlaceholderType
int64Type
uintType
uint64Type
stringType
)
// Set is not thread-safe, for concurrent use, make sure to use it with synchronization.
type Set struct {
data map[interface{}]lang.PlaceholderType
tp int
} }
// NewSet returns a managed Set, can only put the values with the same type. // NewSet returns a new type-safe set.
func NewSet() *Set { func NewSet[T comparable]() *Set[T] {
return &Set{ return &Set[T]{
data: make(map[interface{}]lang.PlaceholderType), data: make(map[T]lang.PlaceholderType),
tp: untyped,
} }
} }
// NewUnmanagedSet returns a unmanaged Set, which can put values with different types. // Add adds items to the set. Duplicates are automatically ignored.
func NewUnmanagedSet() *Set { func (s *Set[T]) Add(items ...T) {
return &Set{ for _, item := range items {
data: make(map[interface{}]lang.PlaceholderType), s.data[item] = lang.Placeholder
tp: unmanaged,
} }
} }
// Add adds i into s. // Clear removes all items from the set.
func (s *Set) Add(i ...interface{}) { func (s *Set[T]) Clear() {
for _, each := range i { clear(s.data)
s.add(each)
}
} }
// AddInt adds int values ii into s. // Contains checks if an item exists in the set.
func (s *Set) AddInt(ii ...int) { func (s *Set[T]) Contains(item T) bool {
for _, each := range ii { _, ok := s.data[item]
s.add(each)
}
}
// AddInt64 adds int64 values ii into s.
func (s *Set) AddInt64(ii ...int64) {
for _, each := range ii {
s.add(each)
}
}
// AddUint adds uint values ii into s.
func (s *Set) AddUint(ii ...uint) {
for _, each := range ii {
s.add(each)
}
}
// AddUint64 adds uint64 values ii into s.
func (s *Set) AddUint64(ii ...uint64) {
for _, each := range ii {
s.add(each)
}
}
// AddStr adds string values ss into s.
func (s *Set) AddStr(ss ...string) {
for _, each := range ss {
s.add(each)
}
}
// Contains checks if i is in s.
func (s *Set) Contains(i interface{}) bool {
if len(s.data) == 0 {
return false
}
s.validate(i)
_, ok := s.data[i]
return ok return ok
} }
// Keys returns the keys in s. // Count returns the number of items in the set.
func (s *Set) Keys() []interface{} { func (s *Set[T]) Count() int {
var keys []interface{}
for key := range s.data {
keys = append(keys, key)
}
return keys
}
// KeysInt returns the int keys in s.
func (s *Set) KeysInt() []int {
var keys []int
for key := range s.data {
if intKey, ok := key.(int); ok {
keys = append(keys, intKey)
}
}
return keys
}
// KeysInt64 returns int64 keys in s.
func (s *Set) KeysInt64() []int64 {
var keys []int64
for key := range s.data {
if intKey, ok := key.(int64); ok {
keys = append(keys, intKey)
}
}
return keys
}
// KeysUint returns uint keys in s.
func (s *Set) KeysUint() []uint {
var keys []uint
for key := range s.data {
if intKey, ok := key.(uint); ok {
keys = append(keys, intKey)
}
}
return keys
}
// KeysUint64 returns uint64 keys in s.
func (s *Set) KeysUint64() []uint64 {
var keys []uint64
for key := range s.data {
if intKey, ok := key.(uint64); ok {
keys = append(keys, intKey)
}
}
return keys
}
// KeysStr returns string keys in s.
func (s *Set) KeysStr() []string {
var keys []string
for key := range s.data {
if strKey, ok := key.(string); ok {
keys = append(keys, strKey)
}
}
return keys
}
// Remove removes i from s.
func (s *Set) Remove(i interface{}) {
s.validate(i)
delete(s.data, i)
}
// Count returns the number of items in s.
func (s *Set) Count() int {
return len(s.data) return len(s.data)
} }
func (s *Set) add(i interface{}) { // Keys returns all elements in the set as a slice.
switch s.tp { func (s *Set[T]) Keys() []T {
case unmanaged: keys := make([]T, 0, len(s.data))
// do nothing for key := range s.data {
case untyped: keys = append(keys, key)
s.setType(i)
default:
s.validate(i)
} }
s.data[i] = lang.Placeholder return keys
} }
func (s *Set) setType(i interface{}) { // Remove removes an item from the set.
// s.tp can only be untyped here func (s *Set[T]) Remove(item T) {
switch i.(type) { delete(s.data, item)
case int:
s.tp = intType
case int64:
s.tp = int64Type
case uint:
s.tp = uintType
case uint64:
s.tp = uint64Type
case string:
s.tp = stringType
}
}
func (s *Set) validate(i interface{}) {
if s.tp == unmanaged {
return
}
switch i.(type) {
case int:
if s.tp != intType {
logx.Errorf("Error: element is int, but set contains elements with type %d", s.tp)
}
case int64:
if s.tp != int64Type {
logx.Errorf("Error: element is int64, but set contains elements with type %d", s.tp)
}
case uint:
if s.tp != uintType {
logx.Errorf("Error: element is uint, but set contains elements with type %d", s.tp)
}
case uint64:
if s.tp != uint64Type {
logx.Errorf("Error: element is uint64, but set contains elements with type %d", s.tp)
}
case string:
if s.tp != stringType {
logx.Errorf("Error: element is string, but set contains elements with type %d", s.tp)
}
}
} }

View File

@@ -12,34 +12,117 @@ func init() {
logx.Disable() logx.Disable()
} }
func BenchmarkRawSet(b *testing.B) { // Set functionality tests
m := make(map[interface{}]struct{}) func TestTypedSetInt(t *testing.T) {
for i := 0; i < b.N; i++ { set := NewSet[int]()
m[i] = struct{}{} values := []int{1, 2, 3, 2, 1} // Contains duplicates
_ = m[i]
} // Test adding
set.Add(values...)
assert.Equal(t, 3, set.Count()) // Should only have 3 elements after deduplication
// Test contains
assert.True(t, set.Contains(1))
assert.True(t, set.Contains(2))
assert.True(t, set.Contains(3))
assert.False(t, set.Contains(4))
// Test getting all keys
keys := set.Keys()
sort.Ints(keys)
assert.EqualValues(t, []int{1, 2, 3}, keys)
// Test removal
set.Remove(2)
assert.False(t, set.Contains(2))
assert.Equal(t, 2, set.Count())
} }
func BenchmarkUnmanagedSet(b *testing.B) { func TestTypedSetStringOps(t *testing.T) {
s := NewUnmanagedSet() set := NewSet[string]()
values := []string{"a", "b", "c", "b", "a"}
set.Add(values...)
assert.Equal(t, 3, set.Count())
assert.True(t, set.Contains("a"))
assert.True(t, set.Contains("b"))
assert.True(t, set.Contains("c"))
assert.False(t, set.Contains("d"))
keys := set.Keys()
sort.Strings(keys)
assert.EqualValues(t, []string{"a", "b", "c"}, keys)
}
func TestTypedSetClear(t *testing.T) {
set := NewSet[int]()
set.Add(1, 2, 3)
assert.Equal(t, 3, set.Count())
set.Clear()
assert.Equal(t, 0, set.Count())
assert.False(t, set.Contains(1))
}
func TestTypedSetEmpty(t *testing.T) {
set := NewSet[int]()
assert.Equal(t, 0, set.Count())
assert.False(t, set.Contains(1))
assert.Empty(t, set.Keys())
}
func TestTypedSetMultipleTypes(t *testing.T) {
// Test different typed generic sets
intSet := NewSet[int]()
int64Set := NewSet[int64]()
uintSet := NewSet[uint]()
uint64Set := NewSet[uint64]()
stringSet := NewSet[string]()
intSet.Add(1, 2, 3)
int64Set.Add(1, 2, 3)
uintSet.Add(1, 2, 3)
uint64Set.Add(1, 2, 3)
stringSet.Add("1", "2", "3")
assert.Equal(t, 3, intSet.Count())
assert.Equal(t, 3, int64Set.Count())
assert.Equal(t, 3, uintSet.Count())
assert.Equal(t, 3, uint64Set.Count())
assert.Equal(t, 3, stringSet.Count())
}
// Set benchmarks
func BenchmarkTypedIntSet(b *testing.B) {
s := NewSet[int]()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
s.Add(i) s.Add(i)
_ = s.Contains(i) _ = s.Contains(i)
} }
} }
func BenchmarkSet(b *testing.B) { func BenchmarkTypedStringSet(b *testing.B) {
s := NewSet() s := NewSet[string]()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
s.AddInt(i) s.Add(string(rune(i)))
_ = s.Contains(i) _ = s.Contains(string(rune(i)))
}
}
// Legacy tests remain unchanged for backward compatibility
func BenchmarkRawSet(b *testing.B) {
m := make(map[any]struct{})
for i := 0; i < b.N; i++ {
m[i] = struct{}{}
_ = m[i]
} }
} }
func TestAdd(t *testing.T) { func TestAdd(t *testing.T) {
// given // given
set := NewUnmanagedSet() set := NewSet[int]()
values := []interface{}{1, 2, 3} values := []int{1, 2, 3}
// when // when
set.Add(values...) set.Add(values...)
@@ -51,82 +134,74 @@ func TestAdd(t *testing.T) {
func TestAddInt(t *testing.T) { func TestAddInt(t *testing.T) {
// given // given
set := NewSet() set := NewSet[int]()
values := []int{1, 2, 3} values := []int{1, 2, 3}
// when // when
set.AddInt(values...) set.Add(values...)
// then // then
assert.True(t, set.Contains(1) && set.Contains(2) && set.Contains(3)) assert.True(t, set.Contains(1) && set.Contains(2) && set.Contains(3))
keys := set.KeysInt() keys := set.Keys()
sort.Ints(keys) sort.Ints(keys)
assert.EqualValues(t, values, keys) assert.EqualValues(t, values, keys)
} }
func TestAddInt64(t *testing.T) { func TestAddInt64(t *testing.T) {
// given // given
set := NewSet() set := NewSet[int64]()
values := []int64{1, 2, 3} values := []int64{1, 2, 3}
// when // when
set.AddInt64(values...) set.Add(values...)
// then // then
assert.True(t, set.Contains(int64(1)) && set.Contains(int64(2)) && set.Contains(int64(3))) assert.True(t, set.Contains(1) && set.Contains(2) && set.Contains(3))
assert.Equal(t, len(values), len(set.KeysInt64())) assert.Equal(t, len(values), len(set.Keys()))
} }
func TestAddUint(t *testing.T) { func TestAddUint(t *testing.T) {
// given // given
set := NewSet() set := NewSet[uint]()
values := []uint{1, 2, 3} values := []uint{1, 2, 3}
// when // when
set.AddUint(values...) set.Add(values...)
// then // then
assert.True(t, set.Contains(uint(1)) && set.Contains(uint(2)) && set.Contains(uint(3))) assert.True(t, set.Contains(1) && set.Contains(2) && set.Contains(3))
assert.Equal(t, len(values), len(set.KeysUint())) assert.Equal(t, len(values), len(set.Keys()))
} }
func TestAddUint64(t *testing.T) { func TestAddUint64(t *testing.T) {
// given // given
set := NewSet() set := NewSet[uint64]()
values := []uint64{1, 2, 3} values := []uint64{1, 2, 3}
// when // when
set.AddUint64(values...) set.Add(values...)
// then // then
assert.True(t, set.Contains(uint64(1)) && set.Contains(uint64(2)) && set.Contains(uint64(3))) assert.True(t, set.Contains(1) && set.Contains(2) && set.Contains(3))
assert.Equal(t, len(values), len(set.KeysUint64())) assert.Equal(t, len(values), len(set.Keys()))
} }
func TestAddStr(t *testing.T) { func TestAddStr(t *testing.T) {
// given // given
set := NewSet() set := NewSet[string]()
values := []string{"1", "2", "3"} values := []string{"1", "2", "3"}
// when // when
set.AddStr(values...) set.Add(values...)
// then // then
assert.True(t, set.Contains("1") && set.Contains("2") && set.Contains("3")) assert.True(t, set.Contains("1") && set.Contains("2") && set.Contains("3"))
assert.Equal(t, len(values), len(set.KeysStr())) assert.Equal(t, len(values), len(set.Keys()))
} }
func TestContainsWithoutElements(t *testing.T) { func TestContainsWithoutElements(t *testing.T) {
// given // given
set := NewSet() set := NewSet[int]()
// then
assert.False(t, set.Contains(1))
}
func TestContainsUnmanagedWithoutElements(t *testing.T) {
// given
set := NewUnmanagedSet()
// then // then
assert.False(t, set.Contains(1)) assert.False(t, set.Contains(1))
@@ -134,8 +209,8 @@ func TestContainsUnmanagedWithoutElements(t *testing.T) {
func TestRemove(t *testing.T) { func TestRemove(t *testing.T) {
// given // given
set := NewSet() set := NewSet[int]()
set.Add([]interface{}{1, 2, 3}...) set.Add([]int{1, 2, 3}...)
// when // when
set.Remove(2) set.Remove(2)
@@ -146,57 +221,9 @@ func TestRemove(t *testing.T) {
func TestCount(t *testing.T) { func TestCount(t *testing.T) {
// given // given
set := NewSet() set := NewSet[int]()
set.Add([]interface{}{1, 2, 3}...) set.Add([]int{1, 2, 3}...)
// then // then
assert.Equal(t, set.Count(), 3) assert.Equal(t, set.Count(), 3)
} }
func TestKeysIntMismatch(t *testing.T) {
set := NewSet()
set.add(int64(1))
set.add(2)
vals := set.KeysInt()
assert.EqualValues(t, []int{2}, vals)
}
func TestKeysInt64Mismatch(t *testing.T) {
set := NewSet()
set.add(1)
set.add(int64(2))
vals := set.KeysInt64()
assert.EqualValues(t, []int64{2}, vals)
}
func TestKeysUintMismatch(t *testing.T) {
set := NewSet()
set.add(1)
set.add(uint(2))
vals := set.KeysUint()
assert.EqualValues(t, []uint{2}, vals)
}
func TestKeysUint64Mismatch(t *testing.T) {
set := NewSet()
set.add(1)
set.add(uint64(2))
vals := set.KeysUint64()
assert.EqualValues(t, []uint64{2}, vals)
}
func TestKeysStrMismatch(t *testing.T) {
set := NewSet()
set.add(1)
set.add("2")
vals := set.KeysStr()
assert.EqualValues(t, []string{"2"}, vals)
}
func TestSetType(t *testing.T) {
set := NewUnmanagedSet()
set.add(1)
set.add("2")
vals := set.Keys()
assert.ElementsMatch(t, []interface{}{1, "2"}, vals)
}

View File

@@ -20,7 +20,7 @@ var (
type ( type (
// Execute defines the method to execute the task. // Execute defines the method to execute the task.
Execute func(key, value interface{}) Execute func(key, value any)
// A TimingWheel is a timing wheel object to schedule tasks. // A TimingWheel is a timing wheel object to schedule tasks.
TimingWheel struct { TimingWheel struct {
@@ -33,14 +33,14 @@ type (
execute Execute execute Execute
setChannel chan timingEntry setChannel chan timingEntry
moveChannel chan baseEntry moveChannel chan baseEntry
removeChannel chan interface{} removeChannel chan any
drainChannel chan func(key, value interface{}) drainChannel chan func(key, value any)
stopChannel chan lang.PlaceholderType stopChannel chan lang.PlaceholderType
} }
timingEntry struct { timingEntry struct {
baseEntry baseEntry
value interface{} value any
circle int circle int
diff int diff int
removed bool removed bool
@@ -48,7 +48,7 @@ type (
baseEntry struct { baseEntry struct {
delay time.Duration delay time.Duration
key interface{} key any
} }
positionEntry struct { positionEntry struct {
@@ -57,8 +57,8 @@ type (
} }
timingTask struct { timingTask struct {
key interface{} key any
value interface{} value any
} }
) )
@@ -69,10 +69,11 @@ func NewTimingWheel(interval time.Duration, numSlots int, execute Execute) (*Tim
interval, numSlots, execute) interval, numSlots, execute)
} }
return newTimingWheelWithClock(interval, numSlots, execute, timex.NewTicker(interval)) return NewTimingWheelWithTicker(interval, numSlots, execute, timex.NewTicker(interval))
} }
func newTimingWheelWithClock(interval time.Duration, numSlots int, execute Execute, // NewTimingWheelWithTicker returns a TimingWheel with the given ticker.
func NewTimingWheelWithTicker(interval time.Duration, numSlots int, execute Execute,
ticker timex.Ticker) (*TimingWheel, error) { ticker timex.Ticker) (*TimingWheel, error) {
tw := &TimingWheel{ tw := &TimingWheel{
interval: interval, interval: interval,
@@ -84,8 +85,8 @@ func newTimingWheelWithClock(interval time.Duration, numSlots int, execute Execu
numSlots: numSlots, numSlots: numSlots,
setChannel: make(chan timingEntry), setChannel: make(chan timingEntry),
moveChannel: make(chan baseEntry), moveChannel: make(chan baseEntry),
removeChannel: make(chan interface{}), removeChannel: make(chan any),
drainChannel: make(chan func(key, value interface{})), drainChannel: make(chan func(key, value any)),
stopChannel: make(chan lang.PlaceholderType), stopChannel: make(chan lang.PlaceholderType),
} }
@@ -96,7 +97,7 @@ func newTimingWheelWithClock(interval time.Duration, numSlots int, execute Execu
} }
// Drain drains all items and executes them. // Drain drains all items and executes them.
func (tw *TimingWheel) Drain(fn func(key, value interface{})) error { func (tw *TimingWheel) Drain(fn func(key, value any)) error {
select { select {
case tw.drainChannel <- fn: case tw.drainChannel <- fn:
return nil return nil
@@ -106,7 +107,7 @@ func (tw *TimingWheel) Drain(fn func(key, value interface{})) error {
} }
// MoveTimer moves the task with the given key to the given delay. // MoveTimer moves the task with the given key to the given delay.
func (tw *TimingWheel) MoveTimer(key interface{}, delay time.Duration) error { func (tw *TimingWheel) MoveTimer(key any, delay time.Duration) error {
if delay <= 0 || key == nil { if delay <= 0 || key == nil {
return ErrArgument return ErrArgument
} }
@@ -123,7 +124,7 @@ func (tw *TimingWheel) MoveTimer(key interface{}, delay time.Duration) error {
} }
// RemoveTimer removes the task with the given key. // RemoveTimer removes the task with the given key.
func (tw *TimingWheel) RemoveTimer(key interface{}) error { func (tw *TimingWheel) RemoveTimer(key any) error {
if key == nil { if key == nil {
return ErrArgument return ErrArgument
} }
@@ -137,7 +138,7 @@ func (tw *TimingWheel) RemoveTimer(key interface{}) error {
} }
// SetTimer sets the task value with the given key to the delay. // SetTimer sets the task value with the given key to the delay.
func (tw *TimingWheel) SetTimer(key, value interface{}, delay time.Duration) error { func (tw *TimingWheel) SetTimer(key, value any, delay time.Duration) error {
if delay <= 0 || key == nil { if delay <= 0 || key == nil {
return ErrArgument return ErrArgument
} }
@@ -161,8 +162,9 @@ func (tw *TimingWheel) Stop() {
close(tw.stopChannel) close(tw.stopChannel)
} }
func (tw *TimingWheel) drainAll(fn func(key, value interface{})) { func (tw *TimingWheel) drainAll(fn func(key, value any)) {
runner := threading.NewTaskRunner(drainWorkers) runner := threading.NewTaskRunner(drainWorkers)
for _, slot := range tw.slots { for _, slot := range tw.slots {
for e := slot.Front(); e != nil; { for e := slot.Front(); e != nil; {
task := e.Value.(*timingEntry) task := e.Value.(*timingEntry)
@@ -176,6 +178,8 @@ func (tw *TimingWheel) drainAll(fn func(key, value interface{})) {
} }
} }
} }
runner.Wait()
} }
func (tw *TimingWheel) getPositionAndCircle(d time.Duration) (pos, circle int) { func (tw *TimingWheel) getPositionAndCircle(d time.Duration) (pos, circle int) {
@@ -231,7 +235,7 @@ func (tw *TimingWheel) onTick() {
tw.scanAndRunTasks(l) tw.scanAndRunTasks(l)
} }
func (tw *TimingWheel) removeTask(key interface{}) { func (tw *TimingWheel) removeTask(key any) {
val, ok := tw.timers.Get(key) val, ok := tw.timers.Get(key)
if !ok { if !ok {
return return

View File

@@ -20,13 +20,13 @@ const (
) )
func TestNewTimingWheel(t *testing.T) { func TestNewTimingWheel(t *testing.T) {
_, err := NewTimingWheel(0, 10, func(key, value interface{}) {}) _, err := NewTimingWheel(0, 10, func(key, value any) {})
assert.NotNil(t, err) assert.NotNil(t, err)
} }
func TestTimingWheel_Drain(t *testing.T) { func TestTimingWheel_Drain(t *testing.T) {
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := newTimingWheelWithClock(testStep, 10, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {
}, ticker) }, ticker)
tw.SetTimer("first", 3, testStep*4) tw.SetTimer("first", 3, testStep*4)
tw.SetTimer("second", 5, testStep*7) tw.SetTimer("second", 5, testStep*7)
@@ -36,7 +36,7 @@ func TestTimingWheel_Drain(t *testing.T) {
var lock sync.Mutex var lock sync.Mutex
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(3) wg.Add(3)
tw.Drain(func(key, value interface{}) { tw.Drain(func(key, value any) {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
keys = append(keys, key.(string)) keys = append(keys, key.(string))
@@ -50,19 +50,19 @@ func TestTimingWheel_Drain(t *testing.T) {
assert.EqualValues(t, []string{"first", "second", "third"}, keys) assert.EqualValues(t, []string{"first", "second", "third"}, keys)
assert.EqualValues(t, []int{3, 5, 7}, vals) assert.EqualValues(t, []int{3, 5, 7}, vals)
var count int var count int
tw.Drain(func(key, value interface{}) { tw.Drain(func(key, value any) {
count++ count++
}) })
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
assert.Equal(t, 0, count) assert.Equal(t, 0, count)
tw.Stop() tw.Stop()
assert.Equal(t, ErrClosed, tw.Drain(func(key, value interface{}) {})) assert.Equal(t, ErrClosed, tw.Drain(func(key, value any) {}))
} }
func TestTimingWheel_SetTimerSoon(t *testing.T) { func TestTimingWheel_SetTimerSoon(t *testing.T) {
run := syncx.NewAtomicBool() run := syncx.NewAtomicBool()
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := newTimingWheelWithClock(testStep, 10, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {
assert.True(t, run.CompareAndSwap(false, true)) assert.True(t, run.CompareAndSwap(false, true))
assert.Equal(t, "any", k) assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int)) assert.Equal(t, 3, v.(int))
@@ -78,7 +78,7 @@ func TestTimingWheel_SetTimerSoon(t *testing.T) {
func TestTimingWheel_SetTimerTwice(t *testing.T) { func TestTimingWheel_SetTimerTwice(t *testing.T) {
run := syncx.NewAtomicBool() run := syncx.NewAtomicBool()
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := newTimingWheelWithClock(testStep, 10, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {
assert.True(t, run.CompareAndSwap(false, true)) assert.True(t, run.CompareAndSwap(false, true))
assert.Equal(t, "any", k) assert.Equal(t, "any", k)
assert.Equal(t, 5, v.(int)) assert.Equal(t, 5, v.(int))
@@ -96,7 +96,7 @@ func TestTimingWheel_SetTimerTwice(t *testing.T) {
func TestTimingWheel_SetTimerWrongDelay(t *testing.T) { func TestTimingWheel_SetTimerWrongDelay(t *testing.T) {
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := newTimingWheelWithClock(testStep, 10, func(k, v interface{}) {}, ticker) tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {}, ticker)
defer tw.Stop() defer tw.Stop()
assert.NotPanics(t, func() { assert.NotPanics(t, func() {
tw.SetTimer("any", 3, -testStep) tw.SetTimer("any", 3, -testStep)
@@ -105,7 +105,7 @@ func TestTimingWheel_SetTimerWrongDelay(t *testing.T) {
func TestTimingWheel_SetTimerAfterClose(t *testing.T) { func TestTimingWheel_SetTimerAfterClose(t *testing.T) {
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := newTimingWheelWithClock(testStep, 10, func(k, v interface{}) {}, ticker) tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {}, ticker)
tw.Stop() tw.Stop()
assert.Equal(t, ErrClosed, tw.SetTimer("any", 3, testStep)) assert.Equal(t, ErrClosed, tw.SetTimer("any", 3, testStep))
} }
@@ -113,7 +113,7 @@ func TestTimingWheel_SetTimerAfterClose(t *testing.T) {
func TestTimingWheel_MoveTimer(t *testing.T) { func TestTimingWheel_MoveTimer(t *testing.T) {
run := syncx.NewAtomicBool() run := syncx.NewAtomicBool()
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := newTimingWheelWithClock(testStep, 3, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 3, func(k, v any) {
assert.True(t, run.CompareAndSwap(false, true)) assert.True(t, run.CompareAndSwap(false, true))
assert.Equal(t, "any", k) assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int)) assert.Equal(t, 3, v.(int))
@@ -139,7 +139,7 @@ func TestTimingWheel_MoveTimer(t *testing.T) {
func TestTimingWheel_MoveTimerSoon(t *testing.T) { func TestTimingWheel_MoveTimerSoon(t *testing.T) {
run := syncx.NewAtomicBool() run := syncx.NewAtomicBool()
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := newTimingWheelWithClock(testStep, 3, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 3, func(k, v any) {
assert.True(t, run.CompareAndSwap(false, true)) assert.True(t, run.CompareAndSwap(false, true))
assert.Equal(t, "any", k) assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int)) assert.Equal(t, 3, v.(int))
@@ -155,7 +155,7 @@ func TestTimingWheel_MoveTimerSoon(t *testing.T) {
func TestTimingWheel_MoveTimerEarlier(t *testing.T) { func TestTimingWheel_MoveTimerEarlier(t *testing.T) {
run := syncx.NewAtomicBool() run := syncx.NewAtomicBool()
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := newTimingWheelWithClock(testStep, 10, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {
assert.True(t, run.CompareAndSwap(false, true)) assert.True(t, run.CompareAndSwap(false, true))
assert.Equal(t, "any", k) assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int)) assert.Equal(t, 3, v.(int))
@@ -173,7 +173,7 @@ func TestTimingWheel_MoveTimerEarlier(t *testing.T) {
func TestTimingWheel_RemoveTimer(t *testing.T) { func TestTimingWheel_RemoveTimer(t *testing.T) {
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := newTimingWheelWithClock(testStep, 10, func(k, v interface{}) {}, ticker) tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {}, ticker)
tw.SetTimer("any", 3, testStep) tw.SetTimer("any", 3, testStep)
assert.NotPanics(t, func() { assert.NotPanics(t, func() {
tw.RemoveTimer("any") tw.RemoveTimer("any")
@@ -236,7 +236,7 @@ func TestTimingWheel_SetTimer(t *testing.T) {
} }
var actual int32 var actual int32
done := make(chan lang.PlaceholderType) done := make(chan lang.PlaceholderType)
tw, err := newTimingWheelWithClock(testStep, test.slots, func(key, value interface{}) { tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value any) {
assert.Equal(t, 1, key.(int)) assert.Equal(t, 1, key.(int))
assert.Equal(t, 2, value.(int)) assert.Equal(t, 2, value.(int))
actual = atomic.LoadInt32(&count) actual = atomic.LoadInt32(&count)
@@ -317,7 +317,7 @@ func TestTimingWheel_SetAndMoveThenStart(t *testing.T) {
} }
var actual int32 var actual int32
done := make(chan lang.PlaceholderType) done := make(chan lang.PlaceholderType)
tw, err := newTimingWheelWithClock(testStep, test.slots, func(key, value interface{}) { tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value any) {
actual = atomic.LoadInt32(&count) actual = atomic.LoadInt32(&count)
close(done) close(done)
}, ticker) }, ticker)
@@ -405,7 +405,7 @@ func TestTimingWheel_SetAndMoveTwice(t *testing.T) {
} }
var actual int32 var actual int32
done := make(chan lang.PlaceholderType) done := make(chan lang.PlaceholderType)
tw, err := newTimingWheelWithClock(testStep, test.slots, func(key, value interface{}) { tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value any) {
actual = atomic.LoadInt32(&count) actual = atomic.LoadInt32(&count)
close(done) close(done)
}, ticker) }, ticker)
@@ -486,7 +486,7 @@ func TestTimingWheel_ElapsedAndSet(t *testing.T) {
} }
var actual int32 var actual int32
done := make(chan lang.PlaceholderType) done := make(chan lang.PlaceholderType)
tw, err := newTimingWheelWithClock(testStep, test.slots, func(key, value interface{}) { tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value any) {
actual = atomic.LoadInt32(&count) actual = atomic.LoadInt32(&count)
close(done) close(done)
}, ticker) }, ticker)
@@ -577,7 +577,7 @@ func TestTimingWheel_ElapsedAndSetThenMove(t *testing.T) {
} }
var actual int32 var actual int32
done := make(chan lang.PlaceholderType) done := make(chan lang.PlaceholderType)
tw, err := newTimingWheelWithClock(testStep, test.slots, func(key, value interface{}) { tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value any) {
actual = atomic.LoadInt32(&count) actual = atomic.LoadInt32(&count)
close(done) close(done)
}, ticker) }, ticker)
@@ -612,7 +612,7 @@ func TestMoveAndRemoveTask(t *testing.T) {
} }
} }
var keys []int var keys []int
tw, _ := newTimingWheelWithClock(testStep, 10, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {
assert.Equal(t, "any", k) assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int)) assert.Equal(t, 3, v.(int))
keys = append(keys, v.(int)) keys = append(keys, v.(int))
@@ -629,10 +629,161 @@ func TestMoveAndRemoveTask(t *testing.T) {
assert.Equal(t, 0, len(keys)) assert.Equal(t, 0, len(keys))
} }
// TestTimingWheel_DrainClosureBug tests the closure capture bug in drainAll
// Issue: https://github.com/zeromicro/go-zero/issues/5314
func TestTimingWheel_DrainClosureBug(t *testing.T) {
ticker := timex.NewFakeTicker()
tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {}, ticker)
defer tw.Stop()
// Set multiple timers with different values
for i := 0; i < 10; i++ {
tw.SetTimer(i, i*10, testStep*5)
}
// Give time for timers to be set
time.Sleep(time.Millisecond * 100)
var mu sync.Mutex
received := make(map[int]int)
var wg sync.WaitGroup
wg.Add(10)
tw.Drain(func(key, value any) {
mu.Lock()
defer mu.Unlock()
k := key.(int)
v := value.(int)
received[k] = v
wg.Done()
})
wg.Wait()
// Check if all values match their keys
for k, v := range received {
expected := k * 10
assert.Equal(t, expected, v, "key %d should have value %d, got %d", k, expected, v)
}
}
// TestTimingWheel_RunTasksClosureBug tests the closure capture bug in runTasks
// Issue: https://github.com/zeromicro/go-zero/issues/5314
func TestTimingWheel_RunTasksClosureBug(t *testing.T) {
ticker := timex.NewFakeTicker()
var mu sync.Mutex
executed := make(map[int]int)
var wg sync.WaitGroup
tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {
mu.Lock()
defer mu.Unlock()
key := k.(int)
val := v.(int)
executed[key] = val
wg.Done()
}, ticker)
defer tw.Stop()
// Set multiple timers that should fire in the same tick
count := 10
wg.Add(count)
for i := 0; i < count; i++ {
tw.SetTimer(i, i*10, testStep)
}
// Advance ticker to trigger tasks
ticker.Tick()
// Wait for execution with timeout
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
// Success
case <-time.After(2 * time.Second):
t.Fatal("timeout waiting for tasks to execute")
}
// Verify all tasks executed with correct values
assert.Equal(t, count, len(executed), "should have executed all tasks")
for k, v := range executed {
expected := k * 10
assert.Equal(t, expected, v, "key %d should have value %d, got %d", k, expected, v)
}
}
// TestTimingWheel_RunTasksRaceCondition tests for race conditions in runTasks
// This test specifically targets the loop variable capture bug
func TestTimingWheel_RunTasksRaceCondition(t *testing.T) {
// Run multiple times to increase likelihood of catching the bug
for attempt := 0; attempt < 10; attempt++ {
t.Run("", func(t *testing.T) {
ticker := timex.NewFakeTicker()
var mu sync.Mutex
keyValues := make(map[int][]int)
var wg sync.WaitGroup
tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {
// Add small delay to increase chance of race
time.Sleep(time.Microsecond)
mu.Lock()
defer mu.Unlock()
key := k.(int)
val := v.(int)
keyValues[key] = append(keyValues[key], val)
wg.Done()
}, ticker)
defer tw.Stop()
// Set many timers rapidly to increase chance of race
count := 50
wg.Add(count)
for i := 0; i < count; i++ {
tw.SetTimer(i, i*100, testStep)
}
ticker.Tick()
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
case <-time.After(5 * time.Second):
t.Fatal("timeout waiting for tasks")
}
// Check for duplicates or wrong values
wrongCount := 0
for key, values := range keyValues {
assert.Equal(t, 1, len(values), "key %d should only execute once, got %v", key, values)
if len(values) > 0 {
expected := key * 100
if values[0] != expected {
wrongCount++
t.Logf("BUG DETECTED: key %d should have value %d, got %d", key, expected, values[0])
}
}
}
if wrongCount > 0 {
t.Errorf("Found %d tasks with wrong values due to closure bug", wrongCount)
}
})
}
}
func BenchmarkTimingWheel(b *testing.B) { func BenchmarkTimingWheel(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
tw, _ := NewTimingWheel(time.Second, 100, func(k, v interface{}) {}) tw, _ := NewTimingWheel(time.Second, 100, func(k, v any) {})
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
tw.SetTimer(i, i, time.Second) tw.SetTimer(i, i, time.Second)
tw.SetTimer(b.N+i, b.N+i, time.Second) tw.SetTimer(b.N+i, b.N+i, time.Second)

View File

@@ -5,20 +5,45 @@ import (
"log" "log"
"os" "os"
"path" "path"
"reflect"
"strings" "strings"
"github.com/zeromicro/go-zero/core/jsonx"
"github.com/zeromicro/go-zero/core/mapping" "github.com/zeromicro/go-zero/core/mapping"
"github.com/zeromicro/go-zero/internal/encoding"
) )
var loaders = map[string]func([]byte, interface{}) error{ const (
jsonTagKey = "json"
jsonTagSep = ','
)
var (
fillDefaultUnmarshaler = mapping.NewUnmarshaler(jsonTagKey, mapping.WithDefault())
loaders = map[string]func([]byte, any) error{
".json": LoadFromJsonBytes, ".json": LoadFromJsonBytes,
".json5": LoadFromJson5Bytes,
".toml": LoadFromTomlBytes, ".toml": LoadFromTomlBytes,
".yaml": LoadFromYamlBytes, ".yaml": LoadFromYamlBytes,
".yml": LoadFromYamlBytes, ".yml": LoadFromYamlBytes,
}
)
// children and mapField should not be both filled.
// named fields and map cannot be bound to the same field name.
type fieldInfo struct {
children map[string]*fieldInfo
mapField *fieldInfo
} }
// Load loads config into v from file, .json, .yaml and .yml are acceptable. // FillDefault fills the default values for the given v,
func Load(file string, v interface{}, opts ...Option) error { // and the premise is that the value of v must be guaranteed to be empty.
func FillDefault(v any) error {
return fillDefaultUnmarshaler.Unmarshal(map[string]any{}, v)
}
// Load loads config into v from file, .json, .json5, .toml, .yaml and .yml are acceptable.
func Load(file string, v any, opts ...Option) error {
content, err := os.ReadFile(file) content, err := os.ReadFile(file)
if err != nil { if err != nil {
return err return err
@@ -41,42 +66,314 @@ func Load(file string, v interface{}, opts ...Option) error {
return loader(content, v) return loader(content, v)
} }
// LoadConfig loads config into v from file, .json, .yaml and .yml are acceptable. // LoadConfig loads config into v from file, .json, .json5, .toml, .yaml and .yml are acceptable.
// Deprecated: use Load instead. // Deprecated: use Load instead.
func LoadConfig(file string, v interface{}, opts ...Option) error { func LoadConfig(file string, v any, opts ...Option) error {
return Load(file, v, opts...) return Load(file, v, opts...)
} }
// LoadFromJsonBytes loads config into v from content json bytes. // LoadFromJsonBytes loads config into v from content json bytes.
func LoadFromJsonBytes(content []byte, v interface{}) error { func LoadFromJsonBytes(content []byte, v any) error {
return mapping.UnmarshalJsonBytes(content, v) info, err := buildFieldsInfo(reflect.TypeOf(v), "")
if err != nil {
return err
}
var m map[string]any
if err = jsonx.Unmarshal(content, &m); err != nil {
return err
}
lowerCaseKeyMap := toLowerCaseKeyMap(m, info)
if err = mapping.UnmarshalJsonMap(lowerCaseKeyMap, v,
mapping.WithCanonicalKeyFunc(toLowerCase)); err != nil {
return err
}
return validate(v)
} }
// LoadConfigFromJsonBytes loads config into v from content json bytes. // LoadConfigFromJsonBytes loads config into v from content json bytes.
// Deprecated: use LoadFromJsonBytes instead. // Deprecated: use LoadFromJsonBytes instead.
func LoadConfigFromJsonBytes(content []byte, v interface{}) error { func LoadConfigFromJsonBytes(content []byte, v any) error {
return LoadFromJsonBytes(content, v) return LoadFromJsonBytes(content, v)
} }
// LoadFromTomlBytes loads config into v from content toml bytes. // LoadFromTomlBytes loads config into v from content toml bytes.
func LoadFromTomlBytes(content []byte, v interface{}) error { func LoadFromTomlBytes(content []byte, v any) error {
return mapping.UnmarshalTomlBytes(content, v) b, err := encoding.TomlToJson(content)
if err != nil {
return err
}
return LoadFromJsonBytes(b, v)
} }
// LoadFromYamlBytes loads config into v from content yaml bytes. // LoadFromYamlBytes loads config into v from content yaml bytes.
func LoadFromYamlBytes(content []byte, v interface{}) error { func LoadFromYamlBytes(content []byte, v any) error {
return mapping.UnmarshalYamlBytes(content, v) b, err := encoding.YamlToJson(content)
if err != nil {
return err
}
return LoadFromJsonBytes(b, v)
}
// LoadFromJson5Bytes loads config into v from content json5 bytes.
func LoadFromJson5Bytes(content []byte, v any) error {
b, err := encoding.Json5ToJson(content)
if err != nil {
return err
}
return LoadFromJsonBytes(b, v)
} }
// LoadConfigFromYamlBytes loads config into v from content yaml bytes. // LoadConfigFromYamlBytes loads config into v from content yaml bytes.
// Deprecated: use LoadFromYamlBytes instead. // Deprecated: use LoadFromYamlBytes instead.
func LoadConfigFromYamlBytes(content []byte, v interface{}) error { func LoadConfigFromYamlBytes(content []byte, v any) error {
return LoadFromYamlBytes(content, v) return LoadFromYamlBytes(content, v)
} }
// MustLoad loads config into v from path, exits on error. // MustLoad loads config into v from path, exits on error.
func MustLoad(path string, v interface{}, opts ...Option) { func MustLoad(path string, v any, opts ...Option) {
if err := Load(path, v, opts...); err != nil { if err := Load(path, v, opts...); err != nil {
log.Fatalf("error: config file %s, %s", path, err.Error()) log.Fatalf("error: config file %s, %s", path, err.Error())
} }
} }
func addOrMergeFields(info *fieldInfo, key string, child *fieldInfo, fullName string) error {
if prev, ok := info.children[key]; ok {
if child.mapField != nil {
return newConflictKeyError(fullName)
}
if err := mergeFields(prev, child.children, fullName); err != nil {
return err
}
} else {
info.children[key] = child
}
return nil
}
func buildAnonymousFieldInfo(info *fieldInfo, lowerCaseName string, ft reflect.Type, fullName string) error {
switch ft.Kind() {
case reflect.Struct:
fields, err := buildFieldsInfo(ft, fullName)
if err != nil {
return err
}
for k, v := range fields.children {
if err = addOrMergeFields(info, k, v, fullName); err != nil {
return err
}
}
case reflect.Map:
elemField, err := buildFieldsInfo(mapping.Deref(ft.Elem()), fullName)
if err != nil {
return err
}
if _, ok := info.children[lowerCaseName]; ok {
return newConflictKeyError(fullName)
}
info.children[lowerCaseName] = &fieldInfo{
children: make(map[string]*fieldInfo),
mapField: elemField,
}
default:
if _, ok := info.children[lowerCaseName]; ok {
return newConflictKeyError(fullName)
}
info.children[lowerCaseName] = &fieldInfo{
children: make(map[string]*fieldInfo),
}
}
return nil
}
func buildFieldsInfo(tp reflect.Type, fullName string) (*fieldInfo, error) {
tp = mapping.Deref(tp)
switch tp.Kind() {
case reflect.Struct:
return buildStructFieldsInfo(tp, fullName)
case reflect.Array, reflect.Slice, reflect.Map:
return buildFieldsInfo(mapping.Deref(tp.Elem()), fullName)
case reflect.Chan, reflect.Func:
return nil, fmt.Errorf("unsupported type: %s, fullName: %s", tp.Kind(), fullName)
default:
return &fieldInfo{
children: make(map[string]*fieldInfo),
}, nil
}
}
func buildNamedFieldInfo(info *fieldInfo, lowerCaseName string, ft reflect.Type, fullName string) error {
var finfo *fieldInfo
var err error
switch ft.Kind() {
case reflect.Struct:
finfo, err = buildFieldsInfo(ft, fullName)
if err != nil {
return err
}
case reflect.Array, reflect.Slice:
finfo, err = buildFieldsInfo(ft.Elem(), fullName)
if err != nil {
return err
}
case reflect.Map:
elemInfo, err := buildFieldsInfo(mapping.Deref(ft.Elem()), fullName)
if err != nil {
return err
}
finfo = &fieldInfo{
children: make(map[string]*fieldInfo),
mapField: elemInfo,
}
default:
finfo, err = buildFieldsInfo(ft, fullName)
if err != nil {
return err
}
}
return addOrMergeFields(info, lowerCaseName, finfo, fullName)
}
func buildStructFieldsInfo(tp reflect.Type, fullName string) (*fieldInfo, error) {
info := &fieldInfo{
children: make(map[string]*fieldInfo),
}
for i := 0; i < tp.NumField(); i++ {
field := tp.Field(i)
if !field.IsExported() {
continue
}
name := getTagName(field)
lowerCaseName := toLowerCase(name)
ft := mapping.Deref(field.Type)
// flatten anonymous fields
if field.Anonymous {
if err := buildAnonymousFieldInfo(info, lowerCaseName, ft,
getFullName(fullName, lowerCaseName)); err != nil {
return nil, err
}
} else if err := buildNamedFieldInfo(info, lowerCaseName, ft,
getFullName(fullName, lowerCaseName)); err != nil {
return nil, err
}
}
return info, nil
}
// getTagName get the tag name of the given field, if no tag name, use file.Name.
// field.Name is returned on tags like `json:""` and `json:",optional"`.
func getTagName(field reflect.StructField) string {
if tag, ok := field.Tag.Lookup(jsonTagKey); ok {
if pos := strings.IndexByte(tag, jsonTagSep); pos >= 0 {
tag = tag[:pos]
}
tag = strings.TrimSpace(tag)
if len(tag) > 0 {
return tag
}
}
return field.Name
}
func mergeFields(prev *fieldInfo, children map[string]*fieldInfo, fullName string) error {
if len(prev.children) == 0 || len(children) == 0 {
return newConflictKeyError(fullName)
}
// merge fields
for k, v := range children {
if _, ok := prev.children[k]; ok {
return newConflictKeyError(fullName)
}
prev.children[k] = v
}
return nil
}
func toLowerCase(s string) string {
return strings.ToLower(s)
}
func toLowerCaseInterface(v any, info *fieldInfo) any {
switch vv := v.(type) {
case map[string]any:
return toLowerCaseKeyMap(vv, info)
case []any:
arr := make([]any, 0, len(vv))
for _, vvv := range vv {
arr = append(arr, toLowerCaseInterface(vvv, info))
}
return arr
default:
return v
}
}
func toLowerCaseKeyMap(m map[string]any, info *fieldInfo) map[string]any {
res := make(map[string]any)
for k, v := range m {
ti, ok := info.children[k]
if ok {
res[k] = toLowerCaseInterface(v, ti)
continue
}
lk := toLowerCase(k)
if ti, ok = info.children[lk]; ok {
res[lk] = toLowerCaseInterface(v, ti)
} else if info.mapField != nil {
res[k] = toLowerCaseInterface(v, info.mapField)
} else if vv, ok := v.(map[string]any); ok {
res[k] = toLowerCaseKeyMap(vv, info)
} else {
res[k] = v
}
}
return res
}
type conflictKeyError struct {
key string
}
func newConflictKeyError(key string) conflictKeyError {
return conflictKeyError{key: key}
}
func (e conflictKeyError) Error() string {
return fmt.Sprintf("conflict key %s, pay attention to anonymous fields", e.key)
}
func getFullName(parent, child string) string {
if len(parent) == 0 {
return child
}
return parent + "." + child
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,6 @@ import (
// PropertyError represents a configuration error message. // PropertyError represents a configuration error message.
type PropertyError struct { type PropertyError struct {
error
message string message string
} }
@@ -46,7 +45,7 @@ func LoadProperties(filename string, opts ...Option) (Properties, error) {
raw := make(map[string]string) raw := make(map[string]string)
for i := range lines { for i := range lines {
pair := strings.Split(lines[i], "=") pair := strings.SplitN(lines[i], "=", 2)
if len(pair) != 2 { if len(pair) != 2 {
// invalid property format // invalid property format
return nil, &PropertyError{ return nil, &PropertyError{

View File

@@ -45,8 +45,7 @@ func TestPropertiesEnv(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
defer os.Remove(tmpfile) defer os.Remove(tmpfile)
os.Setenv("FOO", "2") t.Setenv("FOO", "2")
defer os.Unsetenv("FOO")
props, err := LoadProperties(tmpfile, UseEnv()) props, err := LoadProperties(tmpfile, UseEnv())
assert.Nil(t, err) assert.Nil(t, err)
@@ -93,3 +92,70 @@ func TestLoadBadFile(t *testing.T) {
_, err := LoadProperties("nosuchfile") _, err := LoadProperties("nosuchfile")
assert.NotNil(t, err) assert.NotNil(t, err)
} }
func TestProperties_valueWithEqualSymbols(t *testing.T) {
text := `# test with equal symbols in value
db.url=postgres://localhost:5432/db?param=value
math.equation=a=b=c
base64.data=SGVsbG8=World=Test=
url.with.params=http://example.com?foo=bar&baz=qux
empty.value=
key.with.space = value = with = equals`
tmpfile, err := fs.TempFilenameWithText(text)
assert.Nil(t, err)
defer os.Remove(tmpfile)
props, err := LoadProperties(tmpfile)
assert.Nil(t, err)
assert.Equal(t, "postgres://localhost:5432/db?param=value", props.GetString("db.url"))
assert.Equal(t, "a=b=c", props.GetString("math.equation"))
assert.Equal(t, "SGVsbG8=World=Test=", props.GetString("base64.data"))
assert.Equal(t, "http://example.com?foo=bar&baz=qux", props.GetString("url.with.params"))
assert.Equal(t, "", props.GetString("empty.value"))
assert.Equal(t, "value = with = equals", props.GetString("key.with.space"))
}
func TestProperties_edgeCases(t *testing.T) {
tests := []struct {
name string
content string
wantErr bool
errMsg string
}{
{
name: "no equal sign",
content: "invalid line without equal",
wantErr: true,
},
{
name: "only equal sign",
content: "=",
wantErr: false, // "=" 会被解析为空 key 和空 valuelen(pair) == 2是合法的
},
{
name: "empty key",
content: "=value",
wantErr: false, // 空 key 也会被 trim但 len(pair) == 2 所以不会报错
},
{
name: "equal at end",
content: "key.name=",
wantErr: false, // 空 value 是合法的
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpfile, err := fs.TempFilenameWithText(tt.content)
assert.Nil(t, err)
defer os.Remove(tmpfile)
_, err = LoadProperties(tmpfile)
if tt.wantErr {
assert.NotNil(t, err, "expected error for case: %s", tt.name)
} else {
assert.Nil(t, err, "unexpected error for case: %s", tt.name)
}
})
}
}

View File

@@ -4,6 +4,7 @@
```go ```go
type RestfulConf struct { type RestfulConf struct {
ServiceName string `json:",env=SERVICE_NAME"` // read from env automatically
Host string `json:",default=0.0.0.0"` Host string `json:",default=0.0.0.0"`
Port int Port int
LogMode string `json:",options=[file,console]"` LogMode string `json:",options=[file,console]"`
@@ -11,7 +12,7 @@ type RestfulConf struct {
MaxConns int `json:",default=10000"` MaxConns int `json:",default=10000"`
MaxBytes int64 `json:",default=1048576"` MaxBytes int64 `json:",default=1048576"`
Timeout time.Duration `json:",default=3s"` Timeout time.Duration `json:",default=3s"`
CpuThreshold int64 `json:",default=900,range=[0:1000]"` CpuThreshold int64 `json:",default=900,range=[0:1000)"`
} }
``` ```
@@ -21,20 +22,20 @@ type RestfulConf struct {
```yaml ```yaml
# most fields are optional or have default values # most fields are optional or have default values
Port: 8080 port: 8080
LogMode: console logMode: console
# you can use env settings # you can use env settings
MaxBytes: ${MAX_BYTES} maxBytes: ${MAX_BYTES}
``` ```
- toml example - toml example
```toml ```toml
# most fields are optional or have default values # most fields are optional or have default values
Port = 8_080 port = 8_080
LogMode = "console" logMode = "console"
# you can use env settings # you can use env settings
MaxBytes = "${MAX_BYTES}" maxBytes = "${MAX_BYTES}"
``` ```
3. Load the config from a file: 3. Load the config from a file:

12
core/conf/validate.go Normal file
View File

@@ -0,0 +1,12 @@
package conf
import "github.com/zeromicro/go-zero/core/validation"
// validate validates the value if it implements the Validator interface.
func validate(v any) error {
if val, ok := v.(validation.Validator); ok {
return val.Validate()
}
return nil
}

View File

@@ -0,0 +1,81 @@
package conf
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
type mockType int
func (m mockType) Validate() error {
if m < 10 {
return errors.New("invalid value")
}
return nil
}
type anotherMockType int
func Test_validate(t *testing.T) {
tests := []struct {
name string
v any
wantErr bool
}{
{
name: "invalid",
v: mockType(5),
wantErr: true,
},
{
name: "valid",
v: mockType(10),
wantErr: false,
},
{
name: "not validator",
v: anotherMockType(5),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validate(tt.v)
assert.Equal(t, tt.wantErr, err != nil)
})
}
}
type mockVal struct {
}
func (m mockVal) Validate() error {
return errors.New("invalid value")
}
func Test_validateValPtr(t *testing.T) {
tests := []struct {
name string
v any
wantErr bool
}{
{
name: "invalid",
v: mockVal{},
},
{
name: "invalid value",
v: &mockVal{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Error(t, validate(tt.v))
})
}
}

View File

@@ -0,0 +1,200 @@
package configurator
import (
"errors"
"fmt"
"reflect"
"strings"
"sync"
"sync/atomic"
"github.com/zeromicro/go-zero/core/configcenter/subscriber"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/mapping"
"github.com/zeromicro/go-zero/core/threading"
)
var (
errEmptyConfig = errors.New("empty config value")
errMissingUnmarshalerType = errors.New("missing unmarshaler type")
)
// Configurator is the interface for configuration center.
type Configurator[T any] interface {
// GetConfig returns the subscription value.
GetConfig() (T, error)
// AddListener adds a listener to the subscriber.
AddListener(listener func())
}
type (
// Config is the configuration for Configurator.
Config struct {
// Type is the value type, yaml, json or toml.
Type string `json:",default=yaml,options=[yaml,json,toml]"`
// Log is the flag to control logging.
Log bool `json:",default=true"`
}
configCenter[T any] struct {
conf Config
unmarshaler LoaderFn
subscriber subscriber.Subscriber
listeners []func()
lock sync.Mutex
snapshot atomic.Value
}
value[T any] struct {
data string
marshalData T
err error
}
)
// Configurator is the interface for configuration center.
var _ Configurator[any] = (*configCenter[any])(nil)
// MustNewConfigCenter returns a Configurator, exits on errors.
func MustNewConfigCenter[T any](c Config, subscriber subscriber.Subscriber) Configurator[T] {
cc, err := NewConfigCenter[T](c, subscriber)
logx.Must(err)
return cc
}
// NewConfigCenter returns a Configurator.
func NewConfigCenter[T any](c Config, subscriber subscriber.Subscriber) (Configurator[T], error) {
unmarshaler, ok := Unmarshaler(strings.ToLower(c.Type))
if !ok {
return nil, fmt.Errorf("unknown format: %s", c.Type)
}
cc := &configCenter[T]{
conf: c,
unmarshaler: unmarshaler,
subscriber: subscriber,
}
if err := cc.loadConfig(); err != nil {
return nil, err
}
if err := cc.subscriber.AddListener(cc.onChange); err != nil {
return nil, err
}
if _, err := cc.GetConfig(); err != nil {
return nil, err
}
return cc, nil
}
// AddListener adds listener to s.
func (c *configCenter[T]) AddListener(listener func()) {
c.lock.Lock()
defer c.lock.Unlock()
c.listeners = append(c.listeners, listener)
}
// GetConfig return structured config.
func (c *configCenter[T]) GetConfig() (T, error) {
v := c.value()
if v == nil || len(v.data) == 0 {
var empty T
return empty, errEmptyConfig
}
return v.marshalData, v.err
}
// Value returns the subscription value.
func (c *configCenter[T]) Value() string {
v := c.value()
if v == nil {
return ""
}
return v.data
}
func (c *configCenter[T]) loadConfig() error {
v, err := c.subscriber.Value()
if err != nil {
if c.conf.Log {
logx.Errorf("ConfigCenter loads changed configuration, error: %v", err)
}
return err
}
if c.conf.Log {
logx.Infof("ConfigCenter loads changed configuration, content [%s]", v)
}
c.snapshot.Store(c.genValue(v))
return nil
}
func (c *configCenter[T]) onChange() {
if err := c.loadConfig(); err != nil {
return
}
c.lock.Lock()
listeners := make([]func(), len(c.listeners))
copy(listeners, c.listeners)
c.lock.Unlock()
for _, l := range listeners {
threading.GoSafe(l)
}
}
func (c *configCenter[T]) value() *value[T] {
content := c.snapshot.Load()
if content == nil {
return nil
}
return content.(*value[T])
}
func (c *configCenter[T]) genValue(data string) *value[T] {
v := &value[T]{
data: data,
}
if len(data) == 0 {
return v
}
t := reflect.TypeOf(v.marshalData)
// if the type is nil, it means that the user has not set the type of the configuration.
if t == nil {
v.err = errMissingUnmarshalerType
return v
}
t = mapping.Deref(t)
switch t.Kind() {
case reflect.Struct, reflect.Array, reflect.Slice:
if err := c.unmarshaler([]byte(data), &v.marshalData); err != nil {
v.err = err
if c.conf.Log {
logx.Errorf("ConfigCenter unmarshal configuration failed, err: %+v, content [%s]",
err.Error(), data)
}
}
case reflect.String:
if str, ok := any(data).(T); ok {
v.marshalData = str
} else {
v.err = errMissingUnmarshalerType
}
default:
if c.conf.Log {
logx.Errorf("ConfigCenter unmarshal configuration missing unmarshaler for type: %s, content [%s]",
t.Kind(), data)
}
v.err = errMissingUnmarshalerType
}
return v
}

View File

@@ -0,0 +1,233 @@
package configurator
import (
"errors"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestNewConfigCenter(t *testing.T) {
_, err := NewConfigCenter[any](Config{
Log: true,
}, &mockSubscriber{})
assert.Error(t, err)
_, err = NewConfigCenter[any](Config{
Type: "json",
Log: true,
}, &mockSubscriber{})
assert.Error(t, err)
}
func TestConfigCenter_GetConfig(t *testing.T) {
mock := &mockSubscriber{}
type Data struct {
Name string `json:"name"`
}
mock.v = `{"name": "go-zero"}`
c1, err := NewConfigCenter[Data](Config{
Type: "json",
Log: true,
}, mock)
assert.NoError(t, err)
data, err := c1.GetConfig()
assert.NoError(t, err)
assert.Equal(t, "go-zero", data.Name)
mock.v = `{"name": "111"}`
c2, err := NewConfigCenter[Data](Config{Type: "json"}, mock)
assert.NoError(t, err)
mock.v = `{}`
c3, err := NewConfigCenter[string](Config{
Type: "json",
Log: true,
}, mock)
assert.NoError(t, err)
_, err = c3.GetConfig()
assert.NoError(t, err)
data, err = c2.GetConfig()
assert.NoError(t, err)
mock.lisErr = errors.New("mock error")
_, err = NewConfigCenter[Data](Config{
Type: "json",
Log: true,
}, mock)
assert.Error(t, err)
}
func TestConfigCenter_onChange(t *testing.T) {
mock := &mockSubscriber{}
type Data struct {
Name string `json:"name"`
}
mock.v = `{"name": "go-zero"}`
c1, err := NewConfigCenter[Data](Config{Type: "json", Log: true}, mock)
assert.NoError(t, err)
data, err := c1.GetConfig()
assert.NoError(t, err)
assert.Equal(t, "go-zero", data.Name)
mock.v = `{"name": "go-zero2"}`
mock.change()
data, err = c1.GetConfig()
assert.NoError(t, err)
assert.Equal(t, "go-zero2", data.Name)
mock.valErr = errors.New("mock error")
_, err = NewConfigCenter[Data](Config{Type: "json", Log: false}, mock)
assert.Error(t, err)
}
func TestConfigCenter_Value(t *testing.T) {
mock := &mockSubscriber{}
mock.v = "1234"
c, err := NewConfigCenter[string](Config{
Type: "json",
Log: true,
}, mock)
assert.NoError(t, err)
cc := c.(*configCenter[string])
assert.Equal(t, cc.Value(), "1234")
mock.valErr = errors.New("mock error")
_, err = NewConfigCenter[any](Config{
Type: "json",
Log: true,
}, mock)
assert.Error(t, err)
}
func TestConfigCenter_AddListener(t *testing.T) {
mock := &mockSubscriber{}
mock.v = "1234"
c, err := NewConfigCenter[string](Config{
Type: "json",
Log: true,
}, mock)
assert.NoError(t, err)
cc := c.(*configCenter[string])
var a, b int
var mutex sync.Mutex
cc.AddListener(func() {
mutex.Lock()
a = 1
mutex.Unlock()
})
cc.AddListener(func() {
mutex.Lock()
b = 2
mutex.Unlock()
})
assert.Equal(t, 2, len(cc.listeners))
mock.change()
time.Sleep(time.Millisecond * 100)
mutex.Lock()
assert.Equal(t, 1, a)
assert.Equal(t, 2, b)
mutex.Unlock()
}
func TestConfigCenter_genValue(t *testing.T) {
t.Run("data is empty", func(t *testing.T) {
c := &configCenter[string]{
unmarshaler: registry.unmarshalers["json"],
conf: Config{Log: true},
}
v := c.genValue("")
assert.Equal(t, "", v.data)
})
t.Run("invalid template type", func(t *testing.T) {
c := &configCenter[any]{
unmarshaler: registry.unmarshalers["json"],
conf: Config{Log: true},
}
v := c.genValue("xxxx")
assert.Equal(t, errMissingUnmarshalerType, v.err)
})
t.Run("unsupported template type", func(t *testing.T) {
c := &configCenter[int]{
unmarshaler: registry.unmarshalers["json"],
conf: Config{Log: true},
}
v := c.genValue("1")
assert.Equal(t, errMissingUnmarshalerType, v.err)
})
t.Run("supported template string type", func(t *testing.T) {
c := &configCenter[string]{
unmarshaler: registry.unmarshalers["json"],
conf: Config{Log: true},
}
v := c.genValue("12345")
assert.NoError(t, v.err)
assert.Equal(t, "12345", v.data)
})
t.Run("unmarshal fail", func(t *testing.T) {
c := &configCenter[struct {
Name string `json:"name"`
}]{
unmarshaler: registry.unmarshalers["json"],
conf: Config{Log: true},
}
v := c.genValue(`{"name":"new name}`)
assert.Equal(t, `{"name":"new name}`, v.data)
assert.Error(t, v.err)
})
t.Run("success", func(t *testing.T) {
c := &configCenter[struct {
Name string `json:"name"`
}]{
unmarshaler: registry.unmarshalers["json"],
conf: Config{Log: true},
}
v := c.genValue(`{"name":"new name"}`)
assert.Equal(t, `{"name":"new name"}`, v.data)
assert.Equal(t, "new name", v.marshalData.Name)
assert.NoError(t, v.err)
})
}
type mockSubscriber struct {
v string
lisErr, valErr error
listener func()
}
func (m *mockSubscriber) AddListener(listener func()) error {
m.listener = listener
return m.lisErr
}
func (m *mockSubscriber) Value() (string, error) {
return m.v, m.valErr
}
func (m *mockSubscriber) change() {
if m.listener != nil {
m.listener()
}
}

View File

@@ -0,0 +1,115 @@
package subscriber
import (
"sync"
"sync/atomic"
"github.com/zeromicro/go-zero/core/discov"
"github.com/zeromicro/go-zero/core/logx"
)
type (
// etcdSubscriber is a subscriber that subscribes to etcd.
etcdSubscriber struct {
*discov.Subscriber
}
// EtcdConf is the configuration for etcd.
EtcdConf = discov.EtcdConf
)
// MustNewEtcdSubscriber returns an etcd Subscriber, exits on errors.
func MustNewEtcdSubscriber(conf EtcdConf) Subscriber {
s, err := NewEtcdSubscriber(conf)
logx.Must(err)
return s
}
// NewEtcdSubscriber returns an etcd Subscriber.
func NewEtcdSubscriber(conf EtcdConf) (Subscriber, error) {
opts := buildSubOptions(conf)
s, err := discov.NewSubscriber(conf.Hosts, conf.Key, opts...)
if err != nil {
return nil, err
}
return &etcdSubscriber{Subscriber: s}, nil
}
// buildSubOptions constructs the options for creating a new etcd subscriber.
func buildSubOptions(conf EtcdConf) []discov.SubOption {
opts := []discov.SubOption{
discov.WithExactMatch(),
discov.WithContainer(newContainer()),
}
if len(conf.User) > 0 {
opts = append(opts, discov.WithSubEtcdAccount(conf.User, conf.Pass))
}
if len(conf.CertFile) > 0 || len(conf.CertKeyFile) > 0 || len(conf.CACertFile) > 0 {
opts = append(opts, discov.WithSubEtcdTLS(conf.CertFile, conf.CertKeyFile,
conf.CACertFile, conf.InsecureSkipVerify))
}
return opts
}
// AddListener adds a listener to the subscriber.
func (s *etcdSubscriber) AddListener(listener func()) error {
s.Subscriber.AddListener(listener)
return nil
}
// Value returns the value of the subscriber.
func (s *etcdSubscriber) Value() (string, error) {
vs := s.Subscriber.Values()
if len(vs) > 0 {
return vs[len(vs)-1], nil
}
return "", nil
}
type container struct {
value atomic.Value
listeners []func()
lock sync.Mutex
}
func newContainer() *container {
return &container{}
}
func (c *container) OnAdd(kv discov.KV) {
c.value.Store([]string{kv.Val})
c.notifyChange()
}
func (c *container) OnDelete(_ discov.KV) {
c.value.Store([]string(nil))
c.notifyChange()
}
func (c *container) AddListener(listener func()) {
c.lock.Lock()
c.listeners = append(c.listeners, listener)
c.lock.Unlock()
}
func (c *container) GetValues() []string {
if vals, ok := c.value.Load().([]string); ok {
return vals
}
return []string(nil)
}
func (c *container) notifyChange() {
c.lock.Lock()
listeners := append(([]func())(nil), c.listeners...)
c.lock.Unlock()
for _, listener := range listeners {
listener()
}
}

View File

@@ -0,0 +1,186 @@
package subscriber
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/discov"
)
const (
actionAdd = iota
actionDel
)
func TestConfigCenterContainer(t *testing.T) {
type action struct {
act int
key string
val string
}
tests := []struct {
name string
do []action
expect []string
}{
{
name: "add one",
do: []action{
{
act: actionAdd,
key: "first",
val: "a",
},
},
expect: []string{
"a",
},
},
{
name: "add two",
do: []action{
{
act: actionAdd,
key: "first",
val: "a",
},
{
act: actionAdd,
key: "second",
val: "b",
},
},
expect: []string{
"b",
},
},
{
name: "add two, delete one",
do: []action{
{
act: actionAdd,
key: "first",
val: "a",
},
{
act: actionAdd,
key: "second",
val: "b",
},
{
act: actionDel,
key: "first",
},
},
expect: []string(nil),
},
{
name: "add two, delete two",
do: []action{
{
act: actionAdd,
key: "first",
val: "a",
},
{
act: actionAdd,
key: "second",
val: "b",
},
{
act: actionDel,
key: "first",
},
{
act: actionDel,
key: "second",
},
},
expect: []string(nil),
},
{
name: "add two, dup values",
do: []action{
{
act: actionAdd,
key: "first",
val: "a",
},
{
act: actionAdd,
key: "second",
val: "b",
},
{
act: actionAdd,
key: "third",
val: "a",
},
},
expect: []string{"a"},
},
{
name: "add three, dup values, delete two, add one",
do: []action{
{
act: actionAdd,
key: "first",
val: "a",
},
{
act: actionAdd,
key: "second",
val: "b",
},
{
act: actionAdd,
key: "third",
val: "a",
},
{
act: actionDel,
key: "first",
},
{
act: actionDel,
key: "second",
},
{
act: actionAdd,
key: "forth",
val: "c",
},
},
expect: []string{"c"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var changed bool
c := newContainer()
c.AddListener(func() {
changed = true
})
assert.Nil(t, c.GetValues())
assert.False(t, changed)
for _, order := range test.do {
if order.act == actionAdd {
c.OnAdd(discov.KV{
Key: order.key,
Val: order.val,
})
} else {
c.OnDelete(discov.KV{
Key: order.key,
Val: order.val,
})
}
}
assert.True(t, changed)
assert.ElementsMatch(t, test.expect, c.GetValues())
})
}
}

View File

@@ -0,0 +1,9 @@
package subscriber
// Subscriber is the interface for configcenter subscribers.
type Subscriber interface {
// AddListener adds a listener to the subscriber.
AddListener(listener func()) error
// Value returns the value of the subscriber.
Value() (string, error)
}

View File

@@ -0,0 +1,41 @@
package configurator
import (
"sync"
"github.com/zeromicro/go-zero/core/conf"
)
var registry = &unmarshalerRegistry{
unmarshalers: map[string]LoaderFn{
"json": conf.LoadFromJsonBytes,
"toml": conf.LoadFromTomlBytes,
"yaml": conf.LoadFromYamlBytes,
},
}
type (
// LoaderFn is the function type for loading configuration.
LoaderFn func([]byte, any) error
// unmarshalerRegistry is the registry for unmarshalers.
unmarshalerRegistry struct {
unmarshalers map[string]LoaderFn
mu sync.RWMutex
}
)
// RegisterUnmarshaler registers an unmarshaler.
func RegisterUnmarshaler(name string, fn LoaderFn) {
registry.mu.Lock()
defer registry.mu.Unlock()
registry.unmarshalers[name] = fn
}
// Unmarshaler returns the unmarshaler by name.
func Unmarshaler(name string) (LoaderFn, bool) {
registry.mu.RLock()
defer registry.mu.RUnlock()
fn, ok := registry.unmarshalers[name]
return fn, ok
}

View File

@@ -0,0 +1,28 @@
package configurator
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRegisterUnmarshaler(t *testing.T) {
RegisterUnmarshaler("test", func(data []byte, v interface{}) error {
return nil
})
_, ok := Unmarshaler("test")
assert.True(t, ok)
_, ok = Unmarshaler("test2")
assert.False(t, ok)
_, ok = Unmarshaler("json")
assert.True(t, ok)
_, ok = Unmarshaler("toml")
assert.True(t, ok)
_, ok = Unmarshaler("yaml")
assert.True(t, ok)
}

View File

@@ -14,13 +14,13 @@ type contextValuer struct {
context.Context context.Context
} }
func (cv contextValuer) Value(key string) (interface{}, bool) { func (cv contextValuer) Value(key string) (any, bool) {
v := cv.Context.Value(key) v := cv.Context.Value(key)
return v, v != nil return v, v != nil
} }
// For unmarshals ctx into v. // For unmarshals ctx into v.
func For(ctx context.Context, v interface{}) error { func For(ctx context.Context, v any) error {
return unmarshaler.UnmarshalValuer(contextValuer{ return unmarshaler.UnmarshalValuer(contextValuer{
Context: ctx, Context: ctx,
}, v) }, v)

View File

@@ -13,6 +13,7 @@ var (
type EtcdConf struct { type EtcdConf struct {
Hosts []string Hosts []string
Key string Key string
ID int64 `json:",optional"`
User string `json:",optional"` User string `json:",optional"`
Pass string `json:",optional"` Pass string `json:",optional"`
CertFile string `json:",optional"` CertFile string `json:",optional"`
@@ -26,6 +27,11 @@ func (c EtcdConf) HasAccount() bool {
return len(c.User) > 0 && len(c.Pass) > 0 return len(c.User) > 0 && len(c.Pass) > 0
} }
// HasID returns if ID provided.
func (c EtcdConf) HasID() bool {
return c.ID > 0
}
// HasTLS returns if TLS CertFile/CertKeyFile/CACertFile are provided. // HasTLS returns if TLS CertFile/CertKeyFile/CACertFile are provided.
func (c EtcdConf) HasTLS() bool { func (c EtcdConf) HasTLS() bool {
return len(c.CertFile) > 0 && len(c.CertKeyFile) > 0 && len(c.CACertFile) > 0 return len(c.CertFile) > 0 && len(c.CertKeyFile) > 0 && len(c.CACertFile) > 0

View File

@@ -80,3 +80,90 @@ func TestEtcdConf_HasAccount(t *testing.T) {
assert.Equal(t, test.hasAccount, test.EtcdConf.HasAccount()) assert.Equal(t, test.hasAccount, test.EtcdConf.HasAccount())
} }
} }
func TestEtcdConf_HasID(t *testing.T) {
tests := []struct {
EtcdConf
hasServerID bool
}{
{
EtcdConf: EtcdConf{
Hosts: []string{"any"},
ID: -1,
},
hasServerID: false,
},
{
EtcdConf: EtcdConf{
Hosts: []string{"any"},
ID: 0,
},
hasServerID: false,
},
{
EtcdConf: EtcdConf{
Hosts: []string{"any"},
ID: 10000,
},
hasServerID: true,
},
}
for _, test := range tests {
assert.Equal(t, test.hasServerID, test.EtcdConf.HasID())
}
}
func TestEtcdConf_HasTLS(t *testing.T) {
tests := []struct {
name string
conf EtcdConf
want bool
}{
{
name: "empty config",
conf: EtcdConf{},
want: false,
},
{
name: "missing CertFile",
conf: EtcdConf{
CertKeyFile: "key",
CACertFile: "ca",
},
want: false,
},
{
name: "missing CertKeyFile",
conf: EtcdConf{
CertFile: "cert",
CACertFile: "ca",
},
want: false,
},
{
name: "missing CACertFile",
conf: EtcdConf{
CertFile: "cert",
CertKeyFile: "key",
},
want: false,
},
{
name: "valid config",
conf: EtcdConf{
CertFile: "cert",
CertKeyFile: "key",
CACertFile: "ca",
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.conf.HasTLS()
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -1,12 +1,85 @@
package internal package internal
import ( import (
"os"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/stringx" "github.com/zeromicro/go-zero/core/stringx"
) )
const (
certContent = `-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUEg9GVO2oaPn+YSmiqmFIuAo10WIwDQYJKoZIhvcNAQEM
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMzAzMTExMzIxMjNaGA8yMTIz
MDIxNTEzMjEyM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBALplXlWsIf0O/IgnIplmiZHKGnxyfyufyE2FBRNk
OofRqbKuPH8GNqbkvZm7N29fwTDAQ+mViAggCkDht4hOzoWJMA7KYJt8JnTSWL48
M1lcrpc9DL2gszC/JF/FGvyANbBtLklkZPFBGdHUX14pjrT937wqPtm+SqUHSvRT
B7bmwmm2drRcmhpVm98LSlV7uQ2EgnJgsLjBPITKUejLmVLHfgX0RwQ2xIpX9pS4
FCe1BTacwl2gGp7Mje7y4Mfv3o0ArJW6Tuwbjx59ZXwb1KIP71b7bT04AVS8ZeYO
UMLKKuB5UR9x9Rn6cLXOTWBpcMVyzDgrAFLZjnE9LPUolZMCAwEAAaNRME8wHwYD
VR0jBBgwFoAUeW8w8pmhncbRgTsl48k4/7wnfx8wCQYDVR0TBAIwADALBgNVHQ8E
BAMCBPAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBDAUAA4IBAQAI
y9xaoS88CLPBsX6mxfcTAFVfGNTRW9VN9Ng1cCnUR+YGoXGM/l+qP4f7p8ocdGwK
iYZErVTzXYIn+D27//wpY3klJk3gAnEUBT3QRkStBw7XnpbeZ2oPBK+cmDnCnZPS
BIF1wxPX7vIgaxs5Zsdqwk3qvZ4Djr2wP7LabNWTLSBKgQoUY45Liw6pffLwcGF9
UKlu54bvGze2SufISCR3ib+I+FLvqpvJhXToZWYb/pfI/HccuCL1oot1x8vx6DQy
U+TYxlZsKS5mdNxAX3dqEkEMsgEi+g/tzDPXJImfeCGGBhIOXLm8SRypiuGdEbc9
xkWYxRPegajuEZGvCqVs
-----END CERTIFICATE-----`
keyContent = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAumVeVawh/Q78iCcimWaJkcoafHJ/K5/ITYUFE2Q6h9Gpsq48
fwY2puS9mbs3b1/BMMBD6ZWICCAKQOG3iE7OhYkwDspgm3wmdNJYvjwzWVyulz0M
vaCzML8kX8Ua/IA1sG0uSWRk8UEZ0dRfXimOtP3fvCo+2b5KpQdK9FMHtubCabZ2
tFyaGlWb3wtKVXu5DYSCcmCwuME8hMpR6MuZUsd+BfRHBDbEilf2lLgUJ7UFNpzC
XaAansyN7vLgx+/ejQCslbpO7BuPHn1lfBvUog/vVvttPTgBVLxl5g5Qwsoq4HlR
H3H1Gfpwtc5NYGlwxXLMOCsAUtmOcT0s9SiVkwIDAQABAoIBAD5meTJNMgO55Kjg
ESExxpRcCIno+tHr5+6rvYtEXqPheOIsmmwb9Gfi4+Z3WpOaht5/Pz0Ppj6yGzyl
U//6AgGKb+BDuBvVcDpjwPnOxZIBCSHwejdxeQu0scSuA97MPS0XIAvJ5FEv7ijk
5Bht6SyGYURpECltHygoTNuGgGqmO+McCJRLE9L09lTBI6UQ/JQwWJqSr7wx6iPU
M1Ze/srIV+7cyEPu6i0DGjS1gSQKkX68Lqn1w6oE290O+OZvleO0gZ02fLDWCZke
aeD9+EU/Pw+rqm3H6o0szOFIpzhRp41FUdW9sybB3Yp3u7c/574E+04Z/e30LMKs
TCtE1QECgYEA3K7KIpw0NH2HXL5C3RHcLmr204xeBfS70riBQQuVUgYdmxak2ima
80RInskY8hRhSGTg0l+VYIH8cmjcUyqMSOELS5XfRH99r4QPiK8AguXg80T4VumY
W3Pf+zEC2ssgP/gYthV0g0Xj5m2QxktOF9tRw5nkg739ZR4dI9lm/iECgYEA2Dnf
uwEDGqHiQRF6/fh5BG/nGVMvrefkqx6WvTJQ3k/M/9WhxB+lr/8yH46TuS8N2b29
FoTf3Mr9T7pr/PWkOPzoY3P56nYbKU8xSwCim9xMzhBMzj8/N9ukJvXy27/VOz56
eQaKqnvdXNGtPJrIMDGHps2KKWlKLyAlapzjVTMCgYAA/W++tACv85g13EykfT4F
n0k4LbsGP9DP4zABQLIMyiY72eAncmRVjwrcW36XJ2xATOONTgx3gF3HjZzfaqNy
eD/6uNNllUTVEryXGmHgNHPL45VRnn6memCY2eFvZdXhM5W4y2PYaunY0MkDercA
+GTngbs6tBF88KOk04bYwQKBgFl68cRgsdkmnwwQYNaTKfmVGYzYaQXNzkqmWPko
xmCJo6tHzC7ubdG8iRCYHzfmahPuuj6EdGPZuSRyYFgJi5Ftz/nAN+84OxtIQ3zn
YWOgskQgaLh9YfsKsQ7Sf1NDOsnOnD5TX7UXl07fEpLe9vNCvAFiU8e5Y9LGudU5
4bYTAoGBAMdX3a3bXp4cZvXNBJ/QLVyxC6fP1Q4haCR1Od3m+T00Jth2IX2dk/fl
p6xiJT1av5JtYabv1dFKaXOS5s1kLGGuCCSKpkvFZm826aQ2AFm0XGqEQDLeei5b
A52Kpy/YJ+RkG4BTFtAooFq6DmA0cnoP6oPvG2h6XtDJwDTPInJb
-----END RSA PRIVATE KEY-----`
caContent = `-----BEGIN CERTIFICATE-----
MIIDbTCCAlWgAwIBAgIUBJvFoCowKich7MMfseJ+DYzzirowDQYJKoZIhvcNAQEM
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMzAzMTExMzIxMDNaGA8yMTIz
MDIxNTEzMjEwM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAO4to2YMYj0bxgr2FCiweSTSFuPx33zSw2x/s9Wf
OR41bm2DFsyYT5f3sOIKlXZEdLmOKty2e3ho3yC0EyNpVHdykkkHT3aDI17quZax
kYi/URqqtl1Z08A22txolc04hAZisg2BypGi3vql81UW1t3zyloGnJoIAeXR9uca
ljP6Bk3bwsxoVBLi1JtHrO0hHLQaeHmKhAyrys06X0LRdn7Px48yRZlt6FaLSa8X
YiRM0G44bVy/h6BkoQjMYGwVmCVk6zjJ9U7ZPFqdnDMNxAfR+hjDnYodqdLDMTTR
1NPVrnEnNwFx0AMLvgt/ba/45vZCEAmSZnFXFAJJcM7ai9ECAwEAAaNTMFEwHQYD
VR0OBBYEFHlvMPKZoZ3G0YE7JePJOP+8J38fMB8GA1UdIwQYMBaAFHlvMPKZoZ3G
0YE7JePJOP+8J38fMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggEB
AMX8dNulADOo9uQgBMyFb9TVra7iY0zZjzv4GY5XY7scd52n6CnfAPvYBBDnTr/O
BgNp5jaujb4+9u/2qhV3f9n+/3WOb2CmPehBgVSzlXqHeQ9lshmgwZPeem2T+8Tm
Nnc/xQnsUfCFszUDxpkr55+aLVM22j02RWqcZ4q7TAaVYL+kdFVMc8FoqG/0ro6A
BjE/Qn0Nn7ciX1VUjDt8l+k7ummPJTmzdi6i6E4AwO9dzrGNgGJ4aWL8cC6xYcIX
goVIRTFeONXSDno/oPjWHpIPt7L15heMpKBHNuzPkKx2YVqPHE5QZxWfS+Lzgx+Q
E2oTTM0rYKOZ8p6000mhvKI=
-----END CERTIFICATE-----`
)
func TestAccount(t *testing.T) { func TestAccount(t *testing.T) {
endpoints := []string{ endpoints := []string{
"192.168.0.2:2379", "192.168.0.2:2379",
@@ -32,3 +105,34 @@ func TestAccount(t *testing.T) {
assert.Equal(t, username, account.User) assert.Equal(t, username, account.User)
assert.Equal(t, anotherPassword, account.Pass) assert.Equal(t, anotherPassword, account.Pass)
} }
func TestTLSMethods(t *testing.T) {
certFile := createTempFile(t, []byte(certContent))
defer os.Remove(certFile)
keyFile := createTempFile(t, []byte(keyContent))
defer os.Remove(keyFile)
caFile := createTempFile(t, []byte(caContent))
defer os.Remove(caFile)
assert.NoError(t, AddTLS([]string{"foo"}, certFile, keyFile, caFile, false))
cfg, ok := GetTLS([]string{"foo"})
assert.True(t, ok)
assert.NotNil(t, cfg)
assert.Error(t, AddTLS([]string{"bar"}, "bad-file", keyFile, caFile, false))
assert.Error(t, AddTLS([]string{"bar"}, certFile, keyFile, "bad-file", false))
}
func createTempFile(t *testing.T, body []byte) string {
tmpFile, err := os.CreateTemp(os.TempDir(), "go-unit-*.tmp")
if err != nil {
t.Fatal(err)
}
tmpFile.Close()
if err = os.WriteFile(tmpFile.Name(), body, os.ModePerm); err != nil {
t.Fatal(err)
}
return tmpFile.Name()
}

View File

@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: etcdclient.go // Source: etcdclient.go
//
// Generated by this command:
//
// mockgen -package internal -destination etcdclient_mock.go -source etcdclient.go EtcdClient
//
// Package internal is a generated GoMock package. // Package internal is a generated GoMock package.
package internal package internal
@@ -8,35 +13,36 @@ import (
context "context" context "context"
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock"
clientv3 "go.etcd.io/etcd/client/v3" clientv3 "go.etcd.io/etcd/client/v3"
gomock "go.uber.org/mock/gomock"
grpc "google.golang.org/grpc" grpc "google.golang.org/grpc"
) )
// MockEtcdClient is a mock of EtcdClient interface // MockEtcdClient is a mock of EtcdClient interface.
type MockEtcdClient struct { type MockEtcdClient struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockEtcdClientMockRecorder recorder *MockEtcdClientMockRecorder
isgomock struct{}
} }
// MockEtcdClientMockRecorder is the mock recorder for MockEtcdClient // MockEtcdClientMockRecorder is the mock recorder for MockEtcdClient.
type MockEtcdClientMockRecorder struct { type MockEtcdClientMockRecorder struct {
mock *MockEtcdClient mock *MockEtcdClient
} }
// NewMockEtcdClient creates a new mock instance // NewMockEtcdClient creates a new mock instance.
func NewMockEtcdClient(ctrl *gomock.Controller) *MockEtcdClient { func NewMockEtcdClient(ctrl *gomock.Controller) *MockEtcdClient {
mock := &MockEtcdClient{ctrl: ctrl} mock := &MockEtcdClient{ctrl: ctrl}
mock.recorder = &MockEtcdClientMockRecorder{mock} mock.recorder = &MockEtcdClientMockRecorder{mock}
return mock return mock
} }
// EXPECT returns an object that allows the caller to indicate expected use // EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockEtcdClient) EXPECT() *MockEtcdClientMockRecorder { func (m *MockEtcdClient) EXPECT() *MockEtcdClientMockRecorder {
return m.recorder return m.recorder
} }
// ActiveConnection mocks base method // ActiveConnection mocks base method.
func (m *MockEtcdClient) ActiveConnection() *grpc.ClientConn { func (m *MockEtcdClient) ActiveConnection() *grpc.ClientConn {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ActiveConnection") ret := m.ctrl.Call(m, "ActiveConnection")
@@ -44,13 +50,13 @@ func (m *MockEtcdClient) ActiveConnection() *grpc.ClientConn {
return ret0 return ret0
} }
// ActiveConnection indicates an expected call of ActiveConnection // ActiveConnection indicates an expected call of ActiveConnection.
func (mr *MockEtcdClientMockRecorder) ActiveConnection() *gomock.Call { func (mr *MockEtcdClientMockRecorder) ActiveConnection() *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActiveConnection", reflect.TypeOf((*MockEtcdClient)(nil).ActiveConnection)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActiveConnection", reflect.TypeOf((*MockEtcdClient)(nil).ActiveConnection))
} }
// Close mocks base method // Close mocks base method.
func (m *MockEtcdClient) Close() error { func (m *MockEtcdClient) Close() error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Close") ret := m.ctrl.Call(m, "Close")
@@ -58,13 +64,13 @@ func (m *MockEtcdClient) Close() error {
return ret0 return ret0
} }
// Close indicates an expected call of Close // Close indicates an expected call of Close.
func (mr *MockEtcdClientMockRecorder) Close() *gomock.Call { func (mr *MockEtcdClientMockRecorder) Close() *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockEtcdClient)(nil).Close)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockEtcdClient)(nil).Close))
} }
// Ctx mocks base method // Ctx mocks base method.
func (m *MockEtcdClient) Ctx() context.Context { func (m *MockEtcdClient) Ctx() context.Context {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Ctx") ret := m.ctrl.Call(m, "Ctx")
@@ -72,16 +78,16 @@ func (m *MockEtcdClient) Ctx() context.Context {
return ret0 return ret0
} }
// Ctx indicates an expected call of Ctx // Ctx indicates an expected call of Ctx.
func (mr *MockEtcdClientMockRecorder) Ctx() *gomock.Call { func (mr *MockEtcdClientMockRecorder) Ctx() *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ctx", reflect.TypeOf((*MockEtcdClient)(nil).Ctx)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ctx", reflect.TypeOf((*MockEtcdClient)(nil).Ctx))
} }
// Get mocks base method // Get mocks base method.
func (m *MockEtcdClient) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) { func (m *MockEtcdClient) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{ctx, key} varargs := []any{ctx, key}
for _, a := range opts { for _, a := range opts {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
@@ -91,14 +97,14 @@ func (m *MockEtcdClient) Get(ctx context.Context, key string, opts ...clientv3.O
return ret0, ret1 return ret0, ret1
} }
// Get indicates an expected call of Get // Get indicates an expected call of Get.
func (mr *MockEtcdClientMockRecorder) Get(ctx, key interface{}, opts ...interface{}) *gomock.Call { func (mr *MockEtcdClientMockRecorder) Get(ctx, key any, opts ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{ctx, key}, opts...) varargs := append([]any{ctx, key}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockEtcdClient)(nil).Get), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockEtcdClient)(nil).Get), varargs...)
} }
// Grant mocks base method // Grant mocks base method.
func (m *MockEtcdClient) Grant(ctx context.Context, ttl int64) (*clientv3.LeaseGrantResponse, error) { func (m *MockEtcdClient) Grant(ctx context.Context, ttl int64) (*clientv3.LeaseGrantResponse, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Grant", ctx, ttl) ret := m.ctrl.Call(m, "Grant", ctx, ttl)
@@ -107,13 +113,13 @@ func (m *MockEtcdClient) Grant(ctx context.Context, ttl int64) (*clientv3.LeaseG
return ret0, ret1 return ret0, ret1
} }
// Grant indicates an expected call of Grant // Grant indicates an expected call of Grant.
func (mr *MockEtcdClientMockRecorder) Grant(ctx, ttl interface{}) *gomock.Call { func (mr *MockEtcdClientMockRecorder) Grant(ctx, ttl any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Grant", reflect.TypeOf((*MockEtcdClient)(nil).Grant), ctx, ttl) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Grant", reflect.TypeOf((*MockEtcdClient)(nil).Grant), ctx, ttl)
} }
// KeepAlive mocks base method // KeepAlive mocks base method.
func (m *MockEtcdClient) KeepAlive(ctx context.Context, id clientv3.LeaseID) (<-chan *clientv3.LeaseKeepAliveResponse, error) { func (m *MockEtcdClient) KeepAlive(ctx context.Context, id clientv3.LeaseID) (<-chan *clientv3.LeaseKeepAliveResponse, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "KeepAlive", ctx, id) ret := m.ctrl.Call(m, "KeepAlive", ctx, id)
@@ -122,16 +128,16 @@ func (m *MockEtcdClient) KeepAlive(ctx context.Context, id clientv3.LeaseID) (<-
return ret0, ret1 return ret0, ret1
} }
// KeepAlive indicates an expected call of KeepAlive // KeepAlive indicates an expected call of KeepAlive.
func (mr *MockEtcdClientMockRecorder) KeepAlive(ctx, id interface{}) *gomock.Call { func (mr *MockEtcdClientMockRecorder) KeepAlive(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeepAlive", reflect.TypeOf((*MockEtcdClient)(nil).KeepAlive), ctx, id) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeepAlive", reflect.TypeOf((*MockEtcdClient)(nil).KeepAlive), ctx, id)
} }
// Put mocks base method // Put mocks base method.
func (m *MockEtcdClient) Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error) { func (m *MockEtcdClient) Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{ctx, key, val} varargs := []any{ctx, key, val}
for _, a := range opts { for _, a := range opts {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
@@ -141,14 +147,14 @@ func (m *MockEtcdClient) Put(ctx context.Context, key, val string, opts ...clien
return ret0, ret1 return ret0, ret1
} }
// Put indicates an expected call of Put // Put indicates an expected call of Put.
func (mr *MockEtcdClientMockRecorder) Put(ctx, key, val interface{}, opts ...interface{}) *gomock.Call { func (mr *MockEtcdClientMockRecorder) Put(ctx, key, val any, opts ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{ctx, key, val}, opts...) varargs := append([]any{ctx, key, val}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockEtcdClient)(nil).Put), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockEtcdClient)(nil).Put), varargs...)
} }
// Revoke mocks base method // Revoke mocks base method.
func (m *MockEtcdClient) Revoke(ctx context.Context, id clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error) { func (m *MockEtcdClient) Revoke(ctx context.Context, id clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Revoke", ctx, id) ret := m.ctrl.Call(m, "Revoke", ctx, id)
@@ -157,16 +163,16 @@ func (m *MockEtcdClient) Revoke(ctx context.Context, id clientv3.LeaseID) (*clie
return ret0, ret1 return ret0, ret1
} }
// Revoke indicates an expected call of Revoke // Revoke indicates an expected call of Revoke.
func (mr *MockEtcdClientMockRecorder) Revoke(ctx, id interface{}) *gomock.Call { func (mr *MockEtcdClientMockRecorder) Revoke(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Revoke", reflect.TypeOf((*MockEtcdClient)(nil).Revoke), ctx, id) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Revoke", reflect.TypeOf((*MockEtcdClient)(nil).Revoke), ctx, id)
} }
// Watch mocks base method // Watch mocks base method.
func (m *MockEtcdClient) Watch(ctx context.Context, key string, opts ...clientv3.OpOption) clientv3.WatchChan { func (m *MockEtcdClient) Watch(ctx context.Context, key string, opts ...clientv3.OpOption) clientv3.WatchChan {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{ctx, key} varargs := []any{ctx, key}
for _, a := range opts { for _, a := range opts {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
@@ -175,9 +181,9 @@ func (m *MockEtcdClient) Watch(ctx context.Context, key string, opts ...clientv3
return ret0 return ret0
} }
// Watch indicates an expected call of Watch // Watch indicates an expected call of Watch.
func (mr *MockEtcdClientMockRecorder) Watch(ctx, key interface{}, opts ...interface{}) *gomock.Call { func (mr *MockEtcdClientMockRecorder) Watch(ctx, key any, opts ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{ctx, key}, opts...) varargs := append([]any{ctx, key}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockEtcdClient)(nil).Watch), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockEtcdClient)(nil).Watch), varargs...)
} }

View File

@@ -2,6 +2,7 @@ package internal
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"sort" "sort"
@@ -9,25 +10,30 @@ import (
"sync" "sync"
"time" "time"
"github.com/zeromicro/go-zero/core/contextx"
"github.com/zeromicro/go-zero/core/lang" "github.com/zeromicro/go-zero/core/lang"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logc"
"github.com/zeromicro/go-zero/core/mathx"
"github.com/zeromicro/go-zero/core/syncx" "github.com/zeromicro/go-zero/core/syncx"
"github.com/zeromicro/go-zero/core/threading" "github.com/zeromicro/go-zero/core/threading"
"go.etcd.io/etcd/api/v3/v3rpc/rpctypes"
clientv3 "go.etcd.io/etcd/client/v3" clientv3 "go.etcd.io/etcd/client/v3"
) )
const coolDownDeviation = 0.05
var ( var (
registry = Registry{ registry = Registry{
clusters: make(map[string]*cluster), clusters: make(map[string]*cluster),
} }
connManager = syncx.NewResourceManager() connManager = syncx.NewResourceManager()
coolDownUnstable = mathx.NewUnstable(coolDownDeviation)
errClosed = errors.New("etcd monitor chan has been closed")
) )
// A Registry is a registry that manages the etcd client connections. // A Registry is a registry that manages the etcd client connections.
type Registry struct { type Registry struct {
clusters map[string]*cluster clusters map[string]*cluster
lock sync.Mutex lock sync.RWMutex
} }
// GetRegistry returns a global Registry. // GetRegistry returns a global Registry.
@@ -37,60 +43,148 @@ func GetRegistry() *Registry {
// GetConn returns an etcd client connection associated with given endpoints. // GetConn returns an etcd client connection associated with given endpoints.
func (r *Registry) GetConn(endpoints []string) (EtcdClient, error) { func (r *Registry) GetConn(endpoints []string) (EtcdClient, error) {
c, _ := r.getCluster(endpoints) c, _ := r.getOrCreateCluster(endpoints)
return c.getClient() return c.getClient()
} }
// Monitor monitors the key on given etcd endpoints, notify with the given UpdateListener. // Monitor monitors the key on given etcd endpoints, notify with the given UpdateListener.
func (r *Registry) Monitor(endpoints []string, key string, l UpdateListener) error { func (r *Registry) Monitor(endpoints []string, key string, exactMatch bool, l UpdateListener) error {
c, exists := r.getCluster(endpoints) wkey := watchKey{
key: key,
exactMatch: exactMatch,
}
c, exists := r.getOrCreateCluster(endpoints)
// if exists, the existing values should be updated to the listener. // if exists, the existing values should be updated to the listener.
if exists { if exists {
kvs := c.getCurrent(key) c.lock.Lock()
watcher, ok := c.watchers[wkey]
if ok {
watcher.listeners = append(watcher.listeners, l)
}
c.lock.Unlock()
if ok {
kvs := c.getCurrent(wkey)
for _, kv := range kvs { for _, kv := range kvs {
l.OnAdd(kv) l.OnAdd(kv)
} }
return nil
}
} }
return c.monitor(key, l) return c.monitor(wkey, l)
} }
func (r *Registry) getCluster(endpoints []string) (c *cluster, exists bool) { func (r *Registry) Unmonitor(endpoints []string, key string, exactMatch bool, l UpdateListener) {
c, exists := r.getCluster(endpoints)
if !exists {
return
}
wkey := watchKey{
key: key,
exactMatch: exactMatch,
}
c.lock.Lock()
defer c.lock.Unlock()
watcher, ok := c.watchers[wkey]
if !ok {
return
}
for i, listener := range watcher.listeners {
if listener == l {
watcher.listeners = append(watcher.listeners[:i], watcher.listeners[i+1:]...)
break
}
}
if len(watcher.listeners) == 0 {
if watcher.cancel != nil {
watcher.cancel()
}
delete(c.watchers, wkey)
}
}
func (r *Registry) getCluster(endpoints []string) (*cluster, bool) {
clusterKey := getClusterKey(endpoints) clusterKey := getClusterKey(endpoints)
r.lock.RLock()
c, ok := r.clusters[clusterKey]
r.lock.RUnlock()
return c, ok
}
func (r *Registry) getOrCreateCluster(endpoints []string) (c *cluster, exists bool) {
c, exists = r.getCluster(endpoints)
if !exists {
clusterKey := getClusterKey(endpoints)
r.lock.Lock() r.lock.Lock()
defer r.lock.Unlock() defer r.lock.Unlock()
// double-check locking
c, exists = r.clusters[clusterKey] c, exists = r.clusters[clusterKey]
if !exists { if !exists {
c = newCluster(endpoints) c = newCluster(endpoints)
r.clusters[clusterKey] = c r.clusters[clusterKey] = c
} }
}
return return
} }
type cluster struct { type (
watchKey struct {
key string
exactMatch bool
}
watchValue struct {
listeners []UpdateListener
values map[string]string
cancel context.CancelFunc
}
cluster struct {
endpoints []string endpoints []string
key string key string
values map[string]map[string]string watchers map[watchKey]*watchValue
listeners map[string][]UpdateListener
watchGroup *threading.RoutineGroup watchGroup *threading.RoutineGroup
done chan lang.PlaceholderType done chan lang.PlaceholderType
lock sync.Mutex lock sync.RWMutex
} }
)
func newCluster(endpoints []string) *cluster { func newCluster(endpoints []string) *cluster {
return &cluster{ return &cluster{
endpoints: endpoints, endpoints: endpoints,
key: getClusterKey(endpoints), key: getClusterKey(endpoints),
values: make(map[string]map[string]string), watchers: make(map[watchKey]*watchValue),
listeners: make(map[string][]UpdateListener),
watchGroup: threading.NewRoutineGroup(), watchGroup: threading.NewRoutineGroup(),
done: make(chan lang.PlaceholderType), done: make(chan lang.PlaceholderType),
} }
} }
func (c *cluster) context(cli EtcdClient) context.Context { func (c *cluster) addListener(key watchKey, l UpdateListener) {
return contextx.ValueOnlyFrom(cli.Ctx()) c.lock.Lock()
defer c.lock.Unlock()
watcher, ok := c.watchers[key]
if ok {
watcher.listeners = append(watcher.listeners, l)
return
}
val := newWatchValue()
val.listeners = []UpdateListener{l}
c.watchers[key] = val
} }
func (c *cluster) getClient() (EtcdClient, error) { func (c *cluster) getClient() (EtcdClient, error) {
@@ -104,12 +198,17 @@ func (c *cluster) getClient() (EtcdClient, error) {
return val.(EtcdClient), nil return val.(EtcdClient), nil
} }
func (c *cluster) getCurrent(key string) []KV { func (c *cluster) getCurrent(key watchKey) []KV {
c.lock.Lock() c.lock.RLock()
defer c.lock.Unlock() defer c.lock.RUnlock()
var kvs []KV watcher, ok := c.watchers[key]
for k, v := range c.values[key] { if !ok {
return nil
}
kvs := make([]KV, 0, len(watcher.values))
for k, v := range watcher.values {
kvs = append(kvs, KV{ kvs = append(kvs, KV{
Key: k, Key: k,
Val: v, Val: v,
@@ -119,42 +218,23 @@ func (c *cluster) getCurrent(key string) []KV {
return kvs return kvs
} }
func (c *cluster) handleChanges(key string, kvs []KV) { func (c *cluster) handleChanges(key watchKey, kvs []KV) {
var add []KV
var remove []KV
c.lock.Lock() c.lock.Lock()
listeners := append([]UpdateListener(nil), c.listeners[key]...) watcher, ok := c.watchers[key]
vals, ok := c.values[key]
if !ok { if !ok {
add = kvs c.lock.Unlock()
vals = make(map[string]string) return
}
listeners := append([]UpdateListener(nil), watcher.listeners...)
// watcher.values cannot be nil
vals := watcher.values
newVals := make(map[string]string, len(kvs)+len(vals))
for _, kv := range kvs { for _, kv := range kvs {
vals[kv.Key] = kv.Val newVals[kv.Key] = kv.Val
}
c.values[key] = vals
} else {
m := make(map[string]string)
for _, kv := range kvs {
m[kv.Key] = kv.Val
}
for k, v := range vals {
if val, ok := m[k]; !ok || v != val {
remove = append(remove, KV{
Key: k,
Val: v,
})
}
}
for k, v := range m {
if val, ok := vals[k]; !ok || v != val {
add = append(add, KV{
Key: k,
Val: v,
})
}
}
c.values[key] = m
} }
add, remove := calculateChanges(vals, newVals)
watcher.values = newVals
c.lock.Unlock() c.lock.Unlock()
for _, kv := range add { for _, kv := range add {
@@ -169,20 +249,22 @@ func (c *cluster) handleChanges(key string, kvs []KV) {
} }
} }
func (c *cluster) handleWatchEvents(key string, events []*clientv3.Event) { func (c *cluster) handleWatchEvents(ctx context.Context, key watchKey, events []*clientv3.Event) {
c.lock.Lock() c.lock.RLock()
listeners := append([]UpdateListener(nil), c.listeners[key]...) watcher, ok := c.watchers[key]
c.lock.Unlock() if !ok {
c.lock.RUnlock()
return
}
listeners := append([]UpdateListener(nil), watcher.listeners...)
c.lock.RUnlock()
for _, ev := range events { for _, ev := range events {
switch ev.Type { switch ev.Type {
case clientv3.EventTypePut: case clientv3.EventTypePut:
c.lock.Lock() c.lock.Lock()
if vals, ok := c.values[key]; ok { watcher.values[string(ev.Kv.Key)] = string(ev.Kv.Value)
vals[string(ev.Kv.Key)] = string(ev.Kv.Value)
} else {
c.values[key] = map[string]string{string(ev.Kv.Key): string(ev.Kv.Value)}
}
c.lock.Unlock() c.lock.Unlock()
for _, l := range listeners { for _, l := range listeners {
l.OnAdd(KV{ l.OnAdd(KV{
@@ -192,9 +274,7 @@ func (c *cluster) handleWatchEvents(key string, events []*clientv3.Event) {
} }
case clientv3.EventTypeDelete: case clientv3.EventTypeDelete:
c.lock.Lock() c.lock.Lock()
if vals, ok := c.values[key]; ok { delete(watcher.values, string(ev.Kv.Key))
delete(vals, string(ev.Kv.Key))
}
c.lock.Unlock() c.lock.Unlock()
for _, l := range listeners { for _, l := range listeners {
l.OnDelete(KV{ l.OnDelete(KV{
@@ -203,27 +283,32 @@ func (c *cluster) handleWatchEvents(key string, events []*clientv3.Event) {
}) })
} }
default: default:
logx.Errorf("Unknown event type: %v", ev.Type) logc.Errorf(ctx, "Unknown event type: %v", ev.Type)
} }
} }
} }
func (c *cluster) load(cli EtcdClient, key string) int64 { func (c *cluster) load(cli EtcdClient, key watchKey) int64 {
var resp *clientv3.GetResponse var resp *clientv3.GetResponse
for { for {
var err error var err error
ctx, cancel := context.WithTimeout(c.context(cli), RequestTimeout) ctx, cancel := context.WithTimeout(cli.Ctx(), RequestTimeout)
resp, err = cli.Get(ctx, makeKeyPrefix(key), clientv3.WithPrefix()) if key.exactMatch {
resp, err = cli.Get(ctx, key.key)
} else {
resp, err = cli.Get(ctx, makeKeyPrefix(key.key), clientv3.WithPrefix())
}
cancel() cancel()
if err == nil { if err == nil {
break break
} }
logx.Error(err) logc.Errorf(cli.Ctx(), "%s, key: %s, exactMatch: %t", err.Error(), key.key, key.exactMatch)
time.Sleep(coolDownInterval) time.Sleep(coolDownUnstable.AroundDuration(coolDownInterval))
} }
var kvs []KV kvs := make([]KV, 0, len(resp.Kvs))
for _, ev := range resp.Kvs { for _, ev := range resp.Kvs {
kvs = append(kvs, KV{ kvs = append(kvs, KV{
Key: string(ev.Key), Key: string(ev.Key),
@@ -236,16 +321,13 @@ func (c *cluster) load(cli EtcdClient, key string) int64 {
return resp.Header.Revision return resp.Header.Revision
} }
func (c *cluster) monitor(key string, l UpdateListener) error { func (c *cluster) monitor(key watchKey, l UpdateListener) error {
c.lock.Lock()
c.listeners[key] = append(c.listeners[key], l)
c.lock.Unlock()
cli, err := c.getClient() cli, err := c.getClient()
if err != nil { if err != nil {
return err return err
} }
c.addListener(key, l)
rev := c.load(cli, key) rev := c.load(cli, key)
c.watchGroup.Run(func() { c.watchGroup.Run(func() {
c.watch(cli, key, rev) c.watch(cli, key, rev)
@@ -267,16 +349,22 @@ func (c *cluster) newClient() (EtcdClient, error) {
func (c *cluster) reload(cli EtcdClient) { func (c *cluster) reload(cli EtcdClient) {
c.lock.Lock() c.lock.Lock()
// cancel the previous watches
close(c.done) close(c.done)
c.watchGroup.Wait() c.watchGroup.Wait()
keys := make([]watchKey, 0, len(c.watchers))
for wk, wval := range c.watchers {
keys = append(keys, wk)
if wval.cancel != nil {
wval.cancel()
}
}
c.done = make(chan lang.PlaceholderType) c.done = make(chan lang.PlaceholderType)
c.watchGroup = threading.NewRoutineGroup() c.watchGroup = threading.NewRoutineGroup()
var keys []string
for k := range c.listeners {
keys = append(keys, k)
}
c.lock.Unlock() c.lock.Unlock()
// start new watches
for _, key := range keys { for _, key := range keys {
k := key k := key
c.watchGroup.Run(func() { c.watchGroup.Run(func() {
@@ -286,46 +374,81 @@ func (c *cluster) reload(cli EtcdClient) {
} }
} }
func (c *cluster) watch(cli EtcdClient, key string, rev int64) { func (c *cluster) watch(cli EtcdClient, key watchKey, rev int64) {
for { for {
if c.watchStream(cli, key, rev) { err := c.watchStream(cli, key, rev)
if err == nil {
return return
} }
if rev != 0 && errors.Is(err, rpctypes.ErrCompacted) {
logc.Errorf(cli.Ctx(), "etcd watch stream has been compacted, try to reload, rev %d", rev)
rev = c.load(cli, key)
}
// log the error and retry with cooldown to prevent CPU/disk exhaustion
logc.Error(cli.Ctx(), err)
time.Sleep(coolDownUnstable.AroundDuration(coolDownInterval))
} }
} }
func (c *cluster) watchStream(cli EtcdClient, key string, rev int64) bool { func (c *cluster) watchStream(cli EtcdClient, key watchKey, rev int64) error {
var rch clientv3.WatchChan ctx, rch := c.setupWatch(cli, key, rev)
if rev != 0 {
rch = cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix(),
clientv3.WithRev(rev+1))
} else {
rch = cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix())
}
for { for {
select { select {
case wresp, ok := <-rch: case wresp, ok := <-rch:
if !ok { if !ok {
logx.Error("etcd monitor chan has been closed") return errClosed
return false
} }
if wresp.Canceled { if wresp.Canceled {
logx.Errorf("etcd monitor chan has been canceled, error: %v", wresp.Err()) return fmt.Errorf("etcd monitor chan has been canceled, error: %w", wresp.Err())
return false
} }
if wresp.Err() != nil { if wresp.Err() != nil {
logx.Error(fmt.Sprintf("etcd monitor chan error: %v", wresp.Err())) return fmt.Errorf("etcd monitor chan error: %w", wresp.Err())
return false
} }
c.handleWatchEvents(key, wresp.Events) c.handleWatchEvents(ctx, key, wresp.Events)
case <-ctx.Done():
return nil
case <-c.done: case <-c.done:
return true return nil
} }
} }
} }
func (c *cluster) setupWatch(cli EtcdClient, key watchKey, rev int64) (context.Context, clientv3.WatchChan) {
var (
rch clientv3.WatchChan
ops []clientv3.OpOption
wkey = key.key
)
if !key.exactMatch {
wkey = makeKeyPrefix(key.key)
ops = append(ops, clientv3.WithPrefix())
}
if rev != 0 {
ops = append(ops, clientv3.WithRev(rev+1))
}
ctx, cancel := context.WithCancel(cli.Ctx())
c.lock.Lock()
if watcher, ok := c.watchers[key]; ok {
watcher.cancel = cancel
} else {
val := newWatchValue()
val.cancel = cancel
c.watchers[key] = val
}
c.lock.Unlock()
rch = cli.Watch(clientv3.WithRequireLeader(ctx), wkey, ops...)
return ctx, rch
}
func (c *cluster) watchConnState(cli EtcdClient) { func (c *cluster) watchConnState(cli EtcdClient) {
watcher := newStateWatcher() watcher := newStateWatcher()
watcher.addListener(func() { watcher.addListener(func() {
@@ -340,8 +463,6 @@ func DialClient(endpoints []string) (EtcdClient, error) {
Endpoints: endpoints, Endpoints: endpoints,
AutoSyncInterval: autoSyncInterval, AutoSyncInterval: autoSyncInterval,
DialTimeout: DialTimeout, DialTimeout: DialTimeout,
DialKeepAliveTime: dialKeepAliveTime,
DialKeepAliveTimeout: DialTimeout,
RejectOldCluster: true, RejectOldCluster: true,
PermitWithoutStream: true, PermitWithoutStream: true,
} }
@@ -356,6 +477,28 @@ func DialClient(endpoints []string) (EtcdClient, error) {
return clientv3.New(cfg) return clientv3.New(cfg)
} }
func calculateChanges(oldVals, newVals map[string]string) (add, remove []KV) {
for k, v := range newVals {
if val, ok := oldVals[k]; !ok || v != val {
add = append(add, KV{
Key: k,
Val: v,
})
}
}
for k, v := range oldVals {
if val, ok := newVals[k]; !ok || v != val {
remove = append(remove, KV{
Key: k,
Val: v,
})
}
}
return add, remove
}
func getClusterKey(endpoints []string) string { func getClusterKey(endpoints []string) string {
sort.Strings(endpoints) sort.Strings(endpoints)
return strings.Join(endpoints, endpointsSeparator) return strings.Join(endpoints, endpointsSeparator)
@@ -364,3 +507,10 @@ func getClusterKey(endpoints []string) string {
func makeKeyPrefix(key string) string { func makeKeyPrefix(key string) string {
return fmt.Sprintf("%s%c", key, Delimiter) return fmt.Sprintf("%s%c", key, Delimiter)
} }
// NewClient returns a watchValue that make sure values are not nil.
func newWatchValue() *watchValue {
return &watchValue{
values: make(map[string]string),
}
}

View File

@@ -2,18 +2,22 @@ package internal
import ( import (
"context" "context"
"os"
"sync" "sync"
"testing" "testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/contextx" "github.com/zeromicro/go-zero/core/contextx"
"github.com/zeromicro/go-zero/core/lang" "github.com/zeromicro/go-zero/core/lang"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stringx" "github.com/zeromicro/go-zero/core/stringx"
"github.com/zeromicro/go-zero/core/threading"
"go.etcd.io/etcd/api/v3/etcdserverpb" "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/mvccpb" "go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3" clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/mock/mockserver"
"go.uber.org/mock/gomock"
) )
var mockLock sync.Mutex var mockLock sync.Mutex
@@ -35,9 +39,9 @@ func setMockClient(cli EtcdClient) func() {
func TestGetCluster(t *testing.T) { func TestGetCluster(t *testing.T) {
AddAccount([]string{"first"}, "foo", "bar") AddAccount([]string{"first"}, "foo", "bar")
c1, _ := GetRegistry().getCluster([]string{"first"}) c1, _ := GetRegistry().getOrCreateCluster([]string{"first"})
c2, _ := GetRegistry().getCluster([]string{"second"}) c2, _ := GetRegistry().getOrCreateCluster([]string{"second"})
c3, _ := GetRegistry().getCluster([]string{"first"}) c3, _ := GetRegistry().getOrCreateCluster([]string{"first"})
assert.Equal(t, c1, c3) assert.Equal(t, c1, c3)
assert.NotEqual(t, c1, c2) assert.NotEqual(t, c1, c2)
} }
@@ -47,6 +51,36 @@ func TestGetClusterKey(t *testing.T) {
getClusterKey([]string{"remotehost:5678", "localhost:1234"})) getClusterKey([]string{"remotehost:5678", "localhost:1234"}))
} }
func TestUnmonitor(t *testing.T) {
t.Run("no listener", func(t *testing.T) {
reg := &Registry{
clusters: map[string]*cluster{},
}
assert.NotPanics(t, func() {
reg.Unmonitor([]string{"any"}, "any", false, nil)
})
})
t.Run("no value", func(t *testing.T) {
reg := &Registry{
clusters: map[string]*cluster{
"any": {
watchers: map[watchKey]*watchValue{
{
key: "any",
}: {
values: map[string]string{},
},
},
},
},
}
assert.NotPanics(t, func() {
reg.Unmonitor([]string{"any"}, "another", false, nil)
})
})
}
func TestCluster_HandleChanges(t *testing.T) { func TestCluster_HandleChanges(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
l := NewMockUpdateListener(ctrl) l := NewMockUpdateListener(ctrl)
@@ -75,8 +109,14 @@ func TestCluster_HandleChanges(t *testing.T) {
Val: "4", Val: "4",
}) })
c := newCluster([]string{"any"}) c := newCluster([]string{"any"})
c.listeners["any"] = []UpdateListener{l} key := watchKey{
c.handleChanges("any", []KV{ key: "any",
exactMatch: false,
}
c.watchers[key] = &watchValue{
listeners: []UpdateListener{l},
}
c.handleChanges(key, []KV{
{ {
Key: "first", Key: "first",
Val: "1", Val: "1",
@@ -89,8 +129,8 @@ func TestCluster_HandleChanges(t *testing.T) {
assert.EqualValues(t, map[string]string{ assert.EqualValues(t, map[string]string{
"first": "1", "first": "1",
"second": "2", "second": "2",
}, c.values["any"]) }, c.watchers[key].values)
c.handleChanges("any", []KV{ c.handleChanges(key, []KV{
{ {
Key: "third", Key: "third",
Val: "3", Val: "3",
@@ -103,7 +143,7 @@ func TestCluster_HandleChanges(t *testing.T) {
assert.EqualValues(t, map[string]string{ assert.EqualValues(t, map[string]string{
"third": "3", "third": "3",
"fourth": "4", "fourth": "4",
}, c.values["any"]) }, c.watchers[key].values)
} }
func TestCluster_Load(t *testing.T) { func TestCluster_Load(t *testing.T) {
@@ -123,9 +163,11 @@ func TestCluster_Load(t *testing.T) {
}, nil) }, nil)
cli.EXPECT().Ctx().Return(context.Background()) cli.EXPECT().Ctx().Return(context.Background())
c := &cluster{ c := &cluster{
values: make(map[string]map[string]string), watchers: make(map[watchKey]*watchValue),
} }
c.load(cli, "any") c.load(cli, watchKey{
key: "any",
})
} }
func TestCluster_Watch(t *testing.T) { func TestCluster_Watch(t *testing.T) {
@@ -157,20 +199,25 @@ func TestCluster_Watch(t *testing.T) {
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Add(1)
c := &cluster{ c := &cluster{
listeners: make(map[string][]UpdateListener), watchers: make(map[watchKey]*watchValue),
values: make(map[string]map[string]string), }
key := watchKey{
key: "any",
} }
listener := NewMockUpdateListener(ctrl) listener := NewMockUpdateListener(ctrl)
c.listeners["any"] = []UpdateListener{listener} c.watchers[key] = &watchValue{
listeners: []UpdateListener{listener},
values: make(map[string]string),
}
listener.EXPECT().OnAdd(gomock.Any()).Do(func(kv KV) { listener.EXPECT().OnAdd(gomock.Any()).Do(func(kv KV) {
assert.Equal(t, "hello", kv.Key) assert.Equal(t, "hello", kv.Key)
assert.Equal(t, "world", kv.Val) assert.Equal(t, "world", kv.Val)
wg.Done() wg.Done()
}).MaxTimes(1) }).MaxTimes(1)
listener.EXPECT().OnDelete(gomock.Any()).Do(func(_ interface{}) { listener.EXPECT().OnDelete(gomock.Any()).Do(func(_ any) {
wg.Done() wg.Done()
}).MaxTimes(1) }).MaxTimes(1)
go c.watch(cli, "any", 0) go c.watch(cli, key, 0)
ch <- clientv3.WatchResponse{ ch <- clientv3.WatchResponse{
Events: []*clientv3.Event{ Events: []*clientv3.Event{
{ {
@@ -208,17 +255,111 @@ func TestClusterWatch_RespFailures(t *testing.T) {
ch := make(chan clientv3.WatchResponse) ch := make(chan clientv3.WatchResponse)
cli.EXPECT().Watch(gomock.Any(), "any/", gomock.Any()).Return(ch).AnyTimes() cli.EXPECT().Watch(gomock.Any(), "any/", gomock.Any()).Return(ch).AnyTimes()
cli.EXPECT().Ctx().Return(context.Background()).AnyTimes() cli.EXPECT().Ctx().Return(context.Background()).AnyTimes()
c := new(cluster) c := &cluster{
watchers: make(map[watchKey]*watchValue),
}
c.done = make(chan lang.PlaceholderType) c.done = make(chan lang.PlaceholderType)
go func() { go func() {
ch <- resp ch <- resp
close(c.done) close(c.done)
}() }()
c.watch(cli, "any", 0) key := watchKey{
key: "any",
}
c.watch(cli, key, 0)
}) })
} }
} }
func TestCluster_getCurrent(t *testing.T) {
t.Run("no value", func(t *testing.T) {
c := &cluster{
watchers: map[watchKey]*watchValue{
{
key: "any",
}: {
values: map[string]string{},
},
},
}
assert.Nil(t, c.getCurrent(watchKey{
key: "another",
}))
})
}
func TestCluster_handleWatchEvents(t *testing.T) {
t.Run("no value", func(t *testing.T) {
c := &cluster{
watchers: map[watchKey]*watchValue{
{
key: "any",
}: {
values: map[string]string{},
},
},
}
assert.NotPanics(t, func() {
c.handleWatchEvents(context.Background(), watchKey{
key: "another",
}, nil)
})
})
}
func TestCluster_addListener(t *testing.T) {
t.Run("has listener", func(t *testing.T) {
c := &cluster{
watchers: map[watchKey]*watchValue{
{
key: "any",
}: {
listeners: make([]UpdateListener, 0),
},
},
}
assert.NotPanics(t, func() {
c.addListener(watchKey{
key: "any",
}, nil)
})
})
t.Run("no listener", func(t *testing.T) {
c := &cluster{
watchers: map[watchKey]*watchValue{
{
key: "any",
}: {
listeners: make([]UpdateListener, 0),
},
},
}
assert.NotPanics(t, func() {
c.addListener(watchKey{
key: "another",
}, nil)
})
})
}
func TestCluster_reload(t *testing.T) {
c := &cluster{
watchers: map[watchKey]*watchValue{},
watchGroup: threading.NewRoutineGroup(),
done: make(chan lang.PlaceholderType),
}
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cli := NewMockEtcdClient(ctrl)
restore := setMockClient(cli)
defer restore()
assert.NotPanics(t, func() {
c.reload(cli)
})
}
func TestClusterWatch_CloseChan(t *testing.T) { func TestClusterWatch_CloseChan(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
@@ -228,13 +369,17 @@ func TestClusterWatch_CloseChan(t *testing.T) {
ch := make(chan clientv3.WatchResponse) ch := make(chan clientv3.WatchResponse)
cli.EXPECT().Watch(gomock.Any(), "any/", gomock.Any()).Return(ch).AnyTimes() cli.EXPECT().Watch(gomock.Any(), "any/", gomock.Any()).Return(ch).AnyTimes()
cli.EXPECT().Ctx().Return(context.Background()).AnyTimes() cli.EXPECT().Ctx().Return(context.Background()).AnyTimes()
c := new(cluster) c := &cluster{
watchers: make(map[watchKey]*watchValue),
}
c.done = make(chan lang.PlaceholderType) c.done = make(chan lang.PlaceholderType)
go func() { go func() {
close(ch) close(ch)
close(c.done) close(c.done)
}() }()
c.watch(cli, "any", 0) c.watch(cli, watchKey{
key: "any",
}, 0)
} }
func TestValueOnlyContext(t *testing.T) { func TestValueOnlyContext(t *testing.T) {
@@ -242,3 +387,167 @@ func TestValueOnlyContext(t *testing.T) {
ctx.Done() ctx.Done()
assert.Nil(t, ctx.Err()) assert.Nil(t, ctx.Err())
} }
func TestDialClient(t *testing.T) {
svr, err := mockserver.StartMockServers(1)
assert.NoError(t, err)
svr.StartAt(0)
certFile := createTempFile(t, []byte(certContent))
defer os.Remove(certFile)
keyFile := createTempFile(t, []byte(keyContent))
defer os.Remove(keyFile)
caFile := createTempFile(t, []byte(caContent))
defer os.Remove(caFile)
endpoints := []string{svr.Servers[0].Address}
AddAccount(endpoints, "foo", "bar")
assert.NoError(t, AddTLS(endpoints, certFile, keyFile, caFile, false))
old := DialTimeout
DialTimeout = time.Millisecond
defer func() {
DialTimeout = old
}()
_, err = DialClient(endpoints)
assert.Error(t, err)
}
func TestRegistry_Monitor(t *testing.T) {
svr, err := mockserver.StartMockServers(1)
assert.NoError(t, err)
svr.StartAt(0)
endpoints := []string{svr.Servers[0].Address}
GetRegistry().lock.Lock()
GetRegistry().clusters = map[string]*cluster{
getClusterKey(endpoints): {
watchers: map[watchKey]*watchValue{
{
key: "foo",
exactMatch: true,
}: {
values: map[string]string{
"bar": "baz",
},
},
},
},
}
GetRegistry().lock.Unlock()
assert.Error(t, GetRegistry().Monitor(endpoints, "foo", false, new(mockListener)))
}
func TestRegistry_Unmonitor(t *testing.T) {
svr, err := mockserver.StartMockServers(1)
assert.NoError(t, err)
svr.StartAt(0)
_, cancel := context.WithCancel(context.Background())
endpoints := []string{svr.Servers[0].Address}
GetRegistry().lock.Lock()
GetRegistry().clusters = map[string]*cluster{
getClusterKey(endpoints): {
watchers: map[watchKey]*watchValue{
{
key: "foo",
exactMatch: true,
}: {
values: map[string]string{
"bar": "baz",
},
cancel: cancel,
},
},
},
}
GetRegistry().lock.Unlock()
l := new(mockListener)
assert.NoError(t, GetRegistry().Monitor(endpoints, "foo", true, l))
watchVals := GetRegistry().clusters[getClusterKey(endpoints)].watchers[watchKey{
key: "foo",
exactMatch: true,
}]
assert.Equal(t, 1, len(watchVals.listeners))
GetRegistry().Unmonitor(endpoints, "foo", true, l)
watchVals = GetRegistry().clusters[getClusterKey(endpoints)].watchers[watchKey{
key: "foo",
exactMatch: true,
}]
assert.Nil(t, watchVals)
}
// TestCluster_ConcurrentMonitor tests the race condition fix in setupWatch
// This test specifically covers the scenario from issue #5394 where:
// - addListener() writes to the watchers map (with lock)
// - setupWatch() reads from the watchers map (now with lock after fix)
// Running with -race flag will detect any race conditions
func TestCluster_ConcurrentMonitor(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
cli := NewMockEtcdClient(ctrl)
cli.EXPECT().Ctx().Return(context.Background()).AnyTimes()
cli.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return(make(chan clientv3.WatchResponse)).AnyTimes()
c := &cluster{
endpoints: []string{"localhost:2379"},
key: "test-cluster",
watchers: make(map[watchKey]*watchValue),
watchGroup: threading.NewRoutineGroup(),
done: make(chan lang.PlaceholderType),
lock: sync.RWMutex{},
}
// Spawn multiple concurrent operations that simulate the race condition:
// - Some goroutines call addListener (write to map)
// - Some goroutines call setupWatch (read from map)
var wg sync.WaitGroup
numGoroutines := 20
wg.Add(numGoroutines)
keys := []watchKey{
{key: "key-0", exactMatch: false},
{key: "key-1", exactMatch: false},
{key: "key-2", exactMatch: false},
}
for i := 0; i < numGoroutines; i++ {
idx := i
go func() {
defer wg.Done()
key := keys[idx%len(keys)]
if idx%2 == 0 {
// Half the goroutines add listeners (write operation)
c.addListener(key, &mockListener{})
} else {
// Half the goroutines setup watches (read operation)
_, _ = c.setupWatch(cli, key, 0)
}
}()
}
// Wait for all goroutines to complete
wg.Wait()
// Verify that watchers were correctly added
c.lock.RLock()
assert.True(t, len(c.watchers) > 0, "watchers should be added")
for _, watcher := range c.watchers {
assert.NotNil(t, watcher, "watcher should not be nil")
}
c.lock.RUnlock()
// Clean up
close(c.done)
}
type mockListener struct {
}
func (m *mockListener) OnAdd(_ KV) {
}
func (m *mockListener) OnDelete(_ KV) {
}

View File

@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: statewatcher.go // Source: statewatcher.go
//
// Generated by this command:
//
// mockgen -package internal -destination statewatcher_mock.go -source statewatcher.go etcdConn
//
// Package internal is a generated GoMock package. // Package internal is a generated GoMock package.
package internal package internal
@@ -8,34 +13,35 @@ import (
context "context" context "context"
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
connectivity "google.golang.org/grpc/connectivity" connectivity "google.golang.org/grpc/connectivity"
) )
// MocketcdConn is a mock of etcdConn interface // MocketcdConn is a mock of etcdConn interface.
type MocketcdConn struct { type MocketcdConn struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MocketcdConnMockRecorder recorder *MocketcdConnMockRecorder
isgomock struct{}
} }
// MocketcdConnMockRecorder is the mock recorder for MocketcdConn // MocketcdConnMockRecorder is the mock recorder for MocketcdConn.
type MocketcdConnMockRecorder struct { type MocketcdConnMockRecorder struct {
mock *MocketcdConn mock *MocketcdConn
} }
// NewMocketcdConn creates a new mock instance // NewMocketcdConn creates a new mock instance.
func NewMocketcdConn(ctrl *gomock.Controller) *MocketcdConn { func NewMocketcdConn(ctrl *gomock.Controller) *MocketcdConn {
mock := &MocketcdConn{ctrl: ctrl} mock := &MocketcdConn{ctrl: ctrl}
mock.recorder = &MocketcdConnMockRecorder{mock} mock.recorder = &MocketcdConnMockRecorder{mock}
return mock return mock
} }
// EXPECT returns an object that allows the caller to indicate expected use // EXPECT returns an object that allows the caller to indicate expected use.
func (m *MocketcdConn) EXPECT() *MocketcdConnMockRecorder { func (m *MocketcdConn) EXPECT() *MocketcdConnMockRecorder {
return m.recorder return m.recorder
} }
// GetState mocks base method // GetState mocks base method.
func (m *MocketcdConn) GetState() connectivity.State { func (m *MocketcdConn) GetState() connectivity.State {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetState") ret := m.ctrl.Call(m, "GetState")
@@ -43,13 +49,13 @@ func (m *MocketcdConn) GetState() connectivity.State {
return ret0 return ret0
} }
// GetState indicates an expected call of GetState // GetState indicates an expected call of GetState.
func (mr *MocketcdConnMockRecorder) GetState() *gomock.Call { func (mr *MocketcdConnMockRecorder) GetState() *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetState", reflect.TypeOf((*MocketcdConn)(nil).GetState)) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetState", reflect.TypeOf((*MocketcdConn)(nil).GetState))
} }
// WaitForStateChange mocks base method // WaitForStateChange mocks base method.
func (m *MocketcdConn) WaitForStateChange(ctx context.Context, sourceState connectivity.State) bool { func (m *MocketcdConn) WaitForStateChange(ctx context.Context, sourceState connectivity.State) bool {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "WaitForStateChange", ctx, sourceState) ret := m.ctrl.Call(m, "WaitForStateChange", ctx, sourceState)
@@ -57,8 +63,8 @@ func (m *MocketcdConn) WaitForStateChange(ctx context.Context, sourceState conne
return ret0 return ret0
} }
// WaitForStateChange indicates an expected call of WaitForStateChange // WaitForStateChange indicates an expected call of WaitForStateChange.
func (mr *MocketcdConnMockRecorder) WaitForStateChange(ctx, sourceState interface{}) *gomock.Call { func (mr *MocketcdConnMockRecorder) WaitForStateChange(ctx, sourceState any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForStateChange", reflect.TypeOf((*MocketcdConn)(nil).WaitForStateChange), ctx, sourceState) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForStateChange", reflect.TypeOf((*MocketcdConn)(nil).WaitForStateChange), ctx, sourceState)
} }

View File

@@ -4,7 +4,7 @@ import (
"sync" "sync"
"testing" "testing"
"github.com/golang/mock/gomock" "go.uber.org/mock/gomock"
"google.golang.org/grpc/connectivity" "google.golang.org/grpc/connectivity"
) )

View File

@@ -10,6 +10,7 @@ type (
} }
// UpdateListener wraps the OnAdd and OnDelete methods. // UpdateListener wraps the OnAdd and OnDelete methods.
// The implementation should be thread-safe and idempotent.
UpdateListener interface { UpdateListener interface {
OnAdd(kv KV) OnAdd(kv KV)
OnDelete(kv KV) OnDelete(kv KV)

View File

@@ -1,5 +1,10 @@
// Code generated by MockGen. DO NOT EDIT. // Code generated by MockGen. DO NOT EDIT.
// Source: updatelistener.go // Source: updatelistener.go
//
// Generated by this command:
//
// mockgen -package internal -destination updatelistener_mock.go -source updatelistener.go UpdateListener
//
// Package internal is a generated GoMock package. // Package internal is a generated GoMock package.
package internal package internal
@@ -7,52 +12,53 @@ package internal
import ( import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "go.uber.org/mock/gomock"
) )
// MockUpdateListener is a mock of UpdateListener interface // MockUpdateListener is a mock of UpdateListener interface.
type MockUpdateListener struct { type MockUpdateListener struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockUpdateListenerMockRecorder recorder *MockUpdateListenerMockRecorder
isgomock struct{}
} }
// MockUpdateListenerMockRecorder is the mock recorder for MockUpdateListener // MockUpdateListenerMockRecorder is the mock recorder for MockUpdateListener.
type MockUpdateListenerMockRecorder struct { type MockUpdateListenerMockRecorder struct {
mock *MockUpdateListener mock *MockUpdateListener
} }
// NewMockUpdateListener creates a new mock instance // NewMockUpdateListener creates a new mock instance.
func NewMockUpdateListener(ctrl *gomock.Controller) *MockUpdateListener { func NewMockUpdateListener(ctrl *gomock.Controller) *MockUpdateListener {
mock := &MockUpdateListener{ctrl: ctrl} mock := &MockUpdateListener{ctrl: ctrl}
mock.recorder = &MockUpdateListenerMockRecorder{mock} mock.recorder = &MockUpdateListenerMockRecorder{mock}
return mock return mock
} }
// EXPECT returns an object that allows the caller to indicate expected use // EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockUpdateListener) EXPECT() *MockUpdateListenerMockRecorder { func (m *MockUpdateListener) EXPECT() *MockUpdateListenerMockRecorder {
return m.recorder return m.recorder
} }
// OnAdd mocks base method // OnAdd mocks base method.
func (m *MockUpdateListener) OnAdd(kv KV) { func (m *MockUpdateListener) OnAdd(kv KV) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "OnAdd", kv) m.ctrl.Call(m, "OnAdd", kv)
} }
// OnAdd indicates an expected call of OnAdd // OnAdd indicates an expected call of OnAdd.
func (mr *MockUpdateListenerMockRecorder) OnAdd(kv interface{}) *gomock.Call { func (mr *MockUpdateListenerMockRecorder) OnAdd(kv any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnAdd", reflect.TypeOf((*MockUpdateListener)(nil).OnAdd), kv) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnAdd", reflect.TypeOf((*MockUpdateListener)(nil).OnAdd), kv)
} }
// OnDelete mocks base method // OnDelete mocks base method.
func (m *MockUpdateListener) OnDelete(kv KV) { func (m *MockUpdateListener) OnDelete(kv KV) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
m.ctrl.Call(m, "OnDelete", kv) m.ctrl.Call(m, "OnDelete", kv)
} }
// OnDelete indicates an expected call of OnDelete // OnDelete indicates an expected call of OnDelete.
func (mr *MockUpdateListenerMockRecorder) OnDelete(kv interface{}) *gomock.Call { func (mr *MockUpdateListenerMockRecorder) OnDelete(kv any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnDelete", reflect.TypeOf((*MockUpdateListener)(nil).OnDelete), kv) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnDelete", reflect.TypeOf((*MockUpdateListener)(nil).OnDelete), kv)
} }

View File

@@ -9,7 +9,6 @@ const (
autoSyncInterval = time.Minute autoSyncInterval = time.Minute
coolDownInterval = time.Second coolDownInterval = time.Second
dialTimeout = 5 * time.Second dialTimeout = 5 * time.Second
dialKeepAliveTime = 5 * time.Second
requestTimeout = 3 * time.Second requestTimeout = 3 * time.Second
endpointsSeparator = "," endpointsSeparator = ","
) )

View File

@@ -1,8 +1,11 @@
package discov package discov
import ( import (
"time"
"github.com/zeromicro/go-zero/core/discov/internal" "github.com/zeromicro/go-zero/core/discov/internal"
"github.com/zeromicro/go-zero/core/lang" "github.com/zeromicro/go-zero/core/lang"
"github.com/zeromicro/go-zero/core/logc"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/proc" "github.com/zeromicro/go-zero/core/proc"
"github.com/zeromicro/go-zero/core/syncx" "github.com/zeromicro/go-zero/core/syncx"
@@ -51,12 +54,7 @@ func NewPublisher(endpoints []string, key, value string, opts ...PubOption) *Pub
// KeepAlive keeps key:value alive. // KeepAlive keeps key:value alive.
func (p *Publisher) KeepAlive() error { func (p *Publisher) KeepAlive() error {
cli, err := internal.GetRegistry().GetConn(p.endpoints) cli, err := p.doRegister()
if err != nil {
return err
}
p.lease, err = p.register(cli)
if err != nil { if err != nil {
return err return err
} }
@@ -83,6 +81,43 @@ func (p *Publisher) Stop() {
p.quit.Close() p.quit.Close()
} }
func (p *Publisher) doKeepAlive() error {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for range ticker.C {
select {
case <-p.quit.Done():
return nil
default:
cli, err := p.doRegister()
if err != nil {
logc.Errorf(cli.Ctx(), "etcd publisher doRegister: %v", err)
break
}
if err := p.keepAliveAsync(cli); err != nil {
logc.Errorf(cli.Ctx(), "etcd publisher keepAliveAsync: %v", err)
break
}
return nil
}
}
return nil
}
func (p *Publisher) doRegister() (internal.EtcdClient, error) {
cli, err := internal.GetRegistry().GetConn(p.endpoints)
if err != nil {
return nil, err
}
p.lease, err = p.register(cli)
return cli, err
}
func (p *Publisher) keepAliveAsync(cli internal.EtcdClient) error { func (p *Publisher) keepAliveAsync(cli internal.EtcdClient) error {
ch, err := cli.KeepAlive(cli.Ctx(), p.lease) ch, err := cli.KeepAlive(cli.Ctx(), p.lease)
if err != nil { if err != nil {
@@ -90,23 +125,48 @@ func (p *Publisher) keepAliveAsync(cli internal.EtcdClient) error {
} }
threading.GoSafe(func() { threading.GoSafe(func() {
wch := cli.Watch(cli.Ctx(), p.fullKey, clientv3.WithFilterPut())
for { for {
select { select {
case _, ok := <-ch: case _, ok := <-ch:
if !ok { if !ok {
p.revoke(cli) p.revoke(cli)
if err := p.KeepAlive(); err != nil { if err := p.doKeepAlive(); err != nil {
logx.Errorf("KeepAlive: %s", err.Error()) logc.Errorf(cli.Ctx(), "etcd publisher KeepAlive: %v", err)
} }
return return
} }
case c := <-wch:
if c.Err() != nil {
logc.Errorf(cli.Ctx(), "etcd publisher watch: %v", c.Err())
if err := p.doKeepAlive(); err != nil {
logc.Errorf(cli.Ctx(), "etcd publisher KeepAlive: %v", err)
}
return
}
for _, evt := range c.Events {
if evt.Type == clientv3.EventTypeDelete {
logc.Infof(cli.Ctx(), "etcd publisher watch: %s, event: %v",
evt.Kv.Key, evt.Type)
_, err := cli.Put(cli.Ctx(), p.fullKey, p.value, clientv3.WithLease(p.lease))
if err != nil {
logc.Errorf(cli.Ctx(), "etcd publisher re-put key: %v", err)
} else {
logc.Infof(cli.Ctx(), "etcd publisher re-put key: %s, value: %s",
p.fullKey, p.value)
}
}
}
case <-p.pauseChan: case <-p.pauseChan:
logx.Infof("paused etcd renew, key: %s, value: %s", p.key, p.value) logc.Infof(cli.Ctx(), "paused etcd renew, key: %s, value: %s", p.key, p.value)
p.revoke(cli) p.revoke(cli)
select { select {
case <-p.resumeChan: case <-p.resumeChan:
if err := p.KeepAlive(); err != nil { if err := p.doKeepAlive(); err != nil {
logx.Errorf("KeepAlive: %s", err.Error()) logc.Errorf(cli.Ctx(), "etcd publisher KeepAlive: %v", err)
} }
return return
case <-p.quit.Done(): case <-p.quit.Done():
@@ -141,7 +201,7 @@ func (p *Publisher) register(client internal.EtcdClient) (clientv3.LeaseID, erro
func (p *Publisher) revoke(cli internal.EtcdClient) { func (p *Publisher) revoke(cli internal.EtcdClient) {
if _, err := cli.Revoke(cli.Ctx(), p.lease); err != nil { if _, err := cli.Revoke(cli.Ctx(), p.lease); err != nil {
logx.Error(err) logc.Errorf(cli.Ctx(), "etcd publisher revoke: %v", err)
} }
} }

View File

@@ -1,18 +1,99 @@
package discov package discov
import ( import (
"context"
"errors" "errors"
"net"
"os"
"sync" "sync"
"testing" "testing"
"time" "time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/discov/internal" "github.com/zeromicro/go-zero/core/discov/internal"
"github.com/zeromicro/go-zero/core/lang" "github.com/zeromicro/go-zero/core/lang"
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stringx" "github.com/zeromicro/go-zero/core/stringx"
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3" clientv3 "go.etcd.io/etcd/client/v3"
"go.uber.org/mock/gomock"
"golang.org/x/net/http2"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/resolver/manual"
)
const (
certContent = `-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUEg9GVO2oaPn+YSmiqmFIuAo10WIwDQYJKoZIhvcNAQEM
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMzAzMTExMzIxMjNaGA8yMTIz
MDIxNTEzMjEyM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBALplXlWsIf0O/IgnIplmiZHKGnxyfyufyE2FBRNk
OofRqbKuPH8GNqbkvZm7N29fwTDAQ+mViAggCkDht4hOzoWJMA7KYJt8JnTSWL48
M1lcrpc9DL2gszC/JF/FGvyANbBtLklkZPFBGdHUX14pjrT937wqPtm+SqUHSvRT
B7bmwmm2drRcmhpVm98LSlV7uQ2EgnJgsLjBPITKUejLmVLHfgX0RwQ2xIpX9pS4
FCe1BTacwl2gGp7Mje7y4Mfv3o0ArJW6Tuwbjx59ZXwb1KIP71b7bT04AVS8ZeYO
UMLKKuB5UR9x9Rn6cLXOTWBpcMVyzDgrAFLZjnE9LPUolZMCAwEAAaNRME8wHwYD
VR0jBBgwFoAUeW8w8pmhncbRgTsl48k4/7wnfx8wCQYDVR0TBAIwADALBgNVHQ8E
BAMCBPAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBDAUAA4IBAQAI
y9xaoS88CLPBsX6mxfcTAFVfGNTRW9VN9Ng1cCnUR+YGoXGM/l+qP4f7p8ocdGwK
iYZErVTzXYIn+D27//wpY3klJk3gAnEUBT3QRkStBw7XnpbeZ2oPBK+cmDnCnZPS
BIF1wxPX7vIgaxs5Zsdqwk3qvZ4Djr2wP7LabNWTLSBKgQoUY45Liw6pffLwcGF9
UKlu54bvGze2SufISCR3ib+I+FLvqpvJhXToZWYb/pfI/HccuCL1oot1x8vx6DQy
U+TYxlZsKS5mdNxAX3dqEkEMsgEi+g/tzDPXJImfeCGGBhIOXLm8SRypiuGdEbc9
xkWYxRPegajuEZGvCqVs
-----END CERTIFICATE-----`
keyContent = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAumVeVawh/Q78iCcimWaJkcoafHJ/K5/ITYUFE2Q6h9Gpsq48
fwY2puS9mbs3b1/BMMBD6ZWICCAKQOG3iE7OhYkwDspgm3wmdNJYvjwzWVyulz0M
vaCzML8kX8Ua/IA1sG0uSWRk8UEZ0dRfXimOtP3fvCo+2b5KpQdK9FMHtubCabZ2
tFyaGlWb3wtKVXu5DYSCcmCwuME8hMpR6MuZUsd+BfRHBDbEilf2lLgUJ7UFNpzC
XaAansyN7vLgx+/ejQCslbpO7BuPHn1lfBvUog/vVvttPTgBVLxl5g5Qwsoq4HlR
H3H1Gfpwtc5NYGlwxXLMOCsAUtmOcT0s9SiVkwIDAQABAoIBAD5meTJNMgO55Kjg
ESExxpRcCIno+tHr5+6rvYtEXqPheOIsmmwb9Gfi4+Z3WpOaht5/Pz0Ppj6yGzyl
U//6AgGKb+BDuBvVcDpjwPnOxZIBCSHwejdxeQu0scSuA97MPS0XIAvJ5FEv7ijk
5Bht6SyGYURpECltHygoTNuGgGqmO+McCJRLE9L09lTBI6UQ/JQwWJqSr7wx6iPU
M1Ze/srIV+7cyEPu6i0DGjS1gSQKkX68Lqn1w6oE290O+OZvleO0gZ02fLDWCZke
aeD9+EU/Pw+rqm3H6o0szOFIpzhRp41FUdW9sybB3Yp3u7c/574E+04Z/e30LMKs
TCtE1QECgYEA3K7KIpw0NH2HXL5C3RHcLmr204xeBfS70riBQQuVUgYdmxak2ima
80RInskY8hRhSGTg0l+VYIH8cmjcUyqMSOELS5XfRH99r4QPiK8AguXg80T4VumY
W3Pf+zEC2ssgP/gYthV0g0Xj5m2QxktOF9tRw5nkg739ZR4dI9lm/iECgYEA2Dnf
uwEDGqHiQRF6/fh5BG/nGVMvrefkqx6WvTJQ3k/M/9WhxB+lr/8yH46TuS8N2b29
FoTf3Mr9T7pr/PWkOPzoY3P56nYbKU8xSwCim9xMzhBMzj8/N9ukJvXy27/VOz56
eQaKqnvdXNGtPJrIMDGHps2KKWlKLyAlapzjVTMCgYAA/W++tACv85g13EykfT4F
n0k4LbsGP9DP4zABQLIMyiY72eAncmRVjwrcW36XJ2xATOONTgx3gF3HjZzfaqNy
eD/6uNNllUTVEryXGmHgNHPL45VRnn6memCY2eFvZdXhM5W4y2PYaunY0MkDercA
+GTngbs6tBF88KOk04bYwQKBgFl68cRgsdkmnwwQYNaTKfmVGYzYaQXNzkqmWPko
xmCJo6tHzC7ubdG8iRCYHzfmahPuuj6EdGPZuSRyYFgJi5Ftz/nAN+84OxtIQ3zn
YWOgskQgaLh9YfsKsQ7Sf1NDOsnOnD5TX7UXl07fEpLe9vNCvAFiU8e5Y9LGudU5
4bYTAoGBAMdX3a3bXp4cZvXNBJ/QLVyxC6fP1Q4haCR1Od3m+T00Jth2IX2dk/fl
p6xiJT1av5JtYabv1dFKaXOS5s1kLGGuCCSKpkvFZm826aQ2AFm0XGqEQDLeei5b
A52Kpy/YJ+RkG4BTFtAooFq6DmA0cnoP6oPvG2h6XtDJwDTPInJb
-----END RSA PRIVATE KEY-----`
caContent = `-----BEGIN CERTIFICATE-----
MIIDbTCCAlWgAwIBAgIUBJvFoCowKich7MMfseJ+DYzzirowDQYJKoZIhvcNAQEM
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMzAzMTExMzIxMDNaGA8yMTIz
MDIxNTEzMjEwM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAO4to2YMYj0bxgr2FCiweSTSFuPx33zSw2x/s9Wf
OR41bm2DFsyYT5f3sOIKlXZEdLmOKty2e3ho3yC0EyNpVHdykkkHT3aDI17quZax
kYi/URqqtl1Z08A22txolc04hAZisg2BypGi3vql81UW1t3zyloGnJoIAeXR9uca
ljP6Bk3bwsxoVBLi1JtHrO0hHLQaeHmKhAyrys06X0LRdn7Px48yRZlt6FaLSa8X
YiRM0G44bVy/h6BkoQjMYGwVmCVk6zjJ9U7ZPFqdnDMNxAfR+hjDnYodqdLDMTTR
1NPVrnEnNwFx0AMLvgt/ba/45vZCEAmSZnFXFAJJcM7ai9ECAwEAAaNTMFEwHQYD
VR0OBBYEFHlvMPKZoZ3G0YE7JePJOP+8J38fMB8GA1UdIwQYMBaAFHlvMPKZoZ3G
0YE7JePJOP+8J38fMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggEB
AMX8dNulADOo9uQgBMyFb9TVra7iY0zZjzv4GY5XY7scd52n6CnfAPvYBBDnTr/O
BgNp5jaujb4+9u/2qhV3f9n+/3WOb2CmPehBgVSzlXqHeQ9lshmgwZPeem2T+8Tm
Nnc/xQnsUfCFszUDxpkr55+aLVM22j02RWqcZ4q7TAaVYL+kdFVMc8FoqG/0ro6A
BjE/Qn0Nn7ciX1VUjDt8l+k7ummPJTmzdi6i6E4AwO9dzrGNgGJ4aWL8cC6xYcIX
goVIRTFeONXSDno/oPjWHpIPt7L15heMpKBHNuzPkKx2YVqPHE5QZxWfS+Lzgx+Q
E2oTTM0rYKOZ8p6000mhvKI=
-----END CERTIFICATE-----`
) )
func init() { func init() {
@@ -37,7 +118,7 @@ func TestPublisher_register(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
} }
func TestPublisher_registerWithId(t *testing.T) { func TestPublisher_registerWithOptions(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
const id = 2 const id = 2
@@ -49,7 +130,15 @@ func TestPublisher_registerWithId(t *testing.T) {
ID: 1, ID: 1,
}, nil) }, nil)
cli.EXPECT().Put(gomock.Any(), makeEtcdKey("thekey", id), "thevalue", gomock.Any()) cli.EXPECT().Put(gomock.Any(), makeEtcdKey("thekey", id), "thevalue", gomock.Any())
pub := NewPublisher(nil, "thekey", "thevalue", WithId(id))
certFile := createTempFile(t, []byte(certContent))
defer os.Remove(certFile)
keyFile := createTempFile(t, []byte(keyContent))
defer os.Remove(keyFile)
caFile := createTempFile(t, []byte(caContent))
defer os.Remove(caFile)
pub := NewPublisher(nil, "thekey", "thevalue", WithId(id),
WithPubEtcdTLS(certFile, keyFile, caFile, true))
_, err := pub.register(cli) _, err := pub.register(cli)
assert.Nil(t, err) assert.Nil(t, err)
} }
@@ -123,9 +212,12 @@ func TestPublisher_keepAliveAsyncQuit(t *testing.T) {
defer restore() defer restore()
cli.EXPECT().Ctx().AnyTimes() cli.EXPECT().Ctx().AnyTimes()
cli.EXPECT().KeepAlive(gomock.Any(), id) cli.EXPECT().KeepAlive(gomock.Any(), id)
// Add Watch expectation for the new watch mechanism
watchChan := make(<-chan clientv3.WatchResponse)
cli.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return(watchChan)
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Add(1)
cli.EXPECT().Revoke(gomock.Any(), id).Do(func(_, _ interface{}) { cli.EXPECT().Revoke(gomock.Any(), id).Do(func(_, _ any) {
wg.Done() wg.Done()
}) })
pub := NewPublisher(nil, "thekey", "thevalue") pub := NewPublisher(nil, "thekey", "thevalue")
@@ -144,10 +236,13 @@ func TestPublisher_keepAliveAsyncPause(t *testing.T) {
defer restore() defer restore()
cli.EXPECT().Ctx().AnyTimes() cli.EXPECT().Ctx().AnyTimes()
cli.EXPECT().KeepAlive(gomock.Any(), id) cli.EXPECT().KeepAlive(gomock.Any(), id)
// Add Watch expectation for the new watch mechanism
watchChan := make(<-chan clientv3.WatchResponse)
cli.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return(watchChan)
pub := NewPublisher(nil, "thekey", "thevalue") pub := NewPublisher(nil, "thekey", "thevalue")
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Add(1)
cli.EXPECT().Revoke(gomock.Any(), id).Do(func(_, _ interface{}) { cli.EXPECT().Revoke(gomock.Any(), id).Do(func(_, _ any) {
pub.Stop() pub.Stop()
wg.Done() wg.Done()
}) })
@@ -157,6 +252,112 @@ func TestPublisher_keepAliveAsyncPause(t *testing.T) {
wg.Wait() wg.Wait()
} }
// Test case for key deletion and re-registration (covers lines 148-155)
func TestPublisher_keepAliveAsyncKeyDeletion(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
const id clientv3.LeaseID = 1
cli := internal.NewMockEtcdClient(ctrl)
restore := setMockClient(cli)
defer restore()
cli.EXPECT().Ctx().AnyTimes()
cli.EXPECT().KeepAlive(gomock.Any(), id)
// Create a watch channel that will send a delete event
watchChan := make(chan clientv3.WatchResponse, 1)
watchResp := clientv3.WatchResponse{
Events: []*clientv3.Event{{
Type: clientv3.EventTypeDelete,
Kv: &mvccpb.KeyValue{
Key: []byte("thekey"),
},
}},
}
watchChan <- watchResp
cli.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return((<-chan clientv3.WatchResponse)(watchChan))
var wg sync.WaitGroup
wg.Add(1) // Only wait for Revoke call
// Use a channel to signal when Put has been called
putCalled := make(chan struct{})
// Expect the re-put operation when key is deleted
cli.EXPECT().Put(gomock.Any(), "thekey", "thevalue", gomock.Any()).Do(func(_, _, _, _ any) {
close(putCalled) // Signal that Put has been called
}).Return(nil, nil)
// Expect revoke when Stop is called
cli.EXPECT().Revoke(gomock.Any(), id).Do(func(_, _ any) {
wg.Done()
})
pub := NewPublisher(nil, "thekey", "thevalue")
pub.lease = id
pub.fullKey = "thekey"
assert.Nil(t, pub.keepAliveAsync(cli))
// Wait for Put to be called, then stop
<-putCalled
pub.Stop()
wg.Wait()
}
// Test case for key deletion with re-put error (covers error branch in lines 151-152)
func TestPublisher_keepAliveAsyncKeyDeletionPutError(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
const id clientv3.LeaseID = 1
cli := internal.NewMockEtcdClient(ctrl)
restore := setMockClient(cli)
defer restore()
cli.EXPECT().Ctx().AnyTimes()
cli.EXPECT().KeepAlive(gomock.Any(), id)
// Create a watch channel that will send a delete event
watchChan := make(chan clientv3.WatchResponse, 1)
watchResp := clientv3.WatchResponse{
Events: []*clientv3.Event{{
Type: clientv3.EventTypeDelete,
Kv: &mvccpb.KeyValue{
Key: []byte("thekey"),
},
}},
}
watchChan <- watchResp
cli.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return((<-chan clientv3.WatchResponse)(watchChan))
var wg sync.WaitGroup
wg.Add(1) // Only wait for Revoke call
// Use a channel to signal when Put has been called
putCalled := make(chan struct{})
// Expect the re-put operation to fail
cli.EXPECT().Put(gomock.Any(), "thekey", "thevalue", gomock.Any()).Do(func(_, _, _, _ any) {
close(putCalled) // Signal that Put has been called
}).Return(nil, errors.New("put error"))
// Expect revoke when Stop is called
cli.EXPECT().Revoke(gomock.Any(), id).Do(func(_, _ any) {
wg.Done()
})
pub := NewPublisher(nil, "thekey", "thevalue")
pub.lease = id
pub.fullKey = "thekey"
assert.Nil(t, pub.keepAliveAsync(cli))
// Wait for Put to be called, then stop
<-putCalled
pub.Stop()
wg.Wait()
}
func TestPublisher_Resume(t *testing.T) { func TestPublisher_Resume(t *testing.T) {
publisher := new(Publisher) publisher := new(Publisher)
publisher.resumeChan = make(chan lang.PlaceholderType) publisher.resumeChan = make(chan lang.PlaceholderType)
@@ -169,3 +370,95 @@ func TestPublisher_Resume(t *testing.T) {
}() }()
<-publisher.resumeChan <-publisher.resumeChan
} }
func TestPublisher_keepAliveAsync(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
const id clientv3.LeaseID = 1
conn := createMockConn(t)
defer conn.Close()
cli := internal.NewMockEtcdClient(ctrl)
cli.EXPECT().ActiveConnection().Return(conn).AnyTimes()
cli.EXPECT().Close()
defer cli.Close()
cli.ActiveConnection()
restore := setMockClient(cli)
defer restore()
cli.EXPECT().Ctx().AnyTimes()
cli.EXPECT().KeepAlive(gomock.Any(), id)
// Add Watch expectation for the new watch mechanism
watchChan := make(<-chan clientv3.WatchResponse)
cli.EXPECT().Watch(gomock.Any(), gomock.Any(), gomock.Any()).Return(watchChan)
cli.EXPECT().Grant(gomock.Any(), timeToLive).Return(&clientv3.LeaseGrantResponse{
ID: 1,
}, nil)
cli.EXPECT().Put(gomock.Any(), makeEtcdKey("thekey", int64(id)), "thevalue", gomock.Any())
var wg sync.WaitGroup
wg.Add(1)
cli.EXPECT().Revoke(gomock.Any(), id).Do(func(_, _ any) {
wg.Done()
})
pub := NewPublisher([]string{"the-endpoint"}, "thekey", "thevalue")
pub.lease = id
assert.Nil(t, pub.KeepAlive())
pub.Stop()
wg.Wait()
}
func createMockConn(t *testing.T) *grpc.ClientConn {
lis, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatalf("Error while listening. Err: %v", err)
}
defer lis.Close()
lisAddr := resolver.Address{Addr: lis.Addr().String()}
lisDone := make(chan struct{})
dialDone := make(chan struct{})
// 1st listener accepts the connection and then does nothing
go func() {
defer close(lisDone)
conn, err := lis.Accept()
if err != nil {
t.Errorf("Error while accepting. Err: %v", err)
return
}
framer := http2.NewFramer(conn, conn)
if err := framer.WriteSettings(http2.Setting{}); err != nil {
t.Errorf("Error while writing settings. Err: %v", err)
return
}
<-dialDone // Close conn only after dial returns.
}()
r := manual.NewBuilderWithScheme("whatever")
r.InitialState(resolver.State{Addresses: []resolver.Address{lisAddr}})
client, err := grpc.DialContext(context.Background(), r.Scheme()+":///test.server",
grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r))
close(dialDone)
if err != nil {
t.Fatalf("Dial failed. Err: %v", err)
}
timeout := time.After(1 * time.Second)
select {
case <-timeout:
t.Fatal("timed out waiting for server to finish")
case <-lisDone:
}
return client
}
func createTempFile(t *testing.T, body []byte) string {
tmpFile, err := os.CreateTemp(os.TempDir(), "go-unit-*.tmp")
if err != nil {
t.Fatal(err)
}
tmpFile.Close()
if err = os.WriteFile(tmpFile.Name(), body, os.ModePerm); err != nil {
t.Fatal(err)
}
return tmpFile.Name()
}

View File

@@ -13,12 +13,15 @@ type (
// SubOption defines the method to customize a Subscriber. // SubOption defines the method to customize a Subscriber.
SubOption func(sub *Subscriber) SubOption func(sub *Subscriber)
// A Subscriber is used to subscribe the given key on a etcd cluster. // A Subscriber is used to subscribe the given key on an etcd cluster.
Subscriber struct { Subscriber struct {
endpoints []string endpoints []string
exclusive bool exclusive bool
items *container key string
exactMatch bool
items Container
} }
KV = internal.KV
) )
// NewSubscriber returns a Subscriber. // NewSubscriber returns a Subscriber.
@@ -28,13 +31,16 @@ type (
func NewSubscriber(endpoints []string, key string, opts ...SubOption) (*Subscriber, error) { func NewSubscriber(endpoints []string, key string, opts ...SubOption) (*Subscriber, error) {
sub := &Subscriber{ sub := &Subscriber{
endpoints: endpoints, endpoints: endpoints,
key: key,
} }
for _, opt := range opts { for _, opt := range opts {
opt(sub) opt(sub)
} }
if sub.items == nil {
sub.items = newContainer(sub.exclusive) sub.items = newContainer(sub.exclusive)
}
if err := internal.GetRegistry().Monitor(endpoints, key, sub.items); err != nil { if err := internal.GetRegistry().Monitor(endpoints, key, sub.exactMatch, sub.items); err != nil {
return nil, err return nil, err
} }
@@ -43,12 +49,17 @@ func NewSubscriber(endpoints []string, key string, opts ...SubOption) (*Subscrib
// AddListener adds listener to s. // AddListener adds listener to s.
func (s *Subscriber) AddListener(listener func()) { func (s *Subscriber) AddListener(listener func()) {
s.items.addListener(listener) s.items.AddListener(listener)
}
// Close closes the subscriber.
func (s *Subscriber) Close() {
internal.GetRegistry().Unmonitor(s.endpoints, s.key, s.exactMatch, s.items)
} }
// Values returns all the subscription values. // Values returns all the subscription values.
func (s *Subscriber) Values() []string { func (s *Subscriber) Values() []string {
return s.items.getValues() return s.items.GetValues()
} }
// Exclusive means that key value can only be 1:1, // Exclusive means that key value can only be 1:1,
@@ -59,6 +70,13 @@ func Exclusive() SubOption {
} }
} }
// WithExactMatch turn off querying using key prefixes.
func WithExactMatch() SubOption {
return func(sub *Subscriber) {
sub.exactMatch = true
}
}
// WithSubEtcdAccount provides the etcd username/password. // WithSubEtcdAccount provides the etcd username/password.
func WithSubEtcdAccount(user, pass string) SubOption { func WithSubEtcdAccount(user, pass string) SubOption {
return func(sub *Subscriber) { return func(sub *Subscriber) {
@@ -73,7 +91,22 @@ func WithSubEtcdTLS(certFile, certKeyFile, caFile string, insecureSkipVerify boo
} }
} }
type container struct { // WithContainer provides a custom container to the subscriber.
func WithContainer(container Container) SubOption {
return func(sub *Subscriber) {
sub.items = container
}
}
type (
Container interface {
OnAdd(kv internal.KV)
OnDelete(kv internal.KV)
AddListener(listener func())
GetValues() []string
}
container struct {
exclusive bool exclusive bool
values map[string][]string values map[string][]string
mapping map[string]string mapping map[string]string
@@ -81,7 +114,8 @@ type container struct {
dirty *syncx.AtomicBool dirty *syncx.AtomicBool
listeners []func() listeners []func()
lock sync.Mutex lock sync.Mutex
} }
)
func newContainer(exclusive bool) *container { func newContainer(exclusive bool) *container {
return &container{ return &container{
@@ -126,7 +160,7 @@ func (c *container) addKv(key, value string) ([]string, bool) {
return nil, false return nil, false
} }
func (c *container) addListener(listener func()) { func (c *container) AddListener(listener func()) {
c.lock.Lock() c.lock.Lock()
c.listeners = append(c.listeners, listener) c.listeners = append(c.listeners, listener)
c.lock.Unlock() c.lock.Unlock()
@@ -155,7 +189,7 @@ func (c *container) doRemoveKey(key string) {
} }
} }
func (c *container) getValues() []string { func (c *container) GetValues() []string {
if !c.dirty.True() { if !c.dirty.True() {
return c.snapshot.Load().([]string) return c.snapshot.Load().([]string)
} }

View File

@@ -171,10 +171,10 @@ func TestContainer(t *testing.T) {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
var changed bool var changed bool
c := newContainer(exclusive) c := newContainer(exclusive)
c.addListener(func() { c.AddListener(func() {
changed = true changed = true
}) })
assert.Nil(t, c.getValues()) assert.Nil(t, c.GetValues())
assert.False(t, changed) assert.False(t, changed)
for _, order := range test.do { for _, order := range test.do {
@@ -193,9 +193,9 @@ func TestContainer(t *testing.T) {
assert.True(t, changed) assert.True(t, changed)
assert.True(t, c.dirty.True()) assert.True(t, c.dirty.True())
assert.ElementsMatch(t, test.expect, c.getValues()) assert.ElementsMatch(t, test.expect, c.GetValues())
assert.False(t, c.dirty.True()) assert.False(t, c.dirty.True())
assert.ElementsMatch(t, test.expect, c.getValues()) assert.ElementsMatch(t, test.expect, c.GetValues())
}) })
} }
} }
@@ -204,12 +204,14 @@ func TestContainer(t *testing.T) {
func TestSubscriber(t *testing.T) { func TestSubscriber(t *testing.T) {
sub := new(Subscriber) sub := new(Subscriber)
Exclusive()(sub) Exclusive()(sub)
sub.items = newContainer(sub.exclusive) c := newContainer(sub.exclusive)
WithContainer(c)(sub)
sub.items = c
var count int32 var count int32
sub.AddListener(func() { sub.AddListener(func() {
atomic.AddInt32(&count, 1) atomic.AddInt32(&count, 1)
}) })
sub.items.notifyChange() c.notifyChange()
assert.Empty(t, sub.Values()) assert.Empty(t, sub.Values())
assert.Equal(t, int32(1), atomic.LoadInt32(&count)) assert.Equal(t, int32(1), atomic.LoadInt32(&count))
} }
@@ -225,3 +227,29 @@ func TestWithSubEtcdAccount(t *testing.T) {
assert.Equal(t, user, account.User) assert.Equal(t, user, account.User)
assert.Equal(t, "bar", account.Pass) assert.Equal(t, "bar", account.Pass)
} }
func TestWithExactMatch(t *testing.T) {
sub := new(Subscriber)
WithExactMatch()(sub)
c := newContainer(sub.exclusive)
sub.items = c
var count int32
sub.AddListener(func() {
atomic.AddInt32(&count, 1)
})
c.notifyChange()
assert.Empty(t, sub.Values())
assert.Equal(t, int32(1), atomic.LoadInt32(&count))
}
func TestSubscriberClose(t *testing.T) {
l := newContainer(false)
sub := &Subscriber{
endpoints: []string{"localhost:12379"},
key: "foo",
items: l,
}
assert.NotPanics(t, func() {
sub.Close()
})
}

View File

@@ -1,18 +1,21 @@
package errorx package errorx
import "bytes" import (
"errors"
type ( "sync"
// A BatchError is an error that can hold multiple errors.
BatchError struct {
errs errorArray
}
errorArray []error
) )
// Add adds errs to be, nil errors are ignored. // BatchError is an error that can hold multiple errors.
type BatchError struct {
errs []error
lock sync.RWMutex
}
// Add adds one or more non-nil errors to the BatchError instance.
func (be *BatchError) Add(errs ...error) { func (be *BatchError) Add(errs ...error) {
be.lock.Lock()
defer be.lock.Unlock()
for _, err := range errs { for _, err := range errs {
if err != nil { if err != nil {
be.errs = append(be.errs, err) be.errs = append(be.errs, err)
@@ -20,33 +23,20 @@ func (be *BatchError) Add(errs ...error) {
} }
} }
// Err returns an error that represents all errors. // Err returns an error that represents all accumulated errors.
// It returns nil if there are no errors.
func (be *BatchError) Err() error { func (be *BatchError) Err() error {
switch len(be.errs) { be.lock.RLock()
case 0: defer be.lock.RUnlock()
return nil
case 1: // If there are no non-nil errors, errors.Join(...) returns nil.
return be.errs[0] return errors.Join(be.errs...)
default:
return be.errs
}
} }
// NotNil checks if any error inside. // NotNil checks if there is at least one error inside the BatchError.
func (be *BatchError) NotNil() bool { func (be *BatchError) NotNil() bool {
be.lock.RLock()
defer be.lock.RUnlock()
return len(be.errs) > 0 return len(be.errs) > 0
} }
// Error returns a string that represents inside errors.
func (ea errorArray) Error() string {
var buf bytes.Buffer
for i := range ea {
if i > 0 {
buf.WriteByte('\n')
}
buf.WriteString(ea[i].Error())
}
return buf.String()
}

View File

@@ -3,6 +3,7 @@ package errorx
import ( import (
"errors" "errors"
"fmt" "fmt"
"sync"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -33,7 +34,7 @@ func TestBatchErrorNilFromFunc(t *testing.T) {
func TestBatchErrorOneError(t *testing.T) { func TestBatchErrorOneError(t *testing.T) {
var batch BatchError var batch BatchError
batch.Add(errors.New(err1)) batch.Add(errors.New(err1))
assert.NotNil(t, batch) assert.NotNil(t, batch.Err())
assert.Equal(t, err1, batch.Err().Error()) assert.Equal(t, err1, batch.Err().Error())
assert.True(t, batch.NotNil()) assert.True(t, batch.NotNil())
} }
@@ -42,7 +43,105 @@ func TestBatchErrorWithErrors(t *testing.T) {
var batch BatchError var batch BatchError
batch.Add(errors.New(err1)) batch.Add(errors.New(err1))
batch.Add(errors.New(err2)) batch.Add(errors.New(err2))
assert.NotNil(t, batch) assert.NotNil(t, batch.Err())
assert.Equal(t, fmt.Sprintf("%s\n%s", err1, err2), batch.Err().Error()) assert.Equal(t, fmt.Sprintf("%s\n%s", err1, err2), batch.Err().Error())
assert.True(t, batch.NotNil()) assert.True(t, batch.NotNil())
} }
func TestBatchErrorConcurrentAdd(t *testing.T) {
const count = 10000
var batch BatchError
var wg sync.WaitGroup
wg.Add(count)
for i := 0; i < count; i++ {
go func() {
defer wg.Done()
batch.Add(errors.New(err1))
}()
}
wg.Wait()
assert.NotNil(t, batch.Err())
assert.Equal(t, count, len(batch.errs))
assert.True(t, batch.NotNil())
}
func TestBatchError_Unwrap(t *testing.T) {
t.Run("nil", func(t *testing.T) {
var be BatchError
assert.Nil(t, be.Err())
assert.True(t, errors.Is(be.Err(), nil))
})
t.Run("one error", func(t *testing.T) {
var errFoo = errors.New("foo")
var errBar = errors.New("bar")
var be BatchError
be.Add(errFoo)
assert.True(t, errors.Is(be.Err(), errFoo))
assert.False(t, errors.Is(be.Err(), errBar))
})
t.Run("two errors", func(t *testing.T) {
var errFoo = errors.New("foo")
var errBar = errors.New("bar")
var errBaz = errors.New("baz")
var be BatchError
be.Add(errFoo)
be.Add(errBar)
assert.True(t, errors.Is(be.Err(), errFoo))
assert.True(t, errors.Is(be.Err(), errBar))
assert.False(t, errors.Is(be.Err(), errBaz))
})
}
func TestBatchError_Add(t *testing.T) {
var be BatchError
// Test adding nil errors
be.Add(nil, nil)
assert.False(t, be.NotNil(), "Expected BatchError to be empty after adding nil errors")
// Test adding non-nil errors
err1 := errors.New("error 1")
err2 := errors.New("error 2")
be.Add(err1, err2)
assert.True(t, be.NotNil(), "Expected BatchError to be non-empty after adding errors")
// Test adding a mix of nil and non-nil errors
err3 := errors.New("error 3")
be.Add(nil, err3, nil)
assert.True(t, be.NotNil(), "Expected BatchError to be non-empty after adding a mix of nil and non-nil errors")
}
func TestBatchError_Err(t *testing.T) {
var be BatchError
// Test Err() on empty BatchError
assert.Nil(t, be.Err(), "Expected nil error for empty BatchError")
// Test Err() with multiple errors
err1 := errors.New("error 1")
err2 := errors.New("error 2")
be.Add(err1, err2)
combinedErr := be.Err()
assert.NotNil(t, combinedErr, "Expected nil error for BatchError with multiple errors")
// Check if the combined error contains both error messages
errString := combinedErr.Error()
assert.Truef(t, errors.Is(combinedErr, err1), "Combined error doesn't contain first error: %s", errString)
assert.Truef(t, errors.Is(combinedErr, err2), "Combined error doesn't contain second error: %s", errString)
}
func TestBatchError_NotNil(t *testing.T) {
var be BatchError
// Test NotNil() on empty BatchError
assert.Nil(t, be.Err(), "Expected nil error for empty BatchError")
// Test NotNil() after adding an error
be.Add(errors.New("test error"))
assert.NotNil(t, be.Err(), "Expected non-nil error after adding an error")
}

14
core/errorx/check.go Normal file
View File

@@ -0,0 +1,14 @@
package errorx
import "errors"
// In checks if the given err is one of errs.
func In(err error, errs ...error) bool {
for _, each := range errs {
if errors.Is(err, each) {
return true
}
}
return false
}

70
core/errorx/check_test.go Normal file
View File

@@ -0,0 +1,70 @@
package errorx
import (
"errors"
"testing"
)
func TestIn(t *testing.T) {
err1 := errors.New("error 1")
err2 := errors.New("error 2")
err3 := errors.New("error 3")
tests := []struct {
name string
err error
errs []error
want bool
}{
{
name: "Error matches one of the errors in the list",
err: err1,
errs: []error{err1, err2},
want: true,
},
{
name: "Error does not match any errors in the list",
err: err3,
errs: []error{err1, err2},
want: false,
},
{
name: "Empty error list",
err: err1,
errs: []error{},
want: false,
},
{
name: "Nil error with non-nil list",
err: nil,
errs: []error{err1, err2},
want: false,
},
{
name: "Non-nil error with nil in list",
err: err1,
errs: []error{nil, err2},
want: false,
},
{
name: "Error matches nil error in the list",
err: nil,
errs: []error{nil, err2},
want: true,
},
{
name: "Nil error with empty list",
err: nil,
errs: []error{},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := In(tt.err, tt.errs...); got != tt.want {
t.Errorf("In() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -12,7 +12,7 @@ func Wrap(err error, message string) error {
} }
// Wrapf returns an error that wraps err with given format and args. // Wrapf returns an error that wraps err with given format and args.
func Wrapf(err error, format string, args ...interface{}) error { func Wrapf(err error, format string, args ...any) error {
if err == nil { if err == nil {
return nil return nil
} }

View File

@@ -42,7 +42,7 @@ func NewBulkExecutor(execute Execute, opts ...BulkOption) *BulkExecutor {
} }
// Add adds task into be. // Add adds task into be.
func (be *BulkExecutor) Add(task interface{}) error { func (be *BulkExecutor) Add(task any) error {
be.executor.Add(task) be.executor.Add(task)
return nil return nil
} }
@@ -79,22 +79,22 @@ func newBulkOptions() bulkOptions {
} }
type bulkContainer struct { type bulkContainer struct {
tasks []interface{} tasks []any
execute Execute execute Execute
maxTasks int maxTasks int
} }
func (bc *bulkContainer) AddTask(task interface{}) bool { func (bc *bulkContainer) AddTask(task any) bool {
bc.tasks = append(bc.tasks, task) bc.tasks = append(bc.tasks, task)
return len(bc.tasks) >= bc.maxTasks return len(bc.tasks) >= bc.maxTasks
} }
func (bc *bulkContainer) Execute(tasks interface{}) { func (bc *bulkContainer) Execute(tasks any) {
vals := tasks.([]interface{}) vals := tasks.([]any)
bc.execute(vals) bc.execute(vals)
} }
func (bc *bulkContainer) RemoveAll() interface{} { func (bc *bulkContainer) RemoveAll() any {
tasks := bc.tasks tasks := bc.tasks
bc.tasks = nil bc.tasks = nil
return tasks return tasks

View File

@@ -12,7 +12,7 @@ func TestBulkExecutor(t *testing.T) {
var values []int var values []int
var lock sync.Mutex var lock sync.Mutex
executor := NewBulkExecutor(func(items []interface{}) { executor := NewBulkExecutor(func(items []any) {
lock.Lock() lock.Lock()
values = append(values, len(items)) values = append(values, len(items))
lock.Unlock() lock.Unlock()
@@ -40,7 +40,7 @@ func TestBulkExecutorFlushInterval(t *testing.T) {
var wait sync.WaitGroup var wait sync.WaitGroup
wait.Add(1) wait.Add(1)
executor := NewBulkExecutor(func(items []interface{}) { executor := NewBulkExecutor(func(items []any) {
assert.Equal(t, size, len(items)) assert.Equal(t, size, len(items))
wait.Done() wait.Done()
}, WithBulkTasks(caches), WithBulkInterval(time.Millisecond*100)) }, WithBulkTasks(caches), WithBulkInterval(time.Millisecond*100))
@@ -53,7 +53,7 @@ func TestBulkExecutorFlushInterval(t *testing.T) {
} }
func TestBulkExecutorEmpty(t *testing.T) { func TestBulkExecutorEmpty(t *testing.T) {
NewBulkExecutor(func(items []interface{}) { NewBulkExecutor(func(items []any) {
assert.Fail(t, "should not called") assert.Fail(t, "should not called")
}, WithBulkTasks(10), WithBulkInterval(time.Millisecond)) }, WithBulkTasks(10), WithBulkInterval(time.Millisecond))
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
@@ -67,7 +67,7 @@ func TestBulkExecutorFlush(t *testing.T) {
var wait sync.WaitGroup var wait sync.WaitGroup
wait.Add(1) wait.Add(1)
be := NewBulkExecutor(func(items []interface{}) { be := NewBulkExecutor(func(items []any) {
assert.Equal(t, tasks, len(items)) assert.Equal(t, tasks, len(items))
wait.Done() wait.Done()
}, WithBulkTasks(caches), WithBulkInterval(time.Minute)) }, WithBulkTasks(caches), WithBulkInterval(time.Minute))
@@ -78,11 +78,11 @@ func TestBulkExecutorFlush(t *testing.T) {
wait.Wait() wait.Wait()
} }
func TestBuldExecutorFlushSlowTasks(t *testing.T) { func TestBulkExecutorFlushSlowTasks(t *testing.T) {
const total = 1500 const total = 1500
lock := new(sync.Mutex) lock := new(sync.Mutex)
result := make([]interface{}, 0, 10000) result := make([]any, 0, 10000)
exec := NewBulkExecutor(func(tasks []interface{}) { exec := NewBulkExecutor(func(tasks []any) {
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
@@ -100,7 +100,7 @@ func TestBuldExecutorFlushSlowTasks(t *testing.T) {
func BenchmarkBulkExecutor(b *testing.B) { func BenchmarkBulkExecutor(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
be := NewBulkExecutor(func(tasks []interface{}) { be := NewBulkExecutor(func(tasks []any) {
time.Sleep(time.Millisecond * time.Duration(len(tasks))) time.Sleep(time.Millisecond * time.Duration(len(tasks)))
}) })
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

View File

@@ -42,7 +42,7 @@ func NewChunkExecutor(execute Execute, opts ...ChunkOption) *ChunkExecutor {
} }
// Add adds task with given chunk size into ce. // Add adds task with given chunk size into ce.
func (ce *ChunkExecutor) Add(task interface{}, size int) error { func (ce *ChunkExecutor) Add(task any, size int) error {
ce.executor.Add(chunk{ ce.executor.Add(chunk{
val: task, val: task,
size: size, size: size,
@@ -82,25 +82,25 @@ func newChunkOptions() chunkOptions {
} }
type chunkContainer struct { type chunkContainer struct {
tasks []interface{} tasks []any
execute Execute execute Execute
size int size int
maxChunkSize int maxChunkSize int
} }
func (bc *chunkContainer) AddTask(task interface{}) bool { func (bc *chunkContainer) AddTask(task any) bool {
ck := task.(chunk) ck := task.(chunk)
bc.tasks = append(bc.tasks, ck.val) bc.tasks = append(bc.tasks, ck.val)
bc.size += ck.size bc.size += ck.size
return bc.size >= bc.maxChunkSize return bc.size >= bc.maxChunkSize
} }
func (bc *chunkContainer) Execute(tasks interface{}) { func (bc *chunkContainer) Execute(tasks any) {
vals := tasks.([]interface{}) vals := tasks.([]any)
bc.execute(vals) bc.execute(vals)
} }
func (bc *chunkContainer) RemoveAll() interface{} { func (bc *chunkContainer) RemoveAll() any {
tasks := bc.tasks tasks := bc.tasks
bc.tasks = nil bc.tasks = nil
bc.size = 0 bc.size = 0
@@ -108,6 +108,6 @@ func (bc *chunkContainer) RemoveAll() interface{} {
} }
type chunk struct { type chunk struct {
val interface{} val any
size int size int
} }

View File

@@ -12,7 +12,7 @@ func TestChunkExecutor(t *testing.T) {
var values []int var values []int
var lock sync.Mutex var lock sync.Mutex
executor := NewChunkExecutor(func(items []interface{}) { executor := NewChunkExecutor(func(items []any) {
lock.Lock() lock.Lock()
values = append(values, len(items)) values = append(values, len(items))
lock.Unlock() lock.Unlock()
@@ -40,7 +40,7 @@ func TestChunkExecutorFlushInterval(t *testing.T) {
var wait sync.WaitGroup var wait sync.WaitGroup
wait.Add(1) wait.Add(1)
executor := NewChunkExecutor(func(items []interface{}) { executor := NewChunkExecutor(func(items []any) {
assert.Equal(t, size, len(items)) assert.Equal(t, size, len(items))
wait.Done() wait.Done()
}, WithChunkBytes(caches), WithFlushInterval(time.Millisecond*100)) }, WithChunkBytes(caches), WithFlushInterval(time.Millisecond*100))
@@ -53,10 +53,11 @@ func TestChunkExecutorFlushInterval(t *testing.T) {
} }
func TestChunkExecutorEmpty(t *testing.T) { func TestChunkExecutorEmpty(t *testing.T) {
NewChunkExecutor(func(items []interface{}) { executor := NewChunkExecutor(func(items []any) {
assert.Fail(t, "should not called") assert.Fail(t, "should not called")
}, WithChunkBytes(10), WithFlushInterval(time.Millisecond)) }, WithChunkBytes(10), WithFlushInterval(time.Millisecond))
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
executor.Wait()
} }
func TestChunkExecutorFlush(t *testing.T) { func TestChunkExecutorFlush(t *testing.T) {
@@ -67,7 +68,7 @@ func TestChunkExecutorFlush(t *testing.T) {
var wait sync.WaitGroup var wait sync.WaitGroup
wait.Add(1) wait.Add(1)
be := NewChunkExecutor(func(items []interface{}) { be := NewChunkExecutor(func(items []any) {
assert.Equal(t, tasks, len(items)) assert.Equal(t, tasks, len(items))
wait.Done() wait.Done()
}, WithChunkBytes(caches), WithFlushInterval(time.Minute)) }, WithChunkBytes(caches), WithFlushInterval(time.Minute))
@@ -81,7 +82,7 @@ func TestChunkExecutorFlush(t *testing.T) {
func BenchmarkChunkExecutor(b *testing.B) { func BenchmarkChunkExecutor(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
be := NewChunkExecutor(func(tasks []interface{}) { be := NewChunkExecutor(func(tasks []any) {
time.Sleep(time.Millisecond * time.Duration(len(tasks))) time.Sleep(time.Millisecond * time.Duration(len(tasks)))
}) })
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

View File

@@ -21,16 +21,16 @@ type (
TaskContainer interface { TaskContainer interface {
// AddTask adds the task into the container. // AddTask adds the task into the container.
// Returns true if the container needs to be flushed after the addition. // Returns true if the container needs to be flushed after the addition.
AddTask(task interface{}) bool AddTask(task any) bool
// Execute handles the collected tasks by the container when flushing. // Execute handles the collected tasks by the container when flushing.
Execute(tasks interface{}) Execute(tasks any)
// RemoveAll removes the contained tasks, and return them. // RemoveAll removes the contained tasks, and return them.
RemoveAll() interface{} RemoveAll() any
} }
// A PeriodicalExecutor is an executor that periodically execute tasks. // A PeriodicalExecutor is an executor that periodically execute tasks.
PeriodicalExecutor struct { PeriodicalExecutor struct {
commander chan interface{} commander chan any
interval time.Duration interval time.Duration
container TaskContainer container TaskContainer
waitGroup sync.WaitGroup waitGroup sync.WaitGroup
@@ -48,7 +48,7 @@ type (
func NewPeriodicalExecutor(interval time.Duration, container TaskContainer) *PeriodicalExecutor { func NewPeriodicalExecutor(interval time.Duration, container TaskContainer) *PeriodicalExecutor {
executor := &PeriodicalExecutor{ executor := &PeriodicalExecutor{
// buffer 1 to let the caller go quickly // buffer 1 to let the caller go quickly
commander: make(chan interface{}, 1), commander: make(chan any, 1),
interval: interval, interval: interval,
container: container, container: container,
confirmChan: make(chan lang.PlaceholderType), confirmChan: make(chan lang.PlaceholderType),
@@ -64,7 +64,7 @@ func NewPeriodicalExecutor(interval time.Duration, container TaskContainer) *Per
} }
// Add adds tasks into pe. // Add adds tasks into pe.
func (pe *PeriodicalExecutor) Add(task interface{}) { func (pe *PeriodicalExecutor) Add(task any) {
if vals, ok := pe.addAndCheck(task); ok { if vals, ok := pe.addAndCheck(task); ok {
pe.commander <- vals pe.commander <- vals
<-pe.confirmChan <-pe.confirmChan
@@ -74,14 +74,14 @@ func (pe *PeriodicalExecutor) Add(task interface{}) {
// Flush forces pe to execute tasks. // Flush forces pe to execute tasks.
func (pe *PeriodicalExecutor) Flush() bool { func (pe *PeriodicalExecutor) Flush() bool {
pe.enterExecution() pe.enterExecution()
return pe.executeTasks(func() interface{} { return pe.executeTasks(func() any {
pe.lock.Lock() pe.lock.Lock()
defer pe.lock.Unlock() defer pe.lock.Unlock()
return pe.container.RemoveAll() return pe.container.RemoveAll()
}()) }())
} }
// Sync lets caller to run fn thread-safe with pe, especially for the underlying container. // Sync lets caller run fn thread-safe with pe, especially for the underlying container.
func (pe *PeriodicalExecutor) Sync(fn func()) { func (pe *PeriodicalExecutor) Sync(fn func()) {
pe.lock.Lock() pe.lock.Lock()
defer pe.lock.Unlock() defer pe.lock.Unlock()
@@ -96,7 +96,7 @@ func (pe *PeriodicalExecutor) Wait() {
}) })
} }
func (pe *PeriodicalExecutor) addAndCheck(task interface{}) (interface{}, bool) { func (pe *PeriodicalExecutor) addAndCheck(task any) (any, bool) {
pe.lock.Lock() pe.lock.Lock()
defer func() { defer func() {
if !pe.guarded { if !pe.guarded {
@@ -116,7 +116,7 @@ func (pe *PeriodicalExecutor) addAndCheck(task interface{}) (interface{}, bool)
} }
func (pe *PeriodicalExecutor) backgroundFlush() { func (pe *PeriodicalExecutor) backgroundFlush() {
threading.GoSafe(func() { go func() {
// flush before quit goroutine to avoid missing tasks // flush before quit goroutine to avoid missing tasks
defer pe.Flush() defer pe.Flush()
@@ -144,7 +144,7 @@ func (pe *PeriodicalExecutor) backgroundFlush() {
} }
} }
} }
}) }()
} }
func (pe *PeriodicalExecutor) doneExecution() { func (pe *PeriodicalExecutor) doneExecution() {
@@ -157,18 +157,20 @@ func (pe *PeriodicalExecutor) enterExecution() {
}) })
} }
func (pe *PeriodicalExecutor) executeTasks(tasks interface{}) bool { func (pe *PeriodicalExecutor) executeTasks(tasks any) bool {
defer pe.doneExecution() defer pe.doneExecution()
ok := pe.hasTasks(tasks) ok := pe.hasTasks(tasks)
if ok { if ok {
threading.RunSafe(func() {
pe.container.Execute(tasks) pe.container.Execute(tasks)
})
} }
return ok return ok
} }
func (pe *PeriodicalExecutor) hasTasks(tasks interface{}) bool { func (pe *PeriodicalExecutor) hasTasks(tasks any) bool {
if tasks == nil { if tasks == nil {
return false return false
} }

View File

@@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/proc"
"github.com/zeromicro/go-zero/core/timex" "github.com/zeromicro/go-zero/core/timex"
) )
@@ -16,22 +17,22 @@ const threshold = 10
type container struct { type container struct {
interval time.Duration interval time.Duration
tasks []int tasks []int
execute func(tasks interface{}) execute func(tasks any)
} }
func newContainer(interval time.Duration, execute func(tasks interface{})) *container { func newContainer(interval time.Duration, execute func(tasks any)) *container {
return &container{ return &container{
interval: interval, interval: interval,
execute: execute, execute: execute,
} }
} }
func (c *container) AddTask(task interface{}) bool { func (c *container) AddTask(task any) bool {
c.tasks = append(c.tasks, task.(int)) c.tasks = append(c.tasks, task.(int))
return len(c.tasks) > threshold return len(c.tasks) > threshold
} }
func (c *container) Execute(tasks interface{}) { func (c *container) Execute(tasks any) {
if c.execute != nil { if c.execute != nil {
c.execute(tasks) c.execute(tasks)
} else { } else {
@@ -39,7 +40,7 @@ func (c *container) Execute(tasks interface{}) {
} }
} }
func (c *container) RemoveAll() interface{} { func (c *container) RemoveAll() any {
tasks := c.tasks tasks := c.tasks
c.tasks = nil c.tasks = nil
return tasks return tasks
@@ -67,6 +68,7 @@ func TestPeriodicalExecutor_QuitGoroutine(t *testing.T) {
ticker.Tick() ticker.Tick()
ticker.Wait(time.Millisecond * idleRound) ticker.Wait(time.Millisecond * idleRound)
assert.Equal(t, routines, runtime.NumGoroutine()) assert.Equal(t, routines, runtime.NumGoroutine())
proc.Shutdown()
} }
func TestPeriodicalExecutor_Bulk(t *testing.T) { func TestPeriodicalExecutor_Bulk(t *testing.T) {
@@ -74,7 +76,7 @@ func TestPeriodicalExecutor_Bulk(t *testing.T) {
var vals []int var vals []int
// avoid data race // avoid data race
var lock sync.Mutex var lock sync.Mutex
exec := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, func(tasks interface{}) { exec := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, func(tasks any) {
t := tasks.([]int) t := tasks.([]int)
for _, each := range t { for _, each := range t {
lock.Lock() lock.Lock()
@@ -106,25 +108,83 @@ func TestPeriodicalExecutor_Bulk(t *testing.T) {
lock.Unlock() lock.Unlock()
} }
func TestPeriodicalExecutor_Panic(t *testing.T) {
// avoid data race
var lock sync.Mutex
ticker := timex.NewFakeTicker()
var (
executedTasks []int
expected []int
)
executor := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, func(tasks any) {
tt := tasks.([]int)
lock.Lock()
executedTasks = append(executedTasks, tt...)
lock.Unlock()
if tt[0] == 0 {
panic("test")
}
}))
executor.newTicker = func(duration time.Duration) timex.Ticker {
return ticker
}
for i := 0; i < 30; i++ {
executor.Add(i)
expected = append(expected, i)
}
ticker.Tick()
ticker.Tick()
time.Sleep(time.Millisecond)
lock.Lock()
assert.Equal(t, expected, executedTasks)
lock.Unlock()
}
func TestPeriodicalExecutor_FlushPanic(t *testing.T) {
var (
executedTasks []int
expected []int
lock sync.Mutex
)
executor := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, func(tasks any) {
tt := tasks.([]int)
lock.Lock()
executedTasks = append(executedTasks, tt...)
lock.Unlock()
if tt[0] == 0 {
panic("flush panic")
}
}))
for i := 0; i < 8; i++ {
executor.Add(i)
expected = append(expected, i)
}
executor.Flush()
lock.Lock()
assert.Equal(t, expected, executedTasks)
lock.Unlock()
}
func TestPeriodicalExecutor_Wait(t *testing.T) { func TestPeriodicalExecutor_Wait(t *testing.T) {
var lock sync.Mutex var lock sync.Mutex
executer := NewBulkExecutor(func(tasks []interface{}) { executor := NewBulkExecutor(func(tasks []any) {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
}, WithBulkTasks(1), WithBulkInterval(time.Second)) }, WithBulkTasks(1), WithBulkInterval(time.Second))
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
executer.Add(1) executor.Add(1)
} }
executer.Flush() executor.Flush()
executer.Wait() executor.Wait()
} }
func TestPeriodicalExecutor_WaitFast(t *testing.T) { func TestPeriodicalExecutor_WaitFast(t *testing.T) {
const total = 3 const total = 3
var cnt int var cnt int
var lock sync.Mutex var lock sync.Mutex
executer := NewBulkExecutor(func(tasks []interface{}) { executor := NewBulkExecutor(func(tasks []any) {
defer func() { defer func() {
cnt++ cnt++
}() }()
@@ -133,15 +193,15 @@ func TestPeriodicalExecutor_WaitFast(t *testing.T) {
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
}, WithBulkTasks(1), WithBulkInterval(10*time.Millisecond)) }, WithBulkTasks(1), WithBulkInterval(10*time.Millisecond))
for i := 0; i < total; i++ { for i := 0; i < total; i++ {
executer.Add(2) executor.Add(2)
} }
executer.Flush() executor.Flush()
executer.Wait() executor.Wait()
assert.Equal(t, total, cnt) assert.Equal(t, total, cnt)
} }
func TestPeriodicalExecutor_Deadlock(t *testing.T) { func TestPeriodicalExecutor_Deadlock(t *testing.T) {
executor := NewBulkExecutor(func(tasks []interface{}) { executor := NewBulkExecutor(func(tasks []any) {
}, WithBulkTasks(1), WithBulkInterval(time.Millisecond)) }, WithBulkTasks(1), WithBulkInterval(time.Millisecond))
for i := 0; i < 1e5; i++ { for i := 0; i < 1e5; i++ {
executor.Add(1) executor.Add(1)
@@ -149,13 +209,7 @@ func TestPeriodicalExecutor_Deadlock(t *testing.T) {
} }
func TestPeriodicalExecutor_hasTasks(t *testing.T) { func TestPeriodicalExecutor_hasTasks(t *testing.T) {
ticker := timex.NewFakeTicker()
defer ticker.Stop()
exec := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, nil)) exec := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, nil))
exec.newTicker = func(d time.Duration) timex.Ticker {
return ticker
}
assert.False(t, exec.hasTasks(nil)) assert.False(t, exec.hasTasks(nil))
assert.True(t, exec.hasTasks(1)) assert.True(t, exec.hasTasks(1))
} }

View File

@@ -5,4 +5,4 @@ import "time"
const defaultFlushInterval = time.Second const defaultFlushInterval = time.Second
// Execute defines the method to execute tasks. // Execute defines the method to execute tasks.
type Execute func(tasks []interface{}) type Execute func(tasks []any)

View File

@@ -35,6 +35,7 @@ func firstLine(file *os.File) (string, error) {
for { for {
buf := make([]byte, bufSize) buf := make([]byte, bufSize)
n, err := file.ReadAt(buf, offset) n, err := file.ReadAt(buf, offset)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return "", err return "", err
} }
@@ -45,6 +46,10 @@ func firstLine(file *os.File) (string, error) {
} }
} }
if err == io.EOF {
return string(append(first, buf[:n]...)), nil
}
first = append(first, buf[:n]...) first = append(first, buf[:n]...)
offset += bufSize offset += bufSize
} }
@@ -57,30 +62,42 @@ func lastLine(filename string, file *os.File) (string, error) {
} }
var last []byte var last []byte
bufLen := int64(bufSize)
offset := info.Size() offset := info.Size()
for {
offset -= bufSize for offset > 0 {
if offset < 0 { if offset < bufLen {
bufLen = offset
offset = 0 offset = 0
} else {
offset -= bufLen
} }
buf := make([]byte, bufSize)
buf := make([]byte, bufLen)
n, err := file.ReadAt(buf, offset) n, err := file.ReadAt(buf, offset)
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return "", err return "", err
} }
if n == 0 {
break
}
if buf[n-1] == '\n' { if buf[n-1] == '\n' {
buf = buf[:n-1] buf = buf[:n-1]
n-- n--
} else { } else {
buf = buf[:n] buf = buf[:n]
} }
for n--; n >= 0; n-- {
if buf[n] == '\n' { for i := n - 1; i >= 0; i-- {
return string(append(buf[n+1:], last...)), nil if buf[i] == '\n' {
return string(append(buf[i+1:], last...)), nil
} }
} }
last = append(buf, last...) last = append(buf, last...)
} }
return string(last), nil
} }

View File

@@ -52,6 +52,7 @@ last line`
second line second line
last line last line
` `
emptyContent = ``
) )
func TestFirstLine(t *testing.T) { func TestFirstLine(t *testing.T) {
@@ -74,6 +75,31 @@ func TestFirstLineShort(t *testing.T) {
assert.Equal(t, "first line", val) assert.Equal(t, "first line", val)
} }
func TestFirstLineError(t *testing.T) {
_, err := FirstLine("/tmp/does-not-exist")
assert.Error(t, err)
}
func TestFirstLineEmptyFile(t *testing.T) {
filename, err := fs.TempFilenameWithText(emptyContent)
assert.Nil(t, err)
defer os.Remove(filename)
val, err := FirstLine(filename)
assert.Nil(t, err)
assert.Equal(t, "", val)
}
func TestFirstLineWithoutNewline(t *testing.T) {
filename, err := fs.TempFilenameWithText(longLine)
assert.Nil(t, err)
defer os.Remove(filename)
val, err := FirstLine(filename)
assert.Nil(t, err)
assert.Equal(t, longLine, val)
}
func TestLastLine(t *testing.T) { func TestLastLine(t *testing.T) {
filename, err := fs.TempFilenameWithText(text) filename, err := fs.TempFilenameWithText(text)
assert.Nil(t, err) assert.Nil(t, err)
@@ -94,6 +120,16 @@ func TestLastLineWithLastNewline(t *testing.T) {
assert.Equal(t, longLine, val) assert.Equal(t, longLine, val)
} }
func TestLastLineWithoutLastNewline(t *testing.T) {
filename, err := fs.TempFilenameWithText(longLine)
assert.Nil(t, err)
defer os.Remove(filename)
val, err := LastLine(filename)
assert.Nil(t, err)
assert.Equal(t, longLine, val)
}
func TestLastLineShort(t *testing.T) { func TestLastLineShort(t *testing.T) {
filename, err := fs.TempFilenameWithText(shortText) filename, err := fs.TempFilenameWithText(shortText)
assert.Nil(t, err) assert.Nil(t, err)
@@ -113,3 +149,72 @@ func TestLastLineWithLastNewlineShort(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "last line", val) assert.Equal(t, "last line", val)
} }
func TestLastLineError(t *testing.T) {
_, err := LastLine("/tmp/does-not-exist")
assert.Error(t, err)
}
func TestLastLineEmptyFile(t *testing.T) {
filename, err := fs.TempFilenameWithText(emptyContent)
assert.Nil(t, err)
defer os.Remove(filename)
val, err := LastLine(filename)
assert.Nil(t, err)
assert.Equal(t, "", val)
}
func TestFirstLineExactlyBufSize(t *testing.T) {
content := make([]byte, bufSize)
for i := range content {
content[i] = 'a'
}
content[bufSize-1] = '\n' // Ensure there is a newline at the edge
filename, err := fs.TempFilenameWithText(string(content))
assert.Nil(t, err)
defer os.Remove(filename)
val, err := FirstLine(filename)
assert.Nil(t, err)
assert.Equal(t, string(content[:bufSize-1]), val)
}
func TestLastLineExactlyBufSize(t *testing.T) {
content := make([]byte, bufSize)
for i := range content {
content[i] = 'a'
}
content[bufSize-1] = '\n' // Ensure there is a newline at the edge
filename, err := fs.TempFilenameWithText(string(content))
assert.Nil(t, err)
defer os.Remove(filename)
val, err := LastLine(filename)
assert.Nil(t, err)
assert.Equal(t, string(content[:bufSize-1]), val)
}
func TestFirstLineLargeFile(t *testing.T) {
content := text + text + text + "\n" + "extra"
filename, err := fs.TempFilenameWithText(content)
assert.Nil(t, err)
defer os.Remove(filename)
val, err := FirstLine(filename)
assert.Nil(t, err)
assert.Equal(t, "first line", val)
}
func TestLastLineLargeFile(t *testing.T) {
content := text + text + text + "\n" + "extra"
filename, err := fs.TempFilenameWithText(content)
assert.Nil(t, err)
defer os.Remove(filename)
val, err := LastLine(filename)
assert.Nil(t, err)
assert.Equal(t, "extra", val)
}

View File

@@ -5,7 +5,7 @@ import "gopkg.in/cheggaaa/pb.v1"
type ( type (
// A Scanner is used to read lines. // A Scanner is used to read lines.
Scanner interface { Scanner interface {
// Scan checks if has remaining to read. // Scan checks if it has remaining to read.
Scan() bool Scan() bool
// Text returns next line. // Text returns next line.
Text() string Text() string

View File

@@ -1,5 +1,4 @@
//go:build windows //go:build windows
// +build windows
package fs package fs

View File

@@ -1,5 +1,4 @@
//go:build linux || darwin //go:build linux || darwin || freebsd
// +build linux darwin
package fs package fs

15
core/fs/files_test.go Normal file
View File

@@ -0,0 +1,15 @@
package fs
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCloseOnExec(t *testing.T) {
file := os.NewFile(0, os.DevNull)
assert.NotPanics(t, func() {
CloseOnExec(file)
})
}

View File

@@ -11,29 +11,29 @@ import (
// The file is kept as open, the caller should close the file handle, // The file is kept as open, the caller should close the file handle,
// and remove the file by name. // and remove the file by name.
func TempFileWithText(text string) (*os.File, error) { func TempFileWithText(text string) (*os.File, error) {
tmpfile, err := os.CreateTemp(os.TempDir(), hash.Md5Hex([]byte(text))) tmpFile, err := os.CreateTemp(os.TempDir(), hash.Md5Hex([]byte(text)))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := os.WriteFile(tmpfile.Name(), []byte(text), os.ModeTemporary); err != nil { if err := os.WriteFile(tmpFile.Name(), []byte(text), os.ModeTemporary); err != nil {
return nil, err return nil, err
} }
return tmpfile, nil return tmpFile, nil
} }
// TempFilenameWithText creates the file with the given content, // TempFilenameWithText creates the file with the given content,
// and returns the filename (full path). // and returns the filename (full path).
// The caller should remove the file after use. // The caller should remove the file after use.
func TempFilenameWithText(text string) (string, error) { func TempFilenameWithText(text string) (string, error) {
tmpfile, err := TempFileWithText(text) tmpFile, err := TempFileWithText(text)
if err != nil { if err != nil {
return "", err return "", err
} }
filename := tmpfile.Name() filename := tmpFile.Name()
if err = tmpfile.Close(); err != nil { if err = tmpFile.Close(); err != nil {
return "", err return "", err
} }

Some files were not shown because too many files have changed in this diff Show More