Compare commits

...

385 Commits

Author SHA1 Message Date
Kevin Wan
744c18b7cb simplify cgroup controller separation (#384) 2021-01-13 20:58:33 +08:00
miaogaolin
8d6f6f933e fix cgroup bug (#380) 2021-01-13 20:39:57 +08:00
Kevin Wan
37c3b9f5c1 make sure unlock safe even if listeners panic (#383)
* make sure unlock safe even if listeners panic

* fix #378

* fix #378
2021-01-13 18:43:42 +08:00
卢永杰
1f1dcd16e6 fix server.start return nil points (#379)
Co-authored-by: luyongjie <luyongjie@37.com>
2021-01-13 18:40:39 +08:00
文杰
3285436f75 f-fix spell (#381)
Co-authored-by: chenwenjie <chenwenjie@zzstc.cn>
2021-01-13 18:07:31 +08:00
kingxt
7f49bd8a31 code optimized (#382) 2021-01-13 16:37:33 +08:00
kingxt
9cd2015661 fix inner type generate error (#377)
* fix point type bug

* optimized

* fix inner type error
2021-01-13 11:54:53 +08:00
kingxt
cf3a1020b0 Java optimized (#376)
* optiimzed java gen

* optiimzed java gen

* fix
2021-01-12 14:14:49 +08:00
kingxt
ee19fb736b feature: refactor api parse to g4 (#365)
* feature: refactor api parse to g4

* new g4 parser

* add CHANGE_LOG.MD

* refactor

* fix byte bug

* refactor

* optimized

* optimized

* revert

* update readme.md

* update readme.md

* update readme.md

* update readme.md

* remove no need

* fix java gen

* add upgrade

* resolve confilits

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2021-01-11 15:10:51 +08:00
Kevin Wan
b0ccfb8eb4 add more tests for conf (#371) 2021-01-10 21:53:16 +08:00
Kevin Wan
444e5a711f update doc to use table to render plugins (#370) 2021-01-09 19:54:34 +08:00
Kevin Wan
8774d72ddb remove duplicated code in goctl (#369) 2021-01-09 00:17:23 +08:00
HarryWang29
e3fcdbf040 fix return in for (#367)
Co-authored-by: HarryWang29 <wrz890829@gmail.com>
2021-01-08 22:47:27 +08:00
Kevin Wan
2854ca03b4 update goctl version to 1.1.3 (#364) 2021-01-08 14:02:59 +08:00
anqiansong
6c624a6ed0 Feature model fix (#362)
* fix sql builderx adding raw string quotation marks incompatibility bug

* add unit test

* remove comments

* fix sql builderx adding raw string quotation marks incompatibility bug
2021-01-08 12:01:21 +08:00
Kevin Wan
57b73d8b49 make sure offset less than size even it's checked inside (#354) 2021-01-05 16:06:36 +08:00
Kevin Wan
a79cee12ee add godoc for RollingWindow (#351) 2021-01-04 22:43:55 +08:00
zjbztianya
7a921f66e6 simple rolling windows code (#346) 2021-01-04 22:11:18 +08:00
kingxt
12e235efb0 optimized goctl format (#336)
* fix format

* refactor

* refactor

* optimized

* refactor

* refactor

* refactor

* add js path prefix
2021-01-04 18:59:48 +08:00
Kevin Wan
01060cf16d close issue of #337 (#347) 2021-01-04 16:36:27 +08:00
Kevin Wan
0786862a35 align bucket boundary to interval in rolling window (#345) 2021-01-04 11:17:59 +08:00
Kevin Wan
efa43483b2 fix potential data race in PeriodicalExecutor (#344)
* fix potential data race in PeriodicalExecutor

* add comment
2021-01-03 20:56:17 +08:00
Kevin Wan
771371e051 simplify rolling window code, and make tests run faster (#343) 2021-01-03 20:47:29 +08:00
zjbztianya
2ee95f8981 fix rolling window bug (#340) 2021-01-03 20:27:47 +08:00
Kevin Wan
5bc01e4bfd set guarded to false only on quitting background flush (#342)
* set guarded to false only on quitting background flush

* set guarded to false only on quitting background flush, cont.
2021-01-03 19:54:11 +08:00
Kevin Wan
510e966982 simplify periodical executor background routine (#339) 2021-01-03 14:02:51 +08:00
Kevin Wan
10e3b8ac80 optimize code that fixes issue #317 (#338) 2021-01-02 19:01:37 +08:00
Kevin Wan
04059bbf5a add discord chat group in readme 2021-01-02 18:35:33 +08:00
weibobo
d643007c79 fix bug #317 (#335)
* fix bug #317.
* add counter for current task. If it's bigger then zero, do not quit background thread

* Revert "fix issue #317 (#331)"

This reverts commit fc43876cc5.
2021-01-02 18:04:04 +08:00
Kevin Wan
fc43876cc5 fix issue #317 (#331) 2021-01-01 13:24:28 +08:00
FengZhang
a926cb514f modify the goctl gensvc template (#323) 2020-12-30 10:05:26 +08:00
kingxt
25cab2f273 Java (#327)
* add g4 file

* new define api by g4

* reactor parser to g4gen

* add syntax parser & test

* add syntax parser & test

* add syntax parser & test

* update g4 file

* add import parse & test

* ractor AT lexer

* panic with error

* revert AT

* update g4 file

* update g4 file

* update g4 file

* optimize parser

* update g4 file

* parse info

* optimized java generator

* revert

* optimize java generator

* update java generator

* update java generator

* update java generator

* update java generator

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2020-12-29 17:50:41 +08:00
Kevin Wan
8d2e2753a2 simplify http.Flusher implementation (#326)
* simplify code with http.Flusher type conversion

* simplify code with http.Flusher type conversion, better version
2020-12-29 15:02:36 +08:00
Kevin Wan
cc4c50e3eb fix broken link. 2020-12-29 11:54:32 +08:00
Kevin Wan
751072bdb0 fix broken doc link 2020-12-29 11:52:55 +08:00
Kevin Wan
e97e1f10db simplify code with http.Flusher type conversion (#325)
* simplify code with http.Flusher type conversion

* simplify code with http.Flusher type conversion, better version
2020-12-29 10:25:55 +08:00
jichangyun
0bd2a0656c The ResponseWriters defined in rest.handler add Flush interface. (#318) 2020-12-28 21:30:24 +08:00
Kevin Wan
71a2b20301 add more tests for prof (#322) 2020-12-27 14:45:14 +08:00
Kevin Wan
8df7de94e3 add more tests for zrpc (#321) 2020-12-27 14:08:24 +08:00
Kevin Wan
bf21203297 add more tests (#320) 2020-12-27 12:26:31 +08:00
Kevin Wan
ae98375194 add more tests (#319) 2020-12-26 20:30:02 +08:00
Kevin Wan
82d1ccf376 fixes #286 (#315) 2020-12-25 19:47:27 +08:00
Kevin Wan
bb6d49c17e add go report card back (#313)
* add go report card back

* avoid test failure, run tests sequentially
2020-12-25 12:09:59 +08:00
Kevin Wan
ed735ec47c Update codeql-analysis.yml
disable python code analysis, python code is in examples.
2020-12-25 12:09:43 +08:00
Kevin Wan
ba4bac3a03 format code (#312) 2020-12-25 11:53:37 +08:00
FengZhang
08433d7e04 add config load support env var (#309) 2020-12-25 11:42:19 +08:00
anqiansong
a3b525b50d feature model fix (#296)
* add raw stirng quote for sql field

* remove unused code
2020-12-21 09:43:32 +08:00
Kevin Wan
097f6886f2 Update readme.md 2020-12-15 23:47:41 +08:00
Kevin Wan
07a1549634 add wechat micro practice qrcode image (#289) 2020-12-14 17:49:58 +08:00
Kevin Wan
befca26c58 Update readme.md
add goproxy.cn download badge
2020-12-13 00:02:32 +08:00
Kevin Wan
3556a2eef4 Update readme-en.md
goreportcard is not working, submitted an issue to them.
2020-12-12 23:40:26 +08:00
Kevin Wan
807765f77e Update readme.md
goreportcard is not working, submitted a issue to them.
2020-12-12 23:39:28 +08:00
Kevin Wan
e44584e549 Create codeql-analysis.yml 2020-12-12 23:01:15 +08:00
Kevin Wan
acd48f0abb optimize dockerfile generation (#284) 2020-12-12 16:53:06 +08:00
kingxt
f919bc6713 refactor (#283) 2020-12-12 11:18:22 +08:00
Kevin Wan
a0030b8f45 format dockerfile on non-chinese mode (#282) 2020-12-12 10:13:33 +08:00
Kevin Wan
a5f0cce1b1 Update readme-en.md 2020-12-12 09:06:09 +08:00
Kevin Wan
4d13dda605 add EXPOSE in dockerfile generation (#281) 2020-12-12 08:18:01 +08:00
songmeizi
b56cc8e459 optimize test case of TestRpcGenerate (#279)
Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2020-12-11 21:57:04 +08:00
Kevin Wan
c435811479 fix gocyclo warnings (#278) 2020-12-11 20:57:48 +08:00
Kevin Wan
c686c93fb5 fix dockerfile generation bug (#277) 2020-12-11 20:31:31 +08:00
Kevin Wan
da8f76e6bd add category docker & kube (#276) 2020-12-11 18:53:40 +08:00
Kevin Wan
99596a4149 fix issue #266 (#275)
* optimize dockerfile

* fix issue #266
2020-12-11 16:12:33 +08:00
wayne
ec2a9f2c57 fix tracelogger_test TestTraceLog (#271) 2020-12-10 17:04:57 +08:00
Kevin Wan
fd73ced6dc optimize dockerfile (#272) 2020-12-10 16:21:06 +08:00
Kevin Wan
5071736ab4 fmt code (#270) 2020-12-10 15:16:13 +08:00
Kevin Wan
0d7f1d23b4 require go 1.14 (#263)
* refactor & format code

* optimized parse tag (#256)

* feature plugin custom flag (#251)

* support plugin custom flags

* add short name

* remove log

* remove log

* require go 1.14

Co-authored-by: kingxt <kingxt4job@gmail.com>
Co-authored-by: songmeizi <anqiansong@xiaoheiban.cn>
2020-12-09 22:43:42 +08:00
songmeizi
84ab11ac09 feature plugin custom flag (#251)
* support plugin custom flags

* add short name

* remove log

* remove log
2020-12-09 18:08:17 +08:00
kingxt
67804a6bb2 optimized parse tag (#256) 2020-12-09 11:16:38 +08:00
Kevin Wan
65ee877236 refactor & format code (#255) 2020-12-08 23:01:25 +08:00
songmeizi
b060867009 Feature bookstore update (#253)
* update bookstore

* update bookstore
2020-12-08 22:36:48 +08:00
songmeizi
4d53045c6b improve data type conversion (#236)
* improve data type conversion

* update doc
2020-12-08 18:06:15 +08:00
kingxt
cecd4b1b75 goctl add plugin support (#243)
* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* add plugin support

* remove no need

* add plugin support

* rename

* rename

* add plugin support

* refactor

* update plugin

* refactor

* refactor

* refactor

* update plugin

* newline

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2020-12-07 14:55:10 +08:00
Kevin Wan
7cd0463953 fix lint errors (#249)
* simplify code, format makefile

* simplify code

* some optimize by kevwan and benying (#240)

Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>

* optimization (#241)

* optimize docker file generation, make docker build faster

* support k8s deployment yaml generation

* fix lint errors

Co-authored-by: benying <31179034+benyingY@users.noreply.github.com>
Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>
Co-authored-by: bittoy <bittoy@qq.com>
2020-12-07 11:12:02 +08:00
Kevin Wan
7a82cf80ce support k8s deployment yaml generation (#247)
* simplify code, format makefile

* simplify code

* some optimize by kevwan and benying (#240)

Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>

* optimization (#241)

* optimize docker file generation, make docker build faster

* support k8s deployment yaml generation

Co-authored-by: benying <31179034+benyingY@users.noreply.github.com>
Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>
Co-authored-by: bittoy <bittoy@qq.com>
2020-12-07 00:07:50 +08:00
Kevin Wan
f997aee3ba optimize docker file generation, make docker build faster (#244)
* simplify code, format makefile

* simplify code

* some optimize by kevwan and benying (#240)

Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>

* optimization (#241)

* optimize docker file generation, make docker build faster

Co-authored-by: benying <31179034+benyingY@users.noreply.github.com>
Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>
Co-authored-by: bittoy <bittoy@qq.com>
2020-12-05 21:48:09 +08:00
bittoy
88ec89bdbd optimization (#241) 2020-12-02 15:00:07 +08:00
benying
7d1b43780a some optimize by kevwan and benying (#240)
Co-authored-by: 杨志泉 <zhiquan.yang@yiducloud.cn>
2020-12-01 06:44:32 +08:00
Kevin Wan
4b5c2de376 simplify code (#234)
* simplify code, format makefile

* simplify code
2020-11-29 12:41:42 +08:00
Kevin Wan
e5c560e8ba simplify code, format makefile (#233) 2020-11-28 22:27:58 +08:00
xuezonggui
bed494d904 optimization (#221) 2020-11-28 19:43:39 +08:00
Keson
2dfecda465 modify the service name from proto (#230) 2020-11-28 11:48:44 +08:00
voidint
3ebb1e0221 Improve Makefile robustness (#224) 2020-11-27 23:40:07 +08:00
kingxt
348184904c set default handler value (#228)
* set default value

* set default value
2020-11-26 11:57:02 +08:00
Keson
7a27fa50a1 update version (#226) 2020-11-25 12:04:22 +08:00
Kevin Wan
8d4951c990 check go.mod before build docker image (#225) 2020-11-24 23:19:31 +08:00
Keson
6e57f6c527 feature model interface (#222)
* make variable declaration more concise

* add model interface

* optimize interface methods

* fix: go test failed

* warp returns

* optimize
2020-11-24 22:36:23 +08:00
kingxt
b9ac51b6c3 feature: file namestyle (#223)
* add api filename style

* new feature: config.yaml

* optimize

* optimize logic generation

* check hanlder valid

* optimize

* reactor naming style

* optimize

* optimize test

* optimize gen middleware

* format

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-24 15:11:18 +08:00
kevin
702e8d79ce fix doc errors 2020-11-24 10:39:38 +08:00
kevin
95a9dabf8b format import 2020-11-23 16:35:39 +08:00
Chris
bae66c49c2 1.use local variable i; 2.make sure limiter larger than timer period (#218)
Co-authored-by: chris <feilee1987@163.com>
2020-11-23 16:34:51 +08:00
kingxt
e0afe0b4bb optimize api new (#216) 2020-11-19 16:48:48 +08:00
Keson
24fb29a356 patch model&rpc (#207)
* change column to read from information_schema

* reactor generate mode from datasource

* reactor generate mode from datasource

* add primary key check logic

* resolve rebase conflicts

* add naming style

* add filename test case

* resolve rebase conflicts

* reactor test

* add test case

* change shell script to makefile

* update rpc new

* update gen_test.go

* format code

* format code

* update test

* generates alias
2020-11-18 15:32:53 +08:00
kevin
71083b5e64 update readme 2020-11-17 19:01:14 +08:00
kingxt
1174f17bd9 modify image url (#213) 2020-11-17 18:50:22 +08:00
kingxt
d6d8fc21d8 type should not define nested (#212)
* nest type should not supported

* nest type should not supported

* nest type should not supported

* nest type should not supported

* new test

* new test
2020-11-17 18:08:55 +08:00
kevin
9592639cb4 add error handle tests 2020-11-17 18:04:48 +08:00
kevin
abcb28e506 support error customization 2020-11-17 17:11:06 +08:00
kingxt
a92f65580c support type def without struct token (#210)
* add comment support

* add comment support

* 1. group support multi level folder
2. remove force flag

* bug fix

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* support type def without struct token

* support type def without struct token

* support type def without struct token

* support type def without struct token

* support type def without struct token

* support type def without struct token

* support type def without struct token

* optimized

* optimized

* optimized

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-17 15:25:13 +08:00
bittoy
3819f67cf4 add redis geospatial (#209)
* add redis geospatial

* fix go test error
2020-11-16 19:45:43 +08:00
kevin
295c8d2934 fix issue #205 2020-11-16 19:23:24 +08:00
kingxt
88da8685dd optimize parser (#206)
* add comment support

* add comment support

* 1. group support multi level folder
2. remove force flag

* bug fix

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* optimized parser

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-16 10:08:28 +08:00
kevin
c7831ac96d update goctl readme 2020-11-15 21:18:02 +08:00
kevin
e898761762 update example 2020-11-15 21:15:29 +08:00
kevin
13d1c5cd00 update example 2020-11-14 22:01:35 +08:00
kingxt
16bfb1b7be refactor parser and remove deprecated code (#204)
* add comment support

* add comment support

* 1. group support multi level folder
2. remove force flag

* bug fix

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-13 23:01:19 +08:00
kingxt
ef4d4968d6 1. group support multi level folder 2. remove force flag (#203)
* add comment support

* add comment support

* 1. group support multi level folder
2. remove force flag

* bug fix

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-12 19:47:32 +08:00
kingxt
7b4a5e3ec6 api support for comment double slash // (#201)
* add comment support

* add comment support

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-12 16:57:28 +08:00
kevin
e6df21e0d2 format code 2020-11-11 17:20:56 +08:00
SunJun
0a2c2d1eca change grpc interceptor to chain interceptor (#200)
* change grpc interceptor to chain interceptor

* change server rpc interceptors, del testing code
2020-11-11 17:15:22 +08:00
kevin
a5fb29a6f0 update etcd yaml to avoid no such nost resolve problem 2020-11-11 11:06:23 +08:00
zhoushuguang
f8da301e57 no default metric (#199)
Co-authored-by: zhoushuguang <zhoushuguang@xiaoheiban.cn>
2020-11-10 11:47:08 +08:00
kevin
cb9075b737 add dockerfile into template 2020-11-09 18:02:16 +08:00
kingxt
3f389a55c2 format service and add test (#197)
Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-09 17:41:07 +08:00
kevin
afbd565d87 rename postgres 2020-11-09 17:22:51 +08:00
zhoushuguang
d629acc2b7 default metric host (#196)
Co-authored-by: zhoushuguang <zhoushuguang@xiaoheiban.cn>
2020-11-09 16:03:07 +08:00
kingxt
f32c6a9b28 rewrite (#194)
Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-09 10:06:45 +08:00
kevin
95aa65efb9 add dockerfile generator 2020-11-08 21:28:58 +08:00
kevin
3806e66cf1 simplify http server starter 2020-11-08 13:17:14 +08:00
kevin
bd430baf52 graceful shutdown refined 2020-11-08 13:08:00 +08:00
Keson
48f4154ea8 update doc (#193) 2020-11-08 13:02:48 +08:00
super_mario
2599e0d28d Close the process when shutdown is finished (#157)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2020-11-08 12:50:58 +08:00
kingxt
12327fa07d break generator when happen error (#192)
Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-07 21:25:52 +08:00
kevin
57079bf4a4 update cli package 2020-11-07 20:01:25 +08:00
kingxt
7f6eceb5a3 add more test (#189)
* new test

* import bug when with quotation

* new test

* add test condition

* rpc template command use -o param

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-07 17:13:40 +08:00
kevin
7d7cb836af fix issue #186 2020-11-06 12:25:48 +08:00
kevin
f87d9d1dda refine code style 2020-11-06 12:13:28 +08:00
Keson
856b5aadb1 rpc generation fix (#184)
* reactor alert

* optimize

* add test case

* update the target directory in case proto contains option

* fix missing comments and format code
2020-11-05 19:08:34 +08:00
Keson
f7d778e0ed fix duplicate alias (#183) 2020-11-05 18:12:23 +08:00
kevin
88333ee77f faster the tests 2020-11-05 16:04:00 +08:00
Keson
e76f44a35b reactor rpc (#179)
* reactor rpc generation

* update flag

* update command

* update command

* update unit test

* delete test file

* optimize code

* update doc

* update gen pb

* rename target dir

* update mysql data type convert rule

* add done flag

* optimize req/reply parameter

* optimize req/reply parameter

* remove waste code

* remove duplicate parameter

* format code

* format code

* optimize naming

* reactor rpcv2 to rpc

* remove new line

* format code

* rename underline to snake

* reactor getParentPackage

* remove debug log

* reactor background
2020-11-05 14:12:47 +08:00
kevin
c9ec22d5f4 add https listen and serve 2020-11-05 11:56:40 +08:00
Dashuang Li
afffc1048b fix url 404 (#180) 2020-11-04 12:03:07 +08:00
kevin
d0b76b1d9a move redistest into redis package 2020-11-03 16:35:34 +08:00
kevin
b004b070d7 refine tests 2020-11-02 17:51:33 +08:00
kevin
677d581bd1 update doc 2020-11-02 17:05:09 +08:00
kingxt
b776468e69 route support no request and response (#178)
* add more test and support no request and response

* fix slash when run on windows

* optimize test
2020-11-02 13:48:16 +08:00
kevin
4c9315e984 add more tests 2020-10-31 22:10:11 +08:00
kevin
668a7011c4 add more tests 2020-10-31 20:11:12 +08:00
吴亲库里
cc07a1d69b Update sharedcalls.go (#174)
Removes unused parameters
2020-10-31 19:40:07 +08:00
kevin
7f99a3baa8 add gitee url 2020-10-31 13:58:33 +08:00
kevin
9504418462 update doc 2020-10-31 12:41:29 +08:00
kevin
b144a2335c update bookstore example for generation prototype 2020-10-31 11:42:44 +08:00
kevin
7b9ed7a313 update doc 2020-10-30 15:20:19 +08:00
kevin
3d2e9fcb84 remove wechat image 2020-10-30 11:57:32 +08:00
kevin
2b993424c1 update wechat qrcode 2020-10-30 11:54:06 +08:00
kevin
5e87b33b23 support https in rest 2020-10-29 17:44:51 +08:00
kevin
9b7cc43dcb update wechat qrcode 2020-10-29 15:32:08 +08:00
kevin
000b28cf84 update readme 2020-10-29 11:31:35 +08:00
kevin
9fd16cd278 add images back because of gitee not showing 2020-10-29 11:27:40 +08:00
kevin
b71429e16b add images back because of gitee not showing 2020-10-29 11:26:10 +08:00
codingfanlt
a13b48c33e goctl add stdin flag (#170)
* add stdin flag to use stdin receive api doc and use stdout output formatted result

* optimize code and output error through stderr

* fix mistake

* add dir parameter legality verify
2020-10-28 22:37:59 +08:00
kevin
033525fea8 update doc using raw images 2020-10-28 21:04:06 +08:00
Keson
607fc3297a model template fix (#169)
* replace quote

* rpc disable override main.go

* reactor template

* add model flag -style

* add model flag -style

* reactor model  template name of error
2020-10-27 22:42:53 +08:00
cuisongliu
4287877b74 update deployment version (#165) 2020-10-26 16:33:24 +08:00
Keson
2b7545ce11 spell fix (#167) 2020-10-26 16:33:02 +08:00
Keson
60925c1164 fix bug: generate incomplete model code in case findOneByField (#160)
* fix bug: generate incompletely in case findOneByField

* code break line

* add test

* revert command.go

* add test

* remove incorrect test
2020-10-25 23:21:55 +08:00
kingxt
1c9e81aa28 refactor middleware generator (#159)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

* support return ()

* revert

* refactor and rename folder to group

* remove no need

* add anonymous annotation

* optimized

* rename

* rename

* update test

* api add middleware support: usage:

@server(
    middleware: M1, M2
)

* api add middleware support: usage:

@server(
    middleware: M1, M2
)

* simple logic

* optimized

* optimized generator formatted code

* optimized generator formatted code

* add more test

* refactor middleware generator

* revert test

* revert test

* revert test

* revert test

* revert test

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-10-23 21:53:45 +08:00
sjatsh
db7dcaa120 gen api svc add middleware implement temp code (#151) 2020-10-23 21:00:38 +08:00
kevin
099d44054d add logo in readme 2020-10-23 17:01:18 +08:00
Keson
f5f873c6bd api handler generate incompletely while has no request (#158)
* fix: api handler generate incompletely while has no request

* fix: api handler generate incompletely while has no request

* add handler generate test
2020-10-23 16:10:33 +08:00
Keson
6dbd3eada9 update api template (#156)
* update template

* update template
2020-10-23 14:42:57 +08:00
kevin
cf2d20a211 add vote link 2020-10-23 12:02:03 +08:00
maiyang
91bfc093f4 docs: format markdown and add go mod in demo (#155) 2020-10-22 22:24:35 +08:00
kingxt
cf33aae91d ignore blank between bracket and service tag (#154)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

* support return ()

* revert

* refactor and rename folder to group

* remove no need

* add anonymous annotation

* optimized

* rename

* rename

* update test

* api add middleware support: usage:

@server(
    middleware: M1, M2
)

* api add middleware support: usage:

@server(
    middleware: M1, M2
)

* simple logic

* optimized

* optimized generator formatted code

* optimized generator formatted code

* add more test

* ignore black between bracket and service tag

* use join instead

* format

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-10-22 22:19:06 +08:00
Keson
c9494c8bc7 model support globbing patterns (#153)
* model support globbing patterns

* optimize model

* optimize model

* format code
2020-10-22 18:33:09 +08:00
kevin
1fd2ef9347 make tests faster 2020-10-21 21:43:41 +08:00
kevin
efffb40fa3 update wechat info 2020-10-21 20:26:35 +08:00
kevin
9c8f31cf83 can only specify one origin in cors 2020-10-21 16:47:49 +08:00
kevin
96cb7af728 make tests faster 2020-10-21 15:18:22 +08:00
Keson
41964f9d52 gozero template (#147)
* model/rpc generate code from template cache

* delete unused(deprecated) code

* support template init|update|clean|revert

* model: return the execute result for insert and update operation

* // deprecated: containsAny

* add template test

* add default buildVersion

* update build version
2020-10-21 14:59:35 +08:00
kevin
fe0d0687f5 support cors in rest server 2020-10-21 14:10:36 +08:00
kingxt
1c1e4bca86 optimized generator formatted code (#148)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

* support return ()

* revert

* refactor and rename folder to group

* remove no need

* add anonymous annotation

* optimized

* rename

* rename

* update test

* api add middleware support: usage:

@server(
    middleware: M1, M2
)

* api add middleware support: usage:

@server(
    middleware: M1, M2
)

* simple logic

* optimized

* optimized generator formatted code

* optimized generator formatted code

* add more test

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-10-20 19:43:20 +08:00
kevin
1abe21aa2a export WithUnaryClientInterceptor 2020-10-20 18:03:05 +08:00
kevin
cee170f3e9 fix zrpc client interceptor calling problem 2020-10-20 17:57:41 +08:00
kevin
907efd92c9 let balancer to be customizable 2020-10-20 17:01:53 +08:00
kevin
737cd4751a rename NewPatRouter to NewRouter 2020-10-20 14:23:21 +08:00
kevin
dfe6e88529 use goctl template to generate all kinds of templates 2020-10-19 23:13:18 +08:00
kingxt
85a815bea0 fix name typo and format with newline (#143)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

* support return ()

* revert

* refactor and rename folder to group

* remove no need

* add anonymous annotation

* optimized

* rename

* rename

* update test

* api add middleware support: usage:

@server(
    middleware: M1, M2
)

* api add middleware support: usage:

@server(
    middleware: M1, M2
)

* simple logic

* optimized

* bugs fix for name typo and format with newline

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-10-19 21:05:00 +08:00
kingxt
aa3c391919 api add middleware support (#140)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

* support return ()

* revert

* refactor and rename folder to group

* remove no need

* add anonymous annotation

* optimized

* rename

* rename

* update test

* api add middleware support: usage:

@server(
    middleware: M1, M2
)

* api add middleware support: usage:

@server(
    middleware: M1, M2
)

* simple logic

* should reverse middlewares

* optimized

* optimized

* rename

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-10-19 18:34:10 +08:00
kevin
c9b0ac1ee4 add more tests 2020-10-19 15:49:11 +08:00
mywaystay
33faab61a3 add redis Zrevrank (#137)
* update goctl rpc template log print url

* add redis Zrevrank

Co-authored-by: zhangkai <zhangkai@laoyuegou.com>
2020-10-19 15:30:19 +08:00
kevin
81bf122fa4 update breaker doc 2020-10-17 22:58:30 +08:00
firefantasy
a14bd309a9 to correct breaker interface annotation (#136) 2020-10-17 22:55:36 +08:00
kevin
ea7e410145 update doc 2020-10-17 19:25:30 +08:00
kevin
e81358e7fa update doc 2020-10-17 19:20:01 +08:00
kevin
695ea69bfc add logx.Alert 2020-10-17 19:11:01 +08:00
kevin
d2ed14002c add fx.Split 2020-10-17 12:51:46 +08:00
kingxt
1d9c4a4c4b add anonymous annotation (#134)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

* support return ()

* revert

* refactor and rename folder to group

* remove no need

* add anonymous annotation

* optimized

* rename

* rename

* update test

* optimized new command

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-10-16 19:35:18 +08:00
mywaystay
7e83895c6e update goctl rpc template log print url (#133) 2020-10-16 16:21:22 +08:00
kingxt
dc0534573c print more message when parse error (#131)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

* support return ()

* revert

* refactor and rename folder to group

* print more error info when parse error

* remove no need

* refactor

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-10-16 15:56:29 +08:00
kevin
fe3739b7f3 fix golint issues 2020-10-16 11:13:55 +08:00
kevin
94645481b1 fix golint issues 2020-10-16 10:50:43 +08:00
sjatsh
338caf9927 delete goctl rpc main tpl no use import (#130) 2020-10-16 10:44:04 +08:00
kevin
9cc979960f update doc 2020-10-15 17:39:49 +08:00
kevin
f904710811 support api templates 2020-10-15 16:36:49 +08:00
kevin
8291eabc2c assert len > 0 2020-10-15 14:25:10 +08:00
codingfanlt
901fadb5d3 fix: fx/fn.Head func will forever block when n is less than 1 (#128)
* fix fx/Stream Head func will forever block when n is less than 1

* update test case

* update test case
2020-10-15 14:10:37 +08:00
kevin
c824e9e118 fail fast when rolling window size is zero 2020-10-15 11:40:31 +08:00
codingfanlt
6f49639f80 fix syncx/barrier test case (#123) 2020-10-13 19:29:20 +08:00
Keson
7d4a548d29 fix: template cache key (#121) 2020-10-12 14:34:11 +08:00
kevin
936dd67008 simplify code generation 2020-10-12 11:39:50 +08:00
super_mario
84cc41df42 stop rpc server when main function exit (#120)
add defer s.Stop() to mainTemplate, in order to stop rpc server when main function exit
2020-10-12 11:37:43 +08:00
kevin
da1a93e932 faster the tests 2020-10-11 22:07:50 +08:00
Keson
7e61555d42 Gozero sqlgen patch (#119)
* merge upstream

* optimize insert logic

* reactor functions
2020-10-11 21:55:44 +08:00
kevin
7a134ec64d update readme 2020-10-11 20:13:03 +08:00
kevin
d123b00e73 add qq qrcode 2020-10-11 20:02:06 +08:00
kevin
20d53add46 update readme 2020-10-11 19:42:40 +08:00
kevin
a1b141d31a make tests faster 2020-10-10 18:22:49 +08:00
Keson
0a9c427443 Goctl rpc patch (#117)
* remove mock generation

* add: proto project import

* update document

* remove mock generation

* add: proto project import

* update document

* remove NL

* update document

* optimize code

* add test

* add test
2020-10-10 16:19:46 +08:00
kevin
c32759d735 make tests race-free 2020-10-10 15:36:07 +08:00
kevin
fe855c52f1 avoid bigint converted into float64 when unmarshaling 2020-10-10 15:24:29 +08:00
kevin
3f8b080882 add more tests 2020-10-10 13:47:55 +08:00
kevin
adc275872d add more tests 2020-10-10 11:53:49 +08:00
kevin
be39133dba fix data race in tests 2020-10-09 19:13:10 +08:00
kingxt
15a9ab1d18 parser ad test (#116)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

* support return ()

* revert

* refactor and rename folder to group

* parser add test

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-10-09 16:03:00 +08:00
kevin
7c354dcc38 add more tests 2020-10-09 14:53:13 +08:00
kevin
3733b06f1b fix data race in tests 2020-10-09 14:15:27 +08:00
kevin
8115a0932e add more tests 2020-10-09 13:59:38 +08:00
kevin
4df5eb760c add more tests 2020-10-08 22:39:07 +08:00
kevin
4a639b853c add more tests 2020-10-08 09:42:20 +08:00
kevin
1023425c1d add more tests 2020-10-07 23:15:34 +08:00
kevin
360fbfd0fa add more tests 2020-10-07 23:02:58 +08:00
kevin
09b7625f06 add more tests 2020-10-07 22:54:51 +08:00
kevin
6db294b5cc add more tests 2020-10-07 19:33:52 +08:00
kevin
305b6749fd add more tests 2020-10-07 19:13:19 +08:00
kevin
10b855713d add more tests 2020-10-07 19:00:15 +08:00
kevin
1cc0f071d9 add more tests 2020-10-07 18:07:54 +08:00
kevin
02ce8f82c8 add more tests 2020-10-07 11:43:02 +08:00
kevin
8a585afbf0 add more tests 2020-10-07 11:19:10 +08:00
kevin
e356025cef add more tests 2020-10-07 08:11:20 +08:00
kevin
14dee114dd add more tests 2020-10-06 10:12:35 +08:00
kevin
637a94a189 add fx.Count 2020-10-05 18:17:59 +08:00
kevin
173b347c90 add more tests 2020-10-05 12:19:54 +08:00
kevin
6749c5b94a add more tests 2020-10-04 17:52:54 +08:00
刘青
e66cca3710 breaker: remover useless code (#114) 2020-10-04 16:25:26 +08:00
kevin
f90c0aa98e update wechat qrcode 2020-10-04 10:14:08 +08:00
kevin
f00b5416a3 update codecov settings 2020-10-03 23:09:29 +08:00
kevin
f49694d6b6 fix data race 2020-10-02 22:41:25 +08:00
kevin
d809bf2dca add more tests 2020-10-02 22:37:15 +08:00
kevin
44ae5463bc add more tests 2020-10-02 09:00:25 +08:00
kevin
40dbd722d7 add more tests 2020-10-01 23:29:49 +08:00
kevin
709574133b add more tests 2020-10-01 23:22:53 +08:00
kevin
cb1c593108 remove markdown linter 2020-10-01 21:11:19 +08:00
kevin
6ecf575c00 add more tests 2020-10-01 20:58:12 +08:00
kevin
b8fcdd5460 add more tests 2020-10-01 17:50:53 +08:00
kevin
ce42281568 add more tests 2020-10-01 17:27:21 +08:00
kevin
40230d79e7 fix data race 2020-10-01 16:58:07 +08:00
kevin
ba7851795b add more tests 2020-10-01 16:49:39 +08:00
kevin
096fe3bc47 add more tests 2020-10-01 11:57:06 +08:00
kevin
e37858295a add more tests 2020-10-01 11:49:17 +08:00
kevin
5a4afb1518 add more tests 2020-10-01 10:29:03 +08:00
kevin
63f1f39c40 fix int64 primary key problem 2020-09-30 22:25:47 +08:00
kevin
481895d1e4 add more tests 2020-09-30 17:47:56 +08:00
shenbaise9527
9e9ce3bf48 GetBreaker need double-check (#112) 2020-09-30 16:50:02 +08:00
kevin
0ce654968d add more tests 2020-09-30 15:36:13 +08:00
Percy Gauguin
2703493541 update: fix wrong word (#110) 2020-09-30 15:08:47 +08:00
janetyu
d4240cd4b0 perfect the bookstore and shorturl doc (#109)
* perfect the bookstore and shorturl doc

* 避免歧义
2020-09-30 14:22:37 +08:00
kevin
a22bcc84a3 better lock practice in sharedcalls 2020-09-30 12:31:35 +08:00
kevin
93f430a449 update shorturl doc 2020-09-29 17:36:00 +08:00
kevin
d1b303fe7e export cache package, add client interceptor customization 2020-09-29 17:25:49 +08:00
kevin
dbca20e3df add zrpc client interceptor 2020-09-29 16:09:11 +08:00
boob
b3ead4d76c doc: update sharedcalls.md layout (#107) 2020-09-29 14:32:17 +08:00
kevin
33a9db85c8 add unit test, fix interceptor bug 2020-09-29 14:30:22 +08:00
kingxt
e7d46aa6e2 refactor and rename folder to group (#106)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

* support return ()

* support return ()

* revert

* format api

* refactor and rename folder to group

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-29 11:14:52 +08:00
kevin
b282304054 add api doc link 2020-09-28 16:58:29 +08:00
bittoy
0a36031d48 use default mongo db (#103) 2020-09-28 16:35:07 +08:00
kevin
e5d7c3ab04 unmarshal should be struct 2020-09-28 15:19:30 +08:00
kevin
12c08bfd39 Revert "goreportcard not working, remove it temporarily"
This reverts commit 8f465fa439.
2020-09-28 11:41:23 +08:00
kevin
8f465fa439 goreportcard not working, remove it temporarily 2020-09-28 00:31:24 +08:00
kingxt
8a470bb6ee support return () syntax (#101)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

* support return ()

* support return ()

* remove pwd for windows not support

* revert

* remove no need

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-27 17:23:15 +08:00
kevin
9277ad77f7 fix typo of prometheus 2020-09-27 17:15:15 +08:00
kevin
a958400595 rename prommetric to prometheous, add unit tests 2020-09-27 16:14:16 +08:00
kevin
015716d1b5 update wechat and etcd yaml 2020-09-27 14:15:33 +08:00
kevin
54e9d01312 update example 2020-09-27 11:10:21 +08:00
kevin
bc831b75dd export AddOptions, AddStreamInterceptors, AddUnaryInterceptors 2020-09-26 22:05:57 +08:00
kevin
ff112fdaee query from cache first when do cache.Take 2020-09-26 21:58:46 +08:00
kingxt
8d0f7dbb27 rename (#98)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

* rename

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-24 10:31:49 +08:00
Keson
a5ce2c448e fix bug: module parse error (#97) 2020-09-23 22:10:25 +08:00
kevin
0dd8e27557 add more clear error when rpc service is not started 2020-09-23 22:07:26 +08:00
Zhang Hao
17a0908a84 add test (#95) 2020-09-22 19:15:30 +08:00
Keson
9f9c24cce9 fix bug: release empty struct limit (#96) 2020-09-22 19:13:46 +08:00
kingxt
b628bc0086 goctl support import api file (#94)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-22 18:32:26 +08:00
kevin
be9c48da7f add tracing logs in server side and client side 2020-09-22 17:34:39 +08:00
kevin
797a90ae7d remove unnecessary tag 2020-09-21 22:41:14 +08:00
kevin
92e60a5777 use options instead of opts in error message 2020-09-21 22:37:07 +08:00
miaogaolin
46995a4d7d 修改不能编辑代码注释 (#92)
* rename file and function name

* update comments of "code generate"
2020-09-21 18:27:35 +08:00
kingxt
5e6dcac734 feature: goctl jwt (#91)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-21 16:38:23 +08:00
dylanNew
3e7e466526 fix redis error (#88)
Co-authored-by: dylan <wangdi@xiaoheiban.cn>
2020-09-21 16:37:40 +08:00
kingxt
b6b8941a18 update doc (#90)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* update jwt doc

* update jwt doc

* update jwt doc

* update jwt doc

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-21 16:09:02 +08:00
kingxt
878fd14739 remove no need (#87)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* add jwt doc

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-21 14:29:12 +08:00
kevin
5e99f2b85d add trace/span in http logs 2020-09-20 22:02:45 +08:00
Howie
9c23399c33 chore: fix typos (#85)
* chore: fix typos

Signed-off-by: lihaowei <haoweili35@gmail.com>

* chore: fix 2 typos
2020-09-20 14:00:31 +08:00
kevin
86d3de4c89 use package level defined contextKey as context key 2020-09-20 12:46:35 +08:00
kevin
dc17855367 printing context key friendly 2020-09-20 12:08:30 +08:00
kevin
1606a92c6e use contextType as string type 2020-09-20 12:04:49 +08:00
mlboy
029fd3ea35 fix: golint: context.WithValue should should not use basic type as key (#83)
* fix: golint: context.WithValue should should not use basic type as key

* optimiz
2020-09-20 12:01:43 +08:00
kevin
57299a7597 rename ngin to rest in goctl 2020-09-20 09:15:19 +08:00
Changkun Ou
762af9dda2 optimize AtomicError (#82)
This commit optimize AtomicError using atomic.Value. Benchmarks:

name               old time/op  new time/op  delta
AtomicError/Load-6   305ns ±11%    12ns ± 6%  -96.18%  (p=0.000 n=10+10)
AtomicError/Set-6   314ns ±16%    14ns ± 2%  -95.61%  (p=0.000 n=10+9)
2020-09-18 22:45:01 +08:00
kevin
eccfaba614 update doc 2020-09-18 22:33:40 +08:00
kevin
974c19d6d3 update rpc example 2020-09-18 18:15:39 +08:00
Zhang Hao
0f8140031a fix rpc client examle (#81) 2020-09-18 18:07:08 +08:00
kevin
0b1ee79d3a rename rpcx to zrpc 2020-09-18 11:41:52 +08:00
Zhang Hao
26e16107ce fix example tracing edge config (#76) 2020-09-18 08:53:06 +08:00
kevin
1e5e9d63bd update wechat qrcode 2020-09-17 10:28:33 +08:00
kevin
f994e1df1a add more tests 2020-09-16 20:03:30 +08:00
kevin
b5dcadda78 remove markdown linter temporarily 2020-09-16 16:53:38 +08:00
kevin
df37597ac3 simplify mapreduce code 2020-09-16 16:48:59 +08:00
miaogaolin
68335ada54 rename file and function name (#74) 2020-09-16 13:30:47 +08:00
bittoy
ecdae2477e add mapping readme (#75) 2020-09-16 13:30:13 +08:00
kevin
a561884fcf print message when starting api server 2020-09-16 13:27:16 +08:00
kevin
a50bcb90a6 rename function 2020-09-14 21:13:19 +08:00
Keson
e6f8e0e8c3 optimize: api generating for idea plugin (#68)
* add flag: force to generate api

* add flag: force to generate api

* format api template

* Revert "format api template"
2020-09-14 17:12:31 +08:00
kingxt
598ff6d0fc api support empty request or empty response (#72)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* api support empty request or empty response

* update readme

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-14 17:10:45 +08:00
miaogaolin
9a57993e83 fix goctl api (#71) 2020-09-14 14:51:22 +08:00
kingxt
ee45b0a459 optimize route parse (#70)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* optimized route parser

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-14 11:44:53 +08:00
masonchen2014
2896ef1a49 Sharedcalls.md (#69)
* sharedcalls.md

* markdownlint

Co-authored-by: chenmusheng <chenmusheng@laoyuegou.com>
2020-09-14 10:13:33 +08:00
kingxt
05df86436f optimized api new with absolute path like: goctl api new $PWD/xxxx (#67)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* optimized api new with absolute path like: goctl api new $PWD/xxxx

* optimized api new with absolute path like: goctl api new $PWD/xxxx

* optimized api new with absolute path like: goctl api new $PWD/xxxx

* optimized api new with absolute path like: goctl api new $PWD/xxxx

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-13 16:17:21 +08:00
kevin
fb22589cf5 update doc, add metric link 2020-09-12 22:50:37 +08:00
kevin
a8fb010333 drain pipe if reducer not drained 2020-09-12 17:13:32 +08:00
zhoushuguang
8cc09244a0 Metric (#65)
* doc for service metric

* doc for service metric

* doc for service metric

* doc for service metric

Co-authored-by: zhoushuguang <zhoushuguang@xiaoheiban.cn>
2020-09-12 13:08:10 +08:00
Sergey Cheung
21e811887c Markdown lint (#58)
* markdown linter

* format markdown docs

* format exiting markdown docs
2020-09-11 19:42:58 +08:00
kevin
7f0ec14704 update goctl makefile 2020-09-11 18:17:07 +08:00
Keson
d12e9fa2d7 add model&rpc doc (#62) 2020-09-11 16:47:21 +08:00
miaogaolin
ce5961a7d0 fix goctl model (#61) 2020-09-11 16:46:45 +08:00
kingxt
e1d942a799 update doc (#64)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* update doc

* remove update

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-11 16:16:30 +08:00
kingxt
754e631dc4 update quick start (#63)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* update readme.md

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-11 16:06:04 +08:00
kevin
72aeac3fa9 add in-process cache doc 2020-09-11 15:30:20 +08:00
kingxt
1c3c8f4bbc add fast create api demo service (#59)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* add fast create api demo: goctl api new

* refactor

* refactor

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-11 15:27:35 +08:00
Keson
17e6cfb7a9 quickly generating rpc demo service (#60)
* add execute files

* add protoc-osx

* add rpc generation

* add rpc generation

* add: rpc template generation

* add README.md

* format error

* reactor templatex.go

* update project.go & README.md

* add: quickly generate rpc service
2020-09-11 15:26:55 +08:00
kevin
0d151c17f8 update wechat image 2020-09-10 18:05:04 +08:00
miaogaolin
52990550fb fix GOMOD env fetch bug (#55) 2020-09-09 11:43:47 +08:00
zhoushuguang
3a9b9ceace add (#54)
Co-authored-by: zhoushuguang <zhoushuguang@xiaoheiban.cn>
2020-09-08 21:20:44 +08:00
bittoy
3128d63134 fix goctl model path (#53) 2020-09-08 17:05:22 +08:00
kingxt
4408767981 fix command run path bug (#52)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* optimized go path logic

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-08 12:19:34 +08:00
kevin
ff7c14c6b6 make chinese readme as default 2020-09-08 09:24:12 +08:00
kevin
520f4d7c1b update readme to add mapreduce link 2020-09-07 23:35:57 +08:00
zhoushuguang
0e674933f3 add mr tool doc (#50)
Co-authored-by: zhoushuguang <zhoushuguang@xiaoheiban.cn>
2020-09-07 22:40:29 +08:00
kingxt
1d12f20ff6 refactor (#49)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* refactor gomod module logic

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-07 18:47:03 +08:00
kingxt
2b815162f6 refactor (#48)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* refactor gomod module logic

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-07 18:12:03 +08:00
kingxt
1602f6ce81 refactor gomod logic (#47)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* refactor gomod module logic

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-07 18:04:59 +08:00
kevin
c5cd0d32d1 fix typoin doc 2020-09-07 17:08:06 +08:00
kevin
1cb17311dd add unit test for mapreduce 2020-09-06 18:19:19 +08:00
kevin
e987eb60d3 fix mapreduce problem when reducer doesn't write 2020-09-06 18:13:42 +08:00
kevin
99a863e8be add language link 2020-09-06 16:37:14 +08:00
kevin
5333fb93e5 add bookstore english tutorial 2020-09-06 16:36:18 +08:00
kevin
cb13556461 add language link 2020-09-06 15:38:33 +08:00
kevin
561370d5c9 add shorturl english tutorial 2020-09-06 15:36:12 +08:00
kevin
7c779d0433 fix readme typo 2020-09-05 19:18:15 +08:00
kevin
6814c86fcd add english readme 2020-09-05 19:09:18 +08:00
windk
a1d2ea9d85 fix typo (#38)
fix typo: *shorten.ShortenReq -> *transform.ShortenReq
2020-09-04 17:38:20 +08:00
kevin
4dfbd66323 add goctl description 2020-09-04 15:40:12 +08:00
kevin
dbf556e7d2 update readme 2020-09-04 08:16:17 +08:00
kevin
c0d0e00803 update example doc 2020-09-04 08:13:22 +08:00
kevin
b4aa89fc25 add wechat qrcode 2020-09-04 08:00:10 +08:00
kevin
11dd3d75ec add bookstore example 2020-09-03 23:26:04 +08:00
wwek
167422ac4f fix LF (#37)
* fix LF

* fix  remove export
2020-09-03 22:37:52 +08:00
kevin
a74d73fb2e fix bookstore example 2020-09-03 20:23:27 +08:00
kevin
81a9ada2d9 add bookstore example 2020-09-03 18:09:12 +08:00
kevin
55c9c3f3dd replace clickhouse driver to the official one 2020-09-03 16:58:31 +08:00
kevin
8dd93d59a0 refactor code 2020-09-03 14:00:09 +08:00
Keson
3a4e1cbb33 fix bug: miss time import (#36)
* add execute files

* add protoc-osx

* add rpc generation

* add rpc generation

* add: rpc template generation

* optimize gomod cache

* add README.md

* format error

* reactor templatex.go

* update project.go & README.md

* fix bug: miss time import
2020-09-03 13:57:28 +08:00
kevin
d1129e3974 refactor 2020-09-03 10:15:14 +08:00
Leonard Wang
1e85f74fd8 fix shorturl example code (#35) 2020-09-03 08:34:11 +08:00
kingxt
33eb2936e8 fix: root path on windows bug (#34)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* fix bug on windows

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-02 15:14:27 +08:00
kevin
b7a018b33a add shorturl example code 2020-09-01 16:04:39 +08:00
kevin
ea1c9aa250 support go 1.13 2020-09-01 15:04:01 +08:00
kevin
fbad810cd1 update shorturl doc 2020-09-01 13:46:05 +08:00
kevin
6b15475ccd update shorturl doc 2020-09-01 13:38:16 +08:00
kingxt
5c0c3ea467 trim space (#31)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* trim space

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-01 11:46:10 +08:00
kingxt
89f3712347 remove no need empty line (#29)
* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* trim no need line

Co-authored-by: kingxt <dream4kingxt@163.com>
2020-09-01 11:08:19 +08:00
kevin
af7acdd843 fix doc errors 2020-09-01 10:26:16 +08:00
kevin
7ffa3349a9 update readme 2020-08-31 22:45:55 +08:00
kevin
f03862c378 update docs 2020-08-31 22:37:43 +08:00
kevin
fe3e70a60f update shorturl doc 2020-08-31 20:52:29 +08:00
kevin
36174ba5cc make svcCtx as a member for better code generation 2020-08-31 12:32:13 +08:00
kevin
7b17b3604a fix dockerfile generation 2020-08-31 12:27:38 +08:00
kevin
eb40c2731d remove files 2020-08-30 23:56:51 +08:00
kevin
618bec5075 remove makefile generation 2020-08-30 23:52:51 +08:00
kevin
5821b7324e update readme 2020-08-30 23:34:57 +08:00
kevin
befdaab542 update goctl makefile 2020-08-30 18:22:46 +08:00
kevin
431be8ed9d make goctl work on linux 2020-08-30 16:37:34 +08:00
kevin
3c688c319e update shorturl doc 2020-08-29 23:42:42 +08:00
kevin
59ffa75c00 fix typo in doc 2020-08-29 23:33:34 +08:00
kevin
09340e82a7 fix doc error 2020-08-29 22:51:48 +08:00
635 changed files with 31763 additions and 9214 deletions

View File

@@ -1,4 +1,4 @@
ignore:
- "doc"
- "example"
- "tools"
- "tools"

67
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '18 19 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# 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)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -7,7 +7,6 @@ on:
branches: [ master ]
jobs:
build:
name: Build
runs-on: ubuntu-latest

2
.gitignore vendored
View File

@@ -4,6 +4,7 @@
# Unignore all with extensions
!*.*
!**/Dockerfile
!**/Makefile
# Unignore all dirs
!*/
@@ -12,7 +13,6 @@
.idea
**/.DS_Store
**/logs
!Makefile
# gitlab ci
.cache

View File

@@ -13,15 +13,13 @@ const (
// maps as k in the error rate table
maps = 14
setScript = `
local key = KEYS[1]
for _, offset in ipairs(ARGV) do
redis.call("setbit", key, offset, 1)
redis.call("setbit", KEYS[1], offset, 1)
end
`
testScript = `
local key = KEYS[1]
for _, offset in ipairs(ARGV) do
if tonumber(redis.call("getbit", key, offset)) == 0 then
if tonumber(redis.call("getbit", KEYS[1], offset)) == 0 then
return false
end
end
@@ -39,7 +37,6 @@ type (
BloomFilter struct {
bits uint
maps uint
bitSet BitSetProvider
}
)

View File

@@ -3,19 +3,15 @@ package bloom
import (
"testing"
"github.com/alicebob/miniredis"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
)
func TestRedisBitSet_New_Set_Test(t *testing.T) {
s, err := miniredis.Run()
if err != nil {
t.Error("Miniredis could not start")
}
defer s.Close()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
store := redis.NewRedis(s.Addr(), redis.NodeType)
bitSet := newRedisBitSet(store, "test_key", 1024)
isSetBefore, err := bitSet.check([]uint{0})
if err != nil {
@@ -46,13 +42,10 @@ func TestRedisBitSet_New_Set_Test(t *testing.T) {
}
func TestRedisBitSet_Add(t *testing.T) {
s, err := miniredis.Run()
if err != nil {
t.Error("Miniredis could not start")
}
defer s.Close()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
store := redis.NewRedis(s.Addr(), redis.NodeType)
filter := New(store, "test_key", 64)
assert.Nil(t, filter.Add([]byte("hello")))
assert.Nil(t, filter.Add([]byte("world")))

View File

@@ -5,17 +5,12 @@ import (
"fmt"
"strings"
"sync"
"time"
"github.com/tal-tech/go-zero/core/mathx"
"github.com/tal-tech/go-zero/core/proc"
"github.com/tal-tech/go-zero/core/stat"
"github.com/tal-tech/go-zero/core/stringx"
)
const (
StateClosed State = iota
StateOpen
"github.com/tal-tech/go-zero/core/timex"
)
const (
@@ -27,11 +22,10 @@ const (
var ErrServiceUnavailable = errors.New("circuit breaker is open")
type (
State = int32
Acceptable func(err error) bool
Breaker interface {
// Name returns the name of the netflixBreaker.
// Name returns the name of the Breaker.
Name() string
// Allow checks if the request is allowed.
@@ -40,34 +34,34 @@ type (
// If not allow, ErrServiceUnavailable will be returned.
Allow() (Promise, error)
// Do runs the given request if the netflixBreaker accepts it.
// Do returns an error instantly if the netflixBreaker rejects the request.
// If a panic occurs in the request, the netflixBreaker handles it as an error
// Do runs the given request if the Breaker accepts it.
// 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
// and causes the same panic again.
Do(req func() error) error
// DoWithAcceptable runs the given request if the netflixBreaker accepts it.
// Do returns an error instantly if the netflixBreaker rejects the request.
// If a panic occurs in the request, the netflixBreaker handles it as an error
// DoWithAcceptable runs the given request if the Breaker accepts it.
// 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
// and causes the same panic again.
// acceptable checks if it's a successful call, even if the err is not nil.
DoWithAcceptable(req func() error, acceptable Acceptable) error
// DoWithFallback runs the given request if the netflixBreaker accepts it.
// DoWithFallback runs the fallback if the netflixBreaker rejects the request.
// If a panic occurs in the request, the netflixBreaker handles it as an error
// DoWithFallback runs the given request if the Breaker accepts it.
// DoWithFallback runs the fallback if the Breaker rejects the request.
// If a panic occurs in the request, the Breaker handles it as an error
// and causes the same panic again.
DoWithFallback(req func() error, fallback func(err error) error) error
// DoWithFallbackAcceptable runs the given request if the netflixBreaker accepts it.
// DoWithFallback runs the fallback if the netflixBreaker rejects the request.
// If a panic occurs in the request, the netflixBreaker handles it as an error
// DoWithFallbackAcceptable runs the given request if the Breaker accepts it.
// DoWithFallbackAcceptable runs the fallback if the Breaker rejects the request.
// If a panic occurs in the request, the Breaker handles it as an error
// and causes the same panic again.
// acceptable checks if it's a successful call, even if the err is not nil.
DoWithFallbackAcceptable(req func() error, fallback func(err error) error, acceptable Acceptable) error
}
BreakerOption func(breaker *circuitBreaker)
Option func(breaker *circuitBreaker)
Promise interface {
Accept()
@@ -95,7 +89,7 @@ type (
}
)
func NewBreaker(opts ...BreakerOption) Breaker {
func NewBreaker(opts ...Option) Breaker {
var b circuitBreaker
for _, opt := range opts {
opt(&b)
@@ -133,7 +127,7 @@ func (cb *circuitBreaker) Name() string {
return cb.name
}
func WithName(name string) BreakerOption {
func WithName(name string) Option {
return func(b *circuitBreaker) {
b.name = name
}
@@ -195,23 +189,23 @@ type errorWindow struct {
func (ew *errorWindow) add(reason string) {
ew.lock.Lock()
ew.reasons[ew.index] = fmt.Sprintf("%s %s", time.Now().Format(timeFormat), reason)
ew.reasons[ew.index] = fmt.Sprintf("%s %s", timex.Time().Format(timeFormat), reason)
ew.index = (ew.index + 1) % numHistoryReasons
ew.count = mathx.MinInt(ew.count+1, numHistoryReasons)
ew.lock.Unlock()
}
func (ew *errorWindow) String() string {
var builder strings.Builder
var reasons []string
ew.lock.Lock()
for i := ew.index + ew.count - 1; i >= ew.index; i-- {
builder.WriteString(ew.reasons[i%numHistoryReasons])
builder.WriteByte('\n')
// reverse order
for i := ew.index - 1; i >= ew.index-ew.count; i-- {
reasons = append(reasons, ew.reasons[(i+numHistoryReasons)%numHistoryReasons])
}
ew.lock.Unlock()
return builder.String()
return strings.Join(reasons, "\n")
}
type promiseWithReason struct {

View File

@@ -2,7 +2,9 @@ package breaker
import (
"errors"
"fmt"
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -33,6 +35,84 @@ func TestLogReason(t *testing.T) {
assert.Equal(t, numHistoryReasons, errs.count)
}
func TestErrorWindow(t *testing.T) {
tests := []struct {
name string
reasons []string
}{
{
name: "no error",
},
{
name: "one error",
reasons: []string{"foo"},
},
{
name: "two errors",
reasons: []string{"foo", "bar"},
},
{
name: "five errors",
reasons: []string{"first", "second", "third", "fourth", "fifth"},
},
{
name: "six errors",
reasons: []string{"first", "second", "third", "fourth", "fifth", "sixth"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var ew errorWindow
for _, reason := range test.reasons {
ew.add(reason)
}
var reasons []string
if len(test.reasons) > numHistoryReasons {
reasons = test.reasons[len(test.reasons)-numHistoryReasons:]
} else {
reasons = test.reasons
}
for _, reason := range reasons {
assert.True(t, strings.Contains(ew.String(), reason), fmt.Sprintf("actual: %s", ew.String()))
}
})
}
}
func TestPromiseWithReason(t *testing.T) {
tests := []struct {
name string
reason string
expect string
}{
{
name: "success",
},
{
name: "success",
reason: "fail",
expect: "fail",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
promise := promiseWithReason{
promise: new(mockedPromise),
errWin: new(errorWindow),
}
if len(test.reason) == 0 {
promise.Accept()
} else {
promise.Reject(test.reason)
}
assert.True(t, strings.Contains(promise.errWin.String(), test.expect))
})
}
}
func BenchmarkGoogleBreaker(b *testing.B) {
br := NewBreaker()
for i := 0; i < b.N; i++ {
@@ -41,3 +121,12 @@ func BenchmarkGoogleBreaker(b *testing.B) {
})
}
}
type mockedPromise struct {
}
func (m *mockedPromise) Accept() {
}
func (m *mockedPromise) Reject() {
}

View File

@@ -41,10 +41,13 @@ func GetBreaker(name string) Breaker {
}
lock.Lock()
defer lock.Unlock()
b, ok = breakers[name]
if !ok {
b = NewBreaker(WithName(name))
breakers[name] = b
}
lock.Unlock()
b = NewBreaker()
breakers[name] = b
return b
}
@@ -55,20 +58,5 @@ func NoBreakFor(name string) {
}
func do(name string, execute func(b Breaker) error) error {
lock.RLock()
b, ok := breakers[name]
lock.RUnlock()
if ok {
return execute(b)
}
lock.Lock()
b, ok = breakers[name]
if !ok {
b = NewBreaker(WithName(name))
breakers[name] = b
}
lock.Unlock()
return execute(b)
return execute(GetBreaker(name))
}

View File

@@ -2,7 +2,6 @@ package breaker
import (
"math"
"sync/atomic"
"time"
"github.com/tal-tech/go-zero/core/collection"
@@ -21,7 +20,6 @@ const (
// see Client-Side Throttling section in https://landing.google.com/sre/sre-book/chapters/handling-overload/
type googleBreaker struct {
k float64
state int32
stat *collection.RollingWindow
proba *mathx.Proba
}
@@ -32,7 +30,6 @@ func newGoogleBreaker() *googleBreaker {
return &googleBreaker{
stat: st,
k: k,
state: StateClosed,
proba: mathx.NewProba(),
}
}
@@ -43,15 +40,9 @@ func (b *googleBreaker) accept() error {
// https://landing.google.com/sre/sre-book/chapters/handling-overload/#eq2101
dropRatio := math.Max(0, (float64(total-protection)-weightedAccepts)/float64(total+1))
if dropRatio <= 0 {
if atomic.LoadInt32(&b.state) == StateOpen {
atomic.CompareAndSwapInt32(&b.state, StateOpen, StateClosed)
}
return nil
}
if atomic.LoadInt32(&b.state) == StateClosed {
atomic.CompareAndSwapInt32(&b.state, StateClosed, StateOpen)
}
if b.proba.TrueOnProba(dropRatio) {
return ErrServiceUnavailable
}

View File

@@ -2,7 +2,6 @@ package breaker
import (
"errors"
"math"
"math/rand"
"testing"
"time"
@@ -27,7 +26,6 @@ func getGoogleBreaker() *googleBreaker {
return &googleBreaker{
stat: st,
k: 5,
state: StateClosed,
proba: mathx.NewProba(),
}
}
@@ -158,7 +156,7 @@ func TestGoogleBreakerSelfProtection(t *testing.T) {
t.Run("total request > 100, total < 2 * success", func(t *testing.T) {
b := getGoogleBreaker()
size := rand.Intn(10000)
accepts := int(math.Ceil(float64(size))) + 1
accepts := size + 1
markSuccess(b, accepts)
markFailed(b, size-accepts)
assert.Nil(t, b.accept())

View File

@@ -0,0 +1,80 @@
package cmdline
import (
"fmt"
"os"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/iox"
"github.com/tal-tech/go-zero/core/lang"
)
func TestEnterToContinue(t *testing.T) {
restore, err := iox.RedirectInOut()
assert.Nil(t, err)
defer restore()
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
fmt.Println()
}()
go func() {
defer wg.Done()
EnterToContinue()
}()
wait := make(chan lang.PlaceholderType)
go func() {
wg.Wait()
close(wait)
}()
select {
case <-time.After(time.Second):
t.Error("timeout")
case <-wait:
}
}
func TestReadLine(t *testing.T) {
r, w, err := os.Pipe()
assert.Nil(t, err)
ow := os.Stdout
os.Stdout = w
or := os.Stdin
os.Stdin = r
defer func() {
os.Stdin = or
os.Stdout = ow
}()
const message = "hello"
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
fmt.Println(message)
}()
go func() {
defer wg.Done()
input := ReadLine("")
assert.Equal(t, message, input)
}()
wait := make(chan lang.PlaceholderType)
go func() {
wg.Wait()
close(wait)
}()
select {
case <-time.After(time.Second):
t.Error("timeout")
case <-wait:
}
}

View File

@@ -71,3 +71,12 @@ func TestDiffieHellmanMiddleManAttack(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, string(src), string(decryptedSrc))
}
func TestKeyBytes(t *testing.T) {
var empty DhKey
assert.Equal(t, 0, len(empty.Bytes()))
key, err := GenerateKey()
assert.Nil(t, err)
assert.True(t, len(key.Bytes()) > 0)
}

View File

@@ -6,6 +6,8 @@ import (
"io"
)
const unzipLimit = 100 * 1024 * 1024 // 100MB
func Gzip(bs []byte) []byte {
var b bytes.Buffer
@@ -24,8 +26,7 @@ func Gunzip(bs []byte) ([]byte, error) {
defer r.Close()
var c bytes.Buffer
_, err = io.Copy(&c, r)
if err != nil {
if _, err = io.Copy(&c, io.LimitReader(r, unzipLimit)); err != nil {
return nil, err
}

19
core/codec/hmac_test.go Normal file
View File

@@ -0,0 +1,19 @@
package codec
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestHmac(t *testing.T) {
ret := Hmac([]byte("foo"), "bar")
assert.Equal(t, "f9320baf0249169e73850cd6156ded0106e2bb6ad8cab01b7bbbebe6d1065317",
fmt.Sprintf("%x", ret))
}
func TestHmacBase64(t *testing.T) {
ret := HmacBase64([]byte("foo"), "bar")
assert.Equal(t, "+TILrwJJFp5zhQzWFW3tAQbiu2rYyrAbe7vr5tEGUxc=", ret)
}

58
core/codec/rsa_test.go Normal file
View File

@@ -0,0 +1,58 @@
package codec
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/fs"
)
const (
priKey = `-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC4TJk3onpqb2RYE3wwt23J9SHLFstHGSkUYFLe+nl1dEKHbD+/
Zt95L757J3xGTrwoTc7KCTxbrgn+stn0w52BNjj/kIE2ko4lbh/v8Fl14AyVR9ms
fKtKOnhe5FCT72mdtApr+qvzcC3q9hfXwkyQU32pv7q5UimZ205iKSBmgQIDAQAB
AoGAM5mWqGIAXj5z3MkP01/4CDxuyrrGDVD5FHBno3CDgyQa4Gmpa4B0/ywj671B
aTnwKmSmiiCN2qleuQYASixes2zY5fgTzt+7KNkl9JHsy7i606eH2eCKzsUa/s6u
WD8V3w/hGCQ9zYI18ihwyXlGHIgcRz/eeRh+nWcWVJzGOPUCQQD5nr6It/1yHb1p
C6l4fC4xXF19l4KxJjGu1xv/sOpSx0pOqBDEX3Mh//FU954392rUWDXV1/I65BPt
TLphdsu3AkEAvQJ2Qay/lffFj9FaUrvXuftJZ/Ypn0FpaSiUh3Ak3obBT6UvSZS0
bcYdCJCNHDtBOsWHnIN1x+BcWAPrdU7PhwJBAIQ0dUlH2S3VXnoCOTGc44I1Hzbj
Rc65IdsuBqA3fQN2lX5vOOIog3vgaFrOArg1jBkG1wx5IMvb/EnUN2pjVqUCQCza
KLXtCInOAlPemlCHwumfeAvznmzsWNdbieOZ+SXVVIpR6KbNYwOpv7oIk3Pfm9sW
hNffWlPUKhW42Gc+DIECQQDmk20YgBXwXWRM5DRPbhisIV088N5Z58K9DtFWkZsd
OBDT3dFcgZONtlmR1MqZO0pTh30lA4qovYj3Bx7A8i36
-----END RSA PRIVATE KEY-----`
pubKey = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4TJk3onpqb2RYE3wwt23J9SHL
FstHGSkUYFLe+nl1dEKHbD+/Zt95L757J3xGTrwoTc7KCTxbrgn+stn0w52BNjj/
kIE2ko4lbh/v8Fl14AyVR9msfKtKOnhe5FCT72mdtApr+qvzcC3q9hfXwkyQU32p
v7q5UimZ205iKSBmgQIDAQAB
-----END PUBLIC KEY-----`
testBody = `this is the content`
)
func TestCryption(t *testing.T) {
enc, err := NewRsaEncrypter([]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)
dec, err := NewRsaDecrypter(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))
}
func TestBadPubKey(t *testing.T) {
_, err := NewRsaEncrypter([]byte("foo"))
assert.Equal(t, ErrPublicKey, err)
}

View File

@@ -29,7 +29,6 @@ type (
name string
lock sync.Mutex
data map[string]interface{}
evicts *list.List
expire time.Duration
timingWheel *TimingWheel
lruCache lru
@@ -82,12 +81,7 @@ func (c *Cache) Del(key string) {
}
func (c *Cache) Get(key string) (interface{}, bool) {
c.lock.Lock()
value, ok := c.data[key]
if ok {
c.lruCache.add(key)
}
c.lock.Unlock()
value, ok := c.doGet(key)
if ok {
c.stats.IncrementHit()
} else {
@@ -113,12 +107,25 @@ func (c *Cache) Set(key string, value interface{}) {
}
func (c *Cache) Take(key string, fetch func() (interface{}, error)) (interface{}, error) {
val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) {
if val, ok := c.doGet(key); ok {
c.stats.IncrementHit()
return val, nil
}
var fresh bool
val, err := c.barrier.Do(key, func() (interface{}, error) {
// 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
if val, ok := c.doGet(key); ok {
return val, nil
}
v, e := fetch()
if e != nil {
return nil, e
}
fresh = true
c.Set(key, v)
return v, nil
})
@@ -137,6 +144,18 @@ func (c *Cache) Take(key string, fetch func() (interface{}, error)) (interface{}
return val, nil
}
func (c *Cache) doGet(key string) (interface{}, bool) {
c.lock.Lock()
defer c.lock.Unlock()
value, ok := c.data[key]
if ok {
c.lruCache.add(key)
}
return value, ok
}
func (c *Cache) onEvict(key string) {
// already locked
delete(c.data, key)
@@ -258,18 +277,15 @@ func (cs *cacheStat) statLoop() {
ticker := time.NewTicker(statInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
hit := atomic.SwapUint64(&cs.hit, 0)
miss := atomic.SwapUint64(&cs.miss, 0)
total := hit + miss
if total == 0 {
continue
}
percent := 100 * float32(hit) / float32(total)
logx.Statf("cache(%s) - qpm: %d, hit_ratio: %.1f%%, elements: %d, hit: %d, miss: %d",
cs.name, total, percent, cs.sizeCallback(), hit, miss)
for range ticker.C {
hit := atomic.SwapUint64(&cs.hit, 0)
miss := atomic.SwapUint64(&cs.miss, 0)
total := hit + miss
if total == 0 {
continue
}
percent := 100 * float32(hit) / float32(total)
logx.Statf("cache(%s) - qpm: %d, hit_ratio: %.1f%%, elements: %d, hit: %d, miss: %d",
cs.name, total, percent, cs.sizeCallback(), hit, miss)
}
}

View File

@@ -1,6 +1,7 @@
package collection
import (
"errors"
"strconv"
"sync"
"sync/atomic"
@@ -10,6 +11,8 @@ import (
"github.com/stretchr/testify/assert"
)
var errDummy = errors.New("dummy")
func TestCacheSet(t *testing.T) {
cache, err := NewCache(time.Second*2, WithName("any"))
assert.Nil(t, err)
@@ -63,6 +66,54 @@ func TestCacheTake(t *testing.T) {
assert.Equal(t, int32(1), atomic.LoadInt32(&count))
}
func TestCacheTakeExists(t *testing.T) {
cache, err := NewCache(time.Second * 2)
assert.Nil(t, err)
var count int32
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
cache.Set("first", "first element")
cache.Take("first", func() (interface{}, error) {
atomic.AddInt32(&count, 1)
time.Sleep(time.Millisecond * 100)
return "first element", nil
})
wg.Done()
}()
}
wg.Wait()
assert.Equal(t, 1, cache.size())
assert.Equal(t, int32(0), atomic.LoadInt32(&count))
}
func TestCacheTakeError(t *testing.T) {
cache, err := NewCache(time.Second * 2)
assert.Nil(t, err)
var count int32
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
_, err := cache.Take("first", func() (interface{}, error) {
atomic.AddInt32(&count, 1)
time.Sleep(time.Millisecond * 100)
return "", errDummy
})
assert.Equal(t, errDummy, err)
wg.Done()
}()
}
wg.Wait()
assert.Equal(t, 0, cache.size())
assert.Equal(t, int32(1), atomic.LoadInt32(&count))
}
func TestCacheWithLruEvicts(t *testing.T) {
cache, err := NewCache(time.Minute, WithLimit(3))
assert.Nil(t, err)

View File

@@ -6,6 +6,10 @@ type Ring struct {
}
func NewRing(n int) *Ring {
if n < 1 {
panic("n should be greater than 0")
}
return &Ring{
elements: make([]interface{}, n),
}

View File

@@ -6,6 +6,12 @@ import (
"github.com/stretchr/testify/assert"
)
func TestNewRing(t *testing.T) {
assert.Panics(t, func() {
NewRing(0)
})
}
func TestRingLess(t *testing.T) {
ring := NewRing(5)
for i := 0; i < 3; i++ {

View File

@@ -8,8 +8,10 @@ import (
)
type (
// RollingWindowOption let callers customize the RollingWindow.
RollingWindowOption func(rollingWindow *RollingWindow)
// RollingWindow defines a rolling window to calculate the events in buckets with time interval.
RollingWindow struct {
lock sync.RWMutex
size int
@@ -17,11 +19,17 @@ type (
interval time.Duration
offset int
ignoreCurrent bool
lastTime time.Duration
lastTime time.Duration // start time of the last bucket
}
)
// NewRollingWindow returns a RollingWindow that with size buckets and time interval,
// use opts to customize the RollingWindow.
func NewRollingWindow(size int, interval time.Duration, opts ...RollingWindowOption) *RollingWindow {
if size < 1 {
panic("size must be greater than 0")
}
w := &RollingWindow{
size: size,
win: newWindow(size),
@@ -34,6 +42,7 @@ func NewRollingWindow(size int, interval time.Duration, opts ...RollingWindowOpt
return w
}
// Add adds value to current bucket.
func (rw *RollingWindow) Add(v float64) {
rw.lock.Lock()
defer rw.lock.Unlock()
@@ -41,6 +50,7 @@ func (rw *RollingWindow) Add(v float64) {
rw.win.add(rw.offset, v)
}
// Reduce runs fn on all buckets, ignore current bucket if ignoreCurrent was set.
func (rw *RollingWindow) Reduce(fn func(b *Bucket)) {
rw.lock.RLock()
defer rw.lock.RUnlock()
@@ -70,29 +80,23 @@ func (rw *RollingWindow) span() int {
func (rw *RollingWindow) updateOffset() {
span := rw.span()
if span > 0 {
offset := rw.offset
// reset expired buckets
start := offset + 1
steps := start + span
var remainder int
if steps > rw.size {
remainder = steps - rw.size
steps = rw.size
}
for i := start; i < steps; i++ {
rw.win.resetBucket(i)
offset = i
}
for i := 0; i < remainder; i++ {
rw.win.resetBucket(i)
offset = i
}
rw.offset = offset
rw.lastTime = timex.Now()
if span <= 0 {
return
}
offset := rw.offset
// reset expired buckets
for i := 0; i < span; i++ {
rw.win.resetBucket((offset + i + 1) % rw.size)
}
rw.offset = (offset + span) % rw.size
now := timex.Now()
// align to interval time boundary
rw.lastTime = now - (now-rw.lastTime)%rw.interval
}
// Bucket defines the bucket that holds sum and num of additions.
type Bucket struct {
Sum float64
Count int64
@@ -114,9 +118,9 @@ type window struct {
}
func newWindow(size int) *window {
var buckets []*Bucket
buckets := make([]*Bucket, size)
for i := 0; i < size; i++ {
buckets = append(buckets, new(Bucket))
buckets[i] = new(Bucket)
}
return &window{
buckets: buckets,
@@ -130,14 +134,15 @@ func (w *window) add(offset int, v float64) {
func (w *window) reduce(start, count int, fn func(b *Bucket)) {
for i := 0; i < count; i++ {
fn(w.buckets[(start+i)%len(w.buckets)])
fn(w.buckets[(start+i)%w.size])
}
}
func (w *window) resetBucket(offset int) {
w.buckets[offset].reset()
w.buckets[offset%w.size].reset()
}
// IgnoreCurrentBucket lets the Reduce call ignore current bucket.
func IgnoreCurrentBucket() RollingWindowOption {
return func(w *RollingWindow) {
w.ignoreCurrent = true

View File

@@ -11,6 +11,13 @@ import (
const duration = time.Millisecond * 50
func TestNewRollingWindow(t *testing.T) {
assert.NotNil(t, NewRollingWindow(10, time.Second))
assert.Panics(t, func() {
NewRollingWindow(0, time.Second)
})
}
func TestRollingWindowAdd(t *testing.T) {
const size = 3
r := NewRollingWindow(size, duration)
@@ -81,7 +88,7 @@ func TestRollingWindowReduce(t *testing.T) {
for _, test := range tests {
t.Run(stringx.Rand(), func(t *testing.T) {
r := test.win
for x := 0; x < size; x = x + 1 {
for x := 0; x < size; x++ {
for i := 0; i <= x; i++ {
r.Add(float64(i))
}
@@ -98,6 +105,37 @@ func TestRollingWindowReduce(t *testing.T) {
}
}
func TestRollingWindowBucketTimeBoundary(t *testing.T) {
const size = 3
interval := time.Millisecond * 30
r := NewRollingWindow(size, interval)
listBuckets := func() []float64 {
var buckets []float64
r.Reduce(func(b *Bucket) {
buckets = append(buckets, b.Sum)
})
return buckets
}
assert.Equal(t, []float64{0, 0, 0}, listBuckets())
r.Add(1)
assert.Equal(t, []float64{0, 0, 1}, listBuckets())
time.Sleep(time.Millisecond * 45)
r.Add(2)
r.Add(3)
assert.Equal(t, []float64{0, 1, 5}, listBuckets())
// sleep time should be less than interval, and make the bucket change happen
time.Sleep(time.Millisecond * 20)
r.Add(4)
r.Add(5)
r.Add(6)
assert.Equal(t, []float64{1, 5, 15}, listBuckets())
time.Sleep(time.Millisecond * 100)
r.Add(7)
r.Add(8)
r.Add(9)
assert.Equal(t, []float64{0, 0, 24}, listBuckets())
}
func TestRollingWindowDataRace(t *testing.T) {
const size = 3
r := NewRollingWindow(size, duration)

View File

@@ -15,6 +15,7 @@ const (
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
@@ -182,10 +183,7 @@ func (s *Set) add(i interface{}) {
}
func (s *Set) setType(i interface{}) {
if s.tp != untyped {
return
}
// s.tp can only be untyped here
switch i.(type) {
case int:
s.tp = intType

View File

@@ -5,8 +5,13 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx"
)
func init() {
logx.Disable()
}
func BenchmarkRawSet(b *testing.B) {
m := make(map[interface{}]struct{})
for i := 0; i < b.N; i++ {
@@ -147,3 +152,51 @@ func TestCount(t *testing.T) {
// then
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

@@ -204,6 +204,7 @@ func (tw *TimingWheel) removeTask(key interface{}) {
timer := val.(*positionEntry)
timer.item.removed = true
tw.timers.Del(key)
}
func (tw *TimingWheel) run() {
@@ -248,7 +249,6 @@ func (tw *TimingWheel) scanAndRunTasks(l *list.List) {
if task.removed {
next := e.Next()
l.Remove(e)
tw.timers.Del(task.key)
e = next
continue
} else if task.circle > 0 {
@@ -301,6 +301,7 @@ func (tw *TimingWheel) setTask(task *timingEntry) {
func (tw *TimingWheel) setTimerPosition(pos int, task *timingEntry) {
if val, ok := tw.timers.Get(task.key); ok {
timer := val.(*positionEntry)
timer.item = task
timer.pos = pos
} else {
tw.timers.Set(task.key, &positionEntry{

View File

@@ -594,6 +594,31 @@ func TestTimingWheel_ElapsedAndSetThenMove(t *testing.T) {
}
}
func TestMoveAndRemoveTask(t *testing.T) {
ticker := timex.NewFakeTicker()
tick := func(v int) {
for i := 0; i < v; i++ {
ticker.Tick()
}
}
var keys []int
tw, _ := newTimingWheelWithClock(testStep, 10, func(k, v interface{}) {
assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int))
keys = append(keys, v.(int))
ticker.Done()
}, ticker)
defer tw.Stop()
tw.SetTimer("any", 3, testStep*8)
tick(6)
tw.MoveTimer("any", testStep*7)
tick(3)
tw.RemoveTimer("any")
tick(30)
time.Sleep(time.Millisecond)
assert.Equal(t, 0, len(keys))
}
func BenchmarkTimingWheel(b *testing.B) {
b.ReportAllocs()

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"io/ioutil"
"log"
"os"
"path"
"github.com/tal-tech/go-zero/core/mapping"
@@ -19,7 +20,7 @@ func LoadConfig(file string, v interface{}) error {
if content, err := ioutil.ReadFile(file); err != nil {
return err
} else if loader, ok := loaders[path.Ext(file)]; ok {
return loader(content, v)
return loader([]byte(os.ExpandEnv(string(content))), v)
} else {
return fmt.Errorf("unrecoginized file type: %s", file)
}

73
core/conf/config_test.go Normal file
View File

@@ -0,0 +1,73 @@
package conf
import (
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/fs"
"github.com/tal-tech/go-zero/core/hash"
)
func TestLoadConfig_notExists(t *testing.T) {
assert.NotNil(t, LoadConfig("not_a_file", nil))
}
func TestLoadConfig_notRecogFile(t *testing.T) {
filename, err := fs.TempFilenameWithText("hello")
assert.Nil(t, err)
defer os.Remove(filename)
assert.NotNil(t, LoadConfig(filename, nil))
}
func TestConfigJson(t *testing.T) {
tests := []string{
".json",
".yaml",
".yml",
}
text := `{
"a": "foo",
"b": 1,
"c": "${FOO}"
}`
for _, test := range tests {
test := test
t.Run(test, func(t *testing.T) {
os.Setenv("FOO", "2")
defer os.Unsetenv("FOO")
tmpfile, err := createTempFile(test, text)
assert.Nil(t, err)
defer os.Remove(tmpfile)
var val struct {
A string `json:"a"`
B int `json:"b"`
C string `json:"c"`
}
MustLoad(tmpfile, &val)
assert.Equal(t, "foo", val.A)
assert.Equal(t, 1, val.B)
assert.Equal(t, "2", val.C)
})
}
}
func createTempFile(ext, text string) (string, error) {
tmpfile, err := ioutil.TempFile(os.TempDir(), hash.Md5Hex([]byte(text))+"*"+ext)
if err != nil {
return "", err
}
if err := ioutil.WriteFile(tmpfile.Name(), []byte(text), os.ModeTemporary); err != nil {
return "", err
}
filename := tmpfile.Name()
if err = tmpfile.Close(); err != nil {
return "", err
}
return filename, nil
}

View File

@@ -30,12 +30,12 @@ type mapBasedProperties struct {
lock sync.RWMutex
}
// Loads the properties into a properties configuration instance. May return the
// configuration itself along with an error that indicates if there was a problem loading the configuration.
// Loads the properties into a properties configuration instance.
// Returns an error that indicates if there was a problem loading the configuration.
func LoadProperties(filename string) (Properties, error) {
lines, err := iox.ReadTextLines(filename, iox.WithoutBlank(), iox.OmitWithPrefix("#"))
if err != nil {
return nil, nil
return nil, err
}
raw := make(map[string]string)

View File

@@ -24,6 +24,20 @@ func TestProperties(t *testing.T) {
assert.Equal(t, "test", props.GetString("app.name"))
assert.Equal(t, "app", props.GetString("app.program"))
assert.Equal(t, 5, props.GetInt("app.threads"))
val := props.ToString()
assert.Contains(t, val, "app.name")
assert.Contains(t, val, "app.program")
assert.Contains(t, val, "app.threads")
}
func TestLoadProperties_badContent(t *testing.T) {
filename, err := fs.TempFilenameWithText("hello")
assert.Nil(t, err)
defer os.Remove(filename)
_, err = LoadProperties(filename)
assert.NotNil(t, err)
assert.True(t, len(err.Error()) > 0)
}
func TestSetString(t *testing.T) {
@@ -41,3 +55,8 @@ func TestSetInt(t *testing.T) {
props.SetInt(key, value)
assert.Equal(t, value, props.GetInt(key))
}
func TestLoadBadFile(t *testing.T) {
_, err := LoadProperties("nosuchfile")
assert.NotNil(t, err)
}

View File

@@ -10,8 +10,10 @@ import (
func TestShrinkDeadlineLess(t *testing.T) {
deadline := time.Now().Add(time.Second)
ctx, _ := context.WithDeadline(context.Background(), deadline)
ctx, _ = ShrinkDeadline(ctx, time.Minute)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
ctx, cancel = ShrinkDeadline(ctx, time.Minute)
defer cancel()
dl, ok := ctx.Deadline()
assert.True(t, ok)
assert.Equal(t, deadline, dl)
@@ -19,8 +21,10 @@ func TestShrinkDeadlineLess(t *testing.T) {
func TestShrinkDeadlineMore(t *testing.T) {
deadline := time.Now().Add(time.Minute)
ctx, _ := context.WithDeadline(context.Background(), deadline)
ctx, _ = ShrinkDeadline(ctx, time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel()
ctx, cancel = ShrinkDeadline(ctx, time.Second)
defer cancel()
dl, ok := ctx.Deadline()
assert.True(t, ok)
assert.True(t, dl.Before(deadline))

View File

@@ -12,7 +12,8 @@ func TestContextCancel(t *testing.T) {
c := context.WithValue(context.Background(), "key", "value")
c1, cancel := context.WithCancel(c)
o := ValueOnlyFrom(c1)
c2, _ := context.WithCancel(o)
c2, cancel2 := context.WithCancel(o)
defer cancel2()
contexts := []context.Context{c1, c2}
for _, c := range contexts {
@@ -35,7 +36,8 @@ func TestContextCancel(t *testing.T) {
}
func TestContextDeadline(t *testing.T) {
c, _ := context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond))
c, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond))
cancel()
o := ValueOnlyFrom(c)
select {
case <-time.After(100 * time.Millisecond):
@@ -43,9 +45,11 @@ func TestContextDeadline(t *testing.T) {
t.Fatal("ValueOnlyContext: context should not have timed out")
}
c, _ = context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond))
c, cancel = context.WithDeadline(context.Background(), time.Now().Add(10*time.Millisecond))
cancel()
o = ValueOnlyFrom(c)
c, _ = context.WithDeadline(o, time.Now().Add(20*time.Millisecond))
c, cancel = context.WithDeadline(o, time.Now().Add(20*time.Millisecond))
defer cancel()
select {
case <-time.After(100 * time.Millisecond):
t.Fatal("ValueOnlyContext+Deadline: context should have timed out")

View File

@@ -8,7 +8,7 @@ import (
)
const (
indexOfKey = iota
_ = iota
indexOfId
)

View File

@@ -1,4 +1,3 @@
//go:generate mockgen -package internal -destination listener_mock.go -source listener.go Listener
package internal
type Listener interface {

View File

@@ -1,45 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: listener.go
// Package internal is a generated GoMock package.
package internal
import (
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockListener is a mock of Listener interface
type MockListener struct {
ctrl *gomock.Controller
recorder *MockListenerMockRecorder
}
// MockListenerMockRecorder is the mock recorder for MockListener
type MockListenerMockRecorder struct {
mock *MockListener
}
// NewMockListener creates a new mock instance
func NewMockListener(ctrl *gomock.Controller) *MockListener {
mock := &MockListener{ctrl: ctrl}
mock.recorder = &MockListenerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockListener) EXPECT() *MockListenerMockRecorder {
return m.recorder
}
// OnUpdate mocks base method
func (m *MockListener) OnUpdate(keys, values []string, newKey string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "OnUpdate", keys, values, newKey)
}
// OnUpdate indicates an expected call of OnUpdate
func (mr *MockListenerMockRecorder) OnUpdate(keys, values, newKey interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnUpdate", reflect.TypeOf((*MockListener)(nil).OnUpdate), keys, values, newKey)
}

View File

@@ -18,7 +18,8 @@ type (
disconnected bool
currentState connectivity.State
listeners []func()
lock sync.Mutex
// lock only guards listeners, because only listens can be accessed by other goroutines.
lock sync.Mutex
}
)
@@ -32,27 +33,33 @@ func (sw *stateWatcher) addListener(l func()) {
sw.lock.Unlock()
}
func (sw *stateWatcher) notifyListeners() {
sw.lock.Lock()
defer sw.lock.Unlock()
for _, l := range sw.listeners {
l()
}
}
func (sw *stateWatcher) updateState(conn etcdConn) {
sw.currentState = conn.GetState()
switch sw.currentState {
case connectivity.TransientFailure, connectivity.Shutdown:
sw.disconnected = true
case connectivity.Ready:
if sw.disconnected {
sw.disconnected = false
sw.notifyListeners()
}
}
}
func (sw *stateWatcher) watch(conn etcdConn) {
sw.currentState = conn.GetState()
for {
if conn.WaitForStateChange(context.Background(), sw.currentState) {
newState := conn.GetState()
sw.lock.Lock()
sw.currentState = newState
switch newState {
case connectivity.TransientFailure, connectivity.Shutdown:
sw.disconnected = true
case connectivity.Ready:
if sw.disconnected {
sw.disconnected = false
for _, l := range sw.listeners {
l()
}
}
}
sw.lock.Unlock()
sw.updateState(conn)
}
}
}

View File

@@ -35,11 +35,12 @@ spec:
- --listen-client-urls
- http://0.0.0.0:2379
- --advertise-client-urls
- http://etcd0:2379
- http://etcd0.discov:2379
- --initial-cluster
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
- --initial-cluster-state
- new
- --auto-compaction-retention=1
image: quay.io/coreos/etcd:latest
name: etcd0
ports:
@@ -106,11 +107,12 @@ spec:
- --listen-client-urls
- http://0.0.0.0:2379
- --advertise-client-urls
- http://etcd1:2379
- http://etcd1.discov:2379
- --initial-cluster
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
- --initial-cluster-state
- new
- --auto-compaction-retention=1
image: quay.io/coreos/etcd:latest
name: etcd1
ports:
@@ -177,11 +179,12 @@ spec:
- --listen-client-urls
- http://0.0.0.0:2379
- --advertise-client-urls
- http://etcd2:2379
- http://etcd2.discov:2379
- --initial-cluster
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
- --initial-cluster-state
- new
- --auto-compaction-retention=1
image: quay.io/coreos/etcd:latest
name: etcd2
ports:
@@ -248,11 +251,12 @@ spec:
- --listen-client-urls
- http://0.0.0.0:2379
- --advertise-client-urls
- http://etcd3:2379
- http://etcd3.discov:2379
- --initial-cluster
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
- --initial-cluster-state
- new
- --auto-compaction-retention=1
image: quay.io/coreos/etcd:latest
name: etcd3
ports:
@@ -319,11 +323,12 @@ spec:
- --listen-client-urls
- http://0.0.0.0:2379
- --advertise-client-urls
- http://etcd4:2379
- http://etcd4.discov:2379
- --initial-cluster
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
- --initial-cluster-state
- new
- --auto-compaction-retention=1
image: quay.io/coreos/etcd:latest
name: etcd4
ports:

View File

@@ -111,6 +111,10 @@ func TestPublisher_keepAliveAsyncQuit(t *testing.T) {
defer ctrl.Finish()
const id clientv3.LeaseID = 1
cli := internal.NewMockEtcdClient(ctrl)
cli.EXPECT().ActiveConnection()
cli.EXPECT().Close()
defer cli.Close()
cli.ActiveConnection()
restore := setMockClient(cli)
defer restore()
cli.EXPECT().Ctx().AnyTimes()

View File

@@ -1,21 +1,18 @@
package errorx
import "sync"
import "sync/atomic"
type AtomicError struct {
err error
lock sync.Mutex
err atomic.Value // error
}
func (ae *AtomicError) Set(err error) {
ae.lock.Lock()
ae.err = err
ae.lock.Unlock()
ae.err.Store(err)
}
func (ae *AtomicError) Load() error {
ae.lock.Lock()
err := ae.err
ae.lock.Unlock()
return err
if v := ae.err.Load(); v != nil {
return v.(error)
}
return nil
}

View File

@@ -2,6 +2,8 @@ package errorx
import (
"errors"
"sync"
"sync/atomic"
"testing"
"github.com/stretchr/testify/assert"
@@ -19,3 +21,53 @@ func TestAtomicErrorNil(t *testing.T) {
var err AtomicError
assert.Nil(t, err.Load())
}
func BenchmarkAtomicError(b *testing.B) {
var aerr AtomicError
wg := sync.WaitGroup{}
b.Run("Load", func(b *testing.B) {
var done uint32
go func() {
for {
if atomic.LoadUint32(&done) != 0 {
break
}
wg.Add(1)
go func() {
aerr.Set(errDummy)
wg.Done()
}()
}
}()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = aerr.Load()
}
b.StopTimer()
atomic.StoreUint32(&done, 1)
wg.Wait()
})
b.Run("Set", func(b *testing.B) {
var done uint32
go func() {
for {
if atomic.LoadUint32(&done) != 0 {
break
}
wg.Add(1)
go func() {
_ = aerr.Load()
wg.Done()
}()
}
}()
b.ResetTimer()
for i := 0; i < b.N; i++ {
aerr.Set(errDummy)
}
b.StopTimer()
atomic.StoreUint32(&done, 1)
wg.Wait()
})
}

11
core/errorx/callchain.go Normal file
View File

@@ -0,0 +1,11 @@
package errorx
func Chain(fns ...func() error) error {
for _, fn := range fns {
if err := fn(); err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,27 @@
package errorx
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
func TestChain(t *testing.T) {
var errDummy = errors.New("dummy")
assert.Nil(t, Chain(func() error {
return nil
}, func() error {
return nil
}))
assert.Equal(t, errDummy, Chain(func() error {
return errDummy
}, func() error {
return nil
}))
assert.Equal(t, errDummy, Chain(func() error {
return nil
}, func() error {
return errDummy
}))
}

View File

@@ -86,9 +86,7 @@ func TestBuldExecutorFlushSlowTasks(t *testing.T) {
time.Sleep(time.Millisecond * 100)
lock.Lock()
defer lock.Unlock()
for _, i := range tasks {
result = append(result, i)
}
result = append(result, tasks...)
}, WithBulkTasks(1000))
for i := 0; i < total; i++ {
assert.Nil(t, exec.Add(i))

View File

@@ -3,6 +3,7 @@ package executors
import (
"reflect"
"sync"
"sync/atomic"
"time"
"github.com/tal-tech/go-zero/core/lang"
@@ -35,6 +36,7 @@ type (
// avoid race condition on waitGroup when calling wg.Add/Done/Wait(...)
wgBarrier syncx.Barrier
confirmChan chan lang.PlaceholderType
inflight int32
guarded bool
newTicker func(duration time.Duration) timex.Ticker
lock sync.Mutex
@@ -82,6 +84,7 @@ func (pe *PeriodicalExecutor) Sync(fn func()) {
}
func (pe *PeriodicalExecutor) Wait() {
pe.Flush()
pe.wgBarrier.Guard(func() {
pe.waitGroup.Wait()
})
@@ -90,18 +93,16 @@ func (pe *PeriodicalExecutor) Wait() {
func (pe *PeriodicalExecutor) addAndCheck(task interface{}) (interface{}, bool) {
pe.lock.Lock()
defer func() {
var start bool
if !pe.guarded {
pe.guarded = true
start = true
// defer to unlock quickly
defer pe.backgroundFlush()
}
pe.lock.Unlock()
if start {
pe.backgroundFlush()
}
}()
if pe.container.AddTask(task) {
atomic.AddInt32(&pe.inflight, 1)
return pe.container.RemoveAll(), true
}
@@ -110,6 +111,9 @@ func (pe *PeriodicalExecutor) addAndCheck(task interface{}) (interface{}, bool)
func (pe *PeriodicalExecutor) backgroundFlush() {
threading.GoSafe(func() {
// flush before quit goroutine to avoid missing tasks
defer pe.Flush()
ticker := pe.newTicker(pe.interval)
defer ticker.Stop()
@@ -119,6 +123,7 @@ func (pe *PeriodicalExecutor) backgroundFlush() {
select {
case vals := <-pe.commander:
commanded = true
atomic.AddInt32(&pe.inflight, -1)
pe.enterExecution()
pe.confirmChan <- lang.Placeholder
pe.executeTasks(vals)
@@ -128,13 +133,7 @@ func (pe *PeriodicalExecutor) backgroundFlush() {
commanded = false
} else if pe.Flush() {
last = timex.Now()
} else if timex.Since(last) > pe.interval*idleRound {
pe.lock.Lock()
pe.guarded = false
pe.lock.Unlock()
// flush again to avoid missing tasks
pe.Flush()
} else if pe.shallQuit(last) {
return
}
}
@@ -177,3 +176,19 @@ func (pe *PeriodicalExecutor) hasTasks(tasks interface{}) bool {
return true
}
}
func (pe *PeriodicalExecutor) shallQuit(last time.Duration) (stop bool) {
if timex.Since(last) <= pe.interval*idleRound {
return
}
// checking pe.inflight and setting pe.guarded should be locked together
pe.lock.Lock()
if atomic.LoadInt32(&pe.inflight) == 0 {
pe.guarded = false
stop = true
}
pe.lock.Unlock()
return
}

View File

@@ -140,6 +140,26 @@ func TestPeriodicalExecutor_WaitFast(t *testing.T) {
assert.Equal(t, total, cnt)
}
func TestPeriodicalExecutor_Deadlock(t *testing.T) {
executor := NewBulkExecutor(func(tasks []interface{}) {
}, WithBulkTasks(1), WithBulkInterval(time.Millisecond))
for i := 0; i < 1e5; i++ {
executor.Add(1)
}
}
func TestPeriodicalExecutor_hasTasks(t *testing.T) {
ticker := timex.NewFakeTicker()
defer ticker.Stop()
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.True(t, exec.hasTasks(1))
}
// go test -benchtime 10s -bench .
func BenchmarkExecutor(b *testing.B) {
b.ReportAllocs()

View File

@@ -68,6 +68,7 @@ func Range(source <-chan interface{}) Stream {
}
// Buffer buffers the items into a queue with size n.
// It can balance the producer and the consumer if their processing throughput don't match.
func (p Stream) Buffer(n int) Stream {
if n < 0 {
n = 0
@@ -84,6 +85,14 @@ func (p Stream) Buffer(n int) Stream {
return Range(source)
}
// Count counts the number of elements in the result.
func (p Stream) Count() (count int) {
for range p.source {
count++
}
return
}
// Distinct removes the duplicated items base on the given KeyFunc.
func (p Stream) Distinct(fn KeyFunc) Stream {
source := make(chan interface{})
@@ -151,6 +160,10 @@ func (p Stream) Group(fn KeyFunc) Stream {
}
func (p Stream) Head(n int64) Stream {
if n < 1 {
panic("n must be greater than 0")
}
source := make(chan interface{})
go func() {
@@ -235,7 +248,37 @@ func (p Stream) Sort(less LessFunc) Stream {
return Just(items...)
}
// Split splits the elements into chunk with size up to n,
// might be less than n on tailing elements.
func (p Stream) Split(n int) Stream {
if n < 1 {
panic("n should be greater than 0")
}
source := make(chan interface{})
go func() {
var chunk []interface{}
for item := range p.source {
chunk = append(chunk, item)
if len(chunk) == n {
source <- chunk
chunk = nil
}
}
if chunk != nil {
source <- chunk
}
close(source)
}()
return Range(source)
}
func (p Stream) Tail(n int64) Stream {
if n < 1 {
panic("n should be greater than 0")
}
source := make(chan interface{})
go func() {

View File

@@ -49,6 +49,36 @@ func TestBufferNegative(t *testing.T) {
assert.Equal(t, 10, result)
}
func TestCount(t *testing.T) {
tests := []struct {
name string
elements []interface{}
}{
{
name: "no elements with nil",
},
{
name: "no elements",
elements: []interface{}{},
},
{
name: "1 element",
elements: []interface{}{1},
},
{
name: "multiple elements",
elements: []interface{}{1, 2, 3},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
val := Just(test.elements...).Count()
assert.Equal(t, len(test.elements), val)
})
}
}
func TestDone(t *testing.T) {
var count int32
Just(1, 2, 3).Walk(func(item interface{}, pipe chan<- interface{}) {
@@ -139,6 +169,14 @@ func TestHead(t *testing.T) {
assert.Equal(t, 3, result)
}
func TestHeadZero(t *testing.T) {
assert.Panics(t, func() {
Just(1, 2, 3, 4).Head(0).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
return nil, nil
})
})
}
func TestHeadMore(t *testing.T) {
var result int
Just(1, 2, 3, 4).Head(6).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
@@ -245,6 +283,22 @@ func TestSort(t *testing.T) {
})
}
func TestSplit(t *testing.T) {
assert.Panics(t, func() {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).Split(0).Done()
})
var chunks [][]interface{}
Just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).Split(4).ForEach(func(item interface{}) {
chunk := item.([]interface{})
chunks = append(chunks, chunk)
})
assert.EqualValues(t, [][]interface{}{
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10},
}, chunks)
}
func TestTail(t *testing.T) {
var result int
Just(1, 2, 3, 4).Tail(2).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
@@ -256,6 +310,14 @@ func TestTail(t *testing.T) {
assert.Equal(t, 7, result)
}
func TestTailZero(t *testing.T) {
assert.Panics(t, func() {
Just(1, 2, 3, 4).Tail(0).Reduce(func(pipe <-chan interface{}) (interface{}, error) {
return nil, nil
})
})
}
func TestWalk(t *testing.T) {
var result int
Just(1, 2, 3, 4, 5).Walk(func(item interface{}, pipe chan<- interface{}) {

23
core/iox/pipe.go Normal file
View File

@@ -0,0 +1,23 @@
package iox
import "os"
// RedirectInOut redirects stdin to r, stdout to w, and callers need to call restore afterwards.
func RedirectInOut() (restore func(), err error) {
var r, w *os.File
r, w, err = os.Pipe()
if err != nil {
return
}
ow := os.Stdout
os.Stdout = w
or := os.Stdin
os.Stdin = r
restore = func() {
os.Stdin = or
os.Stdout = ow
}
return
}

13
core/iox/pipe_test.go Normal file
View File

@@ -0,0 +1,13 @@
package iox
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRedirectInOut(t *testing.T) {
restore, err := RedirectInOut()
assert.Nil(t, err)
defer restore()
}

View File

@@ -3,9 +3,10 @@ package limit
import (
"testing"
"github.com/alicebob/miniredis"
"github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
)
func TestPeriodLimit_Take(t *testing.T) {
@@ -33,16 +34,16 @@ func TestPeriodLimit_RedisUnavailable(t *testing.T) {
}
func testPeriodLimit(t *testing.T, opts ...LimitOption) {
s, err := miniredis.Run()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer s.Close()
defer clean()
const (
seconds = 1
total = 100
quota = 5
)
l := NewPeriodLimit(seconds, quota, redis.NewRedis(s.Addr(), redis.NodeType), "periodlimit", opts...)
l := NewPeriodLimit(seconds, quota, store, "periodlimit", opts...)
var allowed, hitQuota, overQuota int
for i := 0; i < total; i++ {
val, err := l.Take("first")

View File

@@ -153,13 +153,10 @@ func (lim *TokenLimiter) waitForRedis() {
lim.rescueLock.Unlock()
}()
for {
select {
case <-ticker.C:
if lim.store.Ping() {
atomic.StoreUint32(&lim.redisAlive, 1)
return
}
for range ticker.C {
if lim.store.Ping() {
atomic.StoreUint32(&lim.redisAlive, 1)
return
}
}
}

View File

@@ -4,10 +4,11 @@ import (
"testing"
"time"
"github.com/alicebob/miniredis"
"github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
)
func init() {
@@ -44,16 +45,16 @@ func TestTokenLimit_Rescue(t *testing.T) {
}
func TestTokenLimit_Take(t *testing.T) {
s, err := miniredis.Run()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer s.Close()
defer clean()
const (
total = 100
rate = 5
burst = 10
)
l := NewTokenLimiter(rate, burst, redis.NewRedis(s.Addr(), redis.NodeType), "tokenlimit")
l := NewTokenLimiter(rate, burst, store, "tokenlimit")
var allowed int
for i := 0; i < total; i++ {
time.Sleep(time.Second / time.Duration(total))
@@ -66,16 +67,16 @@ func TestTokenLimit_Take(t *testing.T) {
}
func TestTokenLimit_TakeBurst(t *testing.T) {
s, err := miniredis.Run()
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer s.Close()
defer clean()
const (
total = 100
rate = 5
burst = 10
)
l := NewTokenLimiter(rate, burst, redis.NewRedis(s.Addr(), redis.NodeType), "tokenlimit")
l := NewTokenLimiter(rate, burst, store, "tokenlimit")
var allowed int
for i := 0; i < total; i++ {
if l.Allow() {

View File

@@ -135,6 +135,7 @@ func TestAdaptiveShedderShouldDrop(t *testing.T) {
passCounter: passCounter,
rtCounter: rtCounter,
windows: buckets,
dropTime: syncx.NewAtomicDuration(),
droppedRecently: syncx.NewAtomicBool(),
}
// cpu >= 800, inflight < maxPass
@@ -160,6 +161,40 @@ func TestAdaptiveShedderShouldDrop(t *testing.T) {
}
shedder.avgFlying = 80
assert.False(t, shedder.shouldDrop())
// cpu >= 800, inflight < maxPass
systemOverloadChecker = func(int64) bool {
return true
}
shedder.avgFlying = 80
shedder.flying = 80
_, err := shedder.Allow()
assert.NotNil(t, err)
}
func TestAdaptiveShedderStillHot(t *testing.T) {
logx.Disable()
passCounter := newRollingWindow()
rtCounter := newRollingWindow()
for i := 0; i < 10; i++ {
if i > 0 {
time.Sleep(bucketDuration)
}
passCounter.Add(float64((i + 1) * 100))
for j := i*10 + 1; j <= i*10+10; j++ {
rtCounter.Add(float64(j))
}
}
shedder := &adaptiveShedder{
passCounter: passCounter,
rtCounter: rtCounter,
windows: buckets,
dropTime: syncx.NewAtomicDuration(),
droppedRecently: syncx.ForAtomicBool(true),
}
assert.False(t, shedder.stillHot())
shedder.dropTime.Set(-coolOffDuration * 2)
assert.False(t, shedder.stillHot())
}
func BenchmarkAdaptiveShedder_Allow(b *testing.B) {

View File

@@ -13,3 +13,8 @@ func TestGroup(t *testing.T) {
assert.NotNil(t, limiter)
})
}
func TestShedderClose(t *testing.T) {
var nop nopCloser
assert.Nil(t, nop.Close())
}

View File

@@ -8,55 +8,60 @@ import (
"github.com/tal-tech/go-zero/core/timex"
)
const customCallerDepth = 3
const durationCallerDepth = 3
type customLog logEntry
type durationLogger logEntry
func WithDuration(d time.Duration) Logger {
return customLog{
return &durationLogger{
Duration: timex.ReprOfDuration(d),
}
}
func (l customLog) Error(v ...interface{}) {
func (l *durationLogger) Error(v ...interface{}) {
if shouldLog(ErrorLevel) {
l.write(errorLog, levelError, formatWithCaller(fmt.Sprint(v...), customCallerDepth))
l.write(errorLog, levelError, formatWithCaller(fmt.Sprint(v...), durationCallerDepth))
}
}
func (l customLog) Errorf(format string, v ...interface{}) {
func (l *durationLogger) Errorf(format string, v ...interface{}) {
if shouldLog(ErrorLevel) {
l.write(errorLog, levelError, formatWithCaller(fmt.Sprintf(format, v...), customCallerDepth))
l.write(errorLog, levelError, formatWithCaller(fmt.Sprintf(format, v...), durationCallerDepth))
}
}
func (l customLog) Info(v ...interface{}) {
func (l *durationLogger) Info(v ...interface{}) {
if shouldLog(InfoLevel) {
l.write(infoLog, levelInfo, fmt.Sprint(v...))
}
}
func (l customLog) Infof(format string, v ...interface{}) {
func (l *durationLogger) Infof(format string, v ...interface{}) {
if shouldLog(InfoLevel) {
l.write(infoLog, levelInfo, fmt.Sprintf(format, v...))
}
}
func (l customLog) Slow(v ...interface{}) {
func (l *durationLogger) Slow(v ...interface{}) {
if shouldLog(ErrorLevel) {
l.write(slowLog, levelSlow, fmt.Sprint(v...))
}
}
func (l customLog) Slowf(format string, v ...interface{}) {
func (l *durationLogger) Slowf(format string, v ...interface{}) {
if shouldLog(ErrorLevel) {
l.write(slowLog, levelSlow, fmt.Sprintf(format, v...))
}
}
func (l customLog) write(writer io.Writer, level, content string) {
func (l *durationLogger) WithDuration(duration time.Duration) Logger {
l.Duration = timex.ReprOfDuration(duration)
return l
}
func (l *durationLogger) write(writer io.Writer, level, content string) {
l.Timestamp = getTimestamp()
l.Level = level
l.Content = content
outputJson(writer, logEntry(l))
outputJson(writer, logEntry(*l))
}

View File

@@ -0,0 +1,52 @@
package logx
import (
"log"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestWithDurationError(t *testing.T) {
var builder strings.Builder
log.SetOutput(&builder)
WithDuration(time.Second).Error("foo")
assert.True(t, strings.Contains(builder.String(), "duration"), builder.String())
}
func TestWithDurationErrorf(t *testing.T) {
var builder strings.Builder
log.SetOutput(&builder)
WithDuration(time.Second).Errorf("foo")
assert.True(t, strings.Contains(builder.String(), "duration"), builder.String())
}
func TestWithDurationInfo(t *testing.T) {
var builder strings.Builder
log.SetOutput(&builder)
WithDuration(time.Second).Info("foo")
assert.True(t, strings.Contains(builder.String(), "duration"), builder.String())
}
func TestWithDurationInfof(t *testing.T) {
var builder strings.Builder
log.SetOutput(&builder)
WithDuration(time.Second).Infof("foo")
assert.True(t, strings.Contains(builder.String(), "duration"), builder.String())
}
func TestWithDurationSlow(t *testing.T) {
var builder strings.Builder
log.SetOutput(&builder)
WithDuration(time.Second).Slow("foo")
assert.True(t, strings.Contains(builder.String(), "duration"), builder.String())
}
func TestWithDurationSlowf(t *testing.T) {
var builder strings.Builder
log.SetOutput(&builder)
WithDuration(time.Second).WithDuration(time.Hour).Slowf("foo")
assert.True(t, strings.Contains(builder.String(), "duration"), builder.String())
}

View File

@@ -15,6 +15,7 @@ import (
"strings"
"sync"
"sync/atomic"
"time"
"github.com/tal-tech/go-zero/core/iox"
"github.com/tal-tech/go-zero/core/sysx"
@@ -42,6 +43,7 @@ const (
consoleMode = "console"
volumeMode = "volume"
levelAlert = "alert"
levelInfo = "info"
levelError = "error"
levelSevere = "severe"
@@ -96,6 +98,7 @@ type (
Infof(string, ...interface{})
Slow(...interface{})
Slowf(string, ...interface{})
WithDuration(time.Duration) Logger
}
)
@@ -119,6 +122,10 @@ func SetUp(c LogConf) error {
}
}
func Alert(v string) {
output(errorLog, levelAlert, v)
}
func Close() error {
if writeConsole {
return nil

View File

@@ -6,8 +6,10 @@ import (
"io"
"io/ioutil"
"log"
"os"
"runtime"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
@@ -21,10 +23,13 @@ var (
)
type mockWriter struct {
lock sync.Mutex
builder strings.Builder
}
func (mw *mockWriter) Write(data []byte) (int, error) {
mw.lock.Lock()
defer mw.lock.Unlock()
return mw.builder.Write(data)
}
@@ -32,12 +37,22 @@ func (mw *mockWriter) Close() error {
return nil
}
func (mw *mockWriter) Contains(text string) bool {
mw.lock.Lock()
defer mw.lock.Unlock()
return strings.Contains(mw.builder.String(), text)
}
func (mw *mockWriter) Reset() {
mw.lock.Lock()
defer mw.lock.Unlock()
mw.builder.Reset()
}
func (mw *mockWriter) Contains(text string) bool {
return strings.Contains(mw.builder.String(), text)
func (mw *mockWriter) String() string {
mw.lock.Lock()
defer mw.lock.Unlock()
return mw.builder.String()
}
func TestFileLineFileMode(t *testing.T) {
@@ -69,6 +84,14 @@ func TestFileLineConsoleMode(t *testing.T) {
assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
}
func TestStructedLogAlert(t *testing.T) {
doTestStructedLog(t, levelAlert, func(writer io.WriteCloser) {
errorLog = writer
}, func(v ...interface{}) {
Alert(fmt.Sprint(v...))
})
}
func TestStructedLogInfo(t *testing.T) {
doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) {
infoLog = writer
@@ -85,6 +108,46 @@ func TestStructedLogSlow(t *testing.T) {
})
}
func TestStructedLogSlowf(t *testing.T) {
doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
slowLog = writer
}, func(v ...interface{}) {
Slowf(fmt.Sprint(v...))
})
}
func TestStructedLogStat(t *testing.T) {
doTestStructedLog(t, levelStat, func(writer io.WriteCloser) {
statLog = writer
}, func(v ...interface{}) {
Stat(v...)
})
}
func TestStructedLogStatf(t *testing.T) {
doTestStructedLog(t, levelStat, func(writer io.WriteCloser) {
statLog = writer
}, func(v ...interface{}) {
Statf(fmt.Sprint(v...))
})
}
func TestStructedLogSevere(t *testing.T) {
doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) {
severeLog = writer
}, func(v ...interface{}) {
Severe(v...)
})
}
func TestStructedLogSeveref(t *testing.T) {
doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) {
severeLog = writer
}, func(v ...interface{}) {
Severef(fmt.Sprint(v...))
})
}
func TestStructedLogWithDuration(t *testing.T) {
const message = "hello there"
writer := new(mockWriter)
@@ -135,6 +198,68 @@ func TestMustNil(t *testing.T) {
Must(nil)
}
func TestSetup(t *testing.T) {
MustSetup(LogConf{
ServiceName: "any",
Mode: "console",
})
MustSetup(LogConf{
ServiceName: "any",
Mode: "file",
Path: os.TempDir(),
})
MustSetup(LogConf{
ServiceName: "any",
Mode: "volume",
Path: os.TempDir(),
})
assert.NotNil(t, setupWithVolume(LogConf{}))
assert.NotNil(t, setupWithFiles(LogConf{}))
assert.Nil(t, setupWithFiles(LogConf{
ServiceName: "any",
Path: os.TempDir(),
Compress: true,
KeepDays: 1,
}))
setupLogLevel(LogConf{
Level: levelInfo,
})
setupLogLevel(LogConf{
Level: levelError,
})
setupLogLevel(LogConf{
Level: levelSevere,
})
_, err := createOutput("")
assert.NotNil(t, err)
Disable()
}
func TestDisable(t *testing.T) {
Disable()
var opt logOptions
WithKeepDays(1)(&opt)
WithGzip()(&opt)
assert.Nil(t, Close())
writeConsole = false
assert.Nil(t, Close())
}
func TestWithGzip(t *testing.T) {
fn := WithGzip()
var opt logOptions
fn(&opt)
assert.True(t, opt.gzipEnabled)
}
func TestWithKeepDays(t *testing.T) {
fn := WithKeepDays(1)
var opt logOptions
fn(&opt)
assert.Equal(t, 1, opt.keepDays)
}
func BenchmarkCopyByteSliceAppend(b *testing.B) {
for i := 0; i < b.N; i++ {
var buf []byte
@@ -232,7 +357,7 @@ func doTestStructedLog(t *testing.T, level string, setup func(writer io.WriteClo
t.Error(err)
}
assert.Equal(t, level, entry.Level)
assert.Equal(t, message, entry.Content)
assert.True(t, strings.Contains(entry.Content, message))
}
func testSetLevelTwiceWithMode(t *testing.T, mode string) {
@@ -252,4 +377,10 @@ func testSetLevelTwiceWithMode(t *testing.T, mode string) {
atomic.StoreUint32(&initialized, 1)
Info(message)
assert.Equal(t, 0, writer.builder.Len())
Infof(message)
assert.Equal(t, 0, writer.builder.Len())
ErrorStack(message)
assert.Equal(t, 0, writer.builder.Len())
ErrorStackf(message)
assert.Equal(t, 0, writer.builder.Len())
}

View File

@@ -192,14 +192,16 @@ func (l *RotateLogger) init() error {
}
func (l *RotateLogger) maybeCompressFile(file string) {
if l.compress {
defer func() {
if r := recover(); r != nil {
ErrorStack(r)
}
}()
compressLogFile(file)
if !l.compress {
return
}
defer func() {
if r := recover(); r != nil {
ErrorStack(r)
}
}()
compressLogFile(file)
}
func (l *RotateLogger) maybeDeleteOutdatedFiles() {

View File

@@ -0,0 +1,119 @@
package logx
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/fs"
)
func TestDailyRotateRuleMarkRotated(t *testing.T) {
var rule DailyRotateRule
rule.MarkRotated()
assert.Equal(t, getNowDate(), rule.rotatedTime)
}
func TestDailyRotateRuleOutdatedFiles(t *testing.T) {
var rule DailyRotateRule
assert.Empty(t, rule.OutdatedFiles())
rule.days = 1
assert.Empty(t, rule.OutdatedFiles())
rule.gzip = true
assert.Empty(t, rule.OutdatedFiles())
}
func TestDailyRotateRuleShallRotate(t *testing.T) {
var rule DailyRotateRule
rule.rotatedTime = time.Now().Add(time.Hour * 24).Format(dateFormat)
assert.True(t, rule.ShallRotate())
}
func TestRotateLoggerClose(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
if len(filename) > 0 {
defer os.Remove(filename)
}
logger, err := NewLogger(filename, new(DailyRotateRule), false)
assert.Nil(t, err)
assert.Nil(t, logger.Close())
}
func TestRotateLoggerGetBackupFilename(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
if len(filename) > 0 {
defer os.Remove(filename)
}
logger, err := NewLogger(filename, new(DailyRotateRule), false)
assert.Nil(t, err)
assert.True(t, len(logger.getBackupFilename()) > 0)
logger.backup = ""
assert.True(t, len(logger.getBackupFilename()) > 0)
}
func TestRotateLoggerMayCompressFile(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
if len(filename) > 0 {
defer os.Remove(filename)
}
logger, err := NewLogger(filename, new(DailyRotateRule), false)
assert.Nil(t, err)
logger.maybeCompressFile(filename)
_, err = os.Stat(filename)
assert.Nil(t, err)
}
func TestRotateLoggerMayCompressFileTrue(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
logger, err := NewLogger(filename, new(DailyRotateRule), true)
assert.Nil(t, err)
if len(filename) > 0 {
defer func() {
os.Remove(filename)
os.Remove(filepath.Base(logger.getBackupFilename()) + ".gz")
}()
}
logger.maybeCompressFile(filename)
_, err = os.Stat(filename)
assert.NotNil(t, err)
}
func TestRotateLoggerRotate(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
logger, err := NewLogger(filename, new(DailyRotateRule), true)
assert.Nil(t, err)
if len(filename) > 0 {
defer func() {
os.Remove(filename)
os.Remove(logger.getBackupFilename())
os.Remove(filepath.Base(logger.getBackupFilename()) + ".gz")
}()
}
err = logger.rotate()
assert.Nil(t, err)
}
func TestRotateLoggerWrite(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
rule := new(DailyRotateRule)
logger, err := NewLogger(filename, rule, true)
assert.Nil(t, err)
if len(filename) > 0 {
defer func() {
os.Remove(filename)
os.Remove(logger.getBackupFilename())
os.Remove(filepath.Base(logger.getBackupFilename()) + ".gz")
}()
}
logger.write([]byte(`foo`))
rule.rotatedTime = time.Now().Add(-time.Hour * 24).Format(dateFormat)
logger.write([]byte(`bar`))
}

View File

@@ -15,7 +15,7 @@ const testlog = "Stay hungry, stay foolish."
func TestCollectSysLog(t *testing.T) {
CollectSysLog()
content := getContent(captureOutput(func() {
log.Printf(testlog)
log.Print(testlog)
}))
assert.True(t, strings.Contains(content, testlog))
}
@@ -33,10 +33,10 @@ func captureOutput(f func()) string {
writer := new(mockWriter)
infoLog = writer
prevLevel := logLevel
logLevel = InfoLevel
prevLevel := atomic.LoadUint32(&logLevel)
SetLevel(InfoLevel)
f()
logLevel = prevLevel
SetLevel(prevLevel)
return writer.builder.String()
}

View File

@@ -1,49 +0,0 @@
package logx
import (
"context"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/trace/tracespec"
)
const (
mockTraceId = "mock-trace-id"
mockSpanId = "mock-span-id"
)
var mock tracespec.Trace = new(mockTrace)
func TestTraceLog(t *testing.T) {
var buf strings.Builder
ctx := context.WithValue(context.Background(), tracespec.TracingKey, mock)
WithContext(ctx).(tracingEntry).write(&buf, levelInfo, testlog)
assert.True(t, strings.Contains(buf.String(), mockTraceId))
assert.True(t, strings.Contains(buf.String(), mockSpanId))
}
type mockTrace struct{}
func (t mockTrace) TraceId() string {
return mockTraceId
}
func (t mockTrace) SpanId() string {
return mockSpanId
}
func (t mockTrace) Finish() {
}
func (t mockTrace) Fork(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) {
return nil, nil
}
func (t mockTrace) Follow(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) {
return nil, nil
}
func (t mockTrace) Visit(fn func(key string, val string) bool) {
}

View File

@@ -4,54 +4,61 @@ import (
"context"
"fmt"
"io"
"time"
"github.com/tal-tech/go-zero/core/timex"
"github.com/tal-tech/go-zero/core/trace/tracespec"
)
type tracingEntry struct {
type traceLogger struct {
logEntry
Trace string `json:"trace,omitempty"`
Span string `json:"span,omitempty"`
ctx context.Context `json:"-"`
Trace string `json:"trace,omitempty"`
Span string `json:"span,omitempty"`
ctx context.Context
}
func (l tracingEntry) Error(v ...interface{}) {
func (l *traceLogger) Error(v ...interface{}) {
if shouldLog(ErrorLevel) {
l.write(errorLog, levelError, formatWithCaller(fmt.Sprint(v...), customCallerDepth))
l.write(errorLog, levelError, formatWithCaller(fmt.Sprint(v...), durationCallerDepth))
}
}
func (l tracingEntry) Errorf(format string, v ...interface{}) {
func (l *traceLogger) Errorf(format string, v ...interface{}) {
if shouldLog(ErrorLevel) {
l.write(errorLog, levelError, formatWithCaller(fmt.Sprintf(format, v...), customCallerDepth))
l.write(errorLog, levelError, formatWithCaller(fmt.Sprintf(format, v...), durationCallerDepth))
}
}
func (l tracingEntry) Info(v ...interface{}) {
func (l *traceLogger) Info(v ...interface{}) {
if shouldLog(InfoLevel) {
l.write(infoLog, levelInfo, fmt.Sprint(v...))
}
}
func (l tracingEntry) Infof(format string, v ...interface{}) {
func (l *traceLogger) Infof(format string, v ...interface{}) {
if shouldLog(InfoLevel) {
l.write(infoLog, levelInfo, fmt.Sprintf(format, v...))
}
}
func (l tracingEntry) Slow(v ...interface{}) {
func (l *traceLogger) Slow(v ...interface{}) {
if shouldLog(ErrorLevel) {
l.write(slowLog, levelSlow, fmt.Sprint(v...))
}
}
func (l tracingEntry) Slowf(format string, v ...interface{}) {
func (l *traceLogger) Slowf(format string, v ...interface{}) {
if shouldLog(ErrorLevel) {
l.write(slowLog, levelSlow, fmt.Sprintf(format, v...))
}
}
func (l tracingEntry) write(writer io.Writer, level, content string) {
func (l *traceLogger) WithDuration(duration time.Duration) Logger {
l.Duration = timex.ReprOfDuration(duration)
return l
}
func (l *traceLogger) write(writer io.Writer, level, content string) {
l.Timestamp = getTimestamp()
l.Level = level
l.Content = content
@@ -61,7 +68,7 @@ func (l tracingEntry) write(writer io.Writer, level, content string) {
}
func WithContext(ctx context.Context) Logger {
return tracingEntry{
return &traceLogger{
ctx: ctx,
}
}

View File

@@ -0,0 +1,116 @@
package logx
import (
"context"
"log"
"strings"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/trace/tracespec"
)
const (
mockTraceId = "mock-trace-id"
mockSpanId = "mock-span-id"
)
var mock tracespec.Trace = new(mockTrace)
func TestTraceLog(t *testing.T) {
var buf mockWriter
atomic.StoreUint32(&initialized, 1)
ctx := context.WithValue(context.Background(), tracespec.TracingKey, mock)
WithContext(ctx).(*traceLogger).write(&buf, levelInfo, testlog)
assert.True(t, strings.Contains(buf.String(), mockTraceId))
assert.True(t, strings.Contains(buf.String(), mockSpanId))
}
func TestTraceError(t *testing.T) {
var buf mockWriter
atomic.StoreUint32(&initialized, 1)
errorLog = newLogWriter(log.New(&buf, "", flags))
ctx := context.WithValue(context.Background(), tracespec.TracingKey, mock)
l := WithContext(ctx).(*traceLogger)
SetLevel(InfoLevel)
l.WithDuration(time.Second).Error(testlog)
assert.True(t, strings.Contains(buf.String(), mockTraceId))
assert.True(t, strings.Contains(buf.String(), mockSpanId))
buf.Reset()
l.WithDuration(time.Second).Errorf(testlog)
assert.True(t, strings.Contains(buf.String(), mockTraceId))
assert.True(t, strings.Contains(buf.String(), mockSpanId))
}
func TestTraceInfo(t *testing.T) {
var buf mockWriter
atomic.StoreUint32(&initialized, 1)
infoLog = newLogWriter(log.New(&buf, "", flags))
ctx := context.WithValue(context.Background(), tracespec.TracingKey, mock)
l := WithContext(ctx).(*traceLogger)
SetLevel(InfoLevel)
l.WithDuration(time.Second).Info(testlog)
assert.True(t, strings.Contains(buf.String(), mockTraceId))
assert.True(t, strings.Contains(buf.String(), mockSpanId))
buf.Reset()
l.WithDuration(time.Second).Infof(testlog)
assert.True(t, strings.Contains(buf.String(), mockTraceId))
assert.True(t, strings.Contains(buf.String(), mockSpanId))
}
func TestTraceSlow(t *testing.T) {
var buf mockWriter
atomic.StoreUint32(&initialized, 1)
slowLog = newLogWriter(log.New(&buf, "", flags))
ctx := context.WithValue(context.Background(), tracespec.TracingKey, mock)
l := WithContext(ctx).(*traceLogger)
SetLevel(InfoLevel)
l.WithDuration(time.Second).Slow(testlog)
assert.True(t, strings.Contains(buf.String(), mockTraceId))
assert.True(t, strings.Contains(buf.String(), mockSpanId))
buf.Reset()
l.WithDuration(time.Second).Slowf(testlog)
assert.True(t, strings.Contains(buf.String(), mockTraceId))
assert.True(t, strings.Contains(buf.String(), mockSpanId))
}
func TestTraceWithoutContext(t *testing.T) {
var buf mockWriter
atomic.StoreUint32(&initialized, 1)
infoLog = newLogWriter(log.New(&buf, "", flags))
l := WithContext(context.Background()).(*traceLogger)
SetLevel(InfoLevel)
l.WithDuration(time.Second).Info(testlog)
assert.False(t, strings.Contains(buf.String(), mockTraceId))
assert.False(t, strings.Contains(buf.String(), mockSpanId))
buf.Reset()
l.WithDuration(time.Second).Infof(testlog)
assert.False(t, strings.Contains(buf.String(), mockTraceId))
assert.False(t, strings.Contains(buf.String(), mockSpanId))
}
type mockTrace struct{}
func (t mockTrace) TraceId() string {
return mockTraceId
}
func (t mockTrace) SpanId() string {
return mockSpanId
}
func (t mockTrace) Finish() {
}
func (t mockTrace) Fork(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) {
return nil, nil
}
func (t mockTrace) Follow(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) {
return nil, nil
}
func (t mockTrace) Visit(fn func(key string, val string) bool) {
}

View File

@@ -0,0 +1,31 @@
package mapping
import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
)
type Bar struct {
Val string `json:"val"`
}
func TestFieldOptionOptionalDep(t *testing.T) {
var bar Bar
rt := reflect.TypeOf(bar)
for i := 0; i < rt.NumField(); i++ {
field := rt.Field(i)
val, opt, err := parseKeyAndOptions(jsonTagKey, field)
assert.Equal(t, "val", val)
assert.Nil(t, opt)
assert.Nil(t, err)
}
// check nil working
var o *fieldOptions
check := func(o *fieldOptions) {
assert.Equal(t, 0, len(o.optionalDep()))
}
check(o)
}

View File

@@ -23,6 +23,7 @@ const (
var (
errTypeMismatch = errors.New("type mismatch")
errValueNotSettable = errors.New("value is not settable")
errValueNotStruct = errors.New("value type is not struct")
keyUnmarshaler = NewUnmarshaler(defaultKeyName)
cacheKeys atomic.Value
cacheKeysLock sync.Mutex
@@ -80,6 +81,10 @@ func (u *Unmarshaler) unmarshalWithFullName(m Valuer, v interface{}, fullName st
}
rte := reflect.TypeOf(v).Elem()
if rte.Kind() != reflect.Struct {
return errValueNotStruct
}
rve := rv.Elem()
numFields := rte.NumField()
for i := 0; i < numFields; i++ {
@@ -345,7 +350,7 @@ func (u *Unmarshaler) processNamedFieldWithValue(field reflect.StructField, valu
options := opts.options()
if len(options) > 0 {
if !stringx.Contains(options, mapValue.(string)) {
return fmt.Errorf(`error: value "%s" for field "%s" is not defined in opts "%v"`,
return fmt.Errorf(`error: value "%s" for field "%s" is not defined in options "%v"`,
mapValue, key, options)
}
}

View File

@@ -14,6 +14,13 @@ import (
// so we only can test to 62 bits.
const maxUintBitsToTest = 62
func TestUnmarshalWithFullNameNotStruct(t *testing.T) {
var s map[string]interface{}
content := []byte(`{"name":"xiaoming"}`)
err := UnmarshalJsonBytes(content, &s)
assert.Equal(t, errValueNotStruct, err)
}
func TestUnmarshalWithoutTagName(t *testing.T) {
type inner struct {
Optional bool `key:",optional"`
@@ -2380,6 +2387,13 @@ func TestUnmarshalNestedMapSimpleTypeMatch(t *testing.T) {
assert.Equal(t, "1", c.Anything["id"])
}
func TestUnmarshalValuer(t *testing.T) {
unmarshaler := NewUnmarshaler(jsonTagKey)
var foo string
err := unmarshaler.UnmarshalValuer(nil, foo)
assert.NotNil(t, err)
}
func BenchmarkUnmarshalString(b *testing.B) {
type inner struct {
Value string `key:"value"`

View File

@@ -153,58 +153,57 @@ func doParseKeyAndOptions(field reflect.StructField, value string) (string, *fie
key := strings.TrimSpace(segments[0])
options := segments[1:]
if len(options) > 0 {
var fieldOpts fieldOptions
for _, segment := range options {
option := strings.TrimSpace(segment)
switch {
case option == stringOption:
fieldOpts.FromString = true
case strings.HasPrefix(option, optionalOption):
segs := strings.Split(option, equalToken)
switch len(segs) {
case 1:
fieldOpts.Optional = true
case 2:
fieldOpts.Optional = true
fieldOpts.OptionalDep = segs[1]
default:
return "", nil, fmt.Errorf("field %s has wrong optional", field.Name)
}
case option == optionalOption:
fieldOpts.Optional = true
case strings.HasPrefix(option, optionsOption):
segs := strings.Split(option, equalToken)
if len(segs) != 2 {
return "", nil, fmt.Errorf("field %s has wrong options", field.Name)
} else {
fieldOpts.Options = strings.Split(segs[1], optionSeparator)
}
case strings.HasPrefix(option, defaultOption):
segs := strings.Split(option, equalToken)
if len(segs) != 2 {
return "", nil, fmt.Errorf("field %s has wrong default option", field.Name)
} else {
fieldOpts.Default = strings.TrimSpace(segs[1])
}
case strings.HasPrefix(option, rangeOption):
segs := strings.Split(option, equalToken)
if len(segs) != 2 {
return "", nil, fmt.Errorf("field %s has wrong range", field.Name)
}
if nr, err := parseNumberRange(segs[1]); err != nil {
return "", nil, err
} else {
fieldOpts.Range = nr
}
}
}
return key, &fieldOpts, nil
if len(options) == 0 {
return key, nil, nil
}
return key, nil, nil
var fieldOpts fieldOptions
for _, segment := range options {
option := strings.TrimSpace(segment)
switch {
case option == stringOption:
fieldOpts.FromString = true
case strings.HasPrefix(option, optionalOption):
segs := strings.Split(option, equalToken)
switch len(segs) {
case 1:
fieldOpts.Optional = true
case 2:
fieldOpts.Optional = true
fieldOpts.OptionalDep = segs[1]
default:
return "", nil, fmt.Errorf("field %s has wrong optional", field.Name)
}
case option == optionalOption:
fieldOpts.Optional = true
case strings.HasPrefix(option, optionsOption):
segs := strings.Split(option, equalToken)
if len(segs) != 2 {
return "", nil, fmt.Errorf("field %s has wrong options", field.Name)
} else {
fieldOpts.Options = strings.Split(segs[1], optionSeparator)
}
case strings.HasPrefix(option, defaultOption):
segs := strings.Split(option, equalToken)
if len(segs) != 2 {
return "", nil, fmt.Errorf("field %s has wrong default option", field.Name)
} else {
fieldOpts.Default = strings.TrimSpace(segs[1])
}
case strings.HasPrefix(option, rangeOption):
segs := strings.Split(option, equalToken)
if len(segs) != 2 {
return "", nil, fmt.Errorf("field %s has wrong range", field.Name)
}
if nr, err := parseNumberRange(segs[1]); err != nil {
return "", nil, err
} else {
fieldOpts.Range = nr
}
}
}
return key, &fieldOpts, nil
}
func implicitValueRequiredStruct(tag string, tp reflect.Type) (bool, error) {

View File

@@ -31,6 +31,7 @@ func TestMaxInt(t *testing.T) {
}
for _, each := range cases {
each := each
t.Run(stringx.Rand(), func(t *testing.T) {
actual := MaxInt(each.a, each.b)
assert.Equal(t, each.expect, actual)

View File

@@ -16,7 +16,10 @@ const (
minWorkers = 1
)
var ErrCancelWithNil = errors.New("mapreduce cancelled with nil")
var (
ErrCancelWithNil = errors.New("mapreduce cancelled with nil")
ErrReduceNoOutput = errors.New("reduce not writing value")
)
type (
GenerateFunc func(source chan<- interface{})
@@ -76,7 +79,7 @@ func Map(generate GenerateFunc, mapper MapFunc, opts ...Option) chan interface{}
collector := make(chan interface{}, options.workers)
done := syncx.NewDoneChan()
go mapDispatcher(mapper, source, collector, done.Done(), options.workers)
go executeMappers(mapper, source, collector, done.Done(), options.workers)
return collector
}
@@ -93,7 +96,14 @@ func MapReduceWithSource(source <-chan interface{}, mapper MapperFunc, reducer R
collector := make(chan interface{}, options.workers)
done := syncx.NewDoneChan()
writer := newGuardedWriter(output, done.Done())
var closeOnce sync.Once
var retErr errorx.AtomicError
finish := func() {
closeOnce.Do(func() {
done.Close()
close(output)
})
}
cancel := once(func(err error) {
if err != nil {
retErr.Set(err)
@@ -102,19 +112,24 @@ func MapReduceWithSource(source <-chan interface{}, mapper MapperFunc, reducer R
}
drain(source)
done.Close()
close(output)
finish()
})
go func() {
defer func() {
if r := recover(); r != nil {
cancel(fmt.Errorf("%v", r))
} else {
finish()
}
}()
reducer(collector, writer, cancel)
drain(collector)
}()
go mapperDispatcher(mapper, source, collector, done.Done(), cancel, options.workers)
go executeMappers(func(item interface{}, w Writer) {
mapper(item, w, cancel)
}, source, collector, done.Done(), options.workers)
value, ok := <-output
if err := retErr.Load(); err != nil {
@@ -122,13 +137,14 @@ func MapReduceWithSource(source <-chan interface{}, mapper MapperFunc, reducer R
} else if ok {
return value, nil
} else {
return nil, nil
return nil, ErrReduceNoOutput
}
}
func MapReduceVoid(generator GenerateFunc, mapper MapperFunc, reducer VoidReducerFunc, opts ...Option) error {
_, err := MapReduce(generator, mapper, func(input <-chan interface{}, writer Writer, cancel func(error)) {
reducer(input, cancel)
drain(input)
// We need to write a placeholder to let MapReduce to continue on reducer done,
// otherwise, all goroutines are waiting. The placeholder will be discarded by MapReduce.
writer.Write(lang.Placeholder)
@@ -213,20 +229,6 @@ func executeMappers(mapper MapFunc, input <-chan interface{}, collector chan<- i
}
}
func mapDispatcher(mapper MapFunc, input <-chan interface{}, collector chan<- interface{},
done <-chan lang.PlaceholderType, workers int) {
executeMappers(func(item interface{}, writer Writer) {
mapper(item, writer)
}, input, collector, done, workers)
}
func mapperDispatcher(mapper MapperFunc, input <-chan interface{}, collector chan<- interface{},
done <-chan lang.PlaceholderType, cancel func(error), workers int) {
executeMappers(func(item interface{}, writer Writer) {
mapper(item, writer, cancel)
}, input, collector, done, workers)
}
func newOptions() *mapReduceOptions {
return &mapReduceOptions{
workers: defaultWorkers,

View File

@@ -378,6 +378,22 @@ func TestMapReduceVoidCancelWithRemains(t *testing.T) {
assert.True(t, done.True())
}
func TestMapReduceWithoutReducerWrite(t *testing.T) {
uids := []int{1, 2, 3}
res, err := MapReduce(func(source chan<- interface{}) {
for _, uid := range uids {
source <- uid
}
}, func(item interface{}, writer Writer, cancel func(error)) {
writer.Write(item)
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
drain(pipe)
// not calling writer.Write(...), should not panic
})
assert.Equal(t, ErrReduceNoOutput, err)
assert.Nil(t, res)
}
func BenchmarkMapReduce(b *testing.B) {
b.ReportAllocs()

View File

@@ -0,0 +1,16 @@
package proc
import (
"log"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestDumpGoroutines(t *testing.T) {
var buf strings.Builder
log.SetOutput(&buf)
dumpGoroutines()
assert.True(t, strings.Contains(buf.String(), ".dump"))
}

View File

@@ -26,10 +26,6 @@ var started uint32
// Profile represents an active profiling session.
type Profile struct {
// path holds the base path where various profiling files are written.
// If blank, the base path will be generated by ioutil.TempDir.
path string
// closers holds cleanup functions that run after each profile
closers []func()

21
core/proc/profile_test.go Normal file
View File

@@ -0,0 +1,21 @@
package proc
import (
"log"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestProfile(t *testing.T) {
var buf strings.Builder
log.SetOutput(&buf)
profiler := StartProfile()
// start again should not work
assert.NotNil(t, StartProfile())
profiler.Stop()
// stop twice
profiler.Stop()
assert.True(t, strings.Contains(buf.String(), ".pprof"))
}

View File

@@ -4,12 +4,14 @@ package proc
import "time"
// AddShutdownListener returns fn itself on windows, lets callers call fn on their own.
func AddShutdownListener(fn func()) func() {
return nil
return fn
}
// AddWrapUpListener returns fn itself on windows, lets callers call fn on their own.
func AddWrapUpListener(fn func()) func() {
return nil
return fn
}
func SetTimeoutToForceQuit(duration time.Duration) {

View File

@@ -32,7 +32,7 @@ func AddWrapUpListener(fn func()) (waitForCalled func()) {
return wrapUpListeners.addListener(fn)
}
func SetTimeoutToForceQuit(duration time.Duration) {
func SetTimeToForceQuit(duration time.Duration) {
delayTimeBeforeForceQuit = duration
}

View File

@@ -0,0 +1,16 @@
package prof
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestReport(t *testing.T) {
once.Do(func() {})
assert.NotContains(t, generateReport(), "foo")
report("foo", time.Second)
assert.Contains(t, generateReport(), "foo")
report("foo", time.Second)
}

View File

@@ -0,0 +1,23 @@
package prof
import (
"testing"
"github.com/tal-tech/go-zero/core/utils"
)
func TestProfiler(t *testing.T) {
EnableProfiling()
Start()
Report("foo", ProfilePoint{
ElapsedTimer: utils.NewElapsedTimer(),
})
}
func TestNullProfiler(t *testing.T) {
p := newNullProfiler()
p.Start()
p.Report("foo", ProfilePoint{
ElapsedTimer: utils.NewElapsedTimer(),
})
}

View File

@@ -27,7 +27,7 @@ func newMockedService(multiplier int) *mockedService {
func (s *mockedService) Start() {
mutex.Lock()
number = number * s.multiplier
number *= s.multiplier
mutex.Unlock()
done <- struct{}{}
<-s.quit

View File

@@ -11,6 +11,7 @@ import (
"time"
"github.com/tal-tech/go-zero/core/executors"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/proc"
"github.com/tal-tech/go-zero/core/sysx"
"github.com/tal-tech/go-zero/core/timex"
@@ -23,7 +24,7 @@ const (
)
var (
reporter func(string)
reporter = logx.Alert
lock sync.RWMutex
lessExecutor = executors.NewLessExecutor(time.Minute * 5)
dropped int32

22
core/stat/alert_test.go Normal file
View File

@@ -0,0 +1,22 @@
// +build linux
package stat
import (
"strconv"
"sync/atomic"
"testing"
"github.com/stretchr/testify/assert"
)
func TestReport(t *testing.T) {
var count int32
SetReporter(func(s string) {
atomic.AddInt32(&count, 1)
})
for i := 0; i < 10; i++ {
Report(strconv.Itoa(i))
}
assert.Equal(t, int32(1), count)
}

View File

@@ -92,11 +92,11 @@ func currentCgroup() (*cgroup, error) {
continue
}
cgroups[subsys] = path.Join(cgroupDir, subsys)
if strings.Contains(subsys, ",") {
for _, k := range strings.Split(subsys, ",") {
cgroups[k] = path.Join(cgroupDir, k)
}
// https://man7.org/linux/man-pages/man7/cgroups.7.html
// comma-separated list of controllers for cgroup version 1
fields := strings.Split(subsys, ",")
for _, val := range fields {
cgroups[val] = path.Join(cgroupDir, val)
}
}

View File

@@ -1,6 +1,14 @@
package internal
import "testing"
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRefreshCpu(t *testing.T) {
assert.True(t, RefreshCpu() >= 0)
}
func BenchmarkRefreshCpu(b *testing.B) {
for i := 0; i < b.N; i++ {

37
core/stat/metrics_test.go Normal file
View File

@@ -0,0 +1,37 @@
package stat
import (
"strconv"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestMetrics(t *testing.T) {
counts := []int{1, 5, 10, 100, 1000, 1000}
for _, count := range counts {
m := NewMetrics("foo")
m.SetName("bar")
for i := 0; i < count; i++ {
m.Add(Task{
Duration: time.Millisecond * time.Duration(i),
Description: strconv.Itoa(i),
})
}
m.AddDrop()
var writer mockedWriter
SetReportWriter(&writer)
m.executor.Flush()
assert.Equal(t, "bar", writer.report.Name)
}
}
type mockedWriter struct {
report *StatReport
}
func (m *mockedWriter) Write(report *StatReport) error {
m.report = report
return nil
}

View File

@@ -0,0 +1,30 @@
package stat
import (
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/h2non/gock.v1"
)
func TestRemoteWriter(t *testing.T) {
defer gock.Off()
gock.New("http://foo.com").Reply(200).BodyString("foo")
writer := NewRemoteWriter("http://foo.com")
err := writer.Write(&StatReport{
Name: "bar",
})
assert.Nil(t, err)
}
func TestRemoteWriterFail(t *testing.T) {
defer gock.Off()
gock.New("http://foo.com").Reply(503).BodyString("foo")
writer := NewRemoteWriter("http://foo.com")
err := writer.Write(&StatReport{
Name: "bar",
})
assert.NotNil(t, err)
}

View File

@@ -1,4 +1,4 @@
package internal
package cache
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package internal
package cache
import (
"encoding/json"
@@ -8,11 +8,11 @@ import (
"testing"
"time"
"github.com/alicebob/miniredis"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/errorx"
"github.com/tal-tech/go-zero/core/hash"
"github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
"github.com/tal-tech/go-zero/core/syncx"
)
@@ -76,23 +76,23 @@ func (mc *mockedNode) TakeWithExpire(v interface{}, key string, query func(v int
func TestCache_SetDel(t *testing.T) {
const total = 1000
r1 := miniredis.NewMiniRedis()
assert.Nil(t, r1.Start())
defer r1.Close()
r2 := miniredis.NewMiniRedis()
assert.Nil(t, r2.Start())
defer r2.Close()
r1, clean1, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean1()
r2, clean2, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean2()
conf := ClusterConf{
{
RedisConf: redis.RedisConf{
Host: r1.Addr(),
Host: r1.Addr,
Type: redis.NodeType,
},
Weight: 100,
},
{
RedisConf: redis.RedisConf{
Host: r2.Addr(),
Host: r2.Addr,
Type: redis.NodeType,
},
Weight: 100,
@@ -111,6 +111,45 @@ func TestCache_SetDel(t *testing.T) {
assert.Nil(t, c.GetCache(fmt.Sprintf("key/%d", i), &v))
assert.Equal(t, i, v)
}
assert.Nil(t, c.DelCache())
for i := 0; i < total; i++ {
assert.Nil(t, c.DelCache(fmt.Sprintf("key/%d", i)))
}
for i := 0; i < total; i++ {
var v int
assert.Equal(t, errPlaceholder, c.GetCache(fmt.Sprintf("key/%d", i), &v))
assert.Equal(t, 0, v)
}
}
func TestCache_OneNode(t *testing.T) {
const total = 1000
r, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
conf := ClusterConf{
{
RedisConf: redis.RedisConf{
Host: r.Addr,
Type: redis.NodeType,
},
Weight: 100,
},
}
c := NewCache(conf, syncx.NewSharedCalls(), NewCacheStat("mock"), errPlaceholder)
for i := 0; i < total; i++ {
if i%2 == 0 {
assert.Nil(t, c.SetCache(fmt.Sprintf("key/%d", i), i))
} else {
assert.Nil(t, c.SetCacheWithExpire(fmt.Sprintf("key/%d", i), i, 0))
}
}
for i := 0; i < total; i++ {
var v int
assert.Nil(t, c.GetCache(fmt.Sprintf("key/%d", i), &v))
assert.Equal(t, i, v)
}
assert.Nil(t, c.DelCache())
for i := 0; i < total; i++ {
assert.Nil(t, c.DelCache(fmt.Sprintf("key/%d", i)))
}
@@ -188,6 +227,25 @@ func TestCache_Balance(t *testing.T) {
assert.Equal(t, total/10, count)
}
func TestCacheNoNode(t *testing.T) {
dispatcher := hash.NewConsistentHash()
c := cacheCluster{
dispatcher: dispatcher,
errNotFound: errPlaceholder,
}
assert.NotNil(t, c.DelCache("foo"))
assert.NotNil(t, c.DelCache("foo", "bar", "any"))
assert.NotNil(t, c.GetCache("foo", nil))
assert.NotNil(t, c.SetCache("foo", nil))
assert.NotNil(t, c.SetCacheWithExpire("foo", nil, time.Second))
assert.NotNil(t, c.Take(nil, "foo", func(v interface{}) error {
return nil
}))
assert.NotNil(t, c.TakeWithExpire(nil, "foo", func(v interface{}, duration time.Duration) error {
return nil
}))
}
func calcEntropy(m map[int]int, total int) float64 {
var entropy float64

View File

@@ -1,5 +1,3 @@
package cache
import "github.com/tal-tech/go-zero/core/stores/internal"
type CacheConf = internal.ClusterConf
type CacheConf = ClusterConf

View File

@@ -1,13 +1,13 @@
package internal
package cache
import (
"encoding/json"
"errors"
"fmt"
"math/rand"
"sync"
"time"
"github.com/tal-tech/go-zero/core/jsonx"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/mathx"
"github.com/tal-tech/go-zero/core/stat"
@@ -79,7 +79,7 @@ func (c cacheNode) SetCache(key string, v interface{}) error {
}
func (c cacheNode) SetCacheWithExpire(key string, v interface{}, expire time.Duration) error {
data, err := json.Marshal(v)
data, err := jsonx.Marshal(v)
if err != nil {
return err
}
@@ -168,24 +168,24 @@ func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) e
}
}
return json.Marshal(v)
return jsonx.Marshal(v)
})
if err != nil {
return err
}
if fresh {
return nil
} else {
// got the result from previous ongoing query
c.stat.IncrementTotal()
c.stat.IncrementHit()
}
return json.Unmarshal(val.([]byte), v)
// got the result from previous ongoing query
c.stat.IncrementTotal()
c.stat.IncrementHit()
return jsonx.Unmarshal(val.([]byte), v)
}
func (c cacheNode) processCache(key string, data string, v interface{}) error {
err := json.Unmarshal([]byte(data), v)
err := jsonx.Unmarshal([]byte(data), v)
if err == nil {
return nil
}

207
core/stores/cache/cachenode_test.go vendored Normal file
View File

@@ -0,0 +1,207 @@
package cache
import (
"errors"
"fmt"
"math/rand"
"strconv"
"sync"
"testing"
"time"
"github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/mathx"
"github.com/tal-tech/go-zero/core/stat"
"github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest"
"github.com/tal-tech/go-zero/core/syncx"
)
var errTestNotFound = errors.New("not found")
func init() {
logx.Disable()
stat.SetReporter(nil)
}
func TestCacheNode_DelCache(t *testing.T) {
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
cn := cacheNode{
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewCacheStat("any"),
errNotFound: errTestNotFound,
}
assert.Nil(t, cn.DelCache())
assert.Nil(t, cn.DelCache([]string{}...))
assert.Nil(t, cn.DelCache(make([]string, 0)...))
cn.SetCache("first", "one")
assert.Nil(t, cn.DelCache("first"))
cn.SetCache("first", "one")
cn.SetCache("second", "two")
assert.Nil(t, cn.DelCache("first", "second"))
}
func TestCacheNode_InvalidCache(t *testing.T) {
s, err := miniredis.Run()
assert.Nil(t, err)
defer s.Close()
cn := cacheNode{
rds: redis.NewRedis(s.Addr(), redis.NodeType),
r: rand.New(rand.NewSource(time.Now().UnixNano())),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewCacheStat("any"),
errNotFound: errTestNotFound,
}
s.Set("any", "value")
var str string
assert.NotNil(t, cn.GetCache("any", &str))
assert.Equal(t, "", str)
_, err = s.Get("any")
assert.Equal(t, miniredis.ErrKeyNotFound, err)
}
func TestCacheNode_Take(t *testing.T) {
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
cn := cacheNode{
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSharedCalls(),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewCacheStat("any"),
errNotFound: errTestNotFound,
}
var str string
err = cn.Take(&str, "any", func(v interface{}) error {
*v.(*string) = "value"
return nil
})
assert.Nil(t, err)
assert.Equal(t, "value", str)
assert.Nil(t, cn.GetCache("any", &str))
val, err := store.Get("any")
assert.Nil(t, err)
assert.Equal(t, `"value"`, val)
}
func TestCacheNode_TakeNotFound(t *testing.T) {
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
cn := cacheNode{
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSharedCalls(),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewCacheStat("any"),
errNotFound: errTestNotFound,
}
var str string
err = cn.Take(&str, "any", func(v interface{}) error {
return errTestNotFound
})
assert.Equal(t, errTestNotFound, err)
assert.Equal(t, errTestNotFound, cn.GetCache("any", &str))
val, err := store.Get("any")
assert.Nil(t, err)
assert.Equal(t, `*`, val)
store.Set("any", "*")
err = cn.Take(&str, "any", func(v interface{}) error {
return nil
})
assert.Equal(t, errTestNotFound, err)
assert.Equal(t, errTestNotFound, cn.GetCache("any", &str))
store.Del("any")
var errDummy = errors.New("dummy")
err = cn.Take(&str, "any", func(v interface{}) error {
return errDummy
})
assert.Equal(t, errDummy, err)
}
func TestCacheNode_TakeWithExpire(t *testing.T) {
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
cn := cacheNode{
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSharedCalls(),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewCacheStat("any"),
errNotFound: errors.New("any"),
}
var str string
err = cn.TakeWithExpire(&str, "any", func(v interface{}, expire time.Duration) error {
*v.(*string) = "value"
return nil
})
assert.Nil(t, err)
assert.Equal(t, "value", str)
assert.Nil(t, cn.GetCache("any", &str))
val, err := store.Get("any")
assert.Nil(t, err)
assert.Equal(t, `"value"`, val)
}
func TestCacheNode_String(t *testing.T) {
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
cn := cacheNode{
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSharedCalls(),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewCacheStat("any"),
errNotFound: errors.New("any"),
}
assert.Equal(t, store.Addr, cn.String())
}
func TestCacheValueWithBigInt(t *testing.T) {
store, clean, err := redistest.CreateRedis()
assert.Nil(t, err)
defer clean()
cn := cacheNode{
rds: store,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
barrier: syncx.NewSharedCalls(),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewCacheStat("any"),
errNotFound: errors.New("any"),
}
const (
key = "key"
value int64 = 323427211229009810
)
assert.Nil(t, cn.SetCache(key, value))
var val interface{}
assert.Nil(t, cn.GetCache(key, &val))
assert.Equal(t, strconv.FormatInt(value, 10), fmt.Sprintf("%v", val))
}

View File

@@ -1,21 +1,45 @@
package cache
import (
"time"
import "time"
"github.com/tal-tech/go-zero/core/stores/internal"
const (
defaultExpiry = time.Hour * 24 * 7
defaultNotFoundExpiry = time.Minute
)
type Option = internal.Option
type (
Options struct {
Expiry time.Duration
NotFoundExpiry time.Duration
}
Option func(o *Options)
)
func newOptions(opts ...Option) Options {
var o Options
for _, opt := range opts {
opt(&o)
}
if o.Expiry <= 0 {
o.Expiry = defaultExpiry
}
if o.NotFoundExpiry <= 0 {
o.NotFoundExpiry = defaultNotFoundExpiry
}
return o
}
func WithExpiry(expiry time.Duration) Option {
return func(o *internal.Options) {
return func(o *Options) {
o.Expiry = expiry
}
}
func WithNotFoundExpiry(expiry time.Duration) Option {
return func(o *internal.Options) {
return func(o *Options) {
o.NotFoundExpiry = expiry
}
}

View File

@@ -1,4 +1,4 @@
package internal
package cache
import (
"sync/atomic"
@@ -48,20 +48,17 @@ func (cs *CacheStat) statLoop() {
ticker := time.NewTicker(statInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
total := atomic.SwapUint64(&cs.Total, 0)
if total == 0 {
continue
}
hit := atomic.SwapUint64(&cs.Hit, 0)
percent := 100 * float32(hit) / float32(total)
miss := atomic.SwapUint64(&cs.Miss, 0)
dbf := atomic.SwapUint64(&cs.DbFails, 0)
logx.Statf("dbcache(%s) - qpm: %d, hit_ratio: %.1f%%, hit: %d, miss: %d, db_fails: %d",
cs.name, total, percent, hit, miss, dbf)
for range ticker.C {
total := atomic.SwapUint64(&cs.Total, 0)
if total == 0 {
continue
}
hit := atomic.SwapUint64(&cs.Hit, 0)
percent := 100 * float32(hit) / float32(total)
miss := atomic.SwapUint64(&cs.Miss, 0)
dbf := atomic.SwapUint64(&cs.DbFails, 0)
logx.Statf("dbcache(%s) - qpm: %d, hit_ratio: %.1f%%, hit: %d, miss: %d, db_fails: %d",
cs.name, total, percent, hit, miss, dbf)
}
}

View File

@@ -1,4 +1,4 @@
package internal
package cache
import (
"fmt"

56
core/stores/cache/cleaner_test.go vendored Normal file
View File

@@ -0,0 +1,56 @@
package cache
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestNextDelay(t *testing.T) {
tests := []struct {
name string
input time.Duration
output time.Duration
ok bool
}{
{
name: "second",
input: time.Second,
output: time.Second * 5,
ok: true,
},
{
name: "5 seconds",
input: time.Second * 5,
output: time.Minute,
ok: true,
},
{
name: "minute",
input: time.Minute,
output: time.Minute * 5,
ok: true,
},
{
name: "5 minutes",
input: time.Minute * 5,
output: time.Hour,
ok: true,
},
{
name: "hour",
input: time.Hour,
output: 0,
ok: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
next, ok := nextDelay(test.input)
assert.Equal(t, test.ok, ok)
assert.Equal(t, test.output, next)
})
}
}

View File

@@ -1,4 +1,4 @@
package internal
package cache
import "github.com/tal-tech/go-zero/core/stores/redis"

View File

@@ -1,4 +1,4 @@
package internal
package cache
import "strings"

26
core/stores/cache/util_test.go vendored Normal file
View File

@@ -0,0 +1,26 @@
package cache
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFormatKeys(t *testing.T) {
assert.Equal(t, "a,b", formatKeys([]string{"a", "b"}))
}
func TestTotalWeights(t *testing.T) {
val := TotalWeights([]NodeConf{
{
Weight: -1,
},
{
Weight: 0,
},
{
Weight: 1,
},
})
assert.Equal(t, 1, val)
}

View File

@@ -1,7 +1,7 @@
package clickhouse
import (
_ "github.com/kshvakov/clickhouse"
_ "github.com/ClickHouse/clickhouse-go"
"github.com/tal-tech/go-zero/core/stores/sqlx"
)

View File

@@ -1,65 +0,0 @@
package internal
import (
"errors"
"math/rand"
"sync"
"testing"
"time"
"github.com/alicebob/miniredis"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/mathx"
"github.com/tal-tech/go-zero/core/stat"
"github.com/tal-tech/go-zero/core/stores/redis"
)
func init() {
logx.Disable()
stat.SetReporter(nil)
}
func TestCacheNode_DelCache(t *testing.T) {
s, err := miniredis.Run()
assert.Nil(t, err)
defer s.Close()
cn := cacheNode{
rds: redis.NewRedis(s.Addr(), redis.NodeType),
r: rand.New(rand.NewSource(time.Now().UnixNano())),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewCacheStat("any"),
errNotFound: errors.New("any"),
}
assert.Nil(t, cn.DelCache())
assert.Nil(t, cn.DelCache([]string{}...))
assert.Nil(t, cn.DelCache(make([]string, 0)...))
cn.SetCache("first", "one")
assert.Nil(t, cn.DelCache("first"))
cn.SetCache("first", "one")
cn.SetCache("second", "two")
assert.Nil(t, cn.DelCache("first", "second"))
}
func TestCacheNode_InvalidCache(t *testing.T) {
s, err := miniredis.Run()
assert.Nil(t, err)
defer s.Close()
cn := cacheNode{
rds: redis.NewRedis(s.Addr(), redis.NodeType),
r: rand.New(rand.NewSource(time.Now().UnixNano())),
lock: new(sync.Mutex),
unstableExpiry: mathx.NewUnstable(expiryDeviation),
stat: NewCacheStat("any"),
errNotFound: errors.New("any"),
}
s.Set("any", "value")
var str string
assert.NotNil(t, cn.GetCache("any", &str))
assert.Equal(t, "", str)
_, err = s.Get("any")
assert.Equal(t, miniredis.ErrKeyNotFound, err)
}

View File

@@ -1,33 +0,0 @@
package internal
import "time"
const (
defaultExpiry = time.Hour * 24 * 7
defaultNotFoundExpiry = time.Minute
)
type (
Options struct {
Expiry time.Duration
NotFoundExpiry time.Duration
}
Option func(o *Options)
)
func newOptions(opts ...Option) Options {
var o Options
for _, opt := range opts {
opt(&o)
}
if o.Expiry <= 0 {
o.Expiry = defaultExpiry
}
if o.NotFoundExpiry <= 0 {
o.NotFoundExpiry = defaultNotFoundExpiry
}
return o
}

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