mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-23 22:58:18 +08:00
Compare commits
159 Commits
v1.3.0-alp
...
v1.3.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78ea0769fd | ||
|
|
e0fa8d820d | ||
|
|
dfd58c213c | ||
|
|
83cacf51b7 | ||
|
|
6dccfa29fd | ||
|
|
7e0b0ab0b1 | ||
|
|
ac18cc470d | ||
|
|
f4471846ff | ||
|
|
9c2d526a11 | ||
|
|
2b9fc26c38 | ||
|
|
321dc2d410 | ||
|
|
500bd87c85 | ||
|
|
e9620c8c05 | ||
|
|
70e51bb352 | ||
|
|
278cd123c8 | ||
|
|
3febb1a5d0 | ||
|
|
d8054d8def | ||
|
|
ec271db7a0 | ||
|
|
bbac994c8a | ||
|
|
c1d9e6a00b | ||
|
|
0aeb49a6b0 | ||
|
|
fe262766b4 | ||
|
|
7181505c8a | ||
|
|
f060a226bc | ||
|
|
93d524b797 | ||
|
|
5c169f4f49 | ||
|
|
d29dfa12e3 | ||
|
|
194f55e08e | ||
|
|
c0f9892fe3 | ||
|
|
227104d7d7 | ||
|
|
448029aa4b | ||
|
|
17e0afeac0 | ||
|
|
18916b5189 | ||
|
|
c11a09be23 | ||
|
|
56e1ecf2f3 | ||
|
|
f9e6013a6c | ||
|
|
b5d1d8b0d1 | ||
|
|
09e6d94f9e | ||
|
|
2a5717d7fb | ||
|
|
85cf662c6f | ||
|
|
3279a7ef0f | ||
|
|
fec908a19b | ||
|
|
f5ed0cda58 | ||
|
|
cc9d16f505 | ||
|
|
c05d74b44c | ||
|
|
32c88b6352 | ||
|
|
7dabec260f | ||
|
|
4feb88f9b5 | ||
|
|
2776caed0e | ||
|
|
c55694d957 | ||
|
|
209ffb934b | ||
|
|
26a33932cd | ||
|
|
d6a692971f | ||
|
|
4624390e54 | ||
|
|
63b7d292c1 | ||
|
|
365c569d7c | ||
|
|
68a81fea8a | ||
|
|
08a8bd7ef7 | ||
|
|
b939ce75ba | ||
|
|
3b7ca86e4f | ||
|
|
60760b52ab | ||
|
|
96c128c58a | ||
|
|
0c35f39a7d | ||
|
|
6a66dde0a1 | ||
|
|
36b9fcba44 | ||
|
|
bf99dda620 | ||
|
|
511dfcb409 | ||
|
|
900bc96420 | ||
|
|
be277a7376 | ||
|
|
f15a4f9188 | ||
|
|
e31128650e | ||
|
|
168740b64d | ||
|
|
cc4c4928e0 | ||
|
|
fba6543b23 | ||
|
|
877eb6ac56 | ||
|
|
259a5a13e7 | ||
|
|
cf7c7cb392 | ||
|
|
86d01e2e99 | ||
|
|
7a28e19a27 | ||
|
|
900ea63d68 | ||
|
|
87ab86cdd0 | ||
|
|
0697494ffd | ||
|
|
ffd69a2f5e | ||
|
|
66f10bb5e6 | ||
|
|
8131a0e777 | ||
|
|
32a557dff6 | ||
|
|
db949e40f1 | ||
|
|
e0454138e0 | ||
|
|
3b07ed1b97 | ||
|
|
daa98f5a27 | ||
|
|
842656aa90 | ||
|
|
aa29036cb3 | ||
|
|
607bae27fa | ||
|
|
7c63676be4 | ||
|
|
9e113909b3 | ||
|
|
bd105474ca | ||
|
|
a078f5d764 | ||
|
|
b215fa3ee6 | ||
|
|
50b1928502 | ||
|
|
493e3bcf4b | ||
|
|
6deb80625d | ||
|
|
6ab051568c | ||
|
|
2732d3cdae | ||
|
|
e8c307e4dc | ||
|
|
84ddc660c4 | ||
|
|
e60e707955 | ||
|
|
cf4321b2d0 | ||
|
|
1993faf2f8 | ||
|
|
0ce85376bf | ||
|
|
a40254156f | ||
|
|
05cc62f5ff | ||
|
|
9c2c90e533 | ||
|
|
822ee2e1c5 | ||
|
|
77482c8946 | ||
|
|
7ef0ab3119 | ||
|
|
8bd89a297a | ||
|
|
bb75cc796e | ||
|
|
0fdd8f54eb | ||
|
|
b1ffc464cd | ||
|
|
50174960e4 | ||
|
|
8f46eab977 | ||
|
|
ec299085f5 | ||
|
|
7727d70634 | ||
|
|
5f9d101bc6 | ||
|
|
6c2abe7474 | ||
|
|
14a902c1a7 | ||
|
|
5ad6a6d229 | ||
|
|
6f4b97864a | ||
|
|
0e0abc3a95 | ||
|
|
696fda1db4 | ||
|
|
c1d2634427 | ||
|
|
4b7a680ac5 | ||
|
|
b3e7d2901f | ||
|
|
cdf7ec213c | ||
|
|
f1102fb262 | ||
|
|
09d1fad6e0 | ||
|
|
379c65a3ef | ||
|
|
fdc7f64d6f | ||
|
|
df0f8ed59e | ||
|
|
c903966fc7 | ||
|
|
e57fa8ff53 | ||
|
|
bf2feee5b7 | ||
|
|
ce05c429fc | ||
|
|
272a3f347d | ||
|
|
13db7a1931 | ||
|
|
468c237189 | ||
|
|
b9b80c068b | ||
|
|
9b592b3dee | ||
|
|
2203809e5e | ||
|
|
8d6d37f71e | ||
|
|
ea4f2af67f | ||
|
|
53af194ef9 | ||
|
|
5e0e2d2b14 | ||
|
|
74c99184c5 | ||
|
|
eb4b86137a | ||
|
|
9c4f4f3b4e | ||
|
|
240132e7c7 | ||
|
|
9d67fc4cfb | ||
|
|
892f93a716 |
60
.github/workflows/go.yml
vendored
60
.github/workflows/go.yml
vendored
@@ -7,32 +7,50 @@ on:
|
|||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
test-linux:
|
||||||
name: Build
|
name: Linux
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Set up Go 1.x
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ^1.15
|
||||||
|
id: go
|
||||||
|
|
||||||
- name: Set up Go 1.x
|
- name: Check out code into the Go module directory
|
||||||
uses: actions/setup-go@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
|
||||||
go-version: ^1.14
|
|
||||||
id: go
|
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Get dependencies
|
||||||
uses: actions/checkout@v2
|
run: |
|
||||||
|
go get -v -t -d ./...
|
||||||
|
|
||||||
- name: Get dependencies
|
- name: Lint
|
||||||
run: |
|
run: |
|
||||||
go get -v -t -d ./...
|
go vet -stdmethods=false $(go list ./...)
|
||||||
|
go install mvdan.cc/gofumpt@latest
|
||||||
|
test -z "$(gofumpt -s -l -extra .)" || echo "Please run 'gofumpt -l -w -extra .'"
|
||||||
|
|
||||||
- name: Lint
|
- name: Test
|
||||||
run: |
|
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
go vet -stdmethods=false $(go list ./...)
|
|
||||||
go install mvdan.cc/gofumpt@latest
|
|
||||||
test -z "$(gofumpt -s -l -extra .)" || echo "Please run 'gofumpt -l -w -extra .'"
|
|
||||||
|
|
||||||
- name: Test
|
- name: Codecov
|
||||||
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
uses: codecov/codecov-action@v2
|
||||||
|
|
||||||
- name: Codecov
|
test-win:
|
||||||
uses: codecov/codecov-action@v2
|
name: Windows
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- name: Set up Go 1.x
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ^1.15
|
||||||
|
|
||||||
|
- name: Checkout codebase
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
go mod verify
|
||||||
|
go mod download
|
||||||
|
go test -v -race ./...
|
||||||
|
cd tools/goctl && go build -v goctl.go
|
||||||
|
|||||||
18
.github/workflows/issue-translator.yml
vendored
Normal file
18
.github/workflows/issue-translator.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
name: 'issue-translator'
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: tomsun28/issues-translate-action@v2.6
|
||||||
|
with:
|
||||||
|
IS_MODIFY_TITLE: true
|
||||||
|
# not require, default false, . Decide whether to modify the issue title
|
||||||
|
# if true, the robot account @Issues-translate-bot must have modification permissions, invite @Issues-translate-bot to your project or use your custom bot.
|
||||||
|
CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑🤝🧑👫🧑🏿🤝🧑🏻👩🏾🤝👨🏿👬🏿
|
||||||
|
# not require. Customize the translation robot prefix message.
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,7 +16,8 @@
|
|||||||
**/logs
|
**/logs
|
||||||
|
|
||||||
# for test purpose
|
# for test purpose
|
||||||
adhoc
|
**/adhoc
|
||||||
|
**/testdata
|
||||||
|
|
||||||
# gitlab ci
|
# gitlab ci
|
||||||
.cache
|
.cache
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ We will help you to contribute in different areas like filing issues, developing
|
|||||||
getting your work reviewed and merged.
|
getting your work reviewed and merged.
|
||||||
|
|
||||||
If you have questions about the development process,
|
If you have questions about the development process,
|
||||||
feel free to [file an issue](https://github.com/tal-tech/go-zero/issues/new/choose).
|
feel free to [file an issue](https://github.com/zeromicro/go-zero/issues/new/choose).
|
||||||
|
|
||||||
## Find something to work on
|
## Find something to work on
|
||||||
|
|
||||||
@@ -50,10 +50,10 @@ Here is how you get started.
|
|||||||
|
|
||||||
### Find a good first topic
|
### Find a good first topic
|
||||||
|
|
||||||
[go-zero](https://github.com/tal-tech/go-zero) has beginner-friendly issues that provide a good first issue.
|
[go-zero](https://github.com/zeromicro/go-zero) has beginner-friendly issues that provide a good first issue.
|
||||||
For example, [go-zero](https://github.com/tal-tech/go-zero) has
|
For example, [go-zero](https://github.com/zeromicro/go-zero) has
|
||||||
[help wanted](https://github.com/tal-tech/go-zero/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) and
|
[help wanted](https://github.com/zeromicro/go-zero/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) and
|
||||||
[good first issue](https://github.com/tal-tech/go-zero/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
|
[good first issue](https://github.com/zeromicro/go-zero/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
|
||||||
labels for issues that should not need deep knowledge of the system.
|
labels for issues that should not need deep knowledge of the system.
|
||||||
We can help new contributors who wish to work on such issues.
|
We can help new contributors who wish to work on such issues.
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ This is a rough outline of what a contributor's workflow looks like:
|
|||||||
- Create a topic branch from where to base the contribution. This is usually master.
|
- Create a topic branch from where to base the contribution. This is usually master.
|
||||||
- Make commits of logical units.
|
- Make commits of logical units.
|
||||||
- Push changes in a topic branch to a personal fork of the repository.
|
- Push changes in a topic branch to a personal fork of the repository.
|
||||||
- Submit a pull request to [go-zero](https://github.com/tal-tech/go-zero).
|
- Submit a pull request to [go-zero](https://github.com/zeromicro/go-zero).
|
||||||
|
|
||||||
## Creating Pull Requests
|
## Creating Pull Requests
|
||||||
|
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2020 xiaoheiban_server_go
|
Copyright (c) 2022 zeromicro
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
10
ROADMAP.md
10
ROADMAP.md
@@ -20,9 +20,9 @@ We hope that the items listed below will inspire further engagement from the com
|
|||||||
- [x] Support `goctl bug` to report bugs conveniently
|
- [x] Support `goctl bug` to report bugs conveniently
|
||||||
|
|
||||||
## 2022
|
## 2022
|
||||||
- [ ] Support `goctl mock` command to start a mocking server with given `.api` file
|
- [x] Support `context` in redis related methods for timeout and tracing
|
||||||
- [ ] Add `httpx.Client` with governance, like circuit breaker etc.
|
- [x] Support `context` in sql related methods for timeout and tracing
|
||||||
- [ ] Support `goctl doctor` command to report potential issues for given service
|
|
||||||
- [ ] Support `context` in redis related methods for timeout and tracing
|
|
||||||
- [ ] Support `context` in sql related methods for timeout and tracing
|
|
||||||
- [ ] Support `context` in mongodb related methods for timeout and tracing
|
- [ ] Support `context` in mongodb related methods for timeout and tracing
|
||||||
|
- [x] Add `httpc.Do` with HTTP call governance, like circuit breaker etc.
|
||||||
|
- [ ] Support `goctl doctor` command to report potential issues for given service
|
||||||
|
- [ ] Support `goctl mock` command to start a mocking server with given `.api` file
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ func (lt loggedThrottle) allow() (Promise, error) {
|
|||||||
func (lt loggedThrottle) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error {
|
func (lt loggedThrottle) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error {
|
||||||
return lt.logError(lt.internalThrottle.doReq(req, fallback, func(err error) bool {
|
return lt.logError(lt.internalThrottle.doReq(req, fallback, func(err error) bool {
|
||||||
accept := acceptable(err)
|
accept := acceptable(err)
|
||||||
if !accept {
|
if !accept && err != nil {
|
||||||
lt.errWin.add(err.Error())
|
lt.errWin.add(err.Error())
|
||||||
}
|
}
|
||||||
return accept
|
return accept
|
||||||
|
|||||||
@@ -61,3 +61,41 @@ func TestPutMore(t *testing.T) {
|
|||||||
assert.Equal(t, string(element), string(body.([]byte)))
|
assert.Equal(t, string(element), string(body.([]byte)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPutMoreWithHeaderNotZero(t *testing.T) {
|
||||||
|
elements := [][]byte{
|
||||||
|
[]byte("hello"),
|
||||||
|
[]byte("world"),
|
||||||
|
[]byte("again"),
|
||||||
|
}
|
||||||
|
queue := NewQueue(4)
|
||||||
|
for i := range elements {
|
||||||
|
queue.Put(elements[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// take 1
|
||||||
|
body, ok := queue.Take()
|
||||||
|
assert.True(t, ok)
|
||||||
|
element, ok := body.([]byte)
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, element, []byte("hello"))
|
||||||
|
|
||||||
|
// put more
|
||||||
|
queue.Put([]byte("b4"))
|
||||||
|
queue.Put([]byte("b5")) // will store in elements[0]
|
||||||
|
queue.Put([]byte("b6")) // cause expansion
|
||||||
|
|
||||||
|
results := [][]byte{
|
||||||
|
[]byte("world"),
|
||||||
|
[]byte("again"),
|
||||||
|
[]byte("b4"),
|
||||||
|
[]byte("b5"),
|
||||||
|
[]byte("b6"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, element := range results {
|
||||||
|
body, ok := queue.Take()
|
||||||
|
assert.True(t, ok)
|
||||||
|
assert.Equal(t, string(element), string(body.([]byte)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,13 @@ package discov
|
|||||||
|
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// errEmptyEtcdHosts indicates that etcd hosts are empty.
|
||||||
|
errEmptyEtcdHosts = errors.New("empty etcd hosts")
|
||||||
|
// errEmptyEtcdKey indicates that etcd key is empty.
|
||||||
|
errEmptyEtcdKey = errors.New("empty etcd key")
|
||||||
|
)
|
||||||
|
|
||||||
// EtcdConf is the config item with the given key on etcd.
|
// EtcdConf is the config item with the given key on etcd.
|
||||||
type EtcdConf struct {
|
type EtcdConf struct {
|
||||||
Hosts []string
|
Hosts []string
|
||||||
@@ -27,9 +34,9 @@ func (c EtcdConf) HasTLS() bool {
|
|||||||
// Validate validates c.
|
// Validate validates c.
|
||||||
func (c EtcdConf) Validate() error {
|
func (c EtcdConf) Validate() error {
|
||||||
if len(c.Hosts) == 0 {
|
if len(c.Hosts) == 0 {
|
||||||
return errors.New("empty etcd hosts")
|
return errEmptyEtcdHosts
|
||||||
} else if len(c.Key) == 0 {
|
} else if len(c.Key) == 0 {
|
||||||
return errors.New("empty etcd key")
|
return errEmptyEtcdKey
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ type (
|
|||||||
errorArray []error
|
errorArray []error
|
||||||
)
|
)
|
||||||
|
|
||||||
// Add adds err to be.
|
// Add adds errs to be, nil errors are ignored.
|
||||||
func (be *BatchError) Add(err error) {
|
func (be *BatchError) Add(errs ...error) {
|
||||||
if err != nil {
|
for _, err := range errs {
|
||||||
be.errs = append(be.errs, err)
|
if err != nil {
|
||||||
|
be.errs = append(be.errs, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// errExceedFileSize indicates that the file size is exceeded.
|
||||||
|
var errExceedFileSize = errors.New("exceed file size")
|
||||||
|
|
||||||
// A RangeReader is used to read a range of content from a file.
|
// A RangeReader is used to read a range of content from a file.
|
||||||
type RangeReader struct {
|
type RangeReader struct {
|
||||||
file *os.File
|
file *os.File
|
||||||
@@ -29,7 +32,7 @@ func (rr *RangeReader) Read(p []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if rr.stop < rr.start || rr.start >= stat.Size() {
|
if rr.stop < rr.start || rr.start >= stat.Size() {
|
||||||
return 0, errors.New("exceed file size")
|
return 0, errExceedFileSize
|
||||||
}
|
}
|
||||||
|
|
||||||
if rr.stop-rr.start < int64(len(p)) {
|
if rr.stop-rr.start < int64(len(p)) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/zeromicro/go-zero/core/stringx"
|
"github.com/zeromicro/go-zero/core/stringx"
|
||||||
|
"go.uber.org/goleak"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBuffer(t *testing.T) {
|
func TestBuffer(t *testing.T) {
|
||||||
@@ -563,9 +564,6 @@ func equal(t *testing.T, stream Stream, data []interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runCheckedTest(t *testing.T, fn func(t *testing.T)) {
|
func runCheckedTest(t *testing.T, fn func(t *testing.T)) {
|
||||||
goroutines := runtime.NumGoroutine()
|
defer goleak.VerifyNone(t)
|
||||||
fn(t)
|
fn(t)
|
||||||
// let scheduler schedule first
|
|
||||||
time.Sleep(time.Millisecond)
|
|
||||||
assert.True(t, runtime.NumGoroutine() <= goroutines)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,5 +51,5 @@ func unmarshalUseNumber(decoder *json.Decoder, v interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func formatError(v string, err error) error {
|
func formatError(v string, err error) error {
|
||||||
return fmt.Errorf("string: `%s`, error: `%s`", v, err.Error())
|
return fmt.Errorf("string: `%s`, error: `%w`", v, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,23 +8,20 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// to be compatible with aliyun redis, we cannot use `local key = KEYS[1]` to reuse the key
|
||||||
// to be compatible with aliyun redis, we cannot use `local key = KEYS[1]` to reuse the key
|
const periodScript = `local limit = tonumber(ARGV[1])
|
||||||
periodScript = `local limit = tonumber(ARGV[1])
|
|
||||||
local window = tonumber(ARGV[2])
|
local window = tonumber(ARGV[2])
|
||||||
local current = redis.call("INCRBY", KEYS[1], 1)
|
local current = redis.call("INCRBY", KEYS[1], 1)
|
||||||
if current == 1 then
|
if current == 1 then
|
||||||
redis.call("expire", KEYS[1], window)
|
redis.call("expire", KEYS[1], window)
|
||||||
return 1
|
end
|
||||||
elseif current < limit then
|
if current < limit then
|
||||||
return 1
|
return 1
|
||||||
elseif current == limit then
|
elseif current == limit then
|
||||||
return 2
|
return 2
|
||||||
else
|
else
|
||||||
return 0
|
return 0
|
||||||
end`
|
end`
|
||||||
zoneDiff = 3600 * 8 // GMT+8 for our services
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Unknown means not initialized state.
|
// Unknown means not initialized state.
|
||||||
@@ -104,7 +101,9 @@ func (h *PeriodLimit) Take(key string) (int, error) {
|
|||||||
|
|
||||||
func (h *PeriodLimit) calcExpireSeconds() int {
|
func (h *PeriodLimit) calcExpireSeconds() int {
|
||||||
if h.align {
|
if h.align {
|
||||||
unix := time.Now().Unix() + zoneDiff
|
now := time.Now()
|
||||||
|
_, offset := now.Zone()
|
||||||
|
unix := now.Unix() + int64(offset)
|
||||||
return h.period - int(unix%int64(h.period))
|
return h.period - int(unix%int64(h.period))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +111,8 @@ func (h *PeriodLimit) calcExpireSeconds() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Align returns a func to customize a PeriodLimit with alignment.
|
// Align returns a func to customize a PeriodLimit with alignment.
|
||||||
|
// For example, if we want to limit end users with 5 sms verification messages every day,
|
||||||
|
// we need to align with the local timezone and the start of the day.
|
||||||
func Align() PeriodOption {
|
func Align() PeriodOption {
|
||||||
return func(l *PeriodLimit) {
|
return func(l *PeriodLimit) {
|
||||||
l.align = true
|
l.align = true
|
||||||
|
|||||||
@@ -23,10 +23,9 @@ func TestPeriodLimit_RedisUnavailable(t *testing.T) {
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
seconds = 1
|
seconds = 1
|
||||||
total = 100
|
|
||||||
quota = 5
|
quota = 5
|
||||||
)
|
)
|
||||||
l := NewPeriodLimit(seconds, quota, redis.NewRedis(s.Addr(), redis.NodeType), "periodlimit")
|
l := NewPeriodLimit(seconds, quota, redis.New(s.Addr()), "periodlimit")
|
||||||
s.Close()
|
s.Close()
|
||||||
val, err := l.Take("first")
|
val, err := l.Take("first")
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
@@ -66,3 +65,13 @@ func testPeriodLimit(t *testing.T, opts ...PeriodOption) {
|
|||||||
assert.Equal(t, 1, hitQuota)
|
assert.Equal(t, 1, hitQuota)
|
||||||
assert.Equal(t, total-quota, overQuota)
|
assert.Equal(t, total-quota, overQuota)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestQuotaFull(t *testing.T) {
|
||||||
|
s, err := miniredis.Run()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
l := NewPeriodLimit(1, 1, redis.New(s.Addr()), "periodlimit")
|
||||||
|
val, err := l.Take("first")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, HitQuota, val)
|
||||||
|
}
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ func (lim *TokenLimiter) Allow() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AllowN reports whether n events may happen at time now.
|
// AllowN reports whether n events may happen at time now.
|
||||||
// Use this method if you intend to drop / skip events that exceed the rate rate.
|
// Use this method if you intend to drop / skip events that exceed the rate.
|
||||||
// Otherwise use Reserve or Wait.
|
// Otherwise, use Reserve or Wait.
|
||||||
func (lim *TokenLimiter) AllowN(now time.Time, n int) bool {
|
func (lim *TokenLimiter) AllowN(now time.Time, n int) bool {
|
||||||
return lim.reserveN(now, n)
|
return lim.reserveN(now, n)
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,8 @@ func (lim *TokenLimiter) reserveN(now time.Time, n int) bool {
|
|||||||
// Lua boolean false -> r Nil bulk reply
|
// Lua boolean false -> r Nil bulk reply
|
||||||
if err == redis.Nil {
|
if err == redis.Nil {
|
||||||
return false
|
return false
|
||||||
} else if err != nil {
|
}
|
||||||
|
if err != nil {
|
||||||
logx.Errorf("fail to use rate limiter: %s, use in-process limiter for rescue", err)
|
logx.Errorf("fail to use rate limiter: %s, use in-process limiter for rescue", err)
|
||||||
lim.startMonitor()
|
lim.startMonitor()
|
||||||
return lim.rescueLimiter.AllowN(now, n)
|
return lim.rescueLimiter.AllowN(now, n)
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package logx
|
|||||||
// A LogConf is a logging config.
|
// A LogConf is a logging config.
|
||||||
type LogConf struct {
|
type LogConf struct {
|
||||||
ServiceName string `json:",optional"`
|
ServiceName string `json:",optional"`
|
||||||
Mode string `json:",default=console,options=console|file|volume"`
|
Mode string `json:",default=console,options=[console,file,volume]"`
|
||||||
|
Encoding string `json:",default=json,options=[json,plain]"`
|
||||||
TimeFormat string `json:",optional"`
|
TimeFormat string `json:",optional"`
|
||||||
Path string `json:",default=logs"`
|
Path string `json:",default=logs"`
|
||||||
Level string `json:",default=info,options=info|error|severe"`
|
Level string `json:",default=info,options=[info,error,severe]"`
|
||||||
Compress bool `json:",optional"`
|
Compress bool `json:",optional"`
|
||||||
KeepDays int `json:",optional"`
|
KeepDays int `json:",optional"`
|
||||||
StackCooldownMillis int `json:",default=100"`
|
StackCooldownMillis int `json:",default=100"`
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package logx
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/timex"
|
"github.com/zeromicro/go-zero/core/timex"
|
||||||
@@ -79,10 +80,15 @@ func (l *durationLogger) WithDuration(duration time.Duration) Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *durationLogger) write(writer io.Writer, level string, val interface{}) {
|
func (l *durationLogger) write(writer io.Writer, level string, val interface{}) {
|
||||||
outputJson(writer, &durationLogger{
|
switch atomic.LoadUint32(&encoding) {
|
||||||
Timestamp: getTimestamp(),
|
case plainEncodingType:
|
||||||
Level: level,
|
writePlainAny(writer, level, val, l.Duration)
|
||||||
Content: val,
|
default:
|
||||||
Duration: l.Duration,
|
outputJson(writer, &durationLogger{
|
||||||
})
|
Timestamp: getTimestamp(),
|
||||||
|
Level: level,
|
||||||
|
Content: val,
|
||||||
|
Duration: l.Duration,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package logx
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -37,6 +38,19 @@ func TestWithDurationInfo(t *testing.T) {
|
|||||||
assert.True(t, strings.Contains(builder.String(), "duration"), builder.String())
|
assert.True(t, strings.Contains(builder.String(), "duration"), builder.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWithDurationInfoConsole(t *testing.T) {
|
||||||
|
old := atomic.LoadUint32(&encoding)
|
||||||
|
atomic.StoreUint32(&encoding, plainEncodingType)
|
||||||
|
defer func() {
|
||||||
|
atomic.StoreUint32(&encoding, old)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var builder strings.Builder
|
||||||
|
log.SetOutput(&builder)
|
||||||
|
WithDuration(time.Second).Info("foo")
|
||||||
|
assert.True(t, strings.Contains(builder.String(), "ms"), builder.String())
|
||||||
|
}
|
||||||
|
|
||||||
func TestWithDurationInfof(t *testing.T) {
|
func TestWithDurationInfof(t *testing.T) {
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
log.SetOutput(&builder)
|
log.SetOutput(&builder)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package logx
|
package logx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -31,6 +32,15 @@ const (
|
|||||||
SevereLevel
|
SevereLevel
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
jsonEncodingType = iota
|
||||||
|
plainEncodingType
|
||||||
|
|
||||||
|
jsonEncoding = "json"
|
||||||
|
plainEncoding = "plain"
|
||||||
|
plainEncodingSep = '\t'
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
accessFilename = "access.log"
|
accessFilename = "access.log"
|
||||||
errorFilename = "error.log"
|
errorFilename = "error.log"
|
||||||
@@ -62,9 +72,10 @@ var (
|
|||||||
// ErrLogServiceNameNotSet is an error that indicates that the service name is not set.
|
// ErrLogServiceNameNotSet is an error that indicates that the service name is not set.
|
||||||
ErrLogServiceNameNotSet = errors.New("log service name must be set")
|
ErrLogServiceNameNotSet = errors.New("log service name must be set")
|
||||||
|
|
||||||
timeFormat = "2006-01-02T15:04:05.000Z07"
|
timeFormat = "2006-01-02T15:04:05.000Z07:00"
|
||||||
writeConsole bool
|
writeConsole bool
|
||||||
logLevel uint32
|
logLevel uint32
|
||||||
|
encoding uint32 = jsonEncodingType
|
||||||
// use uint32 for atomic operations
|
// use uint32 for atomic operations
|
||||||
disableStat uint32
|
disableStat uint32
|
||||||
infoLog io.WriteCloser
|
infoLog io.WriteCloser
|
||||||
@@ -124,6 +135,12 @@ func SetUp(c LogConf) error {
|
|||||||
if len(c.TimeFormat) > 0 {
|
if len(c.TimeFormat) > 0 {
|
||||||
timeFormat = c.TimeFormat
|
timeFormat = c.TimeFormat
|
||||||
}
|
}
|
||||||
|
switch c.Encoding {
|
||||||
|
case plainEncoding:
|
||||||
|
atomic.StoreUint32(&encoding, plainEncodingType)
|
||||||
|
default:
|
||||||
|
atomic.StoreUint32(&encoding, jsonEncodingType)
|
||||||
|
}
|
||||||
|
|
||||||
switch c.Mode {
|
switch c.Mode {
|
||||||
case consoleMode:
|
case consoleMode:
|
||||||
@@ -407,21 +424,31 @@ func infoTextSync(msg string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func outputAny(writer io.Writer, level string, val interface{}) {
|
func outputAny(writer io.Writer, level string, val interface{}) {
|
||||||
info := logEntry{
|
switch atomic.LoadUint32(&encoding) {
|
||||||
Timestamp: getTimestamp(),
|
case plainEncodingType:
|
||||||
Level: level,
|
writePlainAny(writer, level, val)
|
||||||
Content: val,
|
default:
|
||||||
|
info := logEntry{
|
||||||
|
Timestamp: getTimestamp(),
|
||||||
|
Level: level,
|
||||||
|
Content: val,
|
||||||
|
}
|
||||||
|
outputJson(writer, info)
|
||||||
}
|
}
|
||||||
outputJson(writer, info)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func outputText(writer io.Writer, level, msg string) {
|
func outputText(writer io.Writer, level, msg string) {
|
||||||
info := logEntry{
|
switch atomic.LoadUint32(&encoding) {
|
||||||
Timestamp: getTimestamp(),
|
case plainEncodingType:
|
||||||
Level: level,
|
writePlainText(writer, level, msg)
|
||||||
Content: msg,
|
default:
|
||||||
|
info := logEntry{
|
||||||
|
Timestamp: getTimestamp(),
|
||||||
|
Level: level,
|
||||||
|
Content: msg,
|
||||||
|
}
|
||||||
|
outputJson(writer, info)
|
||||||
}
|
}
|
||||||
outputJson(writer, info)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func outputError(writer io.Writer, msg string, callDepth int) {
|
func outputError(writer io.Writer, msg string, callDepth int) {
|
||||||
@@ -565,6 +592,62 @@ func statSync(msg string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writePlainAny(writer io.Writer, level string, val interface{}, fields ...string) {
|
||||||
|
switch v := val.(type) {
|
||||||
|
case string:
|
||||||
|
writePlainText(writer, level, v, fields...)
|
||||||
|
case error:
|
||||||
|
writePlainText(writer, level, v.Error(), fields...)
|
||||||
|
case fmt.Stringer:
|
||||||
|
writePlainText(writer, level, v.String(), fields...)
|
||||||
|
default:
|
||||||
|
var buf bytes.Buffer
|
||||||
|
buf.WriteString(getTimestamp())
|
||||||
|
buf.WriteByte(plainEncodingSep)
|
||||||
|
buf.WriteString(level)
|
||||||
|
for _, item := range fields {
|
||||||
|
buf.WriteByte(plainEncodingSep)
|
||||||
|
buf.WriteString(item)
|
||||||
|
}
|
||||||
|
buf.WriteByte(plainEncodingSep)
|
||||||
|
if err := json.NewEncoder(&buf).Encode(val); err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf.WriteByte('\n')
|
||||||
|
if atomic.LoadUint32(&initialized) == 0 || writer == nil {
|
||||||
|
log.Println(buf.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := writer.Write(buf.Bytes()); err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePlainText(writer io.Writer, level, msg string, fields ...string) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
buf.WriteString(getTimestamp())
|
||||||
|
buf.WriteByte(plainEncodingSep)
|
||||||
|
buf.WriteString(level)
|
||||||
|
for _, item := range fields {
|
||||||
|
buf.WriteByte(plainEncodingSep)
|
||||||
|
buf.WriteString(item)
|
||||||
|
}
|
||||||
|
buf.WriteByte(plainEncodingSep)
|
||||||
|
buf.WriteString(msg)
|
||||||
|
buf.WriteByte('\n')
|
||||||
|
if atomic.LoadUint32(&initialized) == 0 || writer == nil {
|
||||||
|
log.Println(buf.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := writer.Write(buf.Bytes()); err != nil {
|
||||||
|
log.Println(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type logWriter struct {
|
type logWriter struct {
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,6 +141,78 @@ func TestStructedLogInfov(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStructedLogInfoConsoleAny(t *testing.T) {
|
||||||
|
doTestStructedLogConsole(t, func(writer io.WriteCloser) {
|
||||||
|
infoLog = writer
|
||||||
|
}, func(v ...interface{}) {
|
||||||
|
old := atomic.LoadUint32(&encoding)
|
||||||
|
atomic.StoreUint32(&encoding, plainEncodingType)
|
||||||
|
defer func() {
|
||||||
|
atomic.StoreUint32(&encoding, old)
|
||||||
|
}()
|
||||||
|
|
||||||
|
Infov(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStructedLogInfoConsoleAnyString(t *testing.T) {
|
||||||
|
doTestStructedLogConsole(t, func(writer io.WriteCloser) {
|
||||||
|
infoLog = writer
|
||||||
|
}, func(v ...interface{}) {
|
||||||
|
old := atomic.LoadUint32(&encoding)
|
||||||
|
atomic.StoreUint32(&encoding, plainEncodingType)
|
||||||
|
defer func() {
|
||||||
|
atomic.StoreUint32(&encoding, old)
|
||||||
|
}()
|
||||||
|
|
||||||
|
Infov(fmt.Sprint(v...))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStructedLogInfoConsoleAnyError(t *testing.T) {
|
||||||
|
doTestStructedLogConsole(t, func(writer io.WriteCloser) {
|
||||||
|
infoLog = writer
|
||||||
|
}, func(v ...interface{}) {
|
||||||
|
old := atomic.LoadUint32(&encoding)
|
||||||
|
atomic.StoreUint32(&encoding, plainEncodingType)
|
||||||
|
defer func() {
|
||||||
|
atomic.StoreUint32(&encoding, old)
|
||||||
|
}()
|
||||||
|
|
||||||
|
Infov(errors.New(fmt.Sprint(v...)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStructedLogInfoConsoleAnyStringer(t *testing.T) {
|
||||||
|
doTestStructedLogConsole(t, func(writer io.WriteCloser) {
|
||||||
|
infoLog = writer
|
||||||
|
}, func(v ...interface{}) {
|
||||||
|
old := atomic.LoadUint32(&encoding)
|
||||||
|
atomic.StoreUint32(&encoding, plainEncodingType)
|
||||||
|
defer func() {
|
||||||
|
atomic.StoreUint32(&encoding, old)
|
||||||
|
}()
|
||||||
|
|
||||||
|
Infov(ValStringer{
|
||||||
|
val: fmt.Sprint(v...),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStructedLogInfoConsoleText(t *testing.T) {
|
||||||
|
doTestStructedLogConsole(t, func(writer io.WriteCloser) {
|
||||||
|
infoLog = writer
|
||||||
|
}, func(v ...interface{}) {
|
||||||
|
old := atomic.LoadUint32(&encoding)
|
||||||
|
atomic.StoreUint32(&encoding, plainEncodingType)
|
||||||
|
defer func() {
|
||||||
|
atomic.StoreUint32(&encoding, old)
|
||||||
|
}()
|
||||||
|
|
||||||
|
Info(fmt.Sprint(v...))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestStructedLogSlow(t *testing.T) {
|
func TestStructedLogSlow(t *testing.T) {
|
||||||
doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
|
doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
|
||||||
slowLog = writer
|
slowLog = writer
|
||||||
@@ -432,6 +504,17 @@ func doTestStructedLog(t *testing.T, level string, setup func(writer io.WriteClo
|
|||||||
assert.True(t, strings.Contains(val, message))
|
assert.True(t, strings.Contains(val, message))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func doTestStructedLogConsole(t *testing.T, setup func(writer io.WriteCloser),
|
||||||
|
write func(...interface{})) {
|
||||||
|
const message = "hello there"
|
||||||
|
writer := new(mockWriter)
|
||||||
|
setup(writer)
|
||||||
|
atomic.StoreUint32(&initialized, 1)
|
||||||
|
write(message)
|
||||||
|
println(writer.String())
|
||||||
|
assert.True(t, strings.Contains(writer.String(), message))
|
||||||
|
}
|
||||||
|
|
||||||
func testSetLevelTwiceWithMode(t *testing.T, mode string) {
|
func testSetLevelTwiceWithMode(t *testing.T, mode string) {
|
||||||
SetUp(LogConf{
|
SetUp(LogConf{
|
||||||
Mode: mode,
|
Mode: mode,
|
||||||
@@ -456,3 +539,11 @@ func testSetLevelTwiceWithMode(t *testing.T, mode string) {
|
|||||||
ErrorStackf(message)
|
ErrorStackf(message)
|
||||||
assert.Equal(t, 0, writer.builder.Len())
|
assert.Equal(t, 0, writer.builder.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ValStringer struct {
|
||||||
|
val string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v ValStringer) String() string {
|
||||||
|
return v.val
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ func TestRedirector(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func captureOutput(f func()) string {
|
func captureOutput(f func()) string {
|
||||||
atomic.StoreUint32(&initialized, 1)
|
|
||||||
writer := new(mockWriter)
|
writer := new(mockWriter)
|
||||||
infoLog = writer
|
infoLog = writer
|
||||||
|
atomic.StoreUint32(&initialized, 1)
|
||||||
|
|
||||||
prevLevel := atomic.LoadUint32(&logLevel)
|
prevLevel := atomic.LoadUint32(&logLevel)
|
||||||
SetLevel(InfoLevel)
|
SetLevel(InfoLevel)
|
||||||
@@ -44,5 +44,9 @@ func captureOutput(f func()) string {
|
|||||||
func getContent(jsonStr string) string {
|
func getContent(jsonStr string) string {
|
||||||
var entry logEntry
|
var entry logEntry
|
||||||
json.Unmarshal([]byte(jsonStr), &entry)
|
json.Unmarshal([]byte(jsonStr), &entry)
|
||||||
return entry.Content.(string)
|
val, ok := entry.Content.(string)
|
||||||
|
if ok {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/timex"
|
"github.com/zeromicro/go-zero/core/timex"
|
||||||
@@ -77,16 +78,24 @@ func (l *traceLogger) WithDuration(duration time.Duration) Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *traceLogger) write(writer io.Writer, level string, val interface{}) {
|
func (l *traceLogger) write(writer io.Writer, level string, val interface{}) {
|
||||||
outputJson(writer, &traceLogger{
|
traceID := traceIdFromContext(l.ctx)
|
||||||
logEntry: logEntry{
|
spanID := spanIdFromContext(l.ctx)
|
||||||
Timestamp: getTimestamp(),
|
|
||||||
Level: level,
|
switch atomic.LoadUint32(&encoding) {
|
||||||
Duration: l.Duration,
|
case plainEncodingType:
|
||||||
Content: val,
|
writePlainAny(writer, level, val, l.Duration, traceID, spanID)
|
||||||
},
|
default:
|
||||||
Trace: traceIdFromContext(l.ctx),
|
outputJson(writer, &traceLogger{
|
||||||
Span: spanIdFromContext(l.ctx),
|
logEntry: logEntry{
|
||||||
})
|
Timestamp: getTimestamp(),
|
||||||
|
Level: level,
|
||||||
|
Duration: l.Duration,
|
||||||
|
Content: val,
|
||||||
|
},
|
||||||
|
Trace: traceID,
|
||||||
|
Span: spanID,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithContext sets ctx to log, for keeping tracing information.
|
// WithContext sets ctx to log, for keeping tracing information.
|
||||||
|
|||||||
@@ -82,6 +82,37 @@ func TestTraceInfo(t *testing.T) {
|
|||||||
assert.True(t, strings.Contains(buf.String(), spanKey))
|
assert.True(t, strings.Contains(buf.String(), spanKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTraceInfoConsole(t *testing.T) {
|
||||||
|
old := atomic.LoadUint32(&encoding)
|
||||||
|
atomic.StoreUint32(&encoding, jsonEncodingType)
|
||||||
|
defer func() {
|
||||||
|
atomic.StoreUint32(&encoding, old)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var buf mockWriter
|
||||||
|
atomic.StoreUint32(&initialized, 1)
|
||||||
|
infoLog = newLogWriter(log.New(&buf, "", flags))
|
||||||
|
otp := otel.GetTracerProvider()
|
||||||
|
tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()))
|
||||||
|
otel.SetTracerProvider(tp)
|
||||||
|
defer otel.SetTracerProvider(otp)
|
||||||
|
|
||||||
|
ctx, _ := tp.Tracer("foo").Start(context.Background(), "bar")
|
||||||
|
l := WithContext(ctx).(*traceLogger)
|
||||||
|
SetLevel(InfoLevel)
|
||||||
|
l.WithDuration(time.Second).Info(testlog)
|
||||||
|
assert.True(t, strings.Contains(buf.String(), traceIdFromContext(ctx)))
|
||||||
|
assert.True(t, strings.Contains(buf.String(), spanIdFromContext(ctx)))
|
||||||
|
buf.Reset()
|
||||||
|
l.WithDuration(time.Second).Infof(testlog)
|
||||||
|
assert.True(t, strings.Contains(buf.String(), traceIdFromContext(ctx)))
|
||||||
|
assert.True(t, strings.Contains(buf.String(), spanIdFromContext(ctx)))
|
||||||
|
buf.Reset()
|
||||||
|
l.WithDuration(time.Second).Infov(testlog)
|
||||||
|
assert.True(t, strings.Contains(buf.String(), traceIdFromContext(ctx)))
|
||||||
|
assert.True(t, strings.Contains(buf.String(), spanIdFromContext(ctx)))
|
||||||
|
}
|
||||||
|
|
||||||
func TestTraceSlow(t *testing.T) {
|
func TestTraceSlow(t *testing.T) {
|
||||||
var buf mockWriter
|
var buf mockWriter
|
||||||
atomic.StoreUint32(&initialized, 1)
|
atomic.StoreUint32(&initialized, 1)
|
||||||
|
|||||||
@@ -448,7 +448,15 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, map
|
|||||||
dereffedBaseType := Deref(baseType)
|
dereffedBaseType := Deref(baseType)
|
||||||
dereffedBaseKind := dereffedBaseType.Kind()
|
dereffedBaseKind := dereffedBaseType.Kind()
|
||||||
refValue := reflect.ValueOf(mapValue)
|
refValue := reflect.ValueOf(mapValue)
|
||||||
|
if refValue.IsNil() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
conv := reflect.MakeSlice(reflect.SliceOf(baseType), refValue.Len(), refValue.Cap())
|
conv := reflect.MakeSlice(reflect.SliceOf(baseType), refValue.Len(), refValue.Cap())
|
||||||
|
if refValue.Len() == 0 {
|
||||||
|
value.Set(conv)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var valid bool
|
var valid bool
|
||||||
for i := 0; i < refValue.Len(); i++ {
|
for i := 0; i < refValue.Len(); i++ {
|
||||||
@@ -742,7 +750,9 @@ func getValueWithChainedKeys(m Valuer, keys []string) (interface{}, bool) {
|
|||||||
if len(keys) == 1 {
|
if len(keys) == 1 {
|
||||||
v, ok := m.Value(keys[0])
|
v, ok := m.Value(keys[0])
|
||||||
return v, ok
|
return v, ok
|
||||||
} else if len(keys) > 1 {
|
}
|
||||||
|
|
||||||
|
if len(keys) > 1 {
|
||||||
if v, ok := m.Value(keys[0]); ok {
|
if v, ok := m.Value(keys[0]); ok {
|
||||||
if nextm, ok := v.(map[string]interface{}); ok {
|
if nextm, ok := v.(map[string]interface{}); ok {
|
||||||
return getValueWithChainedKeys(MapValuer(nextm), keys[1:])
|
return getValueWithChainedKeys(MapValuer(nextm), keys[1:])
|
||||||
|
|||||||
@@ -198,6 +198,49 @@ func TestUnmarshalIntWithDefault(t *testing.T) {
|
|||||||
assert.Equal(t, 1, in.Int)
|
assert.Equal(t, 1, in.Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalBoolSliceRequired(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Bools []bool `key:"bools"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.NotNil(t, UnmarshalKey(map[string]interface{}{}, &in))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalBoolSliceNil(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Bools []bool `key:"bools,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Nil(t, UnmarshalKey(map[string]interface{}{}, &in))
|
||||||
|
assert.Nil(t, in.Bools)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalBoolSliceNilExplicit(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Bools []bool `key:"bools,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Nil(t, UnmarshalKey(map[string]interface{}{
|
||||||
|
"bools": nil,
|
||||||
|
}, &in))
|
||||||
|
assert.Nil(t, in.Bools)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalBoolSliceEmpty(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Bools []bool `key:"bools,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Nil(t, UnmarshalKey(map[string]interface{}{
|
||||||
|
"bools": []bool{},
|
||||||
|
}, &in))
|
||||||
|
assert.Empty(t, in.Bools)
|
||||||
|
}
|
||||||
|
|
||||||
func TestUnmarshalBoolSliceWithDefault(t *testing.T) {
|
func TestUnmarshalBoolSliceWithDefault(t *testing.T) {
|
||||||
type inner struct {
|
type inner struct {
|
||||||
Bools []bool `key:"bools,default=[true,false]"`
|
Bools []bool `key:"bools,default=[true,false]"`
|
||||||
@@ -330,28 +373,34 @@ func TestUnmarshalFloat(t *testing.T) {
|
|||||||
|
|
||||||
func TestUnmarshalInt64Slice(t *testing.T) {
|
func TestUnmarshalInt64Slice(t *testing.T) {
|
||||||
var v struct {
|
var v struct {
|
||||||
Ages []int64 `key:"ages"`
|
Ages []int64 `key:"ages"`
|
||||||
|
Slice []int64 `key:"slice"`
|
||||||
}
|
}
|
||||||
m := map[string]interface{}{
|
m := map[string]interface{}{
|
||||||
"ages": []int64{1, 2},
|
"ages": []int64{1, 2},
|
||||||
|
"slice": []interface{}{},
|
||||||
}
|
}
|
||||||
|
|
||||||
ast := assert.New(t)
|
ast := assert.New(t)
|
||||||
ast.Nil(UnmarshalKey(m, &v))
|
ast.Nil(UnmarshalKey(m, &v))
|
||||||
ast.ElementsMatch([]int64{1, 2}, v.Ages)
|
ast.ElementsMatch([]int64{1, 2}, v.Ages)
|
||||||
|
ast.Equal([]int64{}, v.Slice)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalIntSlice(t *testing.T) {
|
func TestUnmarshalIntSlice(t *testing.T) {
|
||||||
var v struct {
|
var v struct {
|
||||||
Ages []int `key:"ages"`
|
Ages []int `key:"ages"`
|
||||||
|
Slice []int `key:"slice"`
|
||||||
}
|
}
|
||||||
m := map[string]interface{}{
|
m := map[string]interface{}{
|
||||||
"ages": []int{1, 2},
|
"ages": []int{1, 2},
|
||||||
|
"slice": []interface{}{},
|
||||||
}
|
}
|
||||||
|
|
||||||
ast := assert.New(t)
|
ast := assert.New(t)
|
||||||
ast.Nil(UnmarshalKey(m, &v))
|
ast.Nil(UnmarshalKey(m, &v))
|
||||||
ast.ElementsMatch([]int{1, 2}, v.Ages)
|
ast.ElementsMatch([]int{1, 2}, v.Ages)
|
||||||
|
ast.Equal([]int{}, v.Slice)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalString(t *testing.T) {
|
func TestUnmarshalString(t *testing.T) {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
@@ -14,7 +13,7 @@ const yamlTagKey = "json"
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrUnsupportedType is an error that indicates the config format is not supported.
|
// ErrUnsupportedType is an error that indicates the config format is not supported.
|
||||||
ErrUnsupportedType = errors.New("only map-like configs are suported")
|
ErrUnsupportedType = errors.New("only map-like configs are supported")
|
||||||
|
|
||||||
yamlUnmarshaler = NewUnmarshaler(yamlTagKey)
|
yamlUnmarshaler = NewUnmarshaler(yamlTagKey)
|
||||||
)
|
)
|
||||||
@@ -29,39 +28,6 @@ func UnmarshalYamlReader(reader io.Reader, v interface{}) error {
|
|||||||
return unmarshalYamlReader(reader, v, yamlUnmarshaler)
|
return unmarshalYamlReader(reader, v, yamlUnmarshaler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalYamlBytes(content []byte, v interface{}, unmarshaler *Unmarshaler) error {
|
|
||||||
var o interface{}
|
|
||||||
if err := yamlUnmarshal(content, &o); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if m, ok := o.(map[string]interface{}); ok {
|
|
||||||
return unmarshaler.Unmarshal(m, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ErrUnsupportedType
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalYamlReader(reader io.Reader, v interface{}, unmarshaler *Unmarshaler) error {
|
|
||||||
content, err := ioutil.ReadAll(reader)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return unmarshalYamlBytes(content, v, unmarshaler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// yamlUnmarshal YAML to map[string]interface{} instead of map[interface{}]interface{}.
|
|
||||||
func yamlUnmarshal(in []byte, out interface{}) error {
|
|
||||||
var res interface{}
|
|
||||||
if err := yaml.Unmarshal(in, &res); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*out.(*interface{}) = cleanupMapValue(res)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanupInterfaceMap(in map[interface{}]interface{}) map[string]interface{} {
|
func cleanupInterfaceMap(in map[interface{}]interface{}) map[string]interface{} {
|
||||||
res := make(map[string]interface{})
|
res := make(map[string]interface{})
|
||||||
for k, v := range in {
|
for k, v := range in {
|
||||||
@@ -96,3 +62,40 @@ func cleanupMapValue(v interface{}) interface{} {
|
|||||||
return Repr(v)
|
return Repr(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unmarshal(unmarshaler *Unmarshaler, o interface{}, v interface{}) error {
|
||||||
|
if m, ok := o.(map[string]interface{}); ok {
|
||||||
|
return unmarshaler.Unmarshal(m, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrUnsupportedType
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalYamlBytes(content []byte, v interface{}, unmarshaler *Unmarshaler) error {
|
||||||
|
var o interface{}
|
||||||
|
if err := yamlUnmarshal(content, &o); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshal(unmarshaler, o, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalYamlReader(reader io.Reader, v interface{}, unmarshaler *Unmarshaler) error {
|
||||||
|
var res interface{}
|
||||||
|
if err := yaml.NewDecoder(reader).Decode(&res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshal(unmarshaler, cleanupMapValue(res), v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// yamlUnmarshal YAML to map[string]interface{} instead of map[interface{}]interface{}.
|
||||||
|
func yamlUnmarshal(in []byte, out interface{}) error {
|
||||||
|
var res interface{}
|
||||||
|
if err := yaml.Unmarshal(in, &res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*out.(*interface{}) = cleanupMapValue(res)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -926,14 +926,17 @@ func TestUnmarshalYamlBytesError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalYamlReaderError(t *testing.T) {
|
func TestUnmarshalYamlReaderError(t *testing.T) {
|
||||||
payload := `abcd: cdef`
|
|
||||||
reader := strings.NewReader(payload)
|
|
||||||
var v struct {
|
var v struct {
|
||||||
Any string
|
Any string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reader := strings.NewReader(`abcd: cdef`)
|
||||||
err := UnmarshalYamlReader(reader, &v)
|
err := UnmarshalYamlReader(reader, &v)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
reader = strings.NewReader("chenquan")
|
||||||
|
err = UnmarshalYamlReader(reader, &v)
|
||||||
|
assert.ErrorIs(t, err, ErrUnsupportedType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalYamlBadReader(t *testing.T) {
|
func TestUnmarshalYamlBadReader(t *testing.T) {
|
||||||
@@ -1011,6 +1014,6 @@ func TestUnmarshalYamlMapRune(t *testing.T) {
|
|||||||
|
|
||||||
type badReader struct{}
|
type badReader struct{}
|
||||||
|
|
||||||
func (b *badReader) Read(p []byte) (n int, err error) {
|
func (b *badReader) Read(_ []byte) (n int, err error) {
|
||||||
return 0, io.ErrLimitReached
|
return 0, io.ErrLimitReached
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ package mr
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/errorx"
|
"github.com/zeromicro/go-zero/core/errorx"
|
||||||
"github.com/zeromicro/go-zero/core/lang"
|
"github.com/zeromicro/go-zero/core/lang"
|
||||||
"github.com/zeromicro/go-zero/core/threading"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -24,12 +23,12 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// ForEachFunc is used to do element processing, but no output.
|
||||||
|
ForEachFunc func(item interface{})
|
||||||
// GenerateFunc is used to let callers send elements into source.
|
// GenerateFunc is used to let callers send elements into source.
|
||||||
GenerateFunc func(source chan<- interface{})
|
GenerateFunc func(source chan<- interface{})
|
||||||
// MapFunc is used to do element processing and write the output to writer.
|
// MapFunc is used to do element processing and write the output to writer.
|
||||||
MapFunc func(item interface{}, writer Writer)
|
MapFunc func(item interface{}, writer Writer)
|
||||||
// VoidMapFunc is used to do element processing, but no output.
|
|
||||||
VoidMapFunc func(item interface{})
|
|
||||||
// MapperFunc is used to do element processing and write the output to writer,
|
// MapperFunc is used to do element processing and write the output to writer,
|
||||||
// use cancel func to cancel the processing.
|
// use cancel func to cancel the processing.
|
||||||
MapperFunc func(item interface{}, writer Writer, cancel func(error))
|
MapperFunc func(item interface{}, writer Writer, cancel func(error))
|
||||||
@@ -42,6 +41,16 @@ type (
|
|||||||
// Option defines the method to customize the mapreduce.
|
// Option defines the method to customize the mapreduce.
|
||||||
Option func(opts *mapReduceOptions)
|
Option func(opts *mapReduceOptions)
|
||||||
|
|
||||||
|
mapperContext struct {
|
||||||
|
ctx context.Context
|
||||||
|
mapper MapFunc
|
||||||
|
source <-chan interface{}
|
||||||
|
panicChan *onceChan
|
||||||
|
collector chan<- interface{}
|
||||||
|
doneChan <-chan lang.PlaceholderType
|
||||||
|
workers int
|
||||||
|
}
|
||||||
|
|
||||||
mapReduceOptions struct {
|
mapReduceOptions struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
workers int
|
workers int
|
||||||
@@ -69,7 +78,6 @@ func Finish(fns ...func() error) error {
|
|||||||
cancel(err)
|
cancel(err)
|
||||||
}
|
}
|
||||||
}, func(pipe <-chan interface{}, cancel func(error)) {
|
}, func(pipe <-chan interface{}, cancel func(error)) {
|
||||||
drain(pipe)
|
|
||||||
}, WithWorkers(len(fns)))
|
}, WithWorkers(len(fns)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +87,7 @@ func FinishVoid(fns ...func()) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
MapVoid(func(source chan<- interface{}) {
|
ForEach(func(source chan<- interface{}) {
|
||||||
for _, fn := range fns {
|
for _, fn := range fns {
|
||||||
source <- fn
|
source <- fn
|
||||||
}
|
}
|
||||||
@@ -89,41 +97,74 @@ func FinishVoid(fns ...func()) {
|
|||||||
}, WithWorkers(len(fns)))
|
}, WithWorkers(len(fns)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map maps all elements generated from given generate func, and returns an output channel.
|
// ForEach maps all elements from given generate but no output.
|
||||||
func Map(generate GenerateFunc, mapper MapFunc, opts ...Option) chan interface{} {
|
func ForEach(generate GenerateFunc, mapper ForEachFunc, opts ...Option) {
|
||||||
options := buildOptions(opts...)
|
options := buildOptions(opts...)
|
||||||
source := buildSource(generate)
|
panicChan := &onceChan{channel: make(chan interface{})}
|
||||||
|
source := buildSource(generate, panicChan)
|
||||||
collector := make(chan interface{}, options.workers)
|
collector := make(chan interface{}, options.workers)
|
||||||
done := make(chan lang.PlaceholderType)
|
done := make(chan lang.PlaceholderType)
|
||||||
|
|
||||||
go executeMappers(options.ctx, mapper, source, collector, done, options.workers)
|
go executeMappers(mapperContext{
|
||||||
|
ctx: options.ctx,
|
||||||
|
mapper: func(item interface{}, writer Writer) {
|
||||||
|
mapper(item)
|
||||||
|
},
|
||||||
|
source: source,
|
||||||
|
panicChan: panicChan,
|
||||||
|
collector: collector,
|
||||||
|
doneChan: done,
|
||||||
|
workers: options.workers,
|
||||||
|
})
|
||||||
|
|
||||||
return collector
|
for {
|
||||||
|
select {
|
||||||
|
case v := <-panicChan.channel:
|
||||||
|
panic(v)
|
||||||
|
case _, ok := <-collector:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapReduce maps all elements generated from given generate func,
|
// MapReduce maps all elements generated from given generate func,
|
||||||
// and reduces the output elements with given reducer.
|
// and reduces the output elements with given reducer.
|
||||||
func MapReduce(generate GenerateFunc, mapper MapperFunc, reducer ReducerFunc,
|
func MapReduce(generate GenerateFunc, mapper MapperFunc, reducer ReducerFunc,
|
||||||
opts ...Option) (interface{}, error) {
|
opts ...Option) (interface{}, error) {
|
||||||
source := buildSource(generate)
|
panicChan := &onceChan{channel: make(chan interface{})}
|
||||||
return MapReduceWithSource(source, mapper, reducer, opts...)
|
source := buildSource(generate, panicChan)
|
||||||
|
return mapReduceWithPanicChan(source, panicChan, mapper, reducer, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapReduceWithSource maps all elements from source, and reduce the output elements with given reducer.
|
// MapReduceChan maps all elements from source, and reduce the output elements with given reducer.
|
||||||
func MapReduceWithSource(source <-chan interface{}, mapper MapperFunc, reducer ReducerFunc,
|
func MapReduceChan(source <-chan interface{}, mapper MapperFunc, reducer ReducerFunc,
|
||||||
opts ...Option) (interface{}, error) {
|
opts ...Option) (interface{}, error) {
|
||||||
|
panicChan := &onceChan{channel: make(chan interface{})}
|
||||||
|
return mapReduceWithPanicChan(source, panicChan, mapper, reducer, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapReduceChan maps all elements from source, and reduce the output elements with given reducer.
|
||||||
|
func mapReduceWithPanicChan(source <-chan interface{}, panicChan *onceChan, mapper MapperFunc,
|
||||||
|
reducer ReducerFunc, opts ...Option) (interface{}, error) {
|
||||||
options := buildOptions(opts...)
|
options := buildOptions(opts...)
|
||||||
|
// output is used to write the final result
|
||||||
output := make(chan interface{})
|
output := make(chan interface{})
|
||||||
defer func() {
|
defer func() {
|
||||||
|
// reducer can only write once, if more, panic
|
||||||
for range output {
|
for range output {
|
||||||
panic("more than one element written in reducer")
|
panic("more than one element written in reducer")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// collector is used to collect data from mapper, and consume in reducer
|
||||||
collector := make(chan interface{}, options.workers)
|
collector := make(chan interface{}, options.workers)
|
||||||
|
// if done is closed, all mappers and reducer should stop processing
|
||||||
done := make(chan lang.PlaceholderType)
|
done := make(chan lang.PlaceholderType)
|
||||||
writer := newGuardedWriter(options.ctx, output, done)
|
writer := newGuardedWriter(options.ctx, output, done)
|
||||||
var closeOnce sync.Once
|
var closeOnce sync.Once
|
||||||
|
// use atomic.Value to avoid data race
|
||||||
var retErr errorx.AtomicError
|
var retErr errorx.AtomicError
|
||||||
finish := func() {
|
finish := func() {
|
||||||
closeOnce.Do(func() {
|
closeOnce.Do(func() {
|
||||||
@@ -145,28 +186,41 @@ func MapReduceWithSource(source <-chan interface{}, mapper MapperFunc, reducer R
|
|||||||
go func() {
|
go func() {
|
||||||
defer func() {
|
defer func() {
|
||||||
drain(collector)
|
drain(collector)
|
||||||
|
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
cancel(fmt.Errorf("%v", r))
|
panicChan.write(r)
|
||||||
} else {
|
|
||||||
finish()
|
|
||||||
}
|
}
|
||||||
|
finish()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
reducer(collector, writer, cancel)
|
reducer(collector, writer, cancel)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go executeMappers(options.ctx, func(item interface{}, w Writer) {
|
go executeMappers(mapperContext{
|
||||||
mapper(item, w, cancel)
|
ctx: options.ctx,
|
||||||
}, source, collector, done, options.workers)
|
mapper: func(item interface{}, w Writer) {
|
||||||
|
mapper(item, w, cancel)
|
||||||
|
},
|
||||||
|
source: source,
|
||||||
|
panicChan: panicChan,
|
||||||
|
collector: collector,
|
||||||
|
doneChan: done,
|
||||||
|
workers: options.workers,
|
||||||
|
})
|
||||||
|
|
||||||
value, ok := <-output
|
select {
|
||||||
if err := retErr.Load(); err != nil {
|
case <-options.ctx.Done():
|
||||||
return nil, err
|
cancel(context.DeadlineExceeded)
|
||||||
} else if ok {
|
return nil, context.DeadlineExceeded
|
||||||
return value, nil
|
case v := <-panicChan.channel:
|
||||||
} else {
|
panic(v)
|
||||||
return nil, ErrReduceNoOutput
|
case v, ok := <-output:
|
||||||
|
if err := retErr.Load(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if ok {
|
||||||
|
return v, nil
|
||||||
|
} else {
|
||||||
|
return nil, ErrReduceNoOutput
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,18 +229,12 @@ func MapReduceWithSource(source <-chan interface{}, mapper MapperFunc, reducer R
|
|||||||
func MapReduceVoid(generate GenerateFunc, mapper MapperFunc, reducer VoidReducerFunc, opts ...Option) error {
|
func MapReduceVoid(generate GenerateFunc, mapper MapperFunc, reducer VoidReducerFunc, opts ...Option) error {
|
||||||
_, err := MapReduce(generate, mapper, func(input <-chan interface{}, writer Writer, cancel func(error)) {
|
_, err := MapReduce(generate, mapper, func(input <-chan interface{}, writer Writer, cancel func(error)) {
|
||||||
reducer(input, cancel)
|
reducer(input, cancel)
|
||||||
// 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)
|
|
||||||
}, opts...)
|
}, opts...)
|
||||||
return err
|
if errors.Is(err, ErrReduceNoOutput) {
|
||||||
}
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MapVoid maps all elements from given generate but no output.
|
return err
|
||||||
func MapVoid(generate GenerateFunc, mapper VoidMapFunc, opts ...Option) {
|
|
||||||
drain(Map(generate, func(item interface{}, writer Writer) {
|
|
||||||
mapper(item)
|
|
||||||
}, opts...))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithContext customizes a mapreduce processing accepts a given ctx.
|
// WithContext customizes a mapreduce processing accepts a given ctx.
|
||||||
@@ -216,12 +264,18 @@ func buildOptions(opts ...Option) *mapReduceOptions {
|
|||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildSource(generate GenerateFunc) chan interface{} {
|
func buildSource(generate GenerateFunc, panicChan *onceChan) chan interface{} {
|
||||||
source := make(chan interface{})
|
source := make(chan interface{})
|
||||||
threading.GoSafe(func() {
|
go func() {
|
||||||
defer close(source)
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
panicChan.write(r)
|
||||||
|
}
|
||||||
|
close(source)
|
||||||
|
}()
|
||||||
|
|
||||||
generate(source)
|
generate(source)
|
||||||
})
|
}()
|
||||||
|
|
||||||
return source
|
return source
|
||||||
}
|
}
|
||||||
@@ -233,39 +287,43 @@ func drain(channel <-chan interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func executeMappers(ctx context.Context, mapper MapFunc, input <-chan interface{},
|
func executeMappers(mCtx mapperContext) {
|
||||||
collector chan<- interface{}, done <-chan lang.PlaceholderType, workers int) {
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
defer func() {
|
defer func() {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
close(collector)
|
close(mCtx.collector)
|
||||||
|
drain(mCtx.source)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
pool := make(chan lang.PlaceholderType, workers)
|
var failed int32
|
||||||
writer := newGuardedWriter(ctx, collector, done)
|
pool := make(chan lang.PlaceholderType, mCtx.workers)
|
||||||
for {
|
writer := newGuardedWriter(mCtx.ctx, mCtx.collector, mCtx.doneChan)
|
||||||
|
for atomic.LoadInt32(&failed) == 0 {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-mCtx.ctx.Done():
|
||||||
return
|
return
|
||||||
case <-done:
|
case <-mCtx.doneChan:
|
||||||
return
|
return
|
||||||
case pool <- lang.Placeholder:
|
case pool <- lang.Placeholder:
|
||||||
item, ok := <-input
|
item, ok := <-mCtx.source
|
||||||
if !ok {
|
if !ok {
|
||||||
<-pool
|
<-pool
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
// better to safely run caller defined method
|
go func() {
|
||||||
threading.GoSafe(func() {
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
atomic.AddInt32(&failed, 1)
|
||||||
|
mCtx.panicChan.write(r)
|
||||||
|
}
|
||||||
wg.Done()
|
wg.Done()
|
||||||
<-pool
|
<-pool
|
||||||
}()
|
}()
|
||||||
|
|
||||||
mapper(item, writer)
|
mCtx.mapper(item, writer)
|
||||||
})
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,3 +369,16 @@ func (gw guardedWriter) Write(v interface{}) {
|
|||||||
gw.channel <- v
|
gw.channel <- v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type onceChan struct {
|
||||||
|
channel chan interface{}
|
||||||
|
wrote int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oc *onceChan) write(val interface{}) {
|
||||||
|
if atomic.AddInt32(&oc.wrote, 1) > 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
oc.channel <- val
|
||||||
|
}
|
||||||
|
|||||||
78
core/mr/mapreduce_fuzz_test.go
Normal file
78
core/mr/mapreduce_fuzz_test.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
//go:build go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package mr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"go.uber.org/goleak"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FuzzMapReduce(f *testing.F) {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
f.Add(uint(10), uint(runtime.NumCPU()))
|
||||||
|
f.Fuzz(func(t *testing.T, num uint, workers uint) {
|
||||||
|
n := int64(num)%5000 + 5000
|
||||||
|
genPanic := rand.Intn(100) == 0
|
||||||
|
mapperPanic := rand.Intn(100) == 0
|
||||||
|
reducerPanic := rand.Intn(100) == 0
|
||||||
|
genIdx := rand.Int63n(n)
|
||||||
|
mapperIdx := rand.Int63n(n)
|
||||||
|
reducerIdx := rand.Int63n(n)
|
||||||
|
squareSum := (n - 1) * n * (2*n - 1) / 6
|
||||||
|
|
||||||
|
fn := func() (interface{}, error) {
|
||||||
|
defer goleak.VerifyNone(t, goleak.IgnoreCurrent())
|
||||||
|
|
||||||
|
return MapReduce(func(source chan<- interface{}) {
|
||||||
|
for i := int64(0); i < n; i++ {
|
||||||
|
source <- i
|
||||||
|
if genPanic && i == genIdx {
|
||||||
|
panic("foo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
|
v := item.(int64)
|
||||||
|
if mapperPanic && v == mapperIdx {
|
||||||
|
panic("bar")
|
||||||
|
}
|
||||||
|
writer.Write(v * v)
|
||||||
|
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
|
||||||
|
var idx int64
|
||||||
|
var total int64
|
||||||
|
for v := range pipe {
|
||||||
|
if reducerPanic && idx == reducerIdx {
|
||||||
|
panic("baz")
|
||||||
|
}
|
||||||
|
total += v.(int64)
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
writer.Write(total)
|
||||||
|
}, WithWorkers(int(workers)%50+runtime.NumCPU()/2))
|
||||||
|
}
|
||||||
|
|
||||||
|
if genPanic || mapperPanic || reducerPanic {
|
||||||
|
var buf strings.Builder
|
||||||
|
buf.WriteString(fmt.Sprintf("n: %d", n))
|
||||||
|
buf.WriteString(fmt.Sprintf(", genPanic: %t", genPanic))
|
||||||
|
buf.WriteString(fmt.Sprintf(", mapperPanic: %t", mapperPanic))
|
||||||
|
buf.WriteString(fmt.Sprintf(", reducerPanic: %t", reducerPanic))
|
||||||
|
buf.WriteString(fmt.Sprintf(", genIdx: %d", genIdx))
|
||||||
|
buf.WriteString(fmt.Sprintf(", mapperIdx: %d", mapperIdx))
|
||||||
|
buf.WriteString(fmt.Sprintf(", reducerIdx: %d", reducerIdx))
|
||||||
|
assert.Panicsf(t, func() { fn() }, buf.String())
|
||||||
|
} else {
|
||||||
|
val, err := fn()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, squareSum, val.(int64))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
107
core/mr/mapreduce_fuzzcase_test.go
Normal file
107
core/mr/mapreduce_fuzzcase_test.go
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
//go:build fuzz
|
||||||
|
// +build fuzz
|
||||||
|
|
||||||
|
package mr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/zeromicro/go-zero/core/threading"
|
||||||
|
"gopkg.in/cheggaaa/pb.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// If Fuzz stuck, we don't know why, because it only returns hung or unexpected,
|
||||||
|
// so we need to simulate the fuzz test in test mode.
|
||||||
|
func TestMapReduceRandom(t *testing.T) {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
const (
|
||||||
|
times = 10000
|
||||||
|
nRange = 500
|
||||||
|
mega = 1024 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
bar := pb.New(times).Start()
|
||||||
|
runner := threading.NewTaskRunner(runtime.NumCPU())
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(times)
|
||||||
|
for i := 0; i < times; i++ {
|
||||||
|
runner.Schedule(func() {
|
||||||
|
start := time.Now()
|
||||||
|
defer func() {
|
||||||
|
if time.Since(start) > time.Minute {
|
||||||
|
t.Fatal("timeout")
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
|
n := rand.Int63n(nRange)%nRange + nRange
|
||||||
|
workers := rand.Int()%50 + runtime.NumCPU()/2
|
||||||
|
genPanic := rand.Intn(100) == 0
|
||||||
|
mapperPanic := rand.Intn(100) == 0
|
||||||
|
reducerPanic := rand.Intn(100) == 0
|
||||||
|
genIdx := rand.Int63n(n)
|
||||||
|
mapperIdx := rand.Int63n(n)
|
||||||
|
reducerIdx := rand.Int63n(n)
|
||||||
|
squareSum := (n - 1) * n * (2*n - 1) / 6
|
||||||
|
|
||||||
|
fn := func() (interface{}, error) {
|
||||||
|
return MapReduce(func(source chan<- interface{}) {
|
||||||
|
for i := int64(0); i < n; i++ {
|
||||||
|
source <- i
|
||||||
|
if genPanic && i == genIdx {
|
||||||
|
panic("foo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
|
v := item.(int64)
|
||||||
|
if mapperPanic && v == mapperIdx {
|
||||||
|
panic("bar")
|
||||||
|
}
|
||||||
|
writer.Write(v * v)
|
||||||
|
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
|
||||||
|
var idx int64
|
||||||
|
var total int64
|
||||||
|
for v := range pipe {
|
||||||
|
if reducerPanic && idx == reducerIdx {
|
||||||
|
panic("baz")
|
||||||
|
}
|
||||||
|
total += v.(int64)
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
writer.Write(total)
|
||||||
|
}, WithWorkers(int(workers)%50+runtime.NumCPU()/2))
|
||||||
|
}
|
||||||
|
|
||||||
|
if genPanic || mapperPanic || reducerPanic {
|
||||||
|
var buf strings.Builder
|
||||||
|
buf.WriteString(fmt.Sprintf("n: %d", n))
|
||||||
|
buf.WriteString(fmt.Sprintf(", genPanic: %t", genPanic))
|
||||||
|
buf.WriteString(fmt.Sprintf(", mapperPanic: %t", mapperPanic))
|
||||||
|
buf.WriteString(fmt.Sprintf(", reducerPanic: %t", reducerPanic))
|
||||||
|
buf.WriteString(fmt.Sprintf(", genIdx: %d", genIdx))
|
||||||
|
buf.WriteString(fmt.Sprintf(", mapperIdx: %d", mapperIdx))
|
||||||
|
buf.WriteString(fmt.Sprintf(", reducerIdx: %d", reducerIdx))
|
||||||
|
assert.Panicsf(t, func() { fn() }, buf.String())
|
||||||
|
} else {
|
||||||
|
val, err := fn()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, squareSum, val.(int64))
|
||||||
|
}
|
||||||
|
bar.Increment()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
bar.Finish()
|
||||||
|
}
|
||||||
@@ -11,8 +11,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/zeromicro/go-zero/core/stringx"
|
"go.uber.org/goleak"
|
||||||
"github.com/zeromicro/go-zero/core/syncx"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var errDummy = errors.New("dummy")
|
var errDummy = errors.New("dummy")
|
||||||
@@ -22,6 +21,8 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFinish(t *testing.T) {
|
func TestFinish(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
var total uint32
|
var total uint32
|
||||||
err := Finish(func() error {
|
err := Finish(func() error {
|
||||||
atomic.AddUint32(&total, 2)
|
atomic.AddUint32(&total, 2)
|
||||||
@@ -39,14 +40,20 @@ func TestFinish(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFinishNone(t *testing.T) {
|
func TestFinishNone(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
assert.Nil(t, Finish())
|
assert.Nil(t, Finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFinishVoidNone(t *testing.T) {
|
func TestFinishVoidNone(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
FinishVoid()
|
FinishVoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFinishErr(t *testing.T) {
|
func TestFinishErr(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
var total uint32
|
var total uint32
|
||||||
err := Finish(func() error {
|
err := Finish(func() error {
|
||||||
atomic.AddUint32(&total, 2)
|
atomic.AddUint32(&total, 2)
|
||||||
@@ -63,6 +70,8 @@ func TestFinishErr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFinishVoid(t *testing.T) {
|
func TestFinishVoid(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
var total uint32
|
var total uint32
|
||||||
FinishVoid(func() {
|
FinishVoid(func() {
|
||||||
atomic.AddUint32(&total, 2)
|
atomic.AddUint32(&total, 2)
|
||||||
@@ -75,70 +84,107 @@ func TestFinishVoid(t *testing.T) {
|
|||||||
assert.Equal(t, uint32(10), atomic.LoadUint32(&total))
|
assert.Equal(t, uint32(10), atomic.LoadUint32(&total))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMap(t *testing.T) {
|
func TestForEach(t *testing.T) {
|
||||||
tests := []struct {
|
const tasks = 1000
|
||||||
mapper MapFunc
|
|
||||||
expect int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
mapper: func(item interface{}, writer Writer) {
|
|
||||||
v := item.(int)
|
|
||||||
writer.Write(v * v)
|
|
||||||
},
|
|
||||||
expect: 30,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
mapper: func(item interface{}, writer Writer) {
|
|
||||||
v := item.(int)
|
|
||||||
if v%2 == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writer.Write(v * v)
|
|
||||||
},
|
|
||||||
expect: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
mapper: func(item interface{}, writer Writer) {
|
|
||||||
v := item.(int)
|
|
||||||
if v%2 == 0 {
|
|
||||||
panic(v)
|
|
||||||
}
|
|
||||||
writer.Write(v * v)
|
|
||||||
},
|
|
||||||
expect: 10,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
t.Run("all", func(t *testing.T) {
|
||||||
t.Run(stringx.Rand(), func(t *testing.T) {
|
defer goleak.VerifyNone(t)
|
||||||
channel := Map(func(source chan<- interface{}) {
|
|
||||||
for i := 1; i < 5; i++ {
|
var count uint32
|
||||||
|
ForEach(func(source chan<- interface{}) {
|
||||||
|
for i := 0; i < tasks; i++ {
|
||||||
|
source <- i
|
||||||
|
}
|
||||||
|
}, func(item interface{}) {
|
||||||
|
atomic.AddUint32(&count, 1)
|
||||||
|
}, WithWorkers(-1))
|
||||||
|
|
||||||
|
assert.Equal(t, tasks, int(count))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("odd", func(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
|
var count uint32
|
||||||
|
ForEach(func(source chan<- interface{}) {
|
||||||
|
for i := 0; i < tasks; i++ {
|
||||||
|
source <- i
|
||||||
|
}
|
||||||
|
}, func(item interface{}) {
|
||||||
|
if item.(int)%2 == 0 {
|
||||||
|
atomic.AddUint32(&count, 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, tasks/2, int(count))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("all", func(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
|
assert.PanicsWithValue(t, "foo", func() {
|
||||||
|
ForEach(func(source chan<- interface{}) {
|
||||||
|
for i := 0; i < tasks; i++ {
|
||||||
source <- i
|
source <- i
|
||||||
}
|
}
|
||||||
}, test.mapper, WithWorkers(-1))
|
}, func(item interface{}) {
|
||||||
|
panic("foo")
|
||||||
var result int
|
})
|
||||||
for v := range channel {
|
|
||||||
result += v.(int)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, test.expect, result)
|
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGeneratePanic(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
|
t.Run("all", func(t *testing.T) {
|
||||||
|
assert.PanicsWithValue(t, "foo", func() {
|
||||||
|
ForEach(func(source chan<- interface{}) {
|
||||||
|
panic("foo")
|
||||||
|
}, func(item interface{}) {
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapperPanic(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
|
const tasks = 1000
|
||||||
|
var run int32
|
||||||
|
t.Run("all", func(t *testing.T) {
|
||||||
|
assert.PanicsWithValue(t, "foo", func() {
|
||||||
|
_, _ = MapReduce(func(source chan<- interface{}) {
|
||||||
|
for i := 0; i < tasks; i++ {
|
||||||
|
source <- i
|
||||||
|
}
|
||||||
|
}, func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
|
atomic.AddInt32(&run, 1)
|
||||||
|
panic("foo")
|
||||||
|
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
|
||||||
|
})
|
||||||
|
})
|
||||||
|
assert.True(t, atomic.LoadInt32(&run) < tasks/2)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapReduce(t *testing.T) {
|
func TestMapReduce(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
name string
|
||||||
mapper MapperFunc
|
mapper MapperFunc
|
||||||
reducer ReducerFunc
|
reducer ReducerFunc
|
||||||
expectErr error
|
expectErr error
|
||||||
expectValue interface{}
|
expectValue interface{}
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
name: "simple",
|
||||||
expectErr: nil,
|
expectErr: nil,
|
||||||
expectValue: 30,
|
expectValue: 30,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: "cancel with error",
|
||||||
mapper: func(item interface{}, writer Writer, cancel func(error)) {
|
mapper: func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
v := item.(int)
|
v := item.(int)
|
||||||
if v%3 == 0 {
|
if v%3 == 0 {
|
||||||
@@ -149,6 +195,7 @@ func TestMapReduce(t *testing.T) {
|
|||||||
expectErr: errDummy,
|
expectErr: errDummy,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: "cancel with nil",
|
||||||
mapper: func(item interface{}, writer Writer, cancel func(error)) {
|
mapper: func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
v := item.(int)
|
v := item.(int)
|
||||||
if v%3 == 0 {
|
if v%3 == 0 {
|
||||||
@@ -160,6 +207,7 @@ func TestMapReduce(t *testing.T) {
|
|||||||
expectValue: nil,
|
expectValue: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: "cancel with more",
|
||||||
reducer: func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
|
reducer: func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
|
||||||
var result int
|
var result int
|
||||||
for item := range pipe {
|
for item := range pipe {
|
||||||
@@ -174,36 +222,74 @@ func TestMapReduce(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
t.Run("MapReduce", func(t *testing.T) {
|
||||||
t.Run(stringx.Rand(), func(t *testing.T) {
|
for _, test := range tests {
|
||||||
if test.mapper == nil {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
test.mapper = func(item interface{}, writer Writer, cancel func(error)) {
|
if test.mapper == nil {
|
||||||
v := item.(int)
|
test.mapper = func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
writer.Write(v * v)
|
v := item.(int)
|
||||||
}
|
writer.Write(v * v)
|
||||||
}
|
|
||||||
if test.reducer == nil {
|
|
||||||
test.reducer = func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
|
|
||||||
var result int
|
|
||||||
for item := range pipe {
|
|
||||||
result += item.(int)
|
|
||||||
}
|
}
|
||||||
writer.Write(result)
|
|
||||||
}
|
}
|
||||||
}
|
if test.reducer == nil {
|
||||||
value, err := MapReduce(func(source chan<- interface{}) {
|
test.reducer = func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
|
||||||
for i := 1; i < 5; i++ {
|
var result int
|
||||||
source <- i
|
for item := range pipe {
|
||||||
|
result += item.(int)
|
||||||
|
}
|
||||||
|
writer.Write(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, test.mapper, test.reducer, WithWorkers(runtime.NumCPU()))
|
value, err := MapReduce(func(source chan<- interface{}) {
|
||||||
|
for i := 1; i < 5; i++ {
|
||||||
|
source <- i
|
||||||
|
}
|
||||||
|
}, test.mapper, test.reducer, WithWorkers(runtime.NumCPU()))
|
||||||
|
|
||||||
assert.Equal(t, test.expectErr, err)
|
assert.Equal(t, test.expectErr, err)
|
||||||
assert.Equal(t, test.expectValue, value)
|
assert.Equal(t, test.expectValue, value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("MapReduce", func(t *testing.T) {
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
if test.mapper == nil {
|
||||||
|
test.mapper = func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
|
v := item.(int)
|
||||||
|
writer.Write(v * v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if test.reducer == nil {
|
||||||
|
test.reducer = func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
|
||||||
|
var result int
|
||||||
|
for item := range pipe {
|
||||||
|
result += item.(int)
|
||||||
|
}
|
||||||
|
writer.Write(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
source := make(chan interface{})
|
||||||
|
go func() {
|
||||||
|
for i := 1; i < 5; i++ {
|
||||||
|
source <- i
|
||||||
|
}
|
||||||
|
close(source)
|
||||||
|
}()
|
||||||
|
|
||||||
|
value, err := MapReduceChan(source, test.mapper, test.reducer, WithWorkers(-1))
|
||||||
|
assert.Equal(t, test.expectErr, err)
|
||||||
|
assert.Equal(t, test.expectValue, value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapReduceWithReduerWriteMoreThanOnce(t *testing.T) {
|
func TestMapReduceWithReduerWriteMoreThanOnce(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
assert.Panics(t, func() {
|
assert.Panics(t, func() {
|
||||||
MapReduce(func(source chan<- interface{}) {
|
MapReduce(func(source chan<- interface{}) {
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
@@ -220,18 +306,23 @@ func TestMapReduceWithReduerWriteMoreThanOnce(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMapReduceVoid(t *testing.T) {
|
func TestMapReduceVoid(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
var value uint32
|
var value uint32
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
name string
|
||||||
mapper MapperFunc
|
mapper MapperFunc
|
||||||
reducer VoidReducerFunc
|
reducer VoidReducerFunc
|
||||||
expectValue uint32
|
expectValue uint32
|
||||||
expectErr error
|
expectErr error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
name: "simple",
|
||||||
expectValue: 30,
|
expectValue: 30,
|
||||||
expectErr: nil,
|
expectErr: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: "cancel with error",
|
||||||
mapper: func(item interface{}, writer Writer, cancel func(error)) {
|
mapper: func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
v := item.(int)
|
v := item.(int)
|
||||||
if v%3 == 0 {
|
if v%3 == 0 {
|
||||||
@@ -242,6 +333,7 @@ func TestMapReduceVoid(t *testing.T) {
|
|||||||
expectErr: errDummy,
|
expectErr: errDummy,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: "cancel with nil",
|
||||||
mapper: func(item interface{}, writer Writer, cancel func(error)) {
|
mapper: func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
v := item.(int)
|
v := item.(int)
|
||||||
if v%3 == 0 {
|
if v%3 == 0 {
|
||||||
@@ -252,6 +344,7 @@ func TestMapReduceVoid(t *testing.T) {
|
|||||||
expectErr: ErrCancelWithNil,
|
expectErr: ErrCancelWithNil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: "cancel with more",
|
||||||
reducer: func(pipe <-chan interface{}, cancel func(error)) {
|
reducer: func(pipe <-chan interface{}, cancel func(error)) {
|
||||||
for item := range pipe {
|
for item := range pipe {
|
||||||
result := atomic.AddUint32(&value, uint32(item.(int)))
|
result := atomic.AddUint32(&value, uint32(item.(int)))
|
||||||
@@ -265,7 +358,7 @@ func TestMapReduceVoid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(stringx.Rand(), func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
atomic.StoreUint32(&value, 0)
|
atomic.StoreUint32(&value, 0)
|
||||||
|
|
||||||
if test.mapper == nil {
|
if test.mapper == nil {
|
||||||
@@ -296,6 +389,8 @@ func TestMapReduceVoid(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMapReduceVoidWithDelay(t *testing.T) {
|
func TestMapReduceVoidWithDelay(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
var result []int
|
var result []int
|
||||||
err := MapReduceVoid(func(source chan<- interface{}) {
|
err := MapReduceVoid(func(source chan<- interface{}) {
|
||||||
source <- 0
|
source <- 0
|
||||||
@@ -318,38 +413,64 @@ func TestMapReduceVoidWithDelay(t *testing.T) {
|
|||||||
assert.Equal(t, 0, result[1])
|
assert.Equal(t, 0, result[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapVoid(t *testing.T) {
|
func TestMapReducePanic(t *testing.T) {
|
||||||
const tasks = 1000
|
defer goleak.VerifyNone(t)
|
||||||
var count uint32
|
|
||||||
MapVoid(func(source chan<- interface{}) {
|
|
||||||
for i := 0; i < tasks; i++ {
|
|
||||||
source <- i
|
|
||||||
}
|
|
||||||
}, func(item interface{}) {
|
|
||||||
atomic.AddUint32(&count, 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
assert.Equal(t, tasks, int(count))
|
assert.Panics(t, func() {
|
||||||
|
_, _ = MapReduce(func(source chan<- interface{}) {
|
||||||
|
source <- 0
|
||||||
|
source <- 1
|
||||||
|
}, func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
|
i := item.(int)
|
||||||
|
writer.Write(i)
|
||||||
|
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
|
||||||
|
for range pipe {
|
||||||
|
panic("panic")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapReducePanic(t *testing.T) {
|
func TestMapReducePanicOnce(t *testing.T) {
|
||||||
v, err := MapReduce(func(source chan<- interface{}) {
|
defer goleak.VerifyNone(t)
|
||||||
source <- 0
|
|
||||||
source <- 1
|
assert.Panics(t, func() {
|
||||||
}, func(item interface{}, writer Writer, cancel func(error)) {
|
_, _ = MapReduce(func(source chan<- interface{}) {
|
||||||
i := item.(int)
|
for i := 0; i < 100; i++ {
|
||||||
writer.Write(i)
|
source <- i
|
||||||
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
|
}
|
||||||
for range pipe {
|
}, func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
panic("panic")
|
i := item.(int)
|
||||||
}
|
if i == 0 {
|
||||||
|
panic("foo")
|
||||||
|
}
|
||||||
|
writer.Write(i)
|
||||||
|
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
|
||||||
|
for range pipe {
|
||||||
|
panic("bar")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapReducePanicBothMapperAndReducer(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
|
assert.Panics(t, func() {
|
||||||
|
_, _ = MapReduce(func(source chan<- interface{}) {
|
||||||
|
source <- 0
|
||||||
|
source <- 1
|
||||||
|
}, func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
|
panic("foo")
|
||||||
|
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) {
|
||||||
|
panic("bar")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
assert.Nil(t, v)
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
assert.Equal(t, "panic", err.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapReduceVoidCancel(t *testing.T) {
|
func TestMapReduceVoidCancel(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
var result []int
|
var result []int
|
||||||
err := MapReduceVoid(func(source chan<- interface{}) {
|
err := MapReduceVoid(func(source chan<- interface{}) {
|
||||||
source <- 0
|
source <- 0
|
||||||
@@ -371,13 +492,15 @@ func TestMapReduceVoidCancel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMapReduceVoidCancelWithRemains(t *testing.T) {
|
func TestMapReduceVoidCancelWithRemains(t *testing.T) {
|
||||||
var done syncx.AtomicBool
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
|
var done int32
|
||||||
var result []int
|
var result []int
|
||||||
err := MapReduceVoid(func(source chan<- interface{}) {
|
err := MapReduceVoid(func(source chan<- interface{}) {
|
||||||
for i := 0; i < defaultWorkers*2; i++ {
|
for i := 0; i < defaultWorkers*2; i++ {
|
||||||
source <- i
|
source <- i
|
||||||
}
|
}
|
||||||
done.Set(true)
|
atomic.AddInt32(&done, 1)
|
||||||
}, func(item interface{}, writer Writer, cancel func(error)) {
|
}, func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
i := item.(int)
|
i := item.(int)
|
||||||
if i == defaultWorkers/2 {
|
if i == defaultWorkers/2 {
|
||||||
@@ -392,10 +515,12 @@ func TestMapReduceVoidCancelWithRemains(t *testing.T) {
|
|||||||
})
|
})
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
assert.Equal(t, "anything", err.Error())
|
assert.Equal(t, "anything", err.Error())
|
||||||
assert.True(t, done.True())
|
assert.Equal(t, int32(1), done)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapReduceWithoutReducerWrite(t *testing.T) {
|
func TestMapReduceWithoutReducerWrite(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
uids := []int{1, 2, 3}
|
uids := []int{1, 2, 3}
|
||||||
res, err := MapReduce(func(source chan<- interface{}) {
|
res, err := MapReduce(func(source chan<- interface{}) {
|
||||||
for _, uid := range uids {
|
for _, uid := range uids {
|
||||||
@@ -412,33 +537,54 @@ func TestMapReduceWithoutReducerWrite(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMapReduceVoidPanicInReducer(t *testing.T) {
|
func TestMapReduceVoidPanicInReducer(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
const message = "foo"
|
const message = "foo"
|
||||||
var done syncx.AtomicBool
|
assert.Panics(t, func() {
|
||||||
err := MapReduceVoid(func(source chan<- interface{}) {
|
var done int32
|
||||||
|
_ = MapReduceVoid(func(source chan<- interface{}) {
|
||||||
|
for i := 0; i < defaultWorkers*2; i++ {
|
||||||
|
source <- i
|
||||||
|
}
|
||||||
|
atomic.AddInt32(&done, 1)
|
||||||
|
}, func(item interface{}, writer Writer, cancel func(error)) {
|
||||||
|
i := item.(int)
|
||||||
|
writer.Write(i)
|
||||||
|
}, func(pipe <-chan interface{}, cancel func(error)) {
|
||||||
|
panic(message)
|
||||||
|
}, WithWorkers(1))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestForEachWithContext(t *testing.T) {
|
||||||
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
|
var done int32
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
ForEach(func(source chan<- interface{}) {
|
||||||
for i := 0; i < defaultWorkers*2; i++ {
|
for i := 0; i < defaultWorkers*2; i++ {
|
||||||
source <- i
|
source <- i
|
||||||
}
|
}
|
||||||
done.Set(true)
|
atomic.AddInt32(&done, 1)
|
||||||
}, func(item interface{}, writer Writer, cancel func(error)) {
|
}, func(item interface{}) {
|
||||||
i := item.(int)
|
i := item.(int)
|
||||||
writer.Write(i)
|
if i == defaultWorkers/2 {
|
||||||
}, func(pipe <-chan interface{}, cancel func(error)) {
|
cancel()
|
||||||
panic(message)
|
}
|
||||||
}, WithWorkers(1))
|
}, WithContext(ctx))
|
||||||
assert.NotNil(t, err)
|
|
||||||
assert.Equal(t, message, err.Error())
|
|
||||||
assert.True(t, done.True())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMapReduceWithContext(t *testing.T) {
|
func TestMapReduceWithContext(t *testing.T) {
|
||||||
var done syncx.AtomicBool
|
defer goleak.VerifyNone(t)
|
||||||
|
|
||||||
|
var done int32
|
||||||
var result []int
|
var result []int
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
err := MapReduceVoid(func(source chan<- interface{}) {
|
err := MapReduceVoid(func(source chan<- interface{}) {
|
||||||
for i := 0; i < defaultWorkers*2; i++ {
|
for i := 0; i < defaultWorkers*2; i++ {
|
||||||
source <- i
|
source <- i
|
||||||
}
|
}
|
||||||
done.Set(true)
|
atomic.AddInt32(&done, 1)
|
||||||
}, func(item interface{}, writer Writer, c func(error)) {
|
}, func(item interface{}, writer Writer, c func(error)) {
|
||||||
i := item.(int)
|
i := item.(int)
|
||||||
if i == defaultWorkers/2 {
|
if i == defaultWorkers/2 {
|
||||||
@@ -452,7 +598,7 @@ func TestMapReduceWithContext(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}, WithContext(ctx))
|
}, WithContext(ctx))
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
assert.Equal(t, ErrReduceNoOutput, err)
|
assert.Equal(t, context.DeadlineExceeded, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMapReduce(b *testing.B) {
|
func BenchmarkMapReduce(b *testing.B) {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/mr"
|
"github.com/zeromicro/go-zero/core/mr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/mr"
|
"github.com/zeromicro/go-zero/core/mr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -87,4 +87,4 @@ More examples: [https://github.com/zeromicro/zero-examples/tree/main/mapreduce](
|
|||||||
|
|
||||||
## Give a Star! ⭐
|
## Give a Star! ⭐
|
||||||
|
|
||||||
If you like or are using this project to learn or start your solution, please give it a star. Thanks!
|
If you like or are using this project to learn or start your solution, please give it a star. Thanks!
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
//go:build linux || darwin
|
||||||
|
// +build linux darwin
|
||||||
|
|
||||||
package proc
|
package proc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
31
core/prof/runtime.go
Normal file
31
core/prof/runtime.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package prof
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultInterval = time.Second * 5
|
||||||
|
mega = 1024 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
// DisplayStats prints the goroutine, memory, GC stats with given interval, default to 5 seconds.
|
||||||
|
func DisplayStats(interval ...time.Duration) {
|
||||||
|
duration := defaultInterval
|
||||||
|
for _, val := range interval {
|
||||||
|
duration = val
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(duration)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for range ticker.C {
|
||||||
|
var m runtime.MemStats
|
||||||
|
runtime.ReadMemStats(&m)
|
||||||
|
fmt.Printf("Goroutines: %d, Alloc: %vm, TotalAlloc: %vm, Sys: %vm, NumGC: %v\n",
|
||||||
|
runtime.NumGoroutine(), m.Alloc/mega, m.TotalAlloc/mega, m.Sys/mega, m.NumGC)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
@@ -1,78 +1,129 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/iox"
|
"github.com/zeromicro/go-zero/core/iox"
|
||||||
"github.com/zeromicro/go-zero/core/lang"
|
"github.com/zeromicro/go-zero/core/lang"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
const cgroupDir = "/sys/fs/cgroup"
|
const (
|
||||||
|
cgroupDir = "/sys/fs/cgroup"
|
||||||
|
cpuStatFile = cgroupDir + "/cpu.stat"
|
||||||
|
cpusetFile = cgroupDir + "/cpuset.cpus.effective"
|
||||||
|
)
|
||||||
|
|
||||||
type cgroup struct {
|
var (
|
||||||
|
isUnifiedOnce sync.Once
|
||||||
|
isUnified bool
|
||||||
|
inUserNS bool
|
||||||
|
nsOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
type cgroup interface {
|
||||||
|
cpuQuotaUs() (int64, error)
|
||||||
|
cpuPeriodUs() (uint64, error)
|
||||||
|
cpus() ([]uint64, error)
|
||||||
|
usageAllCpus() (uint64, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentCgroup() (cgroup, error) {
|
||||||
|
if isCgroup2UnifiedMode() {
|
||||||
|
return currentCgroupV2()
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentCgroupV1()
|
||||||
|
}
|
||||||
|
|
||||||
|
type cgroupV1 struct {
|
||||||
cgroups map[string]string
|
cgroups map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cgroup) acctUsageAllCpus() (uint64, error) {
|
func (c *cgroupV1) cpuQuotaUs() (int64, error) {
|
||||||
data, err := iox.ReadText(path.Join(c.cgroups["cpuacct"], "cpuacct.usage"))
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseUint(string(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cgroup) acctUsagePerCpu() ([]uint64, error) {
|
|
||||||
data, err := iox.ReadText(path.Join(c.cgroups["cpuacct"], "cpuacct.usage_percpu"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var usage []uint64
|
|
||||||
for _, v := range strings.Fields(string(data)) {
|
|
||||||
u, err := parseUint(v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
usage = append(usage, u)
|
|
||||||
}
|
|
||||||
|
|
||||||
return usage, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cgroup) cpuQuotaUs() (int64, error) {
|
|
||||||
data, err := iox.ReadText(path.Join(c.cgroups["cpu"], "cpu.cfs_quota_us"))
|
data, err := iox.ReadText(path.Join(c.cgroups["cpu"], "cpu.cfs_quota_us"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return strconv.ParseInt(string(data), 10, 64)
|
return strconv.ParseInt(data, 10, 64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cgroup) cpuPeriodUs() (uint64, error) {
|
func (c *cgroupV1) cpuPeriodUs() (uint64, error) {
|
||||||
data, err := iox.ReadText(path.Join(c.cgroups["cpu"], "cpu.cfs_period_us"))
|
data, err := iox.ReadText(path.Join(c.cgroups["cpu"], "cpu.cfs_period_us"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseUint(string(data))
|
return parseUint(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cgroup) cpus() ([]uint64, error) {
|
func (c *cgroupV1) cpus() ([]uint64, error) {
|
||||||
data, err := iox.ReadText(path.Join(c.cgroups["cpuset"], "cpuset.cpus"))
|
data, err := iox.ReadText(path.Join(c.cgroups["cpuset"], "cpuset.cpus"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseUints(string(data))
|
return parseUints(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func currentCgroup() (*cgroup, error) {
|
func (c *cgroupV1) usageAllCpus() (uint64, error) {
|
||||||
|
data, err := iox.ReadText(path.Join(c.cgroups["cpuacct"], "cpuacct.usage"))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseUint(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
type cgroupV2 struct {
|
||||||
|
cgroups map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cgroupV2) cpuQuotaUs() (int64, error) {
|
||||||
|
data, err := iox.ReadText(path.Join(cgroupDir, "cpu.cfs_quota_us"))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return strconv.ParseInt(data, 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cgroupV2) cpuPeriodUs() (uint64, error) {
|
||||||
|
data, err := iox.ReadText(path.Join(cgroupDir, "cpu.cfs_period_us"))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseUint(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cgroupV2) cpus() ([]uint64, error) {
|
||||||
|
data, err := iox.ReadText(cpusetFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseUints(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cgroupV2) usageAllCpus() (uint64, error) {
|
||||||
|
usec, err := parseUint(c.cgroups["usage_usec"])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return usec * uint64(time.Microsecond), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentCgroupV1() (cgroup, error) {
|
||||||
cgroupFile := fmt.Sprintf("/proc/%d/cgroup", os.Getpid())
|
cgroupFile := fmt.Sprintf("/proc/%d/cgroup", os.Getpid())
|
||||||
lines, err := iox.ReadTextLines(cgroupFile, iox.WithoutBlank())
|
lines, err := iox.ReadTextLines(cgroupFile, iox.WithoutBlank())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -100,11 +151,51 @@ func currentCgroup() (*cgroup, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &cgroup{
|
return &cgroupV1{
|
||||||
cgroups: cgroups,
|
cgroups: cgroups,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func currentCgroupV2() (cgroup, error) {
|
||||||
|
lines, err := iox.ReadTextLines(cpuStatFile, iox.WithoutBlank())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cgroups := make(map[string]string)
|
||||||
|
for _, line := range lines {
|
||||||
|
cols := strings.Fields(line)
|
||||||
|
if len(cols) != 2 {
|
||||||
|
return nil, fmt.Errorf("invalid cgroupV2 line: %s", line)
|
||||||
|
}
|
||||||
|
|
||||||
|
cgroups[cols[0]] = cols[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cgroupV2{
|
||||||
|
cgroups: cgroups,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isCgroup2UnifiedMode returns whether we are running in cgroup v2 unified mode.
|
||||||
|
func isCgroup2UnifiedMode() bool {
|
||||||
|
isUnifiedOnce.Do(func() {
|
||||||
|
var st unix.Statfs_t
|
||||||
|
err := unix.Statfs(cgroupDir, &st)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) && runningInUserNS() {
|
||||||
|
// ignore the "not found" error if running in userns
|
||||||
|
isUnified = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("cannot statfs cgroup root: %s", err))
|
||||||
|
}
|
||||||
|
isUnified = st.Type == unix.CGROUP2_SUPER_MAGIC
|
||||||
|
})
|
||||||
|
|
||||||
|
return isUnified
|
||||||
|
}
|
||||||
|
|
||||||
func parseUint(s string) (uint64, error) {
|
func parseUint(s string) (uint64, error) {
|
||||||
v, err := strconv.ParseInt(s, 10, 64)
|
v, err := strconv.ParseInt(s, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -166,3 +257,36 @@ func parseUints(val string) ([]uint64, error) {
|
|||||||
|
|
||||||
return sets, nil
|
return sets, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runningInUserNS detects whether we are currently running in a user namespace.
|
||||||
|
func runningInUserNS() bool {
|
||||||
|
nsOnce.Do(func() {
|
||||||
|
file, err := os.Open("/proc/self/uid_map")
|
||||||
|
if err != nil {
|
||||||
|
// This kernel-provided file only exists if user namespaces are supported
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
buf := bufio.NewReader(file)
|
||||||
|
l, _, err := buf.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
line := string(l)
|
||||||
|
var a, b, c int64
|
||||||
|
fmt.Sscanf(line, "%d %d %d", &a, &b, &c)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We assume we are in the initial user namespace if we have a full
|
||||||
|
* range - 4294967295 uids starting at uid 0.
|
||||||
|
*/
|
||||||
|
if a == 0 && b == 0 && c == 4294967295 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
inUserNS = true
|
||||||
|
})
|
||||||
|
|
||||||
|
return inUserNS
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ var (
|
|||||||
|
|
||||||
// if /proc not present, ignore the cpu calculation, like wsl linux
|
// if /proc not present, ignore the cpu calculation, like wsl linux
|
||||||
func init() {
|
func init() {
|
||||||
cpus, err := perCpuUsage()
|
cpus, err := cpuSets()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logx.Error(err)
|
logx.Error(err)
|
||||||
return
|
return
|
||||||
@@ -117,15 +117,6 @@ func cpuSets() ([]uint64, error) {
|
|||||||
return cg.cpus()
|
return cg.cpus()
|
||||||
}
|
}
|
||||||
|
|
||||||
func perCpuUsage() ([]uint64, error) {
|
|
||||||
cg, err := currentCgroup()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cg.acctUsagePerCpu()
|
|
||||||
}
|
|
||||||
|
|
||||||
func systemCpuUsage() (uint64, error) {
|
func systemCpuUsage() (uint64, error) {
|
||||||
lines, err := iox.ReadTextLines("/proc/stat", iox.WithoutBlank())
|
lines, err := iox.ReadTextLines("/proc/stat", iox.WithoutBlank())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -157,10 +148,10 @@ func systemCpuUsage() (uint64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func totalCpuUsage() (usage uint64, err error) {
|
func totalCpuUsage() (usage uint64, err error) {
|
||||||
var cg *cgroup
|
var cg cgroup
|
||||||
if cg, err = currentCgroup(); err != nil {
|
if cg, err = currentCgroup(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return cg.acctUsageAllCpus()
|
return cg.usageAllCpus()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,18 +30,32 @@ func RawFieldNames(in interface{}, postgresSql ...bool) []string {
|
|||||||
for i := 0; i < v.NumField(); i++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
// gets us a StructField
|
// gets us a StructField
|
||||||
fi := typ.Field(i)
|
fi := typ.Field(i)
|
||||||
if tagv := fi.Tag.Get(dbTag); tagv != "" {
|
tagv := fi.Tag.Get(dbTag)
|
||||||
if pg {
|
switch tagv {
|
||||||
out = append(out, tagv)
|
case "-":
|
||||||
} else {
|
continue
|
||||||
out = append(out, fmt.Sprintf("`%s`", tagv))
|
case "":
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if pg {
|
if pg {
|
||||||
out = append(out, fi.Name)
|
out = append(out, fi.Name)
|
||||||
} else {
|
} else {
|
||||||
out = append(out, fmt.Sprintf("`%s`", fi.Name))
|
out = append(out, fmt.Sprintf("`%s`", fi.Name))
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
// get tag name with the tag opton, e.g.:
|
||||||
|
// `db:"id"`
|
||||||
|
// `db:"id,type=char,length=16"`
|
||||||
|
// `db:",type=char,length=16"`
|
||||||
|
if strings.Contains(tagv, ",") {
|
||||||
|
tagv = strings.TrimSpace(strings.Split(tagv, ",")[0])
|
||||||
|
}
|
||||||
|
if len(tagv) == 0 {
|
||||||
|
tagv = fi.Name
|
||||||
|
}
|
||||||
|
if pg {
|
||||||
|
out = append(out, tagv)
|
||||||
|
} else {
|
||||||
|
out = append(out, fmt.Sprintf("`%s`", tagv))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,3 +22,20 @@ func TestFieldNames(t *testing.T) {
|
|||||||
assert.Equal(t, expected, out)
|
assert.Equal(t, expected, out)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockedUserWithOptions struct {
|
||||||
|
ID string `db:"id" json:"id,omitempty"`
|
||||||
|
UserName string `db:"user_name,type=varchar,length=255" json:"userName,omitempty"`
|
||||||
|
Sex int `db:"sex" json:"sex,omitempty"`
|
||||||
|
UUID string `db:",type=varchar,length=16" uuid:"uuid,omitempty"`
|
||||||
|
Age int `db:"age" json:"age"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFieldNamesWithTagOptions(t *testing.T) {
|
||||||
|
t.Run("new", func(t *testing.T) {
|
||||||
|
var u mockedUserWithOptions
|
||||||
|
out := RawFieldNames(&u)
|
||||||
|
expected := []string{"`id`", "`user_name`", "`sex`", "`UUID`", "`age`"}
|
||||||
|
assert.Equal(t, expected, out)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
104
core/stores/cache/cache.go
vendored
104
core/stores/cache/cache.go
vendored
@@ -1,6 +1,8 @@
|
|||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
@@ -13,13 +15,37 @@ import (
|
|||||||
type (
|
type (
|
||||||
// Cache interface is used to define the cache implementation.
|
// Cache interface is used to define the cache implementation.
|
||||||
Cache interface {
|
Cache interface {
|
||||||
|
// Del deletes cached values with keys.
|
||||||
Del(keys ...string) error
|
Del(keys ...string) error
|
||||||
Get(key string, v interface{}) error
|
// DelCtx deletes cached values with keys.
|
||||||
|
DelCtx(ctx context.Context, keys ...string) error
|
||||||
|
// Get gets the cache with key and fills into v.
|
||||||
|
Get(key string, val interface{}) error
|
||||||
|
// GetCtx gets the cache with key and fills into v.
|
||||||
|
GetCtx(ctx context.Context, key string, val interface{}) error
|
||||||
|
// IsNotFound checks if the given error is the defined errNotFound.
|
||||||
IsNotFound(err error) bool
|
IsNotFound(err error) bool
|
||||||
Set(key string, v interface{}) error
|
// Set sets the cache with key and v, using c.expiry.
|
||||||
SetWithExpire(key string, v interface{}, expire time.Duration) error
|
Set(key string, val interface{}) error
|
||||||
Take(v interface{}, key string, query func(v interface{}) error) error
|
// SetCtx sets the cache with key and v, using c.expiry.
|
||||||
TakeWithExpire(v interface{}, key string, query func(v interface{}, expire time.Duration) error) error
|
SetCtx(ctx context.Context, key string, val interface{}) error
|
||||||
|
// SetWithExpire sets the cache with key and v, using given expire.
|
||||||
|
SetWithExpire(key string, val interface{}, expire time.Duration) error
|
||||||
|
// SetWithExpireCtx sets the cache with key and v, using given expire.
|
||||||
|
SetWithExpireCtx(ctx context.Context, key string, val interface{}, expire time.Duration) error
|
||||||
|
// Take takes the result from cache first, if not found,
|
||||||
|
// query from DB and set cache using c.expiry, then return the result.
|
||||||
|
Take(val interface{}, key string, query func(val interface{}) error) error
|
||||||
|
// TakeCtx takes the result from cache first, if not found,
|
||||||
|
// query from DB and set cache using c.expiry, then return the result.
|
||||||
|
TakeCtx(ctx context.Context, val interface{}, key string, query func(val interface{}) error) error
|
||||||
|
// TakeWithExpire takes the result from cache first, if not found,
|
||||||
|
// query from DB and set cache using given expire, then return the result.
|
||||||
|
TakeWithExpire(val interface{}, key string, query func(val interface{}, expire time.Duration) error) error
|
||||||
|
// TakeWithExpireCtx takes the result from cache first, if not found,
|
||||||
|
// query from DB and set cache using given expire, then return the result.
|
||||||
|
TakeWithExpireCtx(ctx context.Context, val interface{}, key string,
|
||||||
|
query func(val interface{}, expire time.Duration) error) error
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheCluster struct {
|
cacheCluster struct {
|
||||||
@@ -51,7 +77,13 @@ func New(c ClusterConf, barrier syncx.SingleFlight, st *Stat, errNotFound error,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Del deletes cached values with keys.
|
||||||
func (cc cacheCluster) Del(keys ...string) error {
|
func (cc cacheCluster) Del(keys ...string) error {
|
||||||
|
return cc.DelCtx(context.Background(), keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelCtx deletes cached values with keys.
|
||||||
|
func (cc cacheCluster) DelCtx(ctx context.Context, keys ...string) error {
|
||||||
switch len(keys) {
|
switch len(keys) {
|
||||||
case 0:
|
case 0:
|
||||||
return nil
|
return nil
|
||||||
@@ -62,7 +94,7 @@ func (cc cacheCluster) Del(keys ...string) error {
|
|||||||
return cc.errNotFound
|
return cc.errNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.(Cache).Del(key)
|
return c.(Cache).DelCtx(ctx, key)
|
||||||
default:
|
default:
|
||||||
var be errorx.BatchError
|
var be errorx.BatchError
|
||||||
nodes := make(map[interface{}][]string)
|
nodes := make(map[interface{}][]string)
|
||||||
@@ -76,7 +108,7 @@ func (cc cacheCluster) Del(keys ...string) error {
|
|||||||
nodes[c] = append(nodes[c], key)
|
nodes[c] = append(nodes[c], key)
|
||||||
}
|
}
|
||||||
for c, ks := range nodes {
|
for c, ks := range nodes {
|
||||||
if err := c.(Cache).Del(ks...); err != nil {
|
if err := c.(Cache).DelCtx(ctx, ks...); err != nil {
|
||||||
be.Add(err)
|
be.Add(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,52 +117,86 @@ func (cc cacheCluster) Del(keys ...string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc cacheCluster) Get(key string, v interface{}) error {
|
// Get gets the cache with key and fills into v.
|
||||||
|
func (cc cacheCluster) Get(key string, val interface{}) error {
|
||||||
|
return cc.GetCtx(context.Background(), key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCtx gets the cache with key and fills into v.
|
||||||
|
func (cc cacheCluster) GetCtx(ctx context.Context, key string, val interface{}) error {
|
||||||
c, ok := cc.dispatcher.Get(key)
|
c, ok := cc.dispatcher.Get(key)
|
||||||
if !ok {
|
if !ok {
|
||||||
return cc.errNotFound
|
return cc.errNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.(Cache).Get(key, v)
|
return c.(Cache).GetCtx(ctx, key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNotFound checks if the given error is the defined errNotFound.
|
||||||
func (cc cacheCluster) IsNotFound(err error) bool {
|
func (cc cacheCluster) IsNotFound(err error) bool {
|
||||||
return err == cc.errNotFound
|
return errors.Is(err, cc.errNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc cacheCluster) Set(key string, v interface{}) error {
|
// Set sets the cache with key and v, using c.expiry.
|
||||||
|
func (cc cacheCluster) Set(key string, val interface{}) error {
|
||||||
|
return cc.SetCtx(context.Background(), key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCtx sets the cache with key and v, using c.expiry.
|
||||||
|
func (cc cacheCluster) SetCtx(ctx context.Context, key string, val interface{}) error {
|
||||||
c, ok := cc.dispatcher.Get(key)
|
c, ok := cc.dispatcher.Get(key)
|
||||||
if !ok {
|
if !ok {
|
||||||
return cc.errNotFound
|
return cc.errNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.(Cache).Set(key, v)
|
return c.(Cache).SetCtx(ctx, key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc cacheCluster) SetWithExpire(key string, v interface{}, expire time.Duration) error {
|
// SetWithExpire sets the cache with key and v, using given expire.
|
||||||
|
func (cc cacheCluster) SetWithExpire(key string, val interface{}, expire time.Duration) error {
|
||||||
|
return cc.SetWithExpireCtx(context.Background(), key, val, expire)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWithExpireCtx sets the cache with key and v, using given expire.
|
||||||
|
func (cc cacheCluster) SetWithExpireCtx(ctx context.Context, key string, val interface{}, expire time.Duration) error {
|
||||||
c, ok := cc.dispatcher.Get(key)
|
c, ok := cc.dispatcher.Get(key)
|
||||||
if !ok {
|
if !ok {
|
||||||
return cc.errNotFound
|
return cc.errNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.(Cache).SetWithExpire(key, v, expire)
|
return c.(Cache).SetWithExpireCtx(ctx, key, val, expire)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc cacheCluster) Take(v interface{}, key string, query func(v interface{}) error) error {
|
// Take takes the result from cache first, if not found,
|
||||||
|
// query from DB and set cache using c.expiry, then return the result.
|
||||||
|
func (cc cacheCluster) Take(val interface{}, key string, query func(val interface{}) error) error {
|
||||||
|
return cc.TakeCtx(context.Background(), val, key, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TakeCtx takes the result from cache first, if not found,
|
||||||
|
// query from DB and set cache using c.expiry, then return the result.
|
||||||
|
func (cc cacheCluster) TakeCtx(ctx context.Context, val interface{}, key string, query func(val interface{}) error) error {
|
||||||
c, ok := cc.dispatcher.Get(key)
|
c, ok := cc.dispatcher.Get(key)
|
||||||
if !ok {
|
if !ok {
|
||||||
return cc.errNotFound
|
return cc.errNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.(Cache).Take(v, key, query)
|
return c.(Cache).TakeCtx(ctx, val, key, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc cacheCluster) TakeWithExpire(v interface{}, key string,
|
// TakeWithExpire takes the result from cache first, if not found,
|
||||||
query func(v interface{}, expire time.Duration) error) error {
|
// query from DB and set cache using given expire, then return the result.
|
||||||
|
func (cc cacheCluster) TakeWithExpire(val interface{}, key string, query func(val interface{}, expire time.Duration) error) error {
|
||||||
|
return cc.TakeWithExpireCtx(context.Background(), val, key, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TakeWithExpireCtx takes the result from cache first, if not found,
|
||||||
|
// query from DB and set cache using given expire, then return the result.
|
||||||
|
func (cc cacheCluster) TakeWithExpireCtx(ctx context.Context, val interface{}, key string, query func(val interface{}, expire time.Duration) error) error {
|
||||||
c, ok := cc.dispatcher.Get(key)
|
c, ok := cc.dispatcher.Get(key)
|
||||||
if !ok {
|
if !ok {
|
||||||
return cc.errNotFound
|
return cc.errNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.(Cache).TakeWithExpire(v, key, query)
|
return c.(Cache).TakeWithExpireCtx(ctx, val, key, query)
|
||||||
}
|
}
|
||||||
|
|||||||
102
core/stores/cache/cache_test.go
vendored
102
core/stores/cache/cache_test.go
vendored
@@ -1,7 +1,9 @@
|
|||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -16,12 +18,18 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/core/syncx"
|
"github.com/zeromicro/go-zero/core/syncx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ Cache = (*mockedNode)(nil)
|
||||||
|
|
||||||
type mockedNode struct {
|
type mockedNode struct {
|
||||||
vals map[string][]byte
|
vals map[string][]byte
|
||||||
errNotFound error
|
errNotFound error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockedNode) Del(keys ...string) error {
|
func (mc *mockedNode) Del(keys ...string) error {
|
||||||
|
return mc.DelCtx(context.Background(), keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mockedNode) DelCtx(_ context.Context, keys ...string) error {
|
||||||
var be errorx.BatchError
|
var be errorx.BatchError
|
||||||
|
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
@@ -35,21 +43,29 @@ func (mc *mockedNode) Del(keys ...string) error {
|
|||||||
return be.Err()
|
return be.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockedNode) Get(key string, v interface{}) error {
|
func (mc *mockedNode) Get(key string, val interface{}) error {
|
||||||
|
return mc.GetCtx(context.Background(), key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mockedNode) GetCtx(ctx context.Context, key string, val interface{}) error {
|
||||||
bs, ok := mc.vals[key]
|
bs, ok := mc.vals[key]
|
||||||
if ok {
|
if ok {
|
||||||
return json.Unmarshal(bs, v)
|
return json.Unmarshal(bs, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
return mc.errNotFound
|
return mc.errNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockedNode) IsNotFound(err error) bool {
|
func (mc *mockedNode) IsNotFound(err error) bool {
|
||||||
return err == mc.errNotFound
|
return errors.Is(err, mc.errNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockedNode) Set(key string, v interface{}) error {
|
func (mc *mockedNode) Set(key string, val interface{}) error {
|
||||||
data, err := json.Marshal(v)
|
return mc.SetCtx(context.Background(), key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mockedNode) SetCtx(ctx context.Context, key string, val interface{}) error {
|
||||||
|
data, err := json.Marshal(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -58,25 +74,37 @@ func (mc *mockedNode) Set(key string, v interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockedNode) SetWithExpire(key string, v interface{}, expire time.Duration) error {
|
func (mc *mockedNode) SetWithExpire(key string, val interface{}, expire time.Duration) error {
|
||||||
return mc.Set(key, v)
|
return mc.SetWithExpireCtx(context.Background(), key, val, expire)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockedNode) Take(v interface{}, key string, query func(v interface{}) error) error {
|
func (mc *mockedNode) SetWithExpireCtx(ctx context.Context, key string, val interface{}, expire time.Duration) error {
|
||||||
|
return mc.Set(key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mockedNode) Take(val interface{}, key string, query func(val interface{}) error) error {
|
||||||
|
return mc.TakeCtx(context.Background(), val, key, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mockedNode) TakeCtx(ctx context.Context, val interface{}, key string, query func(val interface{}) error) error {
|
||||||
if _, ok := mc.vals[key]; ok {
|
if _, ok := mc.vals[key]; ok {
|
||||||
return mc.Get(key, v)
|
return mc.GetCtx(ctx, key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := query(v); err != nil {
|
if err := query(val); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return mc.Set(key, v)
|
return mc.SetCtx(ctx, key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockedNode) TakeWithExpire(v interface{}, key string, query func(v interface{}, expire time.Duration) error) error {
|
func (mc *mockedNode) TakeWithExpire(val interface{}, key string, query func(val interface{}, expire time.Duration) error) error {
|
||||||
return mc.Take(v, key, func(v interface{}) error {
|
return mc.TakeWithExpireCtx(context.Background(), val, key, query)
|
||||||
return query(v, 0)
|
}
|
||||||
|
|
||||||
|
func (mc *mockedNode) TakeWithExpireCtx(ctx context.Context, val interface{}, key string, query func(val interface{}, expire time.Duration) error) error {
|
||||||
|
return mc.Take(val, key, func(val interface{}) error {
|
||||||
|
return query(val, 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,18 +141,18 @@ func TestCache_SetDel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < total; i++ {
|
for i := 0; i < total; i++ {
|
||||||
var v int
|
var val int
|
||||||
assert.Nil(t, c.Get(fmt.Sprintf("key/%d", i), &v))
|
assert.Nil(t, c.Get(fmt.Sprintf("key/%d", i), &val))
|
||||||
assert.Equal(t, i, v)
|
assert.Equal(t, i, val)
|
||||||
}
|
}
|
||||||
assert.Nil(t, c.Del())
|
assert.Nil(t, c.Del())
|
||||||
for i := 0; i < total; i++ {
|
for i := 0; i < total; i++ {
|
||||||
assert.Nil(t, c.Del(fmt.Sprintf("key/%d", i)))
|
assert.Nil(t, c.Del(fmt.Sprintf("key/%d", i)))
|
||||||
}
|
}
|
||||||
for i := 0; i < total; i++ {
|
for i := 0; i < total; i++ {
|
||||||
var v int
|
var val int
|
||||||
assert.True(t, c.IsNotFound(c.Get(fmt.Sprintf("key/%d", i), &v)))
|
assert.True(t, c.IsNotFound(c.Get(fmt.Sprintf("key/%d", i), &val)))
|
||||||
assert.Equal(t, 0, v)
|
assert.Equal(t, 0, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,18 +179,18 @@ func TestCache_OneNode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < total; i++ {
|
for i := 0; i < total; i++ {
|
||||||
var v int
|
var val int
|
||||||
assert.Nil(t, c.Get(fmt.Sprintf("key/%d", i), &v))
|
assert.Nil(t, c.Get(fmt.Sprintf("key/%d", i), &val))
|
||||||
assert.Equal(t, i, v)
|
assert.Equal(t, i, val)
|
||||||
}
|
}
|
||||||
assert.Nil(t, c.Del())
|
assert.Nil(t, c.Del())
|
||||||
for i := 0; i < total; i++ {
|
for i := 0; i < total; i++ {
|
||||||
assert.Nil(t, c.Del(fmt.Sprintf("key/%d", i)))
|
assert.Nil(t, c.Del(fmt.Sprintf("key/%d", i)))
|
||||||
}
|
}
|
||||||
for i := 0; i < total; i++ {
|
for i := 0; i < total; i++ {
|
||||||
var v int
|
var val int
|
||||||
assert.True(t, c.IsNotFound(c.Get(fmt.Sprintf("key/%d", i), &v)))
|
assert.True(t, c.IsNotFound(c.Get(fmt.Sprintf("key/%d", i), &val)))
|
||||||
assert.Equal(t, 0, v)
|
assert.Equal(t, 0, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,9 +230,9 @@ func TestCache_Balance(t *testing.T) {
|
|||||||
assert.True(t, entropy > .95, fmt.Sprintf("entropy should be greater than 0.95, but got %.2f", entropy))
|
assert.True(t, entropy > .95, fmt.Sprintf("entropy should be greater than 0.95, but got %.2f", entropy))
|
||||||
|
|
||||||
for i := 0; i < total; i++ {
|
for i := 0; i < total; i++ {
|
||||||
var v int
|
var val int
|
||||||
assert.Nil(t, c.Get(strconv.Itoa(i), &v))
|
assert.Nil(t, c.Get(strconv.Itoa(i), &val))
|
||||||
assert.Equal(t, i, v)
|
assert.Equal(t, i, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < total/10; i++ {
|
for i := 0; i < total/10; i++ {
|
||||||
@@ -216,14 +244,14 @@ func TestCache_Balance(t *testing.T) {
|
|||||||
for i := 0; i < total/10; i++ {
|
for i := 0; i < total/10; i++ {
|
||||||
var val int
|
var val int
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
assert.Nil(t, c.Take(&val, strconv.Itoa(i*10), func(v interface{}) error {
|
assert.Nil(t, c.Take(&val, strconv.Itoa(i*10), func(val interface{}) error {
|
||||||
*v.(*int) = i
|
*val.(*int) = i
|
||||||
count++
|
count++
|
||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
assert.Nil(t, c.TakeWithExpire(&val, strconv.Itoa(i*10), func(v interface{}, expire time.Duration) error {
|
assert.Nil(t, c.TakeWithExpire(&val, strconv.Itoa(i*10), func(val interface{}, expire time.Duration) error {
|
||||||
*v.(*int) = i
|
*val.(*int) = i
|
||||||
count++
|
count++
|
||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
@@ -244,10 +272,10 @@ func TestCacheNoNode(t *testing.T) {
|
|||||||
assert.NotNil(t, c.Get("foo", nil))
|
assert.NotNil(t, c.Get("foo", nil))
|
||||||
assert.NotNil(t, c.Set("foo", nil))
|
assert.NotNil(t, c.Set("foo", nil))
|
||||||
assert.NotNil(t, c.SetWithExpire("foo", nil, time.Second))
|
assert.NotNil(t, c.SetWithExpire("foo", nil, time.Second))
|
||||||
assert.NotNil(t, c.Take(nil, "foo", func(v interface{}) error {
|
assert.NotNil(t, c.Take(nil, "foo", func(val interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
assert.NotNil(t, c.TakeWithExpire(nil, "foo", func(v interface{}, duration time.Duration) error {
|
assert.NotNil(t, c.TakeWithExpire(nil, "foo", func(val interface{}, duration time.Duration) error {
|
||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
@@ -255,8 +283,8 @@ func TestCacheNoNode(t *testing.T) {
|
|||||||
func calcEntropy(m map[int]int, total int) float64 {
|
func calcEntropy(m map[int]int, total int) float64 {
|
||||||
var entropy float64
|
var entropy float64
|
||||||
|
|
||||||
for _, v := range m {
|
for _, val := range m {
|
||||||
proba := float64(v) / float64(total)
|
proba := float64(val) / float64(total)
|
||||||
entropy -= proba * math.Log2(proba)
|
entropy -= proba * math.Log2(proba)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
107
core/stores/cache/cachenode.go
vendored
107
core/stores/cache/cachenode.go
vendored
@@ -1,6 +1,7 @@
|
|||||||
package cache
|
package cache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@@ -61,30 +62,39 @@ func NewNode(rds *redis.Redis, barrier syncx.SingleFlight, st *Stat,
|
|||||||
|
|
||||||
// Del deletes cached values with keys.
|
// Del deletes cached values with keys.
|
||||||
func (c cacheNode) Del(keys ...string) error {
|
func (c cacheNode) Del(keys ...string) error {
|
||||||
|
return c.DelCtx(context.Background(), keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelCtx deletes cached values with keys.
|
||||||
|
func (c cacheNode) DelCtx(ctx context.Context, keys ...string) error {
|
||||||
if len(keys) == 0 {
|
if len(keys) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger := logx.WithContext(ctx)
|
||||||
if len(keys) > 1 && c.rds.Type == redis.ClusterType {
|
if len(keys) > 1 && c.rds.Type == redis.ClusterType {
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
if _, err := c.rds.Del(key); err != nil {
|
if _, err := c.rds.DelCtx(ctx, key); err != nil {
|
||||||
logx.Errorf("failed to clear cache with key: %q, error: %v", key, err)
|
logger.Errorf("failed to clear cache with key: %q, error: %v", key, err)
|
||||||
c.asyncRetryDelCache(key)
|
c.asyncRetryDelCache(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if _, err := c.rds.DelCtx(ctx, keys...); err != nil {
|
||||||
if _, err := c.rds.Del(keys...); err != nil {
|
logger.Errorf("failed to clear cache with keys: %q, error: %v", formatKeys(keys), err)
|
||||||
logx.Errorf("failed to clear cache with keys: %q, error: %v", formatKeys(keys), err)
|
c.asyncRetryDelCache(keys...)
|
||||||
c.asyncRetryDelCache(keys...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get gets the cache with key and fills into v.
|
// Get gets the cache with key and fills into v.
|
||||||
func (c cacheNode) Get(key string, v interface{}) error {
|
func (c cacheNode) Get(key string, val interface{}) error {
|
||||||
err := c.doGetCache(key, v)
|
return c.GetCtx(context.Background(), key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCtx gets the cache with key and fills into v.
|
||||||
|
func (c cacheNode) GetCtx(ctx context.Context, key string, val interface{}) error {
|
||||||
|
err := c.doGetCache(ctx, key, val)
|
||||||
if err == errPlaceholder {
|
if err == errPlaceholder {
|
||||||
return c.errNotFound
|
return c.errNotFound
|
||||||
}
|
}
|
||||||
@@ -94,22 +104,32 @@ func (c cacheNode) Get(key string, v interface{}) error {
|
|||||||
|
|
||||||
// IsNotFound checks if the given error is the defined errNotFound.
|
// IsNotFound checks if the given error is the defined errNotFound.
|
||||||
func (c cacheNode) IsNotFound(err error) bool {
|
func (c cacheNode) IsNotFound(err error) bool {
|
||||||
return err == c.errNotFound
|
return errors.Is(err, c.errNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets the cache with key and v, using c.expiry.
|
// Set sets the cache with key and v, using c.expiry.
|
||||||
func (c cacheNode) Set(key string, v interface{}) error {
|
func (c cacheNode) Set(key string, val interface{}) error {
|
||||||
return c.SetWithExpire(key, v, c.aroundDuration(c.expiry))
|
return c.SetCtx(context.Background(), key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCtx sets the cache with key and v, using c.expiry.
|
||||||
|
func (c cacheNode) SetCtx(ctx context.Context, key string, val interface{}) error {
|
||||||
|
return c.SetWithExpireCtx(ctx, key, val, c.aroundDuration(c.expiry))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetWithExpire sets the cache with key and v, using given expire.
|
// SetWithExpire sets the cache with key and v, using given expire.
|
||||||
func (c cacheNode) SetWithExpire(key string, v interface{}, expire time.Duration) error {
|
func (c cacheNode) SetWithExpire(key string, val interface{}, expire time.Duration) error {
|
||||||
data, err := jsonx.Marshal(v)
|
return c.SetWithExpireCtx(context.Background(), key, val, expire)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWithExpireCtx sets the cache with key and v, using given expire.
|
||||||
|
func (c cacheNode) SetWithExpireCtx(ctx context.Context, key string, val interface{}, expire time.Duration) error {
|
||||||
|
data, err := jsonx.Marshal(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.rds.Setex(key, string(data), int(expire.Seconds()))
|
return c.rds.SetexCtx(ctx, key, string(data), int(expire.Seconds()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns a string that represents the cacheNode.
|
// String returns a string that represents the cacheNode.
|
||||||
@@ -119,21 +139,32 @@ func (c cacheNode) String() string {
|
|||||||
|
|
||||||
// Take takes the result from cache first, if not found,
|
// Take takes the result from cache first, if not found,
|
||||||
// query from DB and set cache using c.expiry, then return the result.
|
// query from DB and set cache using c.expiry, then return the result.
|
||||||
func (c cacheNode) Take(v interface{}, key string, query func(v interface{}) error) error {
|
func (c cacheNode) Take(val interface{}, key string, query func(val interface{}) error) error {
|
||||||
return c.doTake(v, key, query, func(v interface{}) error {
|
return c.TakeCtx(context.Background(), val, key, query)
|
||||||
return c.Set(key, v)
|
}
|
||||||
|
|
||||||
|
// TakeCtx takes the result from cache first, if not found,
|
||||||
|
// query from DB and set cache using c.expiry, then return the result.
|
||||||
|
func (c cacheNode) TakeCtx(ctx context.Context, val interface{}, key string, query func(val interface{}) error) error {
|
||||||
|
return c.doTake(ctx, val, key, query, func(v interface{}) error {
|
||||||
|
return c.SetCtx(ctx, key, v)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TakeWithExpire takes the result from cache first, if not found,
|
// TakeWithExpire takes the result from cache first, if not found,
|
||||||
// query from DB and set cache using given expire, then return the result.
|
// query from DB and set cache using given expire, then return the result.
|
||||||
func (c cacheNode) TakeWithExpire(v interface{}, key string, query func(v interface{},
|
func (c cacheNode) TakeWithExpire(val interface{}, key string, query func(val interface{}, expire time.Duration) error) error {
|
||||||
expire time.Duration) error) error {
|
return c.TakeWithExpireCtx(context.Background(), val, key, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TakeWithExpireCtx takes the result from cache first, if not found,
|
||||||
|
// query from DB and set cache using given expire, then return the result.
|
||||||
|
func (c cacheNode) TakeWithExpireCtx(ctx context.Context, val interface{}, key string, query func(val interface{}, expire time.Duration) error) error {
|
||||||
expire := c.aroundDuration(c.expiry)
|
expire := c.aroundDuration(c.expiry)
|
||||||
return c.doTake(v, key, func(v interface{}) error {
|
return c.doTake(ctx, val, key, func(v interface{}) error {
|
||||||
return query(v, expire)
|
return query(v, expire)
|
||||||
}, func(v interface{}) error {
|
}, func(v interface{}) error {
|
||||||
return c.SetWithExpire(key, v, expire)
|
return c.SetWithExpireCtx(ctx, key, v, expire)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,9 +179,9 @@ func (c cacheNode) asyncRetryDelCache(keys ...string) {
|
|||||||
}, keys...)
|
}, keys...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c cacheNode) doGetCache(key string, v interface{}) error {
|
func (c cacheNode) doGetCache(ctx context.Context, key string, v interface{}) error {
|
||||||
c.stat.IncrementTotal()
|
c.stat.IncrementTotal()
|
||||||
data, err := c.rds.Get(key)
|
data, err := c.rds.GetCtx(ctx, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.stat.IncrementMiss()
|
c.stat.IncrementMiss()
|
||||||
return err
|
return err
|
||||||
@@ -166,13 +197,14 @@ func (c cacheNode) doGetCache(key string, v interface{}) error {
|
|||||||
return errPlaceholder
|
return errPlaceholder
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.processCache(key, data, v)
|
return c.processCache(ctx, key, data, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) error,
|
func (c cacheNode) doTake(ctx context.Context, v interface{}, key string,
|
||||||
cacheVal func(v interface{}) error) error {
|
query func(v interface{}) error, cacheVal func(v interface{}) error) error {
|
||||||
|
logger := logx.WithContext(ctx)
|
||||||
val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) {
|
val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) {
|
||||||
if err := c.doGetCache(key, v); err != nil {
|
if err := c.doGetCache(ctx, key, v); err != nil {
|
||||||
if err == errPlaceholder {
|
if err == errPlaceholder {
|
||||||
return nil, c.errNotFound
|
return nil, c.errNotFound
|
||||||
} else if err != c.errNotFound {
|
} else if err != c.errNotFound {
|
||||||
@@ -183,8 +215,8 @@ func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err = query(v); err == c.errNotFound {
|
if err = query(v); err == c.errNotFound {
|
||||||
if err = c.setCacheWithNotFound(key); err != nil {
|
if err = c.setCacheWithNotFound(ctx, key); err != nil {
|
||||||
logx.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, c.errNotFound
|
return nil, c.errNotFound
|
||||||
@@ -194,7 +226,7 @@ func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err = cacheVal(v); err != nil {
|
if err = cacheVal(v); err != nil {
|
||||||
logx.Error(err)
|
logger.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +246,7 @@ func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) e
|
|||||||
return jsonx.Unmarshal(val.([]byte), v)
|
return jsonx.Unmarshal(val.([]byte), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c cacheNode) processCache(key, data string, v interface{}) error {
|
func (c cacheNode) processCache(ctx context.Context, key, data string, v interface{}) error {
|
||||||
err := jsonx.Unmarshal([]byte(data), v)
|
err := jsonx.Unmarshal([]byte(data), v)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -222,10 +254,11 @@ func (c cacheNode) processCache(key, data string, v interface{}) error {
|
|||||||
|
|
||||||
report := fmt.Sprintf("unmarshal cache, node: %s, key: %s, value: %s, error: %v",
|
report := fmt.Sprintf("unmarshal cache, node: %s, key: %s, value: %s, error: %v",
|
||||||
c.rds.Addr, key, data, err)
|
c.rds.Addr, key, data, err)
|
||||||
logx.Error(report)
|
logger := logx.WithContext(ctx)
|
||||||
|
logger.Error(report)
|
||||||
stat.Report(report)
|
stat.Report(report)
|
||||||
if _, e := c.rds.Del(key); e != nil {
|
if _, e := c.rds.DelCtx(ctx, key); e != nil {
|
||||||
logx.Errorf("delete invalid cache, node: %s, key: %s, value: %s, error: %v",
|
logger.Errorf("delete invalid cache, node: %s, key: %s, value: %s, error: %v",
|
||||||
c.rds.Addr, key, data, e)
|
c.rds.Addr, key, data, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,6 +266,6 @@ func (c cacheNode) processCache(key, data string, v interface{}) error {
|
|||||||
return c.errNotFound
|
return c.errNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c cacheNode) setCacheWithNotFound(key string) error {
|
func (c cacheNode) setCacheWithNotFound(ctx context.Context, key string) error {
|
||||||
return c.rds.Setex(key, notFoundPlaceholder, int(c.aroundDuration(c.notFoundExpiry).Seconds()))
|
return c.rds.SetexCtx(ctx, key, notFoundPlaceholder, int(c.aroundDuration(c.notFoundExpiry).Seconds()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ type (
|
|||||||
Expire(key string, seconds int) error
|
Expire(key string, seconds int) error
|
||||||
Expireat(key string, expireTime int64) error
|
Expireat(key string, expireTime int64) error
|
||||||
Get(key string) (string, error)
|
Get(key string) (string, error)
|
||||||
|
GetSet(key, value string) (string, error)
|
||||||
Hdel(key, field string) (bool, error)
|
Hdel(key, field string) (bool, error)
|
||||||
Hexists(key, field string) (bool, error)
|
Hexists(key, field string) (bool, error)
|
||||||
Hget(key, field string) (string, error)
|
Hget(key, field string) (string, error)
|
||||||
@@ -459,6 +460,15 @@ func (cs clusterStore) SetnxEx(key, value string, seconds int) (bool, error) {
|
|||||||
return node.SetnxEx(key, value, seconds)
|
return node.SetnxEx(key, value, seconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cs clusterStore) GetSet(key, value string) (string, error) {
|
||||||
|
node, err := cs.getRedis(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return node.GetSet(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
func (cs clusterStore) Sismember(key string, value interface{}) (bool, error) {
|
func (cs clusterStore) Sismember(key string, value interface{}) (bool, error) {
|
||||||
node, err := cs.getRedis(key)
|
node, err := cs.getRedis(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -490,6 +490,29 @@ func TestRedis_SetExNx(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRedis_Getset(t *testing.T) {
|
||||||
|
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||||
|
_, err := store.GetSet("hello", "world")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
runOnCluster(t, func(client Store) {
|
||||||
|
val, err := client.GetSet("hello", "world")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "", val)
|
||||||
|
val, err = client.Get("hello")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "world", val)
|
||||||
|
val, err = client.GetSet("hello", "newworld")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "world", val)
|
||||||
|
val, err = client.Get("hello")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "newworld", val)
|
||||||
|
_, err = client.Del("hello")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestRedis_SetGetDelHashField(t *testing.T) {
|
func TestRedis_SetGetDelHashField(t *testing.T) {
|
||||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||||
err := store.Hset("key", "field", "value")
|
err := store.Hset("key", "field", "value")
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ var (
|
|||||||
ErrNotFound = mgo.ErrNotFound
|
ErrNotFound = mgo.ErrNotFound
|
||||||
|
|
||||||
// can't use one SingleFlight per conn, because multiple conns may share the same cache key.
|
// can't use one SingleFlight per conn, because multiple conns may share the same cache key.
|
||||||
sharedCalls = syncx.NewSingleFlight()
|
singleFlight = syncx.NewSingleFlight()
|
||||||
stats = cache.NewStat("mongoc")
|
stats = cache.NewStat("mongoc")
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func TestCollection_Count(t *testing.T) {
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
defer clean()
|
defer clean()
|
||||||
|
|
||||||
cach := cache.NewNode(r, sharedCalls, stats, mgo.ErrNotFound)
|
cach := cache.NewNode(r, singleFlight, stats, mgo.ErrNotFound)
|
||||||
c := newCollection(dummyConn{}, cach)
|
c := newCollection(dummyConn{}, cach)
|
||||||
val, err := c.Count("any")
|
val, err := c.Count("any")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
@@ -98,7 +98,7 @@ func TestStat(t *testing.T) {
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
defer clean()
|
defer clean()
|
||||||
|
|
||||||
cach := cache.NewNode(r, sharedCalls, stats, mgo.ErrNotFound)
|
cach := cache.NewNode(r, singleFlight, stats, mgo.ErrNotFound)
|
||||||
c := newCollection(dummyConn{}, cach).(*cachedCollection)
|
c := newCollection(dummyConn{}, cach).(*cachedCollection)
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
@@ -121,7 +121,7 @@ func TestStatCacheFails(t *testing.T) {
|
|||||||
defer log.SetOutput(os.Stdout)
|
defer log.SetOutput(os.Stdout)
|
||||||
|
|
||||||
r := redis.New("localhost:59999")
|
r := redis.New("localhost:59999")
|
||||||
cach := cache.NewNode(r, sharedCalls, stats, mgo.ErrNotFound)
|
cach := cache.NewNode(r, singleFlight, stats, mgo.ErrNotFound)
|
||||||
c := newCollection(dummyConn{}, cach)
|
c := newCollection(dummyConn{}, cach)
|
||||||
|
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
@@ -142,7 +142,7 @@ func TestStatDbFails(t *testing.T) {
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
defer clean()
|
defer clean()
|
||||||
|
|
||||||
cach := cache.NewNode(r, sharedCalls, stats, mgo.ErrNotFound)
|
cach := cache.NewNode(r, singleFlight, stats, mgo.ErrNotFound)
|
||||||
c := newCollection(dummyConn{}, cach).(*cachedCollection)
|
c := newCollection(dummyConn{}, cach).(*cachedCollection)
|
||||||
|
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
@@ -164,7 +164,7 @@ func TestStatFromMemory(t *testing.T) {
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
defer clean()
|
defer clean()
|
||||||
|
|
||||||
cach := cache.NewNode(r, sharedCalls, stats, mgo.ErrNotFound)
|
cach := cache.NewNode(r, singleFlight, stats, mgo.ErrNotFound)
|
||||||
c := newCollection(dummyConn{}, cach).(*cachedCollection)
|
c := newCollection(dummyConn{}, cach).(*cachedCollection)
|
||||||
|
|
||||||
var all sync.WaitGroup
|
var all sync.WaitGroup
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func MustNewModel(url, collection string, c cache.CacheConf, opts ...cache.Optio
|
|||||||
|
|
||||||
// NewModel returns a Model with a cache cluster.
|
// NewModel returns a Model with a cache cluster.
|
||||||
func NewModel(url, collection string, conf cache.CacheConf, opts ...cache.Option) (*Model, error) {
|
func NewModel(url, collection string, conf cache.CacheConf, opts ...cache.Option) (*Model, error) {
|
||||||
c := cache.New(conf, sharedCalls, stats, mgo.ErrNotFound, opts...)
|
c := cache.New(conf, singleFlight, stats, mgo.ErrNotFound, opts...)
|
||||||
return NewModelWithCache(url, collection, c)
|
return NewModelWithCache(url, collection, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ func NewModelWithCache(url, collection string, c cache.Cache) (*Model, error) {
|
|||||||
|
|
||||||
// NewNodeModel returns a Model with a cache node.
|
// NewNodeModel returns a Model with a cache node.
|
||||||
func NewNodeModel(url, collection string, rds *redis.Redis, opts ...cache.Option) (*Model, error) {
|
func NewNodeModel(url, collection string, rds *redis.Redis, opts ...cache.Option) (*Model, error) {
|
||||||
c := cache.NewNode(rds, sharedCalls, stats, mgo.ErrNotFound, opts...)
|
c := cache.NewNode(rds, singleFlight, stats, mgo.ErrNotFound, opts...)
|
||||||
return NewModelWithCache(url, collection, c)
|
return NewModelWithCache(url, collection, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
83
core/stores/redis/hook.go
Normal file
83
core/stores/redis/hook.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
red "github.com/go-redis/redis/v8"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"github.com/zeromicro/go-zero/core/mapping"
|
||||||
|
"github.com/zeromicro/go-zero/core/timex"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
startTimeKey = contextKey("startTime")
|
||||||
|
durationHook = hook{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
contextKey string
|
||||||
|
hook struct{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h hook) BeforeProcess(ctx context.Context, _ red.Cmder) (context.Context, error) {
|
||||||
|
return context.WithValue(ctx, startTimeKey, timex.Now()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h hook) AfterProcess(ctx context.Context, cmd red.Cmder) error {
|
||||||
|
val := ctx.Value(startTimeKey)
|
||||||
|
if val == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
start, ok := val.(time.Duration)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
duration := timex.Since(start)
|
||||||
|
if duration > slowThreshold.Load() {
|
||||||
|
logDuration(ctx, cmd, duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h hook) BeforeProcessPipeline(ctx context.Context, _ []red.Cmder) (context.Context, error) {
|
||||||
|
return context.WithValue(ctx, startTimeKey, timex.Now()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h hook) AfterProcessPipeline(ctx context.Context, cmds []red.Cmder) error {
|
||||||
|
if len(cmds) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
val := ctx.Value(startTimeKey)
|
||||||
|
if val == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
start, ok := val.(time.Duration)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
duration := timex.Since(start)
|
||||||
|
if duration > slowThreshold.Load()*time.Duration(len(cmds)) {
|
||||||
|
logDuration(ctx, cmds[0], duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func logDuration(ctx context.Context, cmd red.Cmder, duration time.Duration) {
|
||||||
|
var buf strings.Builder
|
||||||
|
for i, arg := range cmd.Args() {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
}
|
||||||
|
buf.WriteString(mapping.Repr(arg))
|
||||||
|
}
|
||||||
|
logx.WithContext(ctx).WithDuration(duration).Slowf("[REDIS] slowcall on executing: %s", buf.String())
|
||||||
|
}
|
||||||
137
core/stores/redis/hook_test.go
Normal file
137
core/stores/redis/hook_test.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
red "github.com/go-redis/redis/v8"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHookProcessCase1(t *testing.T) {
|
||||||
|
writer := log.Writer()
|
||||||
|
var buf strings.Builder
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(writer)
|
||||||
|
|
||||||
|
ctx, err := durationHook.BeforeProcess(context.Background(), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Nil(t, durationHook.AfterProcess(ctx, red.NewCmd(context.Background())))
|
||||||
|
assert.False(t, strings.Contains(buf.String(), "slow"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHookProcessCase2(t *testing.T) {
|
||||||
|
writer := log.Writer()
|
||||||
|
var buf strings.Builder
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(writer)
|
||||||
|
|
||||||
|
ctx, err := durationHook.BeforeProcess(context.Background(), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(slowThreshold.Load() + time.Millisecond)
|
||||||
|
|
||||||
|
assert.Nil(t, durationHook.AfterProcess(ctx, red.NewCmd(context.Background(), "foo", "bar")))
|
||||||
|
assert.True(t, strings.Contains(buf.String(), "slow"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHookProcessCase3(t *testing.T) {
|
||||||
|
writer := log.Writer()
|
||||||
|
var buf strings.Builder
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(writer)
|
||||||
|
|
||||||
|
assert.Nil(t, durationHook.AfterProcess(context.Background(), red.NewCmd(context.Background())))
|
||||||
|
assert.True(t, buf.Len() == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHookProcessCase4(t *testing.T) {
|
||||||
|
writer := log.Writer()
|
||||||
|
var buf strings.Builder
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(writer)
|
||||||
|
|
||||||
|
ctx := context.WithValue(context.Background(), startTimeKey, "foo")
|
||||||
|
assert.Nil(t, durationHook.AfterProcess(ctx, red.NewCmd(context.Background())))
|
||||||
|
assert.True(t, buf.Len() == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHookProcessPipelineCase1(t *testing.T) {
|
||||||
|
writer := log.Writer()
|
||||||
|
var buf strings.Builder
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(writer)
|
||||||
|
|
||||||
|
ctx, err := durationHook.BeforeProcessPipeline(context.Background(), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Nil(t, durationHook.AfterProcessPipeline(ctx, []red.Cmder{
|
||||||
|
red.NewCmd(context.Background()),
|
||||||
|
}))
|
||||||
|
assert.False(t, strings.Contains(buf.String(), "slow"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHookProcessPipelineCase2(t *testing.T) {
|
||||||
|
writer := log.Writer()
|
||||||
|
var buf strings.Builder
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(writer)
|
||||||
|
|
||||||
|
ctx, err := durationHook.BeforeProcessPipeline(context.Background(), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(slowThreshold.Load() + time.Millisecond)
|
||||||
|
|
||||||
|
assert.Nil(t, durationHook.AfterProcessPipeline(ctx, []red.Cmder{
|
||||||
|
red.NewCmd(context.Background(), "foo", "bar"),
|
||||||
|
}))
|
||||||
|
assert.True(t, strings.Contains(buf.String(), "slow"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHookProcessPipelineCase3(t *testing.T) {
|
||||||
|
writer := log.Writer()
|
||||||
|
var buf strings.Builder
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(writer)
|
||||||
|
|
||||||
|
assert.Nil(t, durationHook.AfterProcessPipeline(context.Background(), []red.Cmder{
|
||||||
|
red.NewCmd(context.Background()),
|
||||||
|
}))
|
||||||
|
assert.True(t, buf.Len() == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHookProcessPipelineCase4(t *testing.T) {
|
||||||
|
writer := log.Writer()
|
||||||
|
var buf strings.Builder
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(writer)
|
||||||
|
|
||||||
|
ctx := context.WithValue(context.Background(), startTimeKey, "foo")
|
||||||
|
assert.Nil(t, durationHook.AfterProcessPipeline(ctx, []red.Cmder{
|
||||||
|
red.NewCmd(context.Background()),
|
||||||
|
}))
|
||||||
|
assert.True(t, buf.Len() == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHookProcessPipelineCase5(t *testing.T) {
|
||||||
|
writer := log.Writer()
|
||||||
|
var buf strings.Builder
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer log.SetOutput(writer)
|
||||||
|
|
||||||
|
ctx := context.WithValue(context.Background(), startTimeKey, "foo")
|
||||||
|
assert.Nil(t, durationHook.AfterProcessPipeline(ctx, nil))
|
||||||
|
assert.True(t, buf.Len() == 0)
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
red "github.com/go-redis/redis"
|
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
|
||||||
"github.com/zeromicro/go-zero/core/mapping"
|
|
||||||
"github.com/zeromicro/go-zero/core/timex"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkDuration(proc func(red.Cmder) error) func(red.Cmder) error {
|
|
||||||
return func(cmd red.Cmder) error {
|
|
||||||
start := timex.Now()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
duration := timex.Since(start)
|
|
||||||
if duration > slowThreshold.Load() {
|
|
||||||
var buf strings.Builder
|
|
||||||
for i, arg := range cmd.Args() {
|
|
||||||
if i > 0 {
|
|
||||||
buf.WriteByte(' ')
|
|
||||||
}
|
|
||||||
buf.WriteString(mapping.Repr(arg))
|
|
||||||
}
|
|
||||||
logx.WithDuration(duration).Slowf("[REDIS] slowcall on executing: %s", buf.String())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return proc(cmd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
|||||||
package redis
|
package redis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
@@ -9,8 +10,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alicebob/miniredis/v2"
|
"github.com/alicebob/miniredis/v2"
|
||||||
red "github.com/go-redis/redis"
|
red "github.com/go-redis/redis/v8"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
"github.com/zeromicro/go-zero/core/stringx"
|
"github.com/zeromicro/go-zero/core/stringx"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -361,6 +363,11 @@ func TestRedis_List(t *testing.T) {
|
|||||||
vals, err = client.Lrange("key", 0, 10)
|
vals, err = client.Lrange("key", 0, 10)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.EqualValues(t, []string{"value2", "value3", "value4"}, vals)
|
assert.EqualValues(t, []string{"value2", "value3", "value4"}, vals)
|
||||||
|
err = client.Ltrim("key", 0, 1)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
vals, err = client.Lrange("key", 0, 10)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.EqualValues(t, []string{"value2", "value3"}, vals)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,30 +387,33 @@ func TestRedis_Mget(t *testing.T) {
|
|||||||
|
|
||||||
func TestRedis_SetBit(t *testing.T) {
|
func TestRedis_SetBit(t *testing.T) {
|
||||||
runOnRedis(t, func(client *Redis) {
|
runOnRedis(t, func(client *Redis) {
|
||||||
err := New(client.Addr, badType()).SetBit("key", 1, 1)
|
_, err := New(client.Addr, badType()).SetBit("key", 1, 1)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
err = client.SetBit("key", 1, 1)
|
val, err := client.SetBit("key", 1, 1)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 0, val)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRedis_GetBit(t *testing.T) {
|
func TestRedis_GetBit(t *testing.T) {
|
||||||
runOnRedis(t, func(client *Redis) {
|
runOnRedis(t, func(client *Redis) {
|
||||||
err := client.SetBit("key", 2, 1)
|
val, err := client.SetBit("key", 2, 1)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 0, val)
|
||||||
_, err = New(client.Addr, badType()).GetBit("key", 2)
|
_, err = New(client.Addr, badType()).GetBit("key", 2)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
val, err := client.GetBit("key", 2)
|
v, err := client.GetBit("key", 2)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, val)
|
assert.Equal(t, 1, v)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRedis_BitCount(t *testing.T) {
|
func TestRedis_BitCount(t *testing.T) {
|
||||||
runOnRedis(t, func(client *Redis) {
|
runOnRedis(t, func(client *Redis) {
|
||||||
for i := 0; i < 11; i++ {
|
for i := 0; i < 11; i++ {
|
||||||
err := client.SetBit("key", int64(i), 1)
|
val, err := client.SetBit("key", int64(i), 1)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 0, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := New(client.Addr, badType()).BitCount("key", 0, -1)
|
_, err := New(client.Addr, badType()).BitCount("key", 0, -1)
|
||||||
@@ -694,6 +704,28 @@ func TestRedis_Set(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRedis_GetSet(t *testing.T) {
|
||||||
|
runOnRedis(t, func(client *Redis) {
|
||||||
|
_, err := New(client.Addr, badType()).GetSet("hello", "world")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
val, err := client.GetSet("hello", "world")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "", val)
|
||||||
|
val, err = client.Get("hello")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "world", val)
|
||||||
|
val, err = client.GetSet("hello", "newworld")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "world", val)
|
||||||
|
val, err = client.Get("hello")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, "newworld", val)
|
||||||
|
ret, err := client.Del("hello")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, 1, ret)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestRedis_SetGetDel(t *testing.T) {
|
func TestRedis_SetGetDel(t *testing.T) {
|
||||||
runOnRedis(t, func(client *Redis) {
|
runOnRedis(t, func(client *Redis) {
|
||||||
err := New(client.Addr, badType()).Set("hello", "world")
|
err := New(client.Addr, badType()).Set("hello", "world")
|
||||||
@@ -958,13 +990,14 @@ func TestRedis_SortedSet(t *testing.T) {
|
|||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
client.Zadd("second", 2, "aa")
|
client.Zadd("second", 2, "aa")
|
||||||
client.Zadd("third", 3, "bbb")
|
client.Zadd("third", 3, "bbb")
|
||||||
val, err = client.Zunionstore("union", ZStore{
|
val, err = client.Zunionstore("union", &ZStore{
|
||||||
|
Keys: []string{"second", "third"},
|
||||||
Weights: []float64{1, 2},
|
Weights: []float64{1, 2},
|
||||||
Aggregate: "SUM",
|
Aggregate: "SUM",
|
||||||
}, "second", "third")
|
})
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, int64(2), val)
|
assert.Equal(t, int64(2), val)
|
||||||
_, err = New(client.Addr, badType()).Zunionstore("union", ZStore{})
|
_, err = New(client.Addr, badType()).Zunionstore("union", &ZStore{})
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
vals, err = client.Zrange("union", 0, 10000)
|
vals, err = client.Zrange("union", 0, 10000)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
@@ -982,9 +1015,9 @@ func TestRedis_Pipelined(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
err := client.Pipelined(
|
err := client.Pipelined(
|
||||||
func(pipe Pipeliner) error {
|
func(pipe Pipeliner) error {
|
||||||
pipe.Incr("pipelined_counter")
|
pipe.Incr(context.Background(), "pipelined_counter")
|
||||||
pipe.Expire("pipelined_counter", time.Hour)
|
pipe.Expire(context.Background(), "pipelined_counter", time.Hour)
|
||||||
pipe.ZAdd("zadd", Z{Score: 12, Member: "zadd"})
|
pipe.ZAdd(context.Background(), "zadd", &Z{Score: 12, Member: "zadd"})
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -1130,6 +1163,8 @@ func TestRedis_WithPass(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runOnRedis(t *testing.T, fn func(client *Redis)) {
|
func runOnRedis(t *testing.T, fn func(client *Redis)) {
|
||||||
|
logx.Disable()
|
||||||
|
|
||||||
s, err := miniredis.Run()
|
s, err := miniredis.Run()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -1148,6 +1183,8 @@ func runOnRedis(t *testing.T, fn func(client *Redis)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runOnRedisTLS(t *testing.T, fn func(client *Redis)) {
|
func runOnRedisTLS(t *testing.T, fn func(client *Redis)) {
|
||||||
|
logx.Disable()
|
||||||
|
|
||||||
s, err := miniredis.RunTLS(&tls.Config{
|
s, err := miniredis.RunTLS(&tls.Config{
|
||||||
Certificates: make([]tls.Certificate, 1),
|
Certificates: make([]tls.Certificate, 1),
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
@@ -1177,6 +1214,6 @@ type mockedNode struct {
|
|||||||
RedisNode
|
RedisNode
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n mockedNode) BLPop(timeout time.Duration, keys ...string) *red.StringSliceCmd {
|
func (n mockedNode) BLPop(ctx context.Context, timeout time.Duration, keys ...string) *red.StringSliceCmd {
|
||||||
return red.NewStringSliceCmd("foo", "bar")
|
return red.NewStringSliceCmd(context.Background(), "foo", "bar")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package redis
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
red "github.com/go-redis/redis"
|
red "github.com/go-redis/redis/v8"
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
red "github.com/go-redis/redis"
|
red "github.com/go-redis/redis/v8"
|
||||||
"github.com/zeromicro/go-zero/core/syncx"
|
"github.com/zeromicro/go-zero/core/syncx"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,7 +32,8 @@ func getClient(r *Redis) (*red.Client, error) {
|
|||||||
MinIdleConns: idleConns,
|
MinIdleConns: idleConns,
|
||||||
TLSConfig: tlsConfig,
|
TLSConfig: tlsConfig,
|
||||||
})
|
})
|
||||||
store.WrapProcess(checkDuration)
|
store.AddHook(durationHook)
|
||||||
|
|
||||||
return store, nil
|
return store, nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
red "github.com/go-redis/redis"
|
red "github.com/go-redis/redis/v8"
|
||||||
"github.com/zeromicro/go-zero/core/syncx"
|
"github.com/zeromicro/go-zero/core/syncx"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ func getCluster(r *Redis) (*red.ClusterClient, error) {
|
|||||||
MinIdleConns: idleConns,
|
MinIdleConns: idleConns,
|
||||||
TLSConfig: tlsConfig,
|
TLSConfig: tlsConfig,
|
||||||
})
|
})
|
||||||
store.WrapProcess(checkDuration)
|
store.AddHook(durationHook)
|
||||||
|
|
||||||
return store, nil
|
return store, nil
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,28 +2,36 @@ package redis
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
red "github.com/go-redis/redis"
|
red "github.com/go-redis/redis/v8"
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
"github.com/zeromicro/go-zero/core/stringx"
|
"github.com/zeromicro/go-zero/core/stringx"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
randomLen = 16
|
||||||
|
tolerance = 500 // milliseconds
|
||||||
|
millisPerSecond = 1000
|
||||||
|
lockCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
|
||||||
|
redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2])
|
||||||
|
return "OK"
|
||||||
|
else
|
||||||
|
return redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2])
|
||||||
|
end`
|
||||||
delCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
|
delCommand = `if redis.call("GET", KEYS[1]) == ARGV[1] then
|
||||||
return redis.call("DEL", KEYS[1])
|
return redis.call("DEL", KEYS[1])
|
||||||
else
|
else
|
||||||
return 0
|
return 0
|
||||||
end`
|
end`
|
||||||
randomLen = 16
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A RedisLock is a redis lock.
|
// A RedisLock is a redis lock.
|
||||||
type RedisLock struct {
|
type RedisLock struct {
|
||||||
store *Redis
|
store *Redis
|
||||||
seconds uint32
|
seconds uint32
|
||||||
count int32
|
|
||||||
key string
|
key string
|
||||||
id string
|
id string
|
||||||
}
|
}
|
||||||
@@ -43,35 +51,30 @@ func NewRedisLock(store *Redis, key string) *RedisLock {
|
|||||||
|
|
||||||
// Acquire acquires the lock.
|
// Acquire acquires the lock.
|
||||||
func (rl *RedisLock) Acquire() (bool, error) {
|
func (rl *RedisLock) Acquire() (bool, error) {
|
||||||
newCount := atomic.AddInt32(&rl.count, 1)
|
seconds := atomic.LoadUint32(&rl.seconds)
|
||||||
if newCount > 1 {
|
resp, err := rl.store.Eval(lockCommand, []string{rl.key}, []string{
|
||||||
|
rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance),
|
||||||
|
})
|
||||||
|
if err == red.Nil {
|
||||||
|
return false, nil
|
||||||
|
} else if err != nil {
|
||||||
|
logx.Errorf("Error on acquiring lock for %s, %s", rl.key, err.Error())
|
||||||
|
return false, err
|
||||||
|
} else if resp == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
reply, ok := resp.(string)
|
||||||
|
if ok && reply == "OK" {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
seconds := atomic.LoadUint32(&rl.seconds)
|
logx.Errorf("Unknown reply when acquiring lock for %s: %v", rl.key, resp)
|
||||||
ok, err := rl.store.SetnxEx(rl.key, rl.id, int(seconds+1)) // +1s for tolerance
|
return false, nil
|
||||||
if err == red.Nil {
|
|
||||||
atomic.AddInt32(&rl.count, -1)
|
|
||||||
return false, nil
|
|
||||||
} else if err != nil {
|
|
||||||
atomic.AddInt32(&rl.count, -1)
|
|
||||||
logx.Errorf("Error on acquiring lock for %s, %s", rl.key, err.Error())
|
|
||||||
return false, err
|
|
||||||
} else if !ok {
|
|
||||||
atomic.AddInt32(&rl.count, -1)
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release releases the lock.
|
// Release releases the lock.
|
||||||
func (rl *RedisLock) Release() (bool, error) {
|
func (rl *RedisLock) Release() (bool, error) {
|
||||||
newCount := atomic.AddInt32(&rl.count, -1)
|
|
||||||
if newCount > 0 {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := rl.store.Eval(delCommand, []string{rl.key}, []string{rl.id})
|
resp, err := rl.store.Eval(delCommand, []string{rl.key}, []string{rl.id})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|||||||
@@ -29,25 +29,5 @@ func TestRedisLock(t *testing.T) {
|
|||||||
endAcquire, err := secondLock.Acquire()
|
endAcquire, err := secondLock.Acquire()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.True(t, endAcquire)
|
assert.True(t, endAcquire)
|
||||||
|
|
||||||
endAcquire, err = secondLock.Acquire()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.True(t, endAcquire)
|
|
||||||
|
|
||||||
release, err = secondLock.Release()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.True(t, release)
|
|
||||||
|
|
||||||
againAcquire, err = firstLock.Acquire()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.False(t, againAcquire)
|
|
||||||
|
|
||||||
release, err = secondLock.Release()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.True(t, release)
|
|
||||||
|
|
||||||
firstAcquire, err = firstLock.Acquire()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.True(t, firstAcquire)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ func CreateRedis() (r *redis.Redis, clean func(), err error) {
|
|||||||
|
|
||||||
return redis.New(mr.Addr()), func() {
|
return redis.New(mr.Addr()), func() {
|
||||||
ch := make(chan lang.PlaceholderType)
|
ch := make(chan lang.PlaceholderType)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
mr.Close()
|
mr.Close()
|
||||||
close(ch)
|
close(ch)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-ch:
|
case <-ch:
|
||||||
case <-time.After(time.Second):
|
case <-time.After(time.Second):
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestScriptCache(t *testing.T) {
|
func TestScriptCache(t *testing.T) {
|
||||||
|
logx.Disable()
|
||||||
|
|
||||||
cache := GetScriptCache()
|
cache := GetScriptCache()
|
||||||
cache.SetSha("foo", "bar")
|
cache.SetSha("foo", "bar")
|
||||||
cache.SetSha("bla", "blabla")
|
cache.SetSha("bla", "blabla")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sqlc
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -18,19 +19,27 @@ var (
|
|||||||
ErrNotFound = sqlx.ErrNotFound
|
ErrNotFound = sqlx.ErrNotFound
|
||||||
|
|
||||||
// can't use one SingleFlight per conn, because multiple conns may share the same cache key.
|
// can't use one SingleFlight per conn, because multiple conns may share the same cache key.
|
||||||
exclusiveCalls = syncx.NewSingleFlight()
|
singleFlights = syncx.NewSingleFlight()
|
||||||
stats = cache.NewStat("sqlc")
|
stats = cache.NewStat("sqlc")
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// ExecFn defines the sql exec method.
|
// ExecFn defines the sql exec method.
|
||||||
ExecFn func(conn sqlx.SqlConn) (sql.Result, error)
|
ExecFn func(conn sqlx.SqlConn) (sql.Result, error)
|
||||||
|
// ExecCtxFn defines the sql exec method.
|
||||||
|
ExecCtxFn func(ctx context.Context, conn sqlx.SqlConn) (sql.Result, error)
|
||||||
// IndexQueryFn defines the query method that based on unique indexes.
|
// IndexQueryFn defines the query method that based on unique indexes.
|
||||||
IndexQueryFn func(conn sqlx.SqlConn, v interface{}) (interface{}, error)
|
IndexQueryFn func(conn sqlx.SqlConn, v interface{}) (interface{}, error)
|
||||||
|
// IndexQueryCtxFn defines the query method that based on unique indexes.
|
||||||
|
IndexQueryCtxFn func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (interface{}, error)
|
||||||
// PrimaryQueryFn defines the query method that based on primary keys.
|
// PrimaryQueryFn defines the query method that based on primary keys.
|
||||||
PrimaryQueryFn func(conn sqlx.SqlConn, v, primary interface{}) error
|
PrimaryQueryFn func(conn sqlx.SqlConn, v, primary interface{}) error
|
||||||
|
// PrimaryQueryCtxFn defines the query method that based on primary keys.
|
||||||
|
PrimaryQueryCtxFn func(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error
|
||||||
// QueryFn defines the query method.
|
// QueryFn defines the query method.
|
||||||
QueryFn func(conn sqlx.SqlConn, v interface{}) error
|
QueryFn func(conn sqlx.SqlConn, v interface{}) error
|
||||||
|
// QueryCtxFn defines the query method.
|
||||||
|
QueryCtxFn func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error
|
||||||
|
|
||||||
// A CachedConn is a DB connection with cache capability.
|
// A CachedConn is a DB connection with cache capability.
|
||||||
CachedConn struct {
|
CachedConn struct {
|
||||||
@@ -41,7 +50,7 @@ type (
|
|||||||
|
|
||||||
// NewConn returns a CachedConn with a redis cluster cache.
|
// NewConn returns a CachedConn with a redis cluster cache.
|
||||||
func NewConn(db sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) CachedConn {
|
func NewConn(db sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) CachedConn {
|
||||||
cc := cache.New(c, exclusiveCalls, stats, sql.ErrNoRows, opts...)
|
cc := cache.New(c, singleFlights, stats, sql.ErrNoRows, opts...)
|
||||||
return NewConnWithCache(db, cc)
|
return NewConnWithCache(db, cc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,28 +64,46 @@ func NewConnWithCache(db sqlx.SqlConn, c cache.Cache) CachedConn {
|
|||||||
|
|
||||||
// NewNodeConn returns a CachedConn with a redis node cache.
|
// NewNodeConn returns a CachedConn with a redis node cache.
|
||||||
func NewNodeConn(db sqlx.SqlConn, rds *redis.Redis, opts ...cache.Option) CachedConn {
|
func NewNodeConn(db sqlx.SqlConn, rds *redis.Redis, opts ...cache.Option) CachedConn {
|
||||||
c := cache.NewNode(rds, exclusiveCalls, stats, sql.ErrNoRows, opts...)
|
c := cache.NewNode(rds, singleFlights, stats, sql.ErrNoRows, opts...)
|
||||||
return NewConnWithCache(db, c)
|
return NewConnWithCache(db, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DelCache deletes cache with keys.
|
// DelCache deletes cache with keys.
|
||||||
func (cc CachedConn) DelCache(keys ...string) error {
|
func (cc CachedConn) DelCache(keys ...string) error {
|
||||||
return cc.cache.Del(keys...)
|
return cc.DelCacheCtx(context.Background(), keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelCacheCtx deletes cache with keys.
|
||||||
|
func (cc CachedConn) DelCacheCtx(ctx context.Context, keys ...string) error {
|
||||||
|
return cc.cache.DelCtx(ctx, keys...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCache unmarshals cache with given key into v.
|
// GetCache unmarshals cache with given key into v.
|
||||||
func (cc CachedConn) GetCache(key string, v interface{}) error {
|
func (cc CachedConn) GetCache(key string, v interface{}) error {
|
||||||
return cc.cache.Get(key, v)
|
return cc.GetCacheCtx(context.Background(), key, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCacheCtx unmarshals cache with given key into v.
|
||||||
|
func (cc CachedConn) GetCacheCtx(ctx context.Context, key string, v interface{}) error {
|
||||||
|
return cc.cache.GetCtx(ctx, key, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exec runs given exec on given keys, and returns execution result.
|
// Exec runs given exec on given keys, and returns execution result.
|
||||||
func (cc CachedConn) Exec(exec ExecFn, keys ...string) (sql.Result, error) {
|
func (cc CachedConn) Exec(exec ExecFn, keys ...string) (sql.Result, error) {
|
||||||
res, err := exec(cc.db)
|
execCtx := func(_ context.Context, conn sqlx.SqlConn) (sql.Result, error) {
|
||||||
|
return exec(conn)
|
||||||
|
}
|
||||||
|
return cc.ExecCtx(context.Background(), execCtx, keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecCtx runs given exec on given keys, and returns execution result.
|
||||||
|
func (cc CachedConn) ExecCtx(ctx context.Context, exec ExecCtxFn, keys ...string) (sql.Result, error) {
|
||||||
|
res, err := exec(ctx, cc.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cc.DelCache(keys...); err != nil {
|
if err := cc.DelCacheCtx(ctx, keys...); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,31 +112,61 @@ func (cc CachedConn) Exec(exec ExecFn, keys ...string) (sql.Result, error) {
|
|||||||
|
|
||||||
// ExecNoCache runs exec with given sql statement, without affecting cache.
|
// ExecNoCache runs exec with given sql statement, without affecting cache.
|
||||||
func (cc CachedConn) ExecNoCache(q string, args ...interface{}) (sql.Result, error) {
|
func (cc CachedConn) ExecNoCache(q string, args ...interface{}) (sql.Result, error) {
|
||||||
return cc.db.Exec(q, args...)
|
return cc.ExecNoCacheCtx(context.Background(), q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecNoCacheCtx runs exec with given sql statement, without affecting cache.
|
||||||
|
func (cc CachedConn) ExecNoCacheCtx(ctx context.Context, q string, args ...interface{}) (
|
||||||
|
sql.Result, error) {
|
||||||
|
return cc.db.ExecCtx(ctx, q, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryRow unmarshals into v with given key and query func.
|
// QueryRow unmarshals into v with given key and query func.
|
||||||
func (cc CachedConn) QueryRow(v interface{}, key string, query QueryFn) error {
|
func (cc CachedConn) QueryRow(v interface{}, key string, query QueryFn) error {
|
||||||
return cc.cache.Take(v, key, func(v interface{}) error {
|
queryCtx := func(_ context.Context, conn sqlx.SqlConn, v interface{}) error {
|
||||||
return query(cc.db, v)
|
return query(conn, v)
|
||||||
|
}
|
||||||
|
return cc.QueryRowCtx(context.Background(), v, key, queryCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowCtx unmarshals into v with given key and query func.
|
||||||
|
func (cc CachedConn) QueryRowCtx(ctx context.Context, v interface{}, key string, query QueryCtxFn) error {
|
||||||
|
return cc.cache.TakeCtx(ctx, v, key, func(v interface{}) error {
|
||||||
|
return query(ctx, cc.db, v)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryRowIndex unmarshals into v with given key.
|
// QueryRowIndex unmarshals into v with given key.
|
||||||
func (cc CachedConn) QueryRowIndex(v interface{}, key string, keyer func(primary interface{}) string,
|
func (cc CachedConn) QueryRowIndex(v interface{}, key string, keyer func(primary interface{}) string,
|
||||||
indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error {
|
indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error {
|
||||||
|
indexQueryCtx := func(_ context.Context, conn sqlx.SqlConn, v interface{}) (interface{}, error) {
|
||||||
|
return indexQuery(conn, v)
|
||||||
|
}
|
||||||
|
primaryQueryCtx := func(_ context.Context, conn sqlx.SqlConn, v, primary interface{}) error {
|
||||||
|
return primaryQuery(conn, v, primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cc.QueryRowIndexCtx(context.Background(), v, key, keyer, indexQueryCtx, primaryQueryCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowIndexCtx unmarshals into v with given key.
|
||||||
|
func (cc CachedConn) QueryRowIndexCtx(ctx context.Context, v interface{}, key string,
|
||||||
|
keyer func(primary interface{}) string, indexQuery IndexQueryCtxFn,
|
||||||
|
primaryQuery PrimaryQueryCtxFn) error {
|
||||||
var primaryKey interface{}
|
var primaryKey interface{}
|
||||||
var found bool
|
var found bool
|
||||||
|
|
||||||
if err := cc.cache.TakeWithExpire(&primaryKey, key, func(val interface{}, expire time.Duration) (err error) {
|
if err := cc.cache.TakeWithExpireCtx(ctx, &primaryKey, key,
|
||||||
primaryKey, err = indexQuery(cc.db, v)
|
func(val interface{}, expire time.Duration) (err error) {
|
||||||
if err != nil {
|
primaryKey, err = indexQuery(ctx, cc.db, v)
|
||||||
return
|
if err != nil {
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
|
||||||
found = true
|
found = true
|
||||||
return cc.cache.SetWithExpire(keyer(primaryKey), v, expire+cacheSafeGapBetweenIndexAndPrimary)
|
return cc.cache.SetWithExpireCtx(ctx, keyer(primaryKey), v,
|
||||||
}); err != nil {
|
expire+cacheSafeGapBetweenIndexAndPrimary)
|
||||||
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,28 +174,54 @@ func (cc CachedConn) QueryRowIndex(v interface{}, key string, keyer func(primary
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return cc.cache.Take(v, keyer(primaryKey), func(v interface{}) error {
|
return cc.cache.TakeCtx(ctx, v, keyer(primaryKey), func(v interface{}) error {
|
||||||
return primaryQuery(cc.db, v, primaryKey)
|
return primaryQuery(ctx, cc.db, v, primaryKey)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryRowNoCache unmarshals into v with given statement.
|
// QueryRowNoCache unmarshals into v with given statement.
|
||||||
func (cc CachedConn) QueryRowNoCache(v interface{}, q string, args ...interface{}) error {
|
func (cc CachedConn) QueryRowNoCache(v interface{}, q string, args ...interface{}) error {
|
||||||
return cc.db.QueryRow(v, q, args...)
|
return cc.QueryRowNoCacheCtx(context.Background(), v, q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowNoCacheCtx unmarshals into v with given statement.
|
||||||
|
func (cc CachedConn) QueryRowNoCacheCtx(ctx context.Context, v interface{}, q string,
|
||||||
|
args ...interface{}) error {
|
||||||
|
return cc.db.QueryRowCtx(ctx, v, q, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryRowsNoCache unmarshals into v with given statement.
|
// QueryRowsNoCache unmarshals into v with given statement.
|
||||||
// It doesn't use cache, because it might cause consistency problem.
|
// It doesn't use cache, because it might cause consistency problem.
|
||||||
func (cc CachedConn) QueryRowsNoCache(v interface{}, q string, args ...interface{}) error {
|
func (cc CachedConn) QueryRowsNoCache(v interface{}, q string, args ...interface{}) error {
|
||||||
return cc.db.QueryRows(v, q, args...)
|
return cc.QueryRowsNoCacheCtx(context.Background(), v, q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRowsNoCacheCtx unmarshals into v with given statement.
|
||||||
|
// It doesn't use cache, because it might cause consistency problem.
|
||||||
|
func (cc CachedConn) QueryRowsNoCacheCtx(ctx context.Context, v interface{}, q string,
|
||||||
|
args ...interface{}) error {
|
||||||
|
return cc.db.QueryRowsCtx(ctx, v, q, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCache sets v into cache with given key.
|
// SetCache sets v into cache with given key.
|
||||||
func (cc CachedConn) SetCache(key string, v interface{}) error {
|
func (cc CachedConn) SetCache(key string, val interface{}) error {
|
||||||
return cc.cache.Set(key, v)
|
return cc.SetCacheCtx(context.Background(), key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCacheCtx sets v into cache with given key.
|
||||||
|
func (cc CachedConn) SetCacheCtx(ctx context.Context, key string, val interface{}) error {
|
||||||
|
return cc.cache.SetCtx(ctx, key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transact runs given fn in transaction mode.
|
// Transact runs given fn in transaction mode.
|
||||||
func (cc CachedConn) Transact(fn func(sqlx.Session) error) error {
|
func (cc CachedConn) Transact(fn func(sqlx.Session) error) error {
|
||||||
return cc.db.Transact(fn)
|
fnCtx := func(_ context.Context, session sqlx.Session) error {
|
||||||
|
return fn(session)
|
||||||
|
}
|
||||||
|
return cc.TransactCtx(context.Background(), fnCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactCtx runs given fn in transaction mode.
|
||||||
|
func (cc CachedConn) TransactCtx(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
|
||||||
|
return cc.db.TransactCtx(ctx, fn)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sqlc
|
package sqlc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -568,7 +569,7 @@ func TestNewConnWithCache(t *testing.T) {
|
|||||||
defer clean()
|
defer clean()
|
||||||
|
|
||||||
var conn trackedConn
|
var conn trackedConn
|
||||||
c := NewConnWithCache(&conn, cache.NewNode(r, exclusiveCalls, stats, sql.ErrNoRows))
|
c := NewConnWithCache(&conn, cache.NewNode(r, singleFlights, stats, sql.ErrNoRows))
|
||||||
_, err = c.ExecNoCache("delete from user_table where id='kevin'")
|
_, err = c.ExecNoCache("delete from user_table where id='kevin'")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.True(t, conn.execValue)
|
assert.True(t, conn.execValue)
|
||||||
@@ -585,6 +586,30 @@ type dummySqlConn struct {
|
|||||||
queryRow func(interface{}, string, ...interface{}) error
|
queryRow func(interface{}, string, ...interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d dummySqlConn) ExecCtx(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dummySqlConn) PrepareCtx(ctx context.Context, query string) (sqlx.StmtSession, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dummySqlConn) QueryRowPartialCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dummySqlConn) QueryRowsCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dummySqlConn) QueryRowsPartialCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dummySqlConn) TransactCtx(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d dummySqlConn) Exec(query string, args ...interface{}) (sql.Result, error) {
|
func (d dummySqlConn) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -594,6 +619,10 @@ func (d dummySqlConn) Prepare(query string) (sqlx.StmtSession, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d dummySqlConn) QueryRow(v interface{}, query string, args ...interface{}) error {
|
func (d dummySqlConn) QueryRow(v interface{}, query string, args ...interface{}) error {
|
||||||
|
return d.QueryRowCtx(context.Background(), v, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d dummySqlConn) QueryRowCtx(_ context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
if d.queryRow != nil {
|
if d.queryRow != nil {
|
||||||
return d.queryRow(v, query, args...)
|
return d.queryRow(v, query, args...)
|
||||||
}
|
}
|
||||||
@@ -628,13 +657,21 @@ type trackedConn struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *trackedConn) Exec(query string, args ...interface{}) (sql.Result, error) {
|
func (c *trackedConn) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||||
|
return c.ExecCtx(context.Background(), query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *trackedConn) ExecCtx(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||||
c.execValue = true
|
c.execValue = true
|
||||||
return c.dummySqlConn.Exec(query, args...)
|
return c.dummySqlConn.ExecCtx(ctx, query, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *trackedConn) QueryRows(v interface{}, query string, args ...interface{}) error {
|
func (c *trackedConn) QueryRows(v interface{}, query string, args ...interface{}) error {
|
||||||
|
return c.QueryRowsCtx(context.Background(), v, query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *trackedConn) QueryRowsCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
c.queryRowsValue = true
|
c.queryRowsValue = true
|
||||||
return c.dummySqlConn.QueryRows(v, query, args...)
|
return c.dummySqlConn.QueryRowsCtx(ctx, v, query, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *trackedConn) RawDB() (*sql.DB, error) {
|
func (c *trackedConn) RawDB() (*sql.DB, error) {
|
||||||
@@ -642,6 +679,12 @@ func (c *trackedConn) RawDB() (*sql.DB, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *trackedConn) Transact(fn func(session sqlx.Session) error) error {
|
func (c *trackedConn) Transact(fn func(session sqlx.Session) error) error {
|
||||||
c.transactValue = true
|
return c.TransactCtx(context.Background(), func(_ context.Context, session sqlx.Session) error {
|
||||||
return c.dummySqlConn.Transact(fn)
|
return fn(session)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *trackedConn) TransactCtx(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
|
||||||
|
c.transactValue = true
|
||||||
|
return c.dummySqlConn.TransactCtx(ctx, fn)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ type (
|
|||||||
|
|
||||||
// A BulkInserter is used to batch insert records.
|
// A BulkInserter is used to batch insert records.
|
||||||
// Postgresql is not supported yet, because of the sql is formated with symbol `$`.
|
// Postgresql is not supported yet, because of the sql is formated with symbol `$`.
|
||||||
|
// Oracle is not supported yet, because of the sql is formated with symbol `:`.
|
||||||
BulkInserter struct {
|
BulkInserter struct {
|
||||||
executor *executors.PeriodicalExecutor
|
executor *executors.PeriodicalExecutor
|
||||||
inserter *dbInserter
|
inserter *dbInserter
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sqlx
|
package sqlx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -17,12 +18,40 @@ type mockedConn struct {
|
|||||||
execErr error
|
execErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *mockedConn) Exec(query string, args ...interface{}) (sql.Result, error) {
|
func (c *mockedConn) ExecCtx(_ context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||||
c.query = query
|
c.query = query
|
||||||
c.args = args
|
c.args = args
|
||||||
return nil, c.execErr
|
return nil, c.execErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *mockedConn) PrepareCtx(ctx context.Context, query string) (StmtSession, error) {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockedConn) QueryRowCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockedConn) QueryRowPartialCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockedConn) QueryRowsCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockedConn) QueryRowsPartialCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockedConn) TransactCtx(ctx context.Context, fn func(context.Context, Session) error) error {
|
||||||
|
panic("should not called")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockedConn) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||||
|
return c.ExecCtx(context.Background(), query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *mockedConn) Prepare(query string) (StmtSession, error) {
|
func (c *mockedConn) Prepare(query string) (StmtSession, error) {
|
||||||
panic("should not called")
|
panic("should not called")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sqlx
|
package sqlx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -16,7 +17,7 @@ func TestUnmarshalRowBool(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value bool
|
var value bool
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.True(t, value)
|
assert.True(t, value)
|
||||||
@@ -29,7 +30,7 @@ func TestUnmarshalRowBoolNotSettable(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value bool
|
var value bool
|
||||||
assert.NotNil(t, query(db, func(rows *sql.Rows) error {
|
assert.NotNil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(value, rows, true)
|
return unmarshalRow(value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
})
|
})
|
||||||
@@ -41,7 +42,7 @@ func TestUnmarshalRowInt(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value int
|
var value int
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, 2, value)
|
assert.EqualValues(t, 2, value)
|
||||||
@@ -54,7 +55,7 @@ func TestUnmarshalRowInt8(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value int8
|
var value int8
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, int8(3), value)
|
assert.EqualValues(t, int8(3), value)
|
||||||
@@ -67,7 +68,7 @@ func TestUnmarshalRowInt16(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value int16
|
var value int16
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.Equal(t, int16(4), value)
|
assert.Equal(t, int16(4), value)
|
||||||
@@ -80,7 +81,7 @@ func TestUnmarshalRowInt32(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value int32
|
var value int32
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.Equal(t, int32(5), value)
|
assert.Equal(t, int32(5), value)
|
||||||
@@ -93,7 +94,7 @@ func TestUnmarshalRowInt64(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value int64
|
var value int64
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, int64(6), value)
|
assert.EqualValues(t, int64(6), value)
|
||||||
@@ -106,7 +107,7 @@ func TestUnmarshalRowUint(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value uint
|
var value uint
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, uint(2), value)
|
assert.EqualValues(t, uint(2), value)
|
||||||
@@ -119,7 +120,7 @@ func TestUnmarshalRowUint8(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value uint8
|
var value uint8
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, uint8(3), value)
|
assert.EqualValues(t, uint8(3), value)
|
||||||
@@ -132,7 +133,7 @@ func TestUnmarshalRowUint16(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value uint16
|
var value uint16
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, uint16(4), value)
|
assert.EqualValues(t, uint16(4), value)
|
||||||
@@ -145,7 +146,7 @@ func TestUnmarshalRowUint32(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value uint32
|
var value uint32
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, uint32(5), value)
|
assert.EqualValues(t, uint32(5), value)
|
||||||
@@ -158,7 +159,7 @@ func TestUnmarshalRowUint64(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value uint64
|
var value uint64
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, uint16(6), value)
|
assert.EqualValues(t, uint16(6), value)
|
||||||
@@ -171,7 +172,7 @@ func TestUnmarshalRowFloat32(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value float32
|
var value float32
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, float32(7), value)
|
assert.EqualValues(t, float32(7), value)
|
||||||
@@ -184,7 +185,7 @@ func TestUnmarshalRowFloat64(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value float64
|
var value float64
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, float64(8), value)
|
assert.EqualValues(t, float64(8), value)
|
||||||
@@ -198,7 +199,7 @@ func TestUnmarshalRowString(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value string
|
var value string
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&value, rows, true)
|
return unmarshalRow(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -215,7 +216,7 @@ func TestUnmarshalRowStruct(t *testing.T) {
|
|||||||
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("liao,5")
|
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("liao,5")
|
||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(value, rows, true)
|
return unmarshalRow(value, rows, true)
|
||||||
}, "select name, age from users where user=?", "anyone"))
|
}, "select name, age from users where user=?", "anyone"))
|
||||||
assert.Equal(t, "liao", value.Name)
|
assert.Equal(t, "liao", value.Name)
|
||||||
@@ -233,7 +234,7 @@ func TestUnmarshalRowStructWithTags(t *testing.T) {
|
|||||||
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("liao,5")
|
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("liao,5")
|
||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(value, rows, true)
|
return unmarshalRow(value, rows, true)
|
||||||
}, "select name, age from users where user=?", "anyone"))
|
}, "select name, age from users where user=?", "anyone"))
|
||||||
assert.Equal(t, "liao", value.Name)
|
assert.Equal(t, "liao", value.Name)
|
||||||
@@ -251,7 +252,7 @@ func TestUnmarshalRowStructWithTagsWrongColumns(t *testing.T) {
|
|||||||
rs := sqlmock.NewRows([]string{"name"}).FromCSVString("liao")
|
rs := sqlmock.NewRows([]string{"name"}).FromCSVString("liao")
|
||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
assert.NotNil(t, query(db, func(rows *sql.Rows) error {
|
assert.NotNil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(value, rows, true)
|
return unmarshalRow(value, rows, true)
|
||||||
}, "select name, age from users where user=?", "anyone"))
|
}, "select name, age from users where user=?", "anyone"))
|
||||||
})
|
})
|
||||||
@@ -264,7 +265,7 @@ func TestUnmarshalRowsBool(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []bool
|
var value []bool
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -278,7 +279,7 @@ func TestUnmarshalRowsInt(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []int
|
var value []int
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -292,7 +293,7 @@ func TestUnmarshalRowsInt8(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []int8
|
var value []int8
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -306,7 +307,7 @@ func TestUnmarshalRowsInt16(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []int16
|
var value []int16
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -320,7 +321,7 @@ func TestUnmarshalRowsInt32(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []int32
|
var value []int32
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -334,7 +335,7 @@ func TestUnmarshalRowsInt64(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []int64
|
var value []int64
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -348,7 +349,7 @@ func TestUnmarshalRowsUint(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []uint
|
var value []uint
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -362,7 +363,7 @@ func TestUnmarshalRowsUint8(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []uint8
|
var value []uint8
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -376,7 +377,7 @@ func TestUnmarshalRowsUint16(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []uint16
|
var value []uint16
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -390,7 +391,7 @@ func TestUnmarshalRowsUint32(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []uint32
|
var value []uint32
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -404,7 +405,7 @@ func TestUnmarshalRowsUint64(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []uint64
|
var value []uint64
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -418,7 +419,7 @@ func TestUnmarshalRowsFloat32(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []float32
|
var value []float32
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -432,7 +433,7 @@ func TestUnmarshalRowsFloat64(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []float64
|
var value []float64
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -446,7 +447,7 @@ func TestUnmarshalRowsString(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []string
|
var value []string
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -462,7 +463,7 @@ func TestUnmarshalRowsBoolPtr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*bool
|
var value []*bool
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -478,7 +479,7 @@ func TestUnmarshalRowsIntPtr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*int
|
var value []*int
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -494,7 +495,7 @@ func TestUnmarshalRowsInt8Ptr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*int8
|
var value []*int8
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -510,7 +511,7 @@ func TestUnmarshalRowsInt16Ptr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*int16
|
var value []*int16
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -526,7 +527,7 @@ func TestUnmarshalRowsInt32Ptr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*int32
|
var value []*int32
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -542,7 +543,7 @@ func TestUnmarshalRowsInt64Ptr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*int64
|
var value []*int64
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -558,7 +559,7 @@ func TestUnmarshalRowsUintPtr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*uint
|
var value []*uint
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -574,7 +575,7 @@ func TestUnmarshalRowsUint8Ptr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*uint8
|
var value []*uint8
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -590,7 +591,7 @@ func TestUnmarshalRowsUint16Ptr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*uint16
|
var value []*uint16
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -606,7 +607,7 @@ func TestUnmarshalRowsUint32Ptr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*uint32
|
var value []*uint32
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -622,7 +623,7 @@ func TestUnmarshalRowsUint64Ptr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*uint64
|
var value []*uint64
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -638,7 +639,7 @@ func TestUnmarshalRowsFloat32Ptr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*float32
|
var value []*float32
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -654,7 +655,7 @@ func TestUnmarshalRowsFloat64Ptr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*float64
|
var value []*float64
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -670,7 +671,7 @@ func TestUnmarshalRowsStringPtr(t *testing.T) {
|
|||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
|
|
||||||
var value []*string
|
var value []*string
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select value from users where user=?", "anyone"))
|
}, "select value from users where user=?", "anyone"))
|
||||||
assert.EqualValues(t, expect, value)
|
assert.EqualValues(t, expect, value)
|
||||||
@@ -699,7 +700,7 @@ func TestUnmarshalRowsStruct(t *testing.T) {
|
|||||||
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||||
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
|
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
|
||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select name, age from users where user=?", "anyone"))
|
}, "select name, age from users where user=?", "anyone"))
|
||||||
|
|
||||||
@@ -739,7 +740,7 @@ func TestUnmarshalRowsStructWithNullStringType(t *testing.T) {
|
|||||||
rs := sqlmock.NewRows([]string{"name", "value"}).AddRow(
|
rs := sqlmock.NewRows([]string{"name", "value"}).AddRow(
|
||||||
"first", "firstnullstring").AddRow("second", nil)
|
"first", "firstnullstring").AddRow("second", nil)
|
||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select name, age from users where user=?", "anyone"))
|
}, "select name, age from users where user=?", "anyone"))
|
||||||
|
|
||||||
@@ -773,7 +774,7 @@ func TestUnmarshalRowsStructWithTags(t *testing.T) {
|
|||||||
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||||
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
|
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
|
||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select name, age from users where user=?", "anyone"))
|
}, "select name, age from users where user=?", "anyone"))
|
||||||
|
|
||||||
@@ -814,7 +815,7 @@ func TestUnmarshalRowsStructAndEmbeddedAnonymousStructWithTags(t *testing.T) {
|
|||||||
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||||
rs := sqlmock.NewRows([]string{"name", "age", "value"}).FromCSVString("first,2,3\nsecond,3,4")
|
rs := sqlmock.NewRows([]string{"name", "age", "value"}).FromCSVString("first,2,3\nsecond,3,4")
|
||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select name, age, value from users where user=?", "anyone"))
|
}, "select name, age, value from users where user=?", "anyone"))
|
||||||
|
|
||||||
@@ -856,7 +857,7 @@ func TestUnmarshalRowsStructAndEmbeddedStructPtrAnonymousWithTags(t *testing.T)
|
|||||||
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||||
rs := sqlmock.NewRows([]string{"name", "age", "value"}).FromCSVString("first,2,3\nsecond,3,4")
|
rs := sqlmock.NewRows([]string{"name", "age", "value"}).FromCSVString("first,2,3\nsecond,3,4")
|
||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select name, age, value from users where user=?", "anyone"))
|
}, "select name, age, value from users where user=?", "anyone"))
|
||||||
|
|
||||||
@@ -890,7 +891,7 @@ func TestUnmarshalRowsStructPtr(t *testing.T) {
|
|||||||
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||||
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
|
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
|
||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select name, age from users where user=?", "anyone"))
|
}, "select name, age from users where user=?", "anyone"))
|
||||||
|
|
||||||
@@ -923,7 +924,7 @@ func TestUnmarshalRowsStructWithTagsPtr(t *testing.T) {
|
|||||||
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||||
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
|
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
|
||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select name, age from users where user=?", "anyone"))
|
}, "select name, age from users where user=?", "anyone"))
|
||||||
|
|
||||||
@@ -956,7 +957,7 @@ func TestUnmarshalRowsStructWithTagsPtrWithInnerPtr(t *testing.T) {
|
|||||||
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||||
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
|
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("first,2\nsecond,3")
|
||||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(&value, rows, true)
|
return unmarshalRows(&value, rows, true)
|
||||||
}, "select name, age from users where user=?", "anyone"))
|
}, "select name, age from users where user=?", "anyone"))
|
||||||
|
|
||||||
@@ -976,7 +977,7 @@ func TestCommonSqlConn_QueryRowOptional(t *testing.T) {
|
|||||||
User string `db:"user"`
|
User string `db:"user"`
|
||||||
Age int `db:"age"`
|
Age int `db:"age"`
|
||||||
}
|
}
|
||||||
assert.Nil(t, query(db, func(rows *sql.Rows) error {
|
assert.Nil(t, query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(&r, rows, false)
|
return unmarshalRow(&r, rows, false)
|
||||||
}, "select age from users where user=?", "anyone"))
|
}, "select age from users where user=?", "anyone"))
|
||||||
assert.Empty(t, r.User)
|
assert.Empty(t, r.User)
|
||||||
@@ -1027,7 +1028,7 @@ func TestUnmarshalRowError(t *testing.T) {
|
|||||||
User string `db:"user"`
|
User string `db:"user"`
|
||||||
Age int `db:"age"`
|
Age int `db:"age"`
|
||||||
}
|
}
|
||||||
test.validate(query(db, func(rows *sql.Rows) error {
|
test.validate(query(context.Background(), db, func(rows *sql.Rows) error {
|
||||||
scanner := mockedScanner{
|
scanner := mockedScanner{
|
||||||
colErr: test.colErr,
|
colErr: test.colErr,
|
||||||
scanErr: test.scanErr,
|
scanErr: test.scanErr,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sqlx
|
package sqlx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/breaker"
|
"github.com/zeromicro/go-zero/core/breaker"
|
||||||
@@ -14,11 +15,17 @@ type (
|
|||||||
// Session stands for raw connections or transaction sessions
|
// Session stands for raw connections or transaction sessions
|
||||||
Session interface {
|
Session interface {
|
||||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||||
|
ExecCtx(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
|
||||||
Prepare(query string) (StmtSession, error)
|
Prepare(query string) (StmtSession, error)
|
||||||
|
PrepareCtx(ctx context.Context, query string) (StmtSession, error)
|
||||||
QueryRow(v interface{}, query string, args ...interface{}) error
|
QueryRow(v interface{}, query string, args ...interface{}) error
|
||||||
|
QueryRowCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error
|
||||||
QueryRowPartial(v interface{}, query string, args ...interface{}) error
|
QueryRowPartial(v interface{}, query string, args ...interface{}) error
|
||||||
|
QueryRowPartialCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error
|
||||||
QueryRows(v interface{}, query string, args ...interface{}) error
|
QueryRows(v interface{}, query string, args ...interface{}) error
|
||||||
|
QueryRowsCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error
|
||||||
QueryRowsPartial(v interface{}, query string, args ...interface{}) error
|
QueryRowsPartial(v interface{}, query string, args ...interface{}) error
|
||||||
|
QueryRowsPartialCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// SqlConn only stands for raw connections, so Transact method can be called.
|
// SqlConn only stands for raw connections, so Transact method can be called.
|
||||||
@@ -27,7 +34,8 @@ type (
|
|||||||
// RawDB is for other ORM to operate with, use it with caution.
|
// RawDB is for other ORM to operate with, use it with caution.
|
||||||
// Notice: don't close it.
|
// Notice: don't close it.
|
||||||
RawDB() (*sql.DB, error)
|
RawDB() (*sql.DB, error)
|
||||||
Transact(func(session Session) error) error
|
Transact(fn func(Session) error) error
|
||||||
|
TransactCtx(ctx context.Context, fn func(context.Context, Session) error) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// SqlOption defines the method to customize a sql connection.
|
// SqlOption defines the method to customize a sql connection.
|
||||||
@@ -37,10 +45,15 @@ type (
|
|||||||
StmtSession interface {
|
StmtSession interface {
|
||||||
Close() error
|
Close() error
|
||||||
Exec(args ...interface{}) (sql.Result, error)
|
Exec(args ...interface{}) (sql.Result, error)
|
||||||
|
ExecCtx(ctx context.Context, args ...interface{}) (sql.Result, error)
|
||||||
QueryRow(v interface{}, args ...interface{}) error
|
QueryRow(v interface{}, args ...interface{}) error
|
||||||
|
QueryRowCtx(ctx context.Context, v interface{}, args ...interface{}) error
|
||||||
QueryRowPartial(v interface{}, args ...interface{}) error
|
QueryRowPartial(v interface{}, args ...interface{}) error
|
||||||
|
QueryRowPartialCtx(ctx context.Context, v interface{}, args ...interface{}) error
|
||||||
QueryRows(v interface{}, args ...interface{}) error
|
QueryRows(v interface{}, args ...interface{}) error
|
||||||
|
QueryRowsCtx(ctx context.Context, v interface{}, args ...interface{}) error
|
||||||
QueryRowsPartial(v interface{}, args ...interface{}) error
|
QueryRowsPartial(v interface{}, args ...interface{}) error
|
||||||
|
QueryRowsPartialCtx(ctx context.Context, v interface{}, args ...interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// thread-safe
|
// thread-safe
|
||||||
@@ -58,7 +71,9 @@ type (
|
|||||||
|
|
||||||
sessionConn interface {
|
sessionConn interface {
|
||||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||||
|
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
|
||||||
Query(query string, args ...interface{}) (*sql.Rows, error)
|
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||||
|
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
statement struct {
|
statement struct {
|
||||||
@@ -68,7 +83,9 @@ type (
|
|||||||
|
|
||||||
stmtConn interface {
|
stmtConn interface {
|
||||||
Exec(args ...interface{}) (sql.Result, error)
|
Exec(args ...interface{}) (sql.Result, error)
|
||||||
|
ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error)
|
||||||
Query(args ...interface{}) (*sql.Rows, error)
|
Query(args ...interface{}) (*sql.Rows, error)
|
||||||
|
QueryContext(ctx context.Context, args ...interface{}) (*sql.Rows, error)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -112,6 +129,11 @@ func NewSqlConnFromDB(db *sql.DB, opts ...SqlOption) SqlConn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *commonSqlConn) Exec(q string, args ...interface{}) (result sql.Result, err error) {
|
func (db *commonSqlConn) Exec(q string, args ...interface{}) (result sql.Result, err error) {
|
||||||
|
return db.ExecCtx(context.Background(), q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *commonSqlConn) ExecCtx(ctx context.Context, q string, args ...interface{}) (
|
||||||
|
result sql.Result, err error) {
|
||||||
err = db.brk.DoWithAcceptable(func() error {
|
err = db.brk.DoWithAcceptable(func() error {
|
||||||
var conn *sql.DB
|
var conn *sql.DB
|
||||||
conn, err = db.connProv()
|
conn, err = db.connProv()
|
||||||
@@ -120,7 +142,7 @@ func (db *commonSqlConn) Exec(q string, args ...interface{}) (result sql.Result,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err = exec(conn, q, args...)
|
result, err = exec(ctx, conn, q, args...)
|
||||||
return err
|
return err
|
||||||
}, db.acceptable)
|
}, db.acceptable)
|
||||||
|
|
||||||
@@ -128,6 +150,10 @@ func (db *commonSqlConn) Exec(q string, args ...interface{}) (result sql.Result,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *commonSqlConn) Prepare(query string) (stmt StmtSession, err error) {
|
func (db *commonSqlConn) Prepare(query string) (stmt StmtSession, err error) {
|
||||||
|
return db.PrepareCtx(context.Background(), query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *commonSqlConn) PrepareCtx(ctx context.Context, query string) (stmt StmtSession, err error) {
|
||||||
err = db.brk.DoWithAcceptable(func() error {
|
err = db.brk.DoWithAcceptable(func() error {
|
||||||
var conn *sql.DB
|
var conn *sql.DB
|
||||||
conn, err = db.connProv()
|
conn, err = db.connProv()
|
||||||
@@ -136,7 +162,7 @@ func (db *commonSqlConn) Prepare(query string) (stmt StmtSession, err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
st, err := conn.Prepare(query)
|
st, err := conn.PrepareContext(ctx, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -152,25 +178,45 @@ func (db *commonSqlConn) Prepare(query string) (stmt StmtSession, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *commonSqlConn) QueryRow(v interface{}, q string, args ...interface{}) error {
|
func (db *commonSqlConn) QueryRow(v interface{}, q string, args ...interface{}) error {
|
||||||
return db.queryRows(func(rows *sql.Rows) error {
|
return db.QueryRowCtx(context.Background(), v, q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *commonSqlConn) QueryRowCtx(ctx context.Context, v interface{}, q string,
|
||||||
|
args ...interface{}) error {
|
||||||
|
return db.queryRows(ctx, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(v, rows, true)
|
return unmarshalRow(v, rows, true)
|
||||||
}, q, args...)
|
}, q, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *commonSqlConn) QueryRowPartial(v interface{}, q string, args ...interface{}) error {
|
func (db *commonSqlConn) QueryRowPartial(v interface{}, q string, args ...interface{}) error {
|
||||||
return db.queryRows(func(rows *sql.Rows) error {
|
return db.QueryRowPartialCtx(context.Background(), v, q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *commonSqlConn) QueryRowPartialCtx(ctx context.Context, v interface{},
|
||||||
|
q string, args ...interface{}) error {
|
||||||
|
return db.queryRows(ctx, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(v, rows, false)
|
return unmarshalRow(v, rows, false)
|
||||||
}, q, args...)
|
}, q, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *commonSqlConn) QueryRows(v interface{}, q string, args ...interface{}) error {
|
func (db *commonSqlConn) QueryRows(v interface{}, q string, args ...interface{}) error {
|
||||||
return db.queryRows(func(rows *sql.Rows) error {
|
return db.QueryRowsCtx(context.Background(), v, q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *commonSqlConn) QueryRowsCtx(ctx context.Context, v interface{}, q string,
|
||||||
|
args ...interface{}) error {
|
||||||
|
return db.queryRows(ctx, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(v, rows, true)
|
return unmarshalRows(v, rows, true)
|
||||||
}, q, args...)
|
}, q, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *commonSqlConn) QueryRowsPartial(v interface{}, q string, args ...interface{}) error {
|
func (db *commonSqlConn) QueryRowsPartial(v interface{}, q string, args ...interface{}) error {
|
||||||
return db.queryRows(func(rows *sql.Rows) error {
|
return db.QueryRowsPartialCtx(context.Background(), v, q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *commonSqlConn) QueryRowsPartialCtx(ctx context.Context, v interface{},
|
||||||
|
q string, args ...interface{}) error {
|
||||||
|
return db.queryRows(ctx, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(v, rows, false)
|
return unmarshalRows(v, rows, false)
|
||||||
}, q, args...)
|
}, q, args...)
|
||||||
}
|
}
|
||||||
@@ -180,13 +226,19 @@ func (db *commonSqlConn) RawDB() (*sql.DB, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (db *commonSqlConn) Transact(fn func(Session) error) error {
|
func (db *commonSqlConn) Transact(fn func(Session) error) error {
|
||||||
|
return db.TransactCtx(context.Background(), func(_ context.Context, session Session) error {
|
||||||
|
return fn(session)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *commonSqlConn) TransactCtx(ctx context.Context, fn func(context.Context, Session) error) error {
|
||||||
return db.brk.DoWithAcceptable(func() error {
|
return db.brk.DoWithAcceptable(func() error {
|
||||||
return transact(db, db.beginTx, fn)
|
return transact(ctx, db, db.beginTx, fn)
|
||||||
}, db.acceptable)
|
}, db.acceptable)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *commonSqlConn) acceptable(err error) bool {
|
func (db *commonSqlConn) acceptable(err error) bool {
|
||||||
ok := err == nil || err == sql.ErrNoRows || err == sql.ErrTxDone
|
ok := err == nil || err == sql.ErrNoRows || err == sql.ErrTxDone || err == context.Canceled
|
||||||
if db.accept == nil {
|
if db.accept == nil {
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
@@ -194,7 +246,8 @@ func (db *commonSqlConn) acceptable(err error) bool {
|
|||||||
return ok || db.accept(err)
|
return ok || db.accept(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *commonSqlConn) queryRows(scanner func(*sql.Rows) error, q string, args ...interface{}) error {
|
func (db *commonSqlConn) queryRows(ctx context.Context, scanner func(*sql.Rows) error,
|
||||||
|
q string, args ...interface{}) error {
|
||||||
var qerr error
|
var qerr error
|
||||||
return db.brk.DoWithAcceptable(func() error {
|
return db.brk.DoWithAcceptable(func() error {
|
||||||
conn, err := db.connProv()
|
conn, err := db.connProv()
|
||||||
@@ -203,7 +256,7 @@ func (db *commonSqlConn) queryRows(scanner func(*sql.Rows) error, q string, args
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return query(conn, func(rows *sql.Rows) error {
|
return query(ctx, conn, func(rows *sql.Rows) error {
|
||||||
qerr = scanner(rows)
|
qerr = scanner(rows)
|
||||||
return qerr
|
return qerr
|
||||||
}, q, args...)
|
}, q, args...)
|
||||||
@@ -217,29 +270,49 @@ func (s statement) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s statement) Exec(args ...interface{}) (sql.Result, error) {
|
func (s statement) Exec(args ...interface{}) (sql.Result, error) {
|
||||||
return execStmt(s.stmt, s.query, args...)
|
return s.ExecCtx(context.Background(), args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s statement) ExecCtx(ctx context.Context, args ...interface{}) (sql.Result, error) {
|
||||||
|
return execStmt(ctx, s.stmt, s.query, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s statement) QueryRow(v interface{}, args ...interface{}) error {
|
func (s statement) QueryRow(v interface{}, args ...interface{}) error {
|
||||||
return queryStmt(s.stmt, func(rows *sql.Rows) error {
|
return s.QueryRowCtx(context.Background(), v, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s statement) QueryRowCtx(ctx context.Context, v interface{}, args ...interface{}) error {
|
||||||
|
return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(v, rows, true)
|
return unmarshalRow(v, rows, true)
|
||||||
}, s.query, args...)
|
}, s.query, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s statement) QueryRowPartial(v interface{}, args ...interface{}) error {
|
func (s statement) QueryRowPartial(v interface{}, args ...interface{}) error {
|
||||||
return queryStmt(s.stmt, func(rows *sql.Rows) error {
|
return s.QueryRowPartialCtx(context.Background(), v, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s statement) QueryRowPartialCtx(ctx context.Context, v interface{}, args ...interface{}) error {
|
||||||
|
return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(v, rows, false)
|
return unmarshalRow(v, rows, false)
|
||||||
}, s.query, args...)
|
}, s.query, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s statement) QueryRows(v interface{}, args ...interface{}) error {
|
func (s statement) QueryRows(v interface{}, args ...interface{}) error {
|
||||||
return queryStmt(s.stmt, func(rows *sql.Rows) error {
|
return s.QueryRowsCtx(context.Background(), v, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s statement) QueryRowsCtx(ctx context.Context, v interface{}, args ...interface{}) error {
|
||||||
|
return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(v, rows, true)
|
return unmarshalRows(v, rows, true)
|
||||||
}, s.query, args...)
|
}, s.query, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s statement) QueryRowsPartial(v interface{}, args ...interface{}) error {
|
func (s statement) QueryRowsPartial(v interface{}, args ...interface{}) error {
|
||||||
return queryStmt(s.stmt, func(rows *sql.Rows) error {
|
return s.QueryRowsPartialCtx(context.Background(), v, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s statement) QueryRowsPartialCtx(ctx context.Context, v interface{}, args ...interface{}) error {
|
||||||
|
return queryStmt(ctx, s.stmt, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(v, rows, false)
|
return unmarshalRows(v, rows, false)
|
||||||
}, s.query, args...)
|
}, s.query, args...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sqlx
|
package sqlx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -18,64 +19,65 @@ func SetSlowThreshold(threshold time.Duration) {
|
|||||||
slowThreshold.Set(threshold)
|
slowThreshold.Set(threshold)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exec(conn sessionConn, q string, args ...interface{}) (sql.Result, error) {
|
func exec(ctx context.Context, conn sessionConn, q string, args ...interface{}) (sql.Result, error) {
|
||||||
stmt, err := format(q, args...)
|
stmt, err := format(q, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
startTime := timex.Now()
|
startTime := timex.Now()
|
||||||
result, err := conn.Exec(q, args...)
|
result, err := conn.ExecContext(ctx, q, args...)
|
||||||
duration := timex.Since(startTime)
|
duration := timex.Since(startTime)
|
||||||
if duration > slowThreshold.Load() {
|
if duration > slowThreshold.Load() {
|
||||||
logx.WithDuration(duration).Slowf("[SQL] exec: slowcall - %s", stmt)
|
logx.WithContext(ctx).WithDuration(duration).Slowf("[SQL] exec: slowcall - %s", stmt)
|
||||||
} else {
|
} else {
|
||||||
logx.WithDuration(duration).Infof("sql exec: %s", stmt)
|
logx.WithContext(ctx).WithDuration(duration).Infof("sql exec: %s", stmt)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logSqlError(stmt, err)
|
logSqlError(ctx, stmt, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func execStmt(conn stmtConn, q string, args ...interface{}) (sql.Result, error) {
|
func execStmt(ctx context.Context, conn stmtConn, q string, args ...interface{}) (sql.Result, error) {
|
||||||
stmt, err := format(q, args...)
|
stmt, err := format(q, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
startTime := timex.Now()
|
startTime := timex.Now()
|
||||||
result, err := conn.Exec(args...)
|
result, err := conn.ExecContext(ctx, args...)
|
||||||
duration := timex.Since(startTime)
|
duration := timex.Since(startTime)
|
||||||
if duration > slowThreshold.Load() {
|
if duration > slowThreshold.Load() {
|
||||||
logx.WithDuration(duration).Slowf("[SQL] execStmt: slowcall - %s", stmt)
|
logx.WithContext(ctx).WithDuration(duration).Slowf("[SQL] execStmt: slowcall - %s", stmt)
|
||||||
} else {
|
} else {
|
||||||
logx.WithDuration(duration).Infof("sql execStmt: %s", stmt)
|
logx.WithContext(ctx).WithDuration(duration).Infof("sql execStmt: %s", stmt)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logSqlError(stmt, err)
|
logSqlError(ctx, stmt, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func query(conn sessionConn, scanner func(*sql.Rows) error, q string, args ...interface{}) error {
|
func query(ctx context.Context, conn sessionConn, scanner func(*sql.Rows) error,
|
||||||
|
q string, args ...interface{}) error {
|
||||||
stmt, err := format(q, args...)
|
stmt, err := format(q, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
startTime := timex.Now()
|
startTime := timex.Now()
|
||||||
rows, err := conn.Query(q, args...)
|
rows, err := conn.QueryContext(ctx, q, args...)
|
||||||
duration := timex.Since(startTime)
|
duration := timex.Since(startTime)
|
||||||
if duration > slowThreshold.Load() {
|
if duration > slowThreshold.Load() {
|
||||||
logx.WithDuration(duration).Slowf("[SQL] query: slowcall - %s", stmt)
|
logx.WithContext(ctx).WithDuration(duration).Slowf("[SQL] query: slowcall - %s", stmt)
|
||||||
} else {
|
} else {
|
||||||
logx.WithDuration(duration).Infof("sql query: %s", stmt)
|
logx.WithContext(ctx).WithDuration(duration).Infof("sql query: %s", stmt)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logSqlError(stmt, err)
|
logSqlError(ctx, stmt, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
@@ -83,22 +85,23 @@ func query(conn sessionConn, scanner func(*sql.Rows) error, q string, args ...in
|
|||||||
return scanner(rows)
|
return scanner(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryStmt(conn stmtConn, scanner func(*sql.Rows) error, q string, args ...interface{}) error {
|
func queryStmt(ctx context.Context, conn stmtConn, scanner func(*sql.Rows) error,
|
||||||
|
q string, args ...interface{}) error {
|
||||||
stmt, err := format(q, args...)
|
stmt, err := format(q, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
startTime := timex.Now()
|
startTime := timex.Now()
|
||||||
rows, err := conn.Query(args...)
|
rows, err := conn.QueryContext(ctx, args...)
|
||||||
duration := timex.Since(startTime)
|
duration := timex.Since(startTime)
|
||||||
if duration > slowThreshold.Load() {
|
if duration > slowThreshold.Load() {
|
||||||
logx.WithDuration(duration).Slowf("[SQL] queryStmt: slowcall - %s", stmt)
|
logx.WithContext(ctx).WithDuration(duration).Slowf("[SQL] queryStmt: slowcall - %s", stmt)
|
||||||
} else {
|
} else {
|
||||||
logx.WithDuration(duration).Infof("sql queryStmt: %s", stmt)
|
logx.WithContext(ctx).WithDuration(duration).Infof("sql queryStmt: %s", stmt)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logSqlError(stmt, err)
|
logSqlError(ctx, stmt, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sqlx
|
package sqlx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -57,7 +58,7 @@ func TestStmt_exec(t *testing.T) {
|
|||||||
test := test
|
test := test
|
||||||
fns := []func(args ...interface{}) (sql.Result, error){
|
fns := []func(args ...interface{}) (sql.Result, error){
|
||||||
func(args ...interface{}) (sql.Result, error) {
|
func(args ...interface{}) (sql.Result, error) {
|
||||||
return exec(&mockedSessionConn{
|
return exec(context.Background(), &mockedSessionConn{
|
||||||
lastInsertId: test.lastInsertId,
|
lastInsertId: test.lastInsertId,
|
||||||
rowsAffected: test.rowsAffected,
|
rowsAffected: test.rowsAffected,
|
||||||
err: test.err,
|
err: test.err,
|
||||||
@@ -65,7 +66,7 @@ func TestStmt_exec(t *testing.T) {
|
|||||||
}, test.query, args...)
|
}, test.query, args...)
|
||||||
},
|
},
|
||||||
func(args ...interface{}) (sql.Result, error) {
|
func(args ...interface{}) (sql.Result, error) {
|
||||||
return execStmt(&mockedStmtConn{
|
return execStmt(context.Background(), &mockedStmtConn{
|
||||||
lastInsertId: test.lastInsertId,
|
lastInsertId: test.lastInsertId,
|
||||||
rowsAffected: test.rowsAffected,
|
rowsAffected: test.rowsAffected,
|
||||||
err: test.err,
|
err: test.err,
|
||||||
@@ -137,7 +138,7 @@ func TestStmt_query(t *testing.T) {
|
|||||||
test := test
|
test := test
|
||||||
fns := []func(args ...interface{}) error{
|
fns := []func(args ...interface{}) error{
|
||||||
func(args ...interface{}) error {
|
func(args ...interface{}) error {
|
||||||
return query(&mockedSessionConn{
|
return query(context.Background(), &mockedSessionConn{
|
||||||
err: test.err,
|
err: test.err,
|
||||||
delay: test.delay,
|
delay: test.delay,
|
||||||
}, func(rows *sql.Rows) error {
|
}, func(rows *sql.Rows) error {
|
||||||
@@ -145,7 +146,7 @@ func TestStmt_query(t *testing.T) {
|
|||||||
}, test.query, args...)
|
}, test.query, args...)
|
||||||
},
|
},
|
||||||
func(args ...interface{}) error {
|
func(args ...interface{}) error {
|
||||||
return queryStmt(&mockedStmtConn{
|
return queryStmt(context.Background(), &mockedStmtConn{
|
||||||
err: test.err,
|
err: test.err,
|
||||||
delay: test.delay,
|
delay: test.delay,
|
||||||
}, func(rows *sql.Rows) error {
|
}, func(rows *sql.Rows) error {
|
||||||
@@ -185,6 +186,10 @@ type mockedSessionConn struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedSessionConn) Exec(query string, args ...interface{}) (sql.Result, error) {
|
func (m *mockedSessionConn) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||||
|
return m.ExecContext(context.Background(), query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedSessionConn) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||||
if m.delay {
|
if m.delay {
|
||||||
time.Sleep(defaultSlowThreshold + time.Millisecond)
|
time.Sleep(defaultSlowThreshold + time.Millisecond)
|
||||||
}
|
}
|
||||||
@@ -195,6 +200,10 @@ func (m *mockedSessionConn) Exec(query string, args ...interface{}) (sql.Result,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedSessionConn) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
func (m *mockedSessionConn) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
||||||
|
return m.QueryContext(context.Background(), query, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedSessionConn) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
|
||||||
if m.delay {
|
if m.delay {
|
||||||
time.Sleep(defaultSlowThreshold + time.Millisecond)
|
time.Sleep(defaultSlowThreshold + time.Millisecond)
|
||||||
}
|
}
|
||||||
@@ -214,6 +223,10 @@ type mockedStmtConn struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedStmtConn) Exec(args ...interface{}) (sql.Result, error) {
|
func (m *mockedStmtConn) Exec(args ...interface{}) (sql.Result, error) {
|
||||||
|
return m.ExecContext(context.Background(), args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedStmtConn) ExecContext(_ context.Context, _ ...interface{}) (sql.Result, error) {
|
||||||
if m.delay {
|
if m.delay {
|
||||||
time.Sleep(defaultSlowThreshold + time.Millisecond)
|
time.Sleep(defaultSlowThreshold + time.Millisecond)
|
||||||
}
|
}
|
||||||
@@ -224,6 +237,10 @@ func (m *mockedStmtConn) Exec(args ...interface{}) (sql.Result, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockedStmtConn) Query(args ...interface{}) (*sql.Rows, error) {
|
func (m *mockedStmtConn) Query(args ...interface{}) (*sql.Rows, error) {
|
||||||
|
return m.QueryContext(context.Background(), args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockedStmtConn) QueryContext(_ context.Context, _ ...interface{}) (*sql.Rows, error) {
|
||||||
if m.delay {
|
if m.delay {
|
||||||
time.Sleep(defaultSlowThreshold + time.Millisecond)
|
time.Sleep(defaultSlowThreshold + time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sqlx
|
package sqlx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
@@ -26,11 +27,19 @@ func NewSessionFromTx(tx *sql.Tx) Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t txSession) Exec(q string, args ...interface{}) (sql.Result, error) {
|
func (t txSession) Exec(q string, args ...interface{}) (sql.Result, error) {
|
||||||
return exec(t.Tx, q, args...)
|
return t.ExecCtx(context.Background(), q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t txSession) ExecCtx(ctx context.Context, q string, args ...interface{}) (sql.Result, error) {
|
||||||
|
return exec(ctx, t.Tx, q, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t txSession) Prepare(q string) (StmtSession, error) {
|
func (t txSession) Prepare(q string) (StmtSession, error) {
|
||||||
stmt, err := t.Tx.Prepare(q)
|
return t.PrepareCtx(context.Background(), q)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t txSession) PrepareCtx(ctx context.Context, q string) (StmtSession, error) {
|
||||||
|
stmt, err := t.Tx.PrepareContext(ctx, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -42,25 +51,43 @@ func (t txSession) Prepare(q string) (StmtSession, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t txSession) QueryRow(v interface{}, q string, args ...interface{}) error {
|
func (t txSession) QueryRow(v interface{}, q string, args ...interface{}) error {
|
||||||
return query(t.Tx, func(rows *sql.Rows) error {
|
return t.QueryRowCtx(context.Background(), v, q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t txSession) QueryRowCtx(ctx context.Context, v interface{}, q string, args ...interface{}) error {
|
||||||
|
return query(ctx, t.Tx, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(v, rows, true)
|
return unmarshalRow(v, rows, true)
|
||||||
}, q, args...)
|
}, q, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t txSession) QueryRowPartial(v interface{}, q string, args ...interface{}) error {
|
func (t txSession) QueryRowPartial(v interface{}, q string, args ...interface{}) error {
|
||||||
return query(t.Tx, func(rows *sql.Rows) error {
|
return t.QueryRowPartialCtx(context.Background(), v, q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t txSession) QueryRowPartialCtx(ctx context.Context, v interface{}, q string,
|
||||||
|
args ...interface{}) error {
|
||||||
|
return query(ctx, t.Tx, func(rows *sql.Rows) error {
|
||||||
return unmarshalRow(v, rows, false)
|
return unmarshalRow(v, rows, false)
|
||||||
}, q, args...)
|
}, q, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t txSession) QueryRows(v interface{}, q string, args ...interface{}) error {
|
func (t txSession) QueryRows(v interface{}, q string, args ...interface{}) error {
|
||||||
return query(t.Tx, func(rows *sql.Rows) error {
|
return t.QueryRowsCtx(context.Background(), v, q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t txSession) QueryRowsCtx(ctx context.Context, v interface{}, q string, args ...interface{}) error {
|
||||||
|
return query(ctx, t.Tx, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(v, rows, true)
|
return unmarshalRows(v, rows, true)
|
||||||
}, q, args...)
|
}, q, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t txSession) QueryRowsPartial(v interface{}, q string, args ...interface{}) error {
|
func (t txSession) QueryRowsPartial(v interface{}, q string, args ...interface{}) error {
|
||||||
return query(t.Tx, func(rows *sql.Rows) error {
|
return t.QueryRowsPartialCtx(context.Background(), v, q, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t txSession) QueryRowsPartialCtx(ctx context.Context, v interface{}, q string,
|
||||||
|
args ...interface{}) error {
|
||||||
|
return query(ctx, t.Tx, func(rows *sql.Rows) error {
|
||||||
return unmarshalRows(v, rows, false)
|
return unmarshalRows(v, rows, false)
|
||||||
}, q, args...)
|
}, q, args...)
|
||||||
}
|
}
|
||||||
@@ -76,17 +103,19 @@ func begin(db *sql.DB) (trans, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func transact(db *commonSqlConn, b beginnable, fn func(Session) error) (err error) {
|
func transact(ctx context.Context, db *commonSqlConn, b beginnable,
|
||||||
|
fn func(context.Context, Session) error) (err error) {
|
||||||
conn, err := db.connProv()
|
conn, err := db.connProv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
db.onError(err)
|
db.onError(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return transactOnConn(conn, b, fn)
|
return transactOnConn(ctx, conn, b, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func transactOnConn(conn *sql.DB, b beginnable, fn func(Session) error) (err error) {
|
func transactOnConn(ctx context.Context, conn *sql.DB, b beginnable,
|
||||||
|
fn func(context.Context, Session) error) (err error) {
|
||||||
var tx trans
|
var tx trans
|
||||||
tx, err = b(conn)
|
tx, err = b(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -96,18 +125,18 @@ func transactOnConn(conn *sql.DB, b beginnable, fn func(Session) error) (err err
|
|||||||
defer func() {
|
defer func() {
|
||||||
if p := recover(); p != nil {
|
if p := recover(); p != nil {
|
||||||
if e := tx.Rollback(); e != nil {
|
if e := tx.Rollback(); e != nil {
|
||||||
err = fmt.Errorf("recover from %#v, rollback failed: %s", p, e)
|
err = fmt.Errorf("recover from %#v, rollback failed: %w", p, e)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("recoveer from %#v", p)
|
err = fmt.Errorf("recoveer from %#v", p)
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
if e := tx.Rollback(); e != nil {
|
if e := tx.Rollback(); e != nil {
|
||||||
err = fmt.Errorf("transaction failed: %s, rollback failed: %s", err, e)
|
err = fmt.Errorf("transaction failed: %s, rollback failed: %w", err, e)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = tx.Commit()
|
err = tx.Commit()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return fn(tx)
|
return fn(ctx, tx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sqlx
|
package sqlx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -26,26 +27,50 @@ func (mt *mockTx) Exec(q string, args ...interface{}) (sql.Result, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mt *mockTx) ExecCtx(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (mt *mockTx) Prepare(query string) (StmtSession, error) {
|
func (mt *mockTx) Prepare(query string) (StmtSession, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mt *mockTx) PrepareCtx(ctx context.Context, query string) (StmtSession, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (mt *mockTx) QueryRow(v interface{}, q string, args ...interface{}) error {
|
func (mt *mockTx) QueryRow(v interface{}, q string, args ...interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mt *mockTx) QueryRowCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (mt *mockTx) QueryRowPartial(v interface{}, q string, args ...interface{}) error {
|
func (mt *mockTx) QueryRowPartial(v interface{}, q string, args ...interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mt *mockTx) QueryRowPartialCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (mt *mockTx) QueryRows(v interface{}, q string, args ...interface{}) error {
|
func (mt *mockTx) QueryRows(v interface{}, q string, args ...interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mt *mockTx) QueryRowsCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (mt *mockTx) QueryRowsPartial(v interface{}, q string, args ...interface{}) error {
|
func (mt *mockTx) QueryRowsPartial(v interface{}, q string, args ...interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mt *mockTx) QueryRowsPartialCtx(ctx context.Context, v interface{}, query string, args ...interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (mt *mockTx) Rollback() error {
|
func (mt *mockTx) Rollback() error {
|
||||||
mt.status |= mockRollback
|
mt.status |= mockRollback
|
||||||
return nil
|
return nil
|
||||||
@@ -59,18 +84,20 @@ func beginMock(mock *mockTx) beginnable {
|
|||||||
|
|
||||||
func TestTransactCommit(t *testing.T) {
|
func TestTransactCommit(t *testing.T) {
|
||||||
mock := &mockTx{}
|
mock := &mockTx{}
|
||||||
err := transactOnConn(nil, beginMock(mock), func(Session) error {
|
err := transactOnConn(context.Background(), nil, beginMock(mock),
|
||||||
return nil
|
func(context.Context, Session) error {
|
||||||
})
|
return nil
|
||||||
|
})
|
||||||
assert.Equal(t, mockCommit, mock.status)
|
assert.Equal(t, mockCommit, mock.status)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTransactRollback(t *testing.T) {
|
func TestTransactRollback(t *testing.T) {
|
||||||
mock := &mockTx{}
|
mock := &mockTx{}
|
||||||
err := transactOnConn(nil, beginMock(mock), func(Session) error {
|
err := transactOnConn(context.Background(), nil, beginMock(mock),
|
||||||
return errors.New("rollback")
|
func(context.Context, Session) error {
|
||||||
})
|
return errors.New("rollback")
|
||||||
|
})
|
||||||
assert.Equal(t, mockRollback, mock.status)
|
assert.Equal(t, mockRollback, mock.status)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package sqlx
|
package sqlx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -66,7 +67,7 @@ func format(query string, args ...interface{}) (string, error) {
|
|||||||
|
|
||||||
writeValue(&b, args[argIndex])
|
writeValue(&b, args[argIndex])
|
||||||
argIndex++
|
argIndex++
|
||||||
case '$':
|
case ':', '$':
|
||||||
var j int
|
var j int
|
||||||
for j = i + 1; j < bytes; j++ {
|
for j = i + 1; j < bytes; j++ {
|
||||||
char := query[j]
|
char := query[j]
|
||||||
@@ -74,16 +75,18 @@ func format(query string, args ...interface{}) (string, error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if j > i+1 {
|
if j > i+1 {
|
||||||
index, err := strconv.Atoi(query[i+1 : j])
|
index, err := strconv.Atoi(query[i+1 : j])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// index starts from 1 for pg
|
// index starts from 1 for pg or oracle
|
||||||
if index > argIndex {
|
if index > argIndex {
|
||||||
argIndex = index
|
argIndex = index
|
||||||
}
|
}
|
||||||
|
|
||||||
index--
|
index--
|
||||||
if index < 0 || numArgs <= index {
|
if index < 0 || numArgs <= index {
|
||||||
return "", fmt.Errorf("error: wrong index %d in sql", index)
|
return "", fmt.Errorf("error: wrong index %d in sql", index)
|
||||||
@@ -109,9 +112,9 @@ func logInstanceError(datasource string, err error) {
|
|||||||
logx.Errorf("Error on getting sql instance of %s: %v", datasource, err)
|
logx.Errorf("Error on getting sql instance of %s: %v", datasource, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func logSqlError(stmt string, err error) {
|
func logSqlError(ctx context.Context, stmt string, err error) {
|
||||||
if err != nil && err != ErrNotFound {
|
if err != nil && err != ErrNotFound {
|
||||||
logx.Errorf("stmt: %s, error: %s", stmt, err.Error())
|
logx.WithContext(ctx).Errorf("stmt: %s, error: %s", stmt, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,30 @@ func TestFormat(t *testing.T) {
|
|||||||
args: []interface{}{"133", false},
|
args: []interface{}{"133", false},
|
||||||
hasErr: true,
|
hasErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "oracle normal",
|
||||||
|
query: "select name, age from users where bool=:1 and phone=:2",
|
||||||
|
args: []interface{}{true, "133"},
|
||||||
|
expect: "select name, age from users where bool=1 and phone='133'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "oracle normal reverse",
|
||||||
|
query: "select name, age from users where bool=:2 and phone=:1",
|
||||||
|
args: []interface{}{"133", false},
|
||||||
|
expect: "select name, age from users where bool=0 and phone='133'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "oracle error not number",
|
||||||
|
query: "select name, age from users where bool=:a and phone=:1",
|
||||||
|
args: []interface{}{"133", false},
|
||||||
|
hasErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "oracle error more args",
|
||||||
|
query: "select name, age from users where bool=:2 and phone=:1 and nickname=:3",
|
||||||
|
args: []interface{}{"133", false},
|
||||||
|
hasErr: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package stringx
|
|||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
children map[rune]*node
|
children map[rune]*node
|
||||||
|
fail *node
|
||||||
|
depth int
|
||||||
end bool
|
end bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,17 +14,19 @@ func (n *node) add(word string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nd := n
|
nd := n
|
||||||
for _, char := range chars {
|
var depth int
|
||||||
|
for i, char := range chars {
|
||||||
if nd.children == nil {
|
if nd.children == nil {
|
||||||
child := new(node)
|
child := new(node)
|
||||||
nd.children = map[rune]*node{
|
child.depth = i + 1
|
||||||
char: child,
|
nd.children = map[rune]*node{char: child}
|
||||||
}
|
|
||||||
nd = child
|
nd = child
|
||||||
} else if child, ok := nd.children[char]; ok {
|
} else if child, ok := nd.children[char]; ok {
|
||||||
nd = child
|
nd = child
|
||||||
|
depth++
|
||||||
} else {
|
} else {
|
||||||
child := new(node)
|
child := new(node)
|
||||||
|
child.depth = i + 1
|
||||||
nd.children[char] = child
|
nd.children[char] = child
|
||||||
nd = child
|
nd = child
|
||||||
}
|
}
|
||||||
@@ -30,3 +34,67 @@ func (n *node) add(word string) {
|
|||||||
|
|
||||||
nd.end = true
|
nd.end = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *node) build() {
|
||||||
|
var nodes []*node
|
||||||
|
for _, child := range n.children {
|
||||||
|
child.fail = n
|
||||||
|
nodes = append(nodes, child)
|
||||||
|
}
|
||||||
|
for len(nodes) > 0 {
|
||||||
|
nd := nodes[0]
|
||||||
|
nodes = nodes[1:]
|
||||||
|
for key, child := range nd.children {
|
||||||
|
nodes = append(nodes, child)
|
||||||
|
cur := nd
|
||||||
|
for cur != nil {
|
||||||
|
if cur.fail == nil {
|
||||||
|
child.fail = n
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if fail, ok := cur.fail.children[key]; ok {
|
||||||
|
child.fail = fail
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cur = cur.fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) find(chars []rune) []scope {
|
||||||
|
var scopes []scope
|
||||||
|
size := len(chars)
|
||||||
|
cur := n
|
||||||
|
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
child, ok := cur.children[chars[i]]
|
||||||
|
if ok {
|
||||||
|
cur = child
|
||||||
|
} else {
|
||||||
|
for cur != n {
|
||||||
|
cur = cur.fail
|
||||||
|
if child, ok = cur.children[chars[i]]; ok {
|
||||||
|
cur = child
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if child == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for child != n {
|
||||||
|
if child.end {
|
||||||
|
scopes = append(scopes, scope{
|
||||||
|
start: i + 1 - child.depth,
|
||||||
|
stop: i + 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
child = child.fail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scopes
|
||||||
|
}
|
||||||
|
|||||||
87
core/stringx/node_fuzz_test.go
Normal file
87
core/stringx/node_fuzz_test.go
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
//go:build go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package stringx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FuzzNodeFind(f *testing.F) {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
f.Add(10)
|
||||||
|
f.Fuzz(func(t *testing.T, keys int) {
|
||||||
|
str := Randn(rand.Intn(100) + 50)
|
||||||
|
keywords := make(map[string]struct{})
|
||||||
|
for i := 0; i < keys; i++ {
|
||||||
|
keyword := Randn(rand.Intn(10) + 5)
|
||||||
|
if !strings.Contains(str, keyword) {
|
||||||
|
keywords[keyword] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size := len(str)
|
||||||
|
var scopes []scope
|
||||||
|
var n node
|
||||||
|
for i := 0; i < size%20; i++ {
|
||||||
|
start := rand.Intn(size)
|
||||||
|
stop := start + rand.Intn(20) + 1
|
||||||
|
if stop > size {
|
||||||
|
stop = size
|
||||||
|
}
|
||||||
|
if start == stop {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
keyword := str[start:stop]
|
||||||
|
if _, ok := keywords[keyword]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
keywords[keyword] = struct{}{}
|
||||||
|
var pos int
|
||||||
|
for pos <= len(str)-len(keyword) {
|
||||||
|
val := str[pos:]
|
||||||
|
p := strings.Index(val, keyword)
|
||||||
|
if p < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
scopes = append(scopes, scope{
|
||||||
|
start: pos + p,
|
||||||
|
stop: pos + p + len(keyword),
|
||||||
|
})
|
||||||
|
pos += p + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for keyword := range keywords {
|
||||||
|
n.add(keyword)
|
||||||
|
}
|
||||||
|
n.build()
|
||||||
|
|
||||||
|
var buf strings.Builder
|
||||||
|
buf.WriteString("keywords:\n")
|
||||||
|
for key := range keywords {
|
||||||
|
fmt.Fprintf(&buf, "\t%q,\n", key)
|
||||||
|
}
|
||||||
|
buf.WriteString("scopes:\n")
|
||||||
|
for _, scp := range scopes {
|
||||||
|
fmt.Fprintf(&buf, "\t{%d, %d},\n", scp.start, scp.stop)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&buf, "text:\n\t%s\n", str)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf(buf.String())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
assert.ElementsMatchf(t, scopes, n.find([]rune(str)), buf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
195
core/stringx/node_test.go
Normal file
195
core/stringx/node_test.go
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
package stringx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFuzzNodeCase1(t *testing.T) {
|
||||||
|
keywords := []string{
|
||||||
|
"cs8Zh",
|
||||||
|
"G1OihlVuBz",
|
||||||
|
"K6azS2FBHjI",
|
||||||
|
"DQKvghI4",
|
||||||
|
"l7bA86Sze",
|
||||||
|
"tjBLZhCao",
|
||||||
|
"nEsXmVzP",
|
||||||
|
"cbRh8UE1nO3s",
|
||||||
|
"Wta3R2WcbGP",
|
||||||
|
"jpOIcA",
|
||||||
|
"TtkRr4k9hI",
|
||||||
|
"OKbSo0clAYTtk",
|
||||||
|
"uJs1WToEanlKV",
|
||||||
|
"05Y02iFD2",
|
||||||
|
"x2uJs1WToEanlK",
|
||||||
|
"ieaSWe",
|
||||||
|
"Kg",
|
||||||
|
"FD2bCKFazH",
|
||||||
|
}
|
||||||
|
scopes := []scope{
|
||||||
|
{62, 72},
|
||||||
|
{52, 65},
|
||||||
|
{21, 34},
|
||||||
|
{1, 10},
|
||||||
|
{19, 33},
|
||||||
|
{36, 42},
|
||||||
|
{42, 44},
|
||||||
|
{7, 17},
|
||||||
|
}
|
||||||
|
n := new(node)
|
||||||
|
for _, key := range keywords {
|
||||||
|
n.add(key)
|
||||||
|
}
|
||||||
|
n.build()
|
||||||
|
assert.ElementsMatch(t, scopes, n.find([]rune("Z05Y02iFD2bCKFazHtrx2uJs1WToEanlKVWKieaSWeKgmnUXV0ZjOKbSo0clAYTtkRr4k9hI")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuzzNodeCase2(t *testing.T) {
|
||||||
|
keywords := []string{
|
||||||
|
"IP1NPsJKIvt",
|
||||||
|
"Iw7hQARwSTw",
|
||||||
|
"SmZIcA",
|
||||||
|
"OyxHPYkoQzFO",
|
||||||
|
"3suCnuSAS5d",
|
||||||
|
"HUMpbi",
|
||||||
|
"HPdvbGGpY",
|
||||||
|
"49qjMtR8bEt",
|
||||||
|
"a0zrrGKVTJ2",
|
||||||
|
"WbOBcszeo1FfZ",
|
||||||
|
"8tHUi5PJI",
|
||||||
|
"Oa2Di",
|
||||||
|
"6ZWa5nr1tU",
|
||||||
|
"o0LJRfmeXB9bF9",
|
||||||
|
"veF0ehKxH",
|
||||||
|
"Qp73r",
|
||||||
|
"B6Rmds4ELY8",
|
||||||
|
"uNpGybQZG",
|
||||||
|
"Ogm3JqicRZlA4n",
|
||||||
|
"FL6LVErKomc84H",
|
||||||
|
"qv2Pi0xJj3cR1",
|
||||||
|
"bPWLBg4",
|
||||||
|
"hYN8Q4M1sw",
|
||||||
|
"ExkTgNklmlIx",
|
||||||
|
"eVgHHDOxOUEj",
|
||||||
|
"5WPEVv0tR",
|
||||||
|
"CPjnOAqUZgV",
|
||||||
|
"oR3Ogtz",
|
||||||
|
"jwk1Zbg",
|
||||||
|
"DYqguyk8h",
|
||||||
|
"rieroDmpvYFK",
|
||||||
|
"MQ9hZnMjDqrNQe",
|
||||||
|
"EhM4KqkCBd",
|
||||||
|
"m9xalj6q",
|
||||||
|
"d5CTL5mzK",
|
||||||
|
"XJOoTvFtI8U",
|
||||||
|
"iFAwspJ",
|
||||||
|
"iGv8ErnRZIuSWX",
|
||||||
|
"i8C1BqsYX",
|
||||||
|
"vXN1KOaOgU",
|
||||||
|
"GHJFB",
|
||||||
|
"Y6OlAqbZxYG8",
|
||||||
|
"dzd4QscSih4u",
|
||||||
|
"SsLYMkKvB9APx",
|
||||||
|
"gi0huB3",
|
||||||
|
"CMICHDCSvSrgiACXVkN",
|
||||||
|
"MwOvyHbaxdaqpZpU",
|
||||||
|
"wOvyHbaxdaqpZpUbI",
|
||||||
|
"2TT5WEy",
|
||||||
|
"eoCq0T2MC",
|
||||||
|
"ZpUbI7",
|
||||||
|
"oCq0T2MCp",
|
||||||
|
"CpLFgLg0g",
|
||||||
|
"FgLg0gh",
|
||||||
|
"w5awC5HeoCq",
|
||||||
|
"1c",
|
||||||
|
}
|
||||||
|
scopes := []scope{
|
||||||
|
{0, 19},
|
||||||
|
{57, 73},
|
||||||
|
{58, 75},
|
||||||
|
{47, 54},
|
||||||
|
{29, 38},
|
||||||
|
{70, 76},
|
||||||
|
{30, 39},
|
||||||
|
{37, 46},
|
||||||
|
{40, 47},
|
||||||
|
{22, 33},
|
||||||
|
{92, 94},
|
||||||
|
}
|
||||||
|
n := new(node)
|
||||||
|
for _, key := range keywords {
|
||||||
|
n.add(key)
|
||||||
|
}
|
||||||
|
n.build()
|
||||||
|
assert.ElementsMatch(t, scopes, n.find([]rune("CMICHDCSvSrgiACXVkNF9lw5awC5HeoCq0T2MCpLFgLg0gh2TT5WEyINrMwOvyHbaxdaqpZpUbI7SpIY5yVWf33MuX7K1c")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuzzNodeCase3(t *testing.T) {
|
||||||
|
keywords := []string{
|
||||||
|
"QAraACKOftI4",
|
||||||
|
"unRmd2EO0",
|
||||||
|
"s25OtuoU",
|
||||||
|
"aGlmn7KnbE4HCX",
|
||||||
|
"kuK6Uh",
|
||||||
|
"ckuK6Uh",
|
||||||
|
"uK6Uh",
|
||||||
|
"Iy",
|
||||||
|
"h",
|
||||||
|
"PMSSUNvyi",
|
||||||
|
"ahz0i",
|
||||||
|
"Lhs4XZ1e",
|
||||||
|
"shPp1Va7aQNVme",
|
||||||
|
"yIUckuK6Uh",
|
||||||
|
"pKjIyI",
|
||||||
|
"jIyIUckuK6Uh",
|
||||||
|
"UckuK6Uh",
|
||||||
|
"Uh",
|
||||||
|
"JPAULjQgHJ",
|
||||||
|
"Wp",
|
||||||
|
"sbkZxXurrI",
|
||||||
|
"pKjIyIUckuK6Uh",
|
||||||
|
}
|
||||||
|
scopes := []scope{
|
||||||
|
{9, 15},
|
||||||
|
{8, 15},
|
||||||
|
{5, 15},
|
||||||
|
{1, 7},
|
||||||
|
{10, 15},
|
||||||
|
{3, 15},
|
||||||
|
{0, 2},
|
||||||
|
{1, 15},
|
||||||
|
{7, 15},
|
||||||
|
{13, 15},
|
||||||
|
{4, 6},
|
||||||
|
{14, 15},
|
||||||
|
}
|
||||||
|
n := new(node)
|
||||||
|
for _, key := range keywords {
|
||||||
|
n.add(key)
|
||||||
|
}
|
||||||
|
n.build()
|
||||||
|
assert.ElementsMatch(t, scopes, n.find([]rune("WpKjIyIUckuK6Uh")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNodeFind(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
keywords := []string{
|
||||||
|
"A",
|
||||||
|
"AV",
|
||||||
|
"AV演员",
|
||||||
|
"无名氏",
|
||||||
|
"AV演员色情",
|
||||||
|
"日本AV女优",
|
||||||
|
}
|
||||||
|
trie := new(node)
|
||||||
|
for _, keyword := range keywords {
|
||||||
|
trie.add(keyword)
|
||||||
|
}
|
||||||
|
trie.build()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
trie.find([]rune("日本AV演员兼电视、电影演员。无名氏AV女优是xx出道, 日本AV女优们最精彩的表演是AV演员色情表演"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
replacer struct {
|
replacer struct {
|
||||||
node
|
*node
|
||||||
mapping map[string]string
|
mapping map[string]string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -17,58 +17,81 @@ type (
|
|||||||
// NewReplacer returns a Replacer.
|
// NewReplacer returns a Replacer.
|
||||||
func NewReplacer(mapping map[string]string) Replacer {
|
func NewReplacer(mapping map[string]string) Replacer {
|
||||||
rep := &replacer{
|
rep := &replacer{
|
||||||
|
node: new(node),
|
||||||
mapping: mapping,
|
mapping: mapping,
|
||||||
}
|
}
|
||||||
for k := range mapping {
|
for k := range mapping {
|
||||||
rep.add(k)
|
rep.add(k)
|
||||||
}
|
}
|
||||||
|
rep.build()
|
||||||
|
|
||||||
return rep
|
return rep
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Replace replaces text with given substitutes.
|
||||||
func (r *replacer) Replace(text string) string {
|
func (r *replacer) Replace(text string) string {
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
|
var start int
|
||||||
chars := []rune(text)
|
chars := []rune(text)
|
||||||
size := len(chars)
|
size := len(chars)
|
||||||
start := -1
|
|
||||||
|
|
||||||
for i := 0; i < size; i++ {
|
for start < size {
|
||||||
child, ok := r.children[chars[i]]
|
cur := r.node
|
||||||
if !ok {
|
|
||||||
builder.WriteRune(chars[i])
|
if start > 0 {
|
||||||
continue
|
builder.WriteString(string(chars[:start]))
|
||||||
}
|
}
|
||||||
|
|
||||||
if start < 0 {
|
for i := start; i < size; i++ {
|
||||||
start = i
|
child, ok := cur.children[chars[i]]
|
||||||
}
|
if ok {
|
||||||
end := -1
|
cur = child
|
||||||
if child.end {
|
} else if cur == r.node {
|
||||||
end = i + 1
|
builder.WriteRune(chars[i])
|
||||||
}
|
// cur already points to root, set start only
|
||||||
|
start = i + 1
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
curDepth := cur.depth
|
||||||
|
cur = cur.fail
|
||||||
|
child, ok = cur.children[chars[i]]
|
||||||
|
if !ok {
|
||||||
|
// write this path
|
||||||
|
builder.WriteString(string(chars[i-curDepth : i+1]))
|
||||||
|
// go to root
|
||||||
|
cur = r.node
|
||||||
|
start = i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
j := i + 1
|
failDepth := cur.depth
|
||||||
for ; j < size; j++ {
|
// write path before jump
|
||||||
grandchild, ok := child.children[chars[j]]
|
builder.WriteString(string(chars[start : start+curDepth-failDepth]))
|
||||||
if !ok {
|
start += curDepth - failDepth
|
||||||
|
cur = child
|
||||||
|
}
|
||||||
|
|
||||||
|
if cur.end {
|
||||||
|
val := string(chars[i+1-cur.depth : i+1])
|
||||||
|
builder.WriteString(r.mapping[val])
|
||||||
|
builder.WriteString(string(chars[i+1:]))
|
||||||
|
// only matching this path, all previous paths are done
|
||||||
|
if start >= i+1-cur.depth && i+1 >= size {
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
chars = []rune(builder.String())
|
||||||
|
size = len(chars)
|
||||||
|
builder.Reset()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
child = grandchild
|
|
||||||
if child.end {
|
|
||||||
end = j + 1
|
|
||||||
i = j
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if end > 0 {
|
if !cur.end {
|
||||||
i = j - 1
|
builder.WriteString(string(chars[start:]))
|
||||||
builder.WriteString(r.mapping[string(chars[start:end])])
|
return builder.String()
|
||||||
} else {
|
|
||||||
builder.WriteRune(chars[i])
|
|
||||||
}
|
}
|
||||||
start = -1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.String()
|
return string(chars)
|
||||||
}
|
}
|
||||||
|
|||||||
42
core/stringx/replacer_fuzz_test.go
Normal file
42
core/stringx/replacer_fuzz_test.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
//go:build go1.18
|
||||||
|
// +build go1.18
|
||||||
|
|
||||||
|
package stringx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FuzzReplacerReplace(f *testing.F) {
|
||||||
|
keywords := make(map[string]string)
|
||||||
|
for i := 0; i < 20; i++ {
|
||||||
|
keywords[Randn(rand.Intn(10)+5)] = Randn(rand.Intn(5) + 1)
|
||||||
|
}
|
||||||
|
rep := NewReplacer(keywords)
|
||||||
|
printableKeywords := func() string {
|
||||||
|
var buf strings.Builder
|
||||||
|
for k, v := range keywords {
|
||||||
|
fmt.Fprintf(&buf, "%q: %q,\n", k, v)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
f.Add(50)
|
||||||
|
f.Fuzz(func(t *testing.T, n int) {
|
||||||
|
text := Randn(rand.Intn(n%50+50) + 1)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("mapping: %s\ntext: %s", printableKeywords(), text)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
val := rep.Replace(text)
|
||||||
|
keys := rep.(*replacer).node.find([]rune(val))
|
||||||
|
if len(keys) > 0 {
|
||||||
|
t.Errorf("mapping: %s\ntext: %s\nresult: %s\nmatch: %v",
|
||||||
|
printableKeywords(), text, val, keys)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -15,6 +15,14 @@ func TestReplacer_Replace(t *testing.T) {
|
|||||||
assert.Equal(t, "零1234五", NewReplacer(mapping).Replace("零一二三四五"))
|
assert.Equal(t, "零1234五", NewReplacer(mapping).Replace("零一二三四五"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReplacer_ReplaceOverlap(t *testing.T) {
|
||||||
|
mapping := map[string]string{
|
||||||
|
"3d": "34",
|
||||||
|
"bc": "23",
|
||||||
|
}
|
||||||
|
assert.Equal(t, "a234e", NewReplacer(mapping).Replace("abcde"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestReplacer_ReplaceSingleChar(t *testing.T) {
|
func TestReplacer_ReplaceSingleChar(t *testing.T) {
|
||||||
mapping := map[string]string{
|
mapping := map[string]string{
|
||||||
"二": "2",
|
"二": "2",
|
||||||
@@ -42,3 +50,99 @@ func TestReplacer_ReplaceMultiMatches(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assert.Equal(t, "零一23四五一23四五", NewReplacer(mapping).Replace("零一二三四五一二三四五"))
|
assert.Equal(t, "零一23四五一23四五", NewReplacer(mapping).Replace("零一二三四五一二三四五"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReplacer_ReplaceJumpToFail(t *testing.T) {
|
||||||
|
mapping := map[string]string{
|
||||||
|
"bcdf": "1235",
|
||||||
|
"cde": "234",
|
||||||
|
}
|
||||||
|
assert.Equal(t, "ab234fg", NewReplacer(mapping).Replace("abcdefg"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReplacer_ReplaceJumpToFailDup(t *testing.T) {
|
||||||
|
mapping := map[string]string{
|
||||||
|
"bcdf": "1235",
|
||||||
|
"ccde": "2234",
|
||||||
|
}
|
||||||
|
assert.Equal(t, "ab2234fg", NewReplacer(mapping).Replace("abccdefg"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReplacer_ReplaceJumpToFailEnding(t *testing.T) {
|
||||||
|
mapping := map[string]string{
|
||||||
|
"bcdf": "1235",
|
||||||
|
"cdef": "2345",
|
||||||
|
}
|
||||||
|
assert.Equal(t, "ab2345", NewReplacer(mapping).Replace("abcdef"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReplacer_ReplaceEmpty(t *testing.T) {
|
||||||
|
mapping := map[string]string{
|
||||||
|
"bcdf": "1235",
|
||||||
|
"cdef": "2345",
|
||||||
|
}
|
||||||
|
assert.Equal(t, "", NewReplacer(mapping).Replace(""))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuzzReplacerCase1(t *testing.T) {
|
||||||
|
keywords := map[string]string{
|
||||||
|
"yQyJykiqoh": "xw",
|
||||||
|
"tgN70z": "Q2P",
|
||||||
|
"tXKhEn": "w1G8",
|
||||||
|
"5nfOW1XZO": "GN",
|
||||||
|
"f4Ov9i9nHD": "cT",
|
||||||
|
"1ov9Q": "Y",
|
||||||
|
"7IrC9n": "400i",
|
||||||
|
"JQLxonpHkOjv": "XI",
|
||||||
|
"DyHQ3c7": "Ygxux",
|
||||||
|
"ffyqJi": "u",
|
||||||
|
"UHuvXrbD8pni": "dN",
|
||||||
|
"LIDzNbUlTX": "g",
|
||||||
|
"yN9WZh2rkc8Q": "3U",
|
||||||
|
"Vhk11rz8CObceC": "jf",
|
||||||
|
"R0Rt4H2qChUQf": "7U5M",
|
||||||
|
"MGQzzPCVKjV9": "yYz",
|
||||||
|
"B5jUUl0u1XOY": "l4PZ",
|
||||||
|
"pdvp2qfLgG8X": "BM562",
|
||||||
|
"ZKl9qdApXJ2": "T",
|
||||||
|
"37jnugkSevU66": "aOHFX",
|
||||||
|
}
|
||||||
|
rep := NewReplacer(keywords)
|
||||||
|
text := "yjF8fyqJiiqrczOCVyoYbLvrMpnkj"
|
||||||
|
val := rep.Replace(text)
|
||||||
|
keys := rep.(*replacer).node.find([]rune(val))
|
||||||
|
if len(keys) > 0 {
|
||||||
|
t.Errorf("result: %s, match: %v", val, keys)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuzzReplacerCase2(t *testing.T) {
|
||||||
|
keywords := map[string]string{
|
||||||
|
"dmv2SGZvq9Yz": "TE",
|
||||||
|
"rCL5DRI9uFP8": "hvsc8",
|
||||||
|
"7pSA2jaomgg": "v",
|
||||||
|
"kWSQvjVOIAxR": "Oje",
|
||||||
|
"hgU5bYYkD3r6": "qCXu",
|
||||||
|
"0eh6uI": "MMlt",
|
||||||
|
"3USZSl85EKeMzw": "Pc",
|
||||||
|
"JONmQSuXa": "dX",
|
||||||
|
"EO1WIF": "G",
|
||||||
|
"uUmFJGVmacjF": "1N",
|
||||||
|
"DHpw7": "M",
|
||||||
|
"NYB2bm": "CPya",
|
||||||
|
"9FiNvBAHHNku5": "7FlDE",
|
||||||
|
"tJi3I4WxcY": "q5",
|
||||||
|
"sNJ8Z1ToBV0O": "tl",
|
||||||
|
"0iOg72QcPo": "RP",
|
||||||
|
"pSEqeL": "5KZ",
|
||||||
|
"GOyYqTgmvQ": "9",
|
||||||
|
"Qv4qCsj": "nl52E",
|
||||||
|
"wNQ5tOutYu5s8": "6iGa",
|
||||||
|
}
|
||||||
|
rep := NewReplacer(keywords)
|
||||||
|
text := "AoRxrdKWsGhFpXwVqMLWRL74OukwjBuBh0g7pSrk"
|
||||||
|
val := rep.Replace(text)
|
||||||
|
keys := rep.(*replacer).node.find([]rune(val))
|
||||||
|
if len(keys) > 0 {
|
||||||
|
t.Errorf("result: %s, match: %v", val, keys)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ func NewTrie(words []string, opts ...TrieOption) Trie {
|
|||||||
n.add(word)
|
n.add(word)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n.build()
|
||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +50,7 @@ func (n *trieNode) Filter(text string) (sentence string, keywords []string, foun
|
|||||||
return text, nil, false
|
return text, nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
scopes := n.findKeywordScopes(chars)
|
scopes := n.find(chars)
|
||||||
keywords = n.collectKeywords(chars, scopes)
|
keywords = n.collectKeywords(chars, scopes)
|
||||||
|
|
||||||
for _, match := range scopes {
|
for _, match := range scopes {
|
||||||
@@ -65,7 +67,7 @@ func (n *trieNode) FindKeywords(text string) []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
scopes := n.findKeywordScopes(chars)
|
scopes := n.find(chars)
|
||||||
return n.collectKeywords(chars, scopes)
|
return n.collectKeywords(chars, scopes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,48 +87,6 @@ func (n *trieNode) collectKeywords(chars []rune, scopes []scope) []string {
|
|||||||
return keywords
|
return keywords
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *trieNode) findKeywordScopes(chars []rune) []scope {
|
|
||||||
var scopes []scope
|
|
||||||
size := len(chars)
|
|
||||||
start := -1
|
|
||||||
|
|
||||||
for i := 0; i < size; i++ {
|
|
||||||
child, ok := n.children[chars[i]]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if start < 0 {
|
|
||||||
start = i
|
|
||||||
}
|
|
||||||
if child.end {
|
|
||||||
scopes = append(scopes, scope{
|
|
||||||
start: start,
|
|
||||||
stop: i + 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for j := i + 1; j < size; j++ {
|
|
||||||
grandchild, ok := child.children[chars[j]]
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
child = grandchild
|
|
||||||
if child.end {
|
|
||||||
scopes = append(scopes, scope{
|
|
||||||
start: start,
|
|
||||||
stop: j + 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return scopes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *trieNode) replaceWithAsterisk(chars []rune, start, stop int) {
|
func (n *trieNode) replaceWithAsterisk(chars []rune, start, stop int) {
|
||||||
for i := start; i < stop; i++ {
|
for i := start; i < stop; i++ {
|
||||||
chars[i] = n.mask
|
chars[i] = n.mask
|
||||||
|
|||||||
@@ -6,6 +6,17 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestTrieSimple(t *testing.T) {
|
||||||
|
trie := NewTrie([]string{
|
||||||
|
"bc",
|
||||||
|
"cd",
|
||||||
|
})
|
||||||
|
output, keywords, found := trie.Filter("abcd")
|
||||||
|
assert.True(t, found)
|
||||||
|
assert.Equal(t, "a***", output)
|
||||||
|
assert.ElementsMatch(t, []string{"bc", "cd"}, keywords)
|
||||||
|
}
|
||||||
|
|
||||||
func TestTrie(t *testing.T) {
|
func TestTrie(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
@@ -14,11 +25,11 @@ func TestTrie(t *testing.T) {
|
|||||||
found bool
|
found bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
input: "日本AV演员兼电视、电影演员。苍井空AV女优是xx出道, 日本AV女优们最精彩的表演是AV演员色情表演",
|
input: "日本AV演员兼电视、电影演员。无名氏AV女优是xx出道, 日本AV女优们最精彩的表演是AV演员色情表演",
|
||||||
output: "日本****兼电视、电影演员。*****女优是xx出道, ******们最精彩的表演是******表演",
|
output: "日本****兼电视、电影演员。*****女优是xx出道, ******们最精彩的表演是******表演",
|
||||||
keywords: []string{
|
keywords: []string{
|
||||||
"AV演员",
|
"AV演员",
|
||||||
"苍井空",
|
"无名氏",
|
||||||
"AV",
|
"AV",
|
||||||
"日本AV女优",
|
"日本AV女优",
|
||||||
"AV演员色情",
|
"AV演员色情",
|
||||||
@@ -89,7 +100,7 @@ func TestTrie(t *testing.T) {
|
|||||||
"一不",
|
"一不",
|
||||||
"AV",
|
"AV",
|
||||||
"AV演员",
|
"AV演员",
|
||||||
"苍井空",
|
"无名氏",
|
||||||
"AV演员色情",
|
"AV演员色情",
|
||||||
"日本AV女优",
|
"日本AV女优",
|
||||||
})
|
})
|
||||||
@@ -145,20 +156,3 @@ func TestTrieNested(t *testing.T) {
|
|||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.Equal(t, "零########九十", output)
|
assert.Equal(t, "零########九十", output)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkTrie(b *testing.B) {
|
|
||||||
b.ReportAllocs()
|
|
||||||
|
|
||||||
trie := NewTrie([]string{
|
|
||||||
"A",
|
|
||||||
"AV",
|
|
||||||
"AV演员",
|
|
||||||
"苍井空",
|
|
||||||
"AV演员色情",
|
|
||||||
"日本AV女优",
|
|
||||||
})
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
trie.Filter("日本AV演员兼电视、电影演员。苍井空AV女优是xx出道, 日本AV女优们最精彩的表演是AV演员色情表演")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,10 +3,6 @@ package syncx
|
|||||||
import "sync"
|
import "sync"
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// SharedCalls is an alias of SingleFlight.
|
|
||||||
// Deprecated: use SingleFlight.
|
|
||||||
SharedCalls = SingleFlight
|
|
||||||
|
|
||||||
// SingleFlight lets the concurrent calls with the same key to share the call result.
|
// SingleFlight lets the concurrent calls with the same key to share the call result.
|
||||||
// For example, A called F, before it's done, B called F. Then B would not execute F,
|
// For example, A called F, before it's done, B called F. Then B would not execute F,
|
||||||
// and shared the result returned by F which called by A.
|
// and shared the result returned by F which called by A.
|
||||||
@@ -37,12 +33,6 @@ func NewSingleFlight() SingleFlight {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSharedCalls returns a SingleFlight.
|
|
||||||
// Deprecated: use NewSingleFlight.
|
|
||||||
func NewSharedCalls() SingleFlight {
|
|
||||||
return NewSingleFlight()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *flightGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
|
func (g *flightGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
|
||||||
c, done := g.createCall(key)
|
c, done := g.createCall(key)
|
||||||
if done {
|
if done {
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/core/lang"
|
"github.com/zeromicro/go-zero/core/lang"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// errTimeout indicates a timeout.
|
||||||
|
var errTimeout = errors.New("timeout")
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// Ticker interface wraps the Chan and Stop methods.
|
// Ticker interface wraps the Chan and Stop methods.
|
||||||
Ticker interface {
|
Ticker interface {
|
||||||
@@ -70,7 +73,7 @@ func (ft *fakeTicker) Tick() {
|
|||||||
func (ft *fakeTicker) Wait(d time.Duration) error {
|
func (ft *fakeTicker) Wait(d time.Duration) error {
|
||||||
select {
|
select {
|
||||||
case <-time.After(d):
|
case <-time.After(d):
|
||||||
return errors.New("timeout")
|
return errTimeout
|
||||||
case <-ft.done:
|
case <-ft.done:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// assert that metadataSupplier implements the TextMapCarrier interface
|
// assert that metadataSupplier implements the TextMapCarrier interface
|
||||||
var _ propagation.TextMapCarrier = new(metadataSupplier)
|
var _ propagation.TextMapCarrier = (*metadataSupplier)(nil)
|
||||||
|
|
||||||
type metadataSupplier struct {
|
type metadataSupplier struct {
|
||||||
metadata *metadata.MD
|
metadata *metadata.MD
|
||||||
|
|||||||
@@ -90,14 +90,14 @@ func TestParseFullMethod(t *testing.T) {
|
|||||||
semconv.RPCMethodKey.String("theMethod"),
|
semconv.RPCMethodKey.String("theMethod"),
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
fullMethod: "/pkg.srv",
|
fullMethod: "/pkg.svr",
|
||||||
name: "pkg.srv",
|
name: "pkg.svr",
|
||||||
attr: []attribute.KeyValue(nil),
|
attr: []attribute.KeyValue(nil),
|
||||||
}, {
|
}, {
|
||||||
fullMethod: "/pkg.srv/",
|
fullMethod: "/pkg.svr/",
|
||||||
name: "pkg.srv/",
|
name: "pkg.svr/",
|
||||||
attr: []attribute.KeyValue{
|
attr: []attribute.KeyValue{
|
||||||
semconv.RPCServiceKey.String("pkg.srv"),
|
semconv.RPCServiceKey.String("pkg.svr"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
56
go.mod
56
go.mod
@@ -1,39 +1,33 @@
|
|||||||
module github.com/zeromicro/go-zero
|
module github.com/zeromicro/go-zero
|
||||||
|
|
||||||
go 1.14
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/ClickHouse/clickhouse-go v1.5.1
|
github.com/ClickHouse/clickhouse-go v1.5.1
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||||
github.com/alicebob/miniredis/v2 v2.16.0
|
github.com/alicebob/miniredis/v2 v2.17.0
|
||||||
github.com/fatih/color v1.9.0 // indirect
|
|
||||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
|
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
|
||||||
github.com/go-redis/redis v6.15.9+incompatible
|
|
||||||
github.com/go-sql-driver/mysql v1.6.0
|
github.com/go-sql-driver/mysql v1.6.0
|
||||||
github.com/golang-jwt/jwt v3.2.1+incompatible
|
github.com/golang-jwt/jwt/v4 v4.2.0
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/justinas/alice v1.2.0
|
github.com/justinas/alice v1.2.0
|
||||||
github.com/lib/pq v1.10.3
|
github.com/lib/pq v1.10.4
|
||||||
github.com/mattn/go-colorable v0.1.6 // indirect
|
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
github.com/openzipkin/zipkin-go v0.3.0 // indirect
|
|
||||||
github.com/prometheus/client_golang v1.11.0
|
github.com/prometheus/client_golang v1.11.0
|
||||||
github.com/spaolacci/murmur3 v1.1.0
|
github.com/spaolacci/murmur3 v1.1.0
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
go.etcd.io/etcd/api/v3 v3.5.1
|
go.etcd.io/etcd/api/v3 v3.5.2
|
||||||
go.etcd.io/etcd/client/v3 v3.5.1
|
go.etcd.io/etcd/client/v3 v3.5.2
|
||||||
go.opentelemetry.io/otel v1.1.0
|
go.opentelemetry.io/otel v1.3.0
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.1.0
|
go.opentelemetry.io/otel/exporters/jaeger v1.3.0
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.1.0
|
go.opentelemetry.io/otel/exporters/zipkin v1.3.0
|
||||||
go.opentelemetry.io/otel/sdk v1.1.0
|
go.opentelemetry.io/otel/sdk v1.3.0
|
||||||
go.opentelemetry.io/otel/trace v1.1.0
|
go.opentelemetry.io/otel/trace v1.3.0
|
||||||
go.uber.org/automaxprocs v1.4.0
|
go.uber.org/automaxprocs v1.4.0
|
||||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b // indirect
|
go.uber.org/goleak v1.1.12
|
||||||
golang.org/x/sys v0.0.0-20211106132015-ebca88c72f68 // indirect
|
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11
|
||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
|
google.golang.org/grpc v1.44.0
|
||||||
google.golang.org/genproto v0.0.0-20210928142010-c7af6a1a74c9 // indirect
|
|
||||||
google.golang.org/grpc v1.42.0
|
|
||||||
google.golang.org/protobuf v1.27.1
|
google.golang.org/protobuf v1.27.1
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||||
gopkg.in/h2non/gock.v1 v1.1.2
|
gopkg.in/h2non/gock.v1 v1.1.2
|
||||||
@@ -43,3 +37,25 @@ require (
|
|||||||
k8s.io/client-go v0.20.12
|
k8s.io/client-go v0.20.12
|
||||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fatih/color v1.10.0 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-redis/redis/v8 v8.11.4
|
||||||
|
github.com/google/gofuzz v1.2.0 // indirect
|
||||||
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||||
|
github.com/openzipkin/zipkin-go v0.4.0 // indirect
|
||||||
|
github.com/prometheus/common v0.30.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.7.3 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
go.uber.org/multierr v1.8.0 // indirect
|
||||||
|
go.uber.org/zap v1.21.0 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7 // indirect
|
||||||
|
k8s.io/klog/v2 v2.40.1 // indirect
|
||||||
|
)
|
||||||
|
|||||||
239
go.sum
239
go.sum
@@ -9,17 +9,27 @@ cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6T
|
|||||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||||
|
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||||
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
|
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
|
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
|
github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
|
||||||
@@ -37,12 +47,9 @@ github.com/ClickHouse/clickhouse-go v1.5.1/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHg
|
|||||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
|
||||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
|
||||||
github.com/Shopify/sarama v1.30.0/go.mod h1:zujlQQx1kzHsh4jfV1USnptCQrHAEZ2Hk8fTKCulPVs=
|
github.com/Shopify/sarama v1.30.0/go.mod h1:zujlQQx1kzHsh4jfV1USnptCQrHAEZ2Hk8fTKCulPVs=
|
||||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
|
||||||
github.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0=
|
github.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
@@ -51,10 +58,12 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
|
|||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
|
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
|
||||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||||
github.com/alicebob/miniredis/v2 v2.16.0 h1:ALkyFg7bSTEd1Mkrb4ppq4fnwjklA59dVtIehXCUZkU=
|
github.com/alicebob/miniredis/v2 v2.17.0 h1:EwLdrIS50uczw71Jc7iVSxZluTKj5nfSP8n7ARRnJy0=
|
||||||
github.com/alicebob/miniredis/v2 v2.16.0/go.mod h1:gquAfGbzn92jvtrSC69+6zZnwSODVXVpYDRaGhWaL6I=
|
github.com/alicebob/miniredis/v2 v2.17.0/go.mod h1:gquAfGbzn92jvtrSC69+6zZnwSODVXVpYDRaGhWaL6I=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
|
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||||
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
@@ -62,10 +71,9 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
|||||||
github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk=
|
github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk=
|
||||||
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
|
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
@@ -75,7 +83,6 @@ github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h
|
|||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
|
||||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
@@ -89,10 +96,11 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
|
||||||
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
@@ -103,12 +111,11 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
|
||||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||||
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
|
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
|
||||||
@@ -130,8 +137,14 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
|||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY=
|
|
||||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||||
|
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||||
@@ -139,8 +152,8 @@ github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL9
|
|||||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
|
||||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
|
||||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
@@ -150,8 +163,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
|
|||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
|
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
|
||||||
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
@@ -162,6 +175,8 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
|
|||||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@@ -169,6 +184,7 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
|||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
@@ -180,7 +196,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
|||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
|
||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
@@ -188,21 +203,27 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
|||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
|
||||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@@ -212,8 +233,6 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
|
|||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I=
|
github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I=
|
||||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||||
@@ -239,8 +258,9 @@ github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhB
|
|||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
|
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
@@ -263,29 +283,29 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
|
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
|
||||||
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
|
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
@@ -299,24 +319,21 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N
|
|||||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
|
||||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
|
github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
|
||||||
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE=
|
|
||||||
github.com/openzipkin/zipkin-go v0.3.0 h1:XtuXmOLIXLjiU2XduuWREDT0LOKtSgos/g7i7RYyoZQ=
|
|
||||||
github.com/openzipkin/zipkin-go v0.3.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ=
|
github.com/openzipkin/zipkin-go v0.3.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ=
|
||||||
|
github.com/openzipkin/zipkin-go v0.4.0 h1:CtfRrOVZtbDj8rt1WXjklw0kqqJQwICrCKmlfUuBUUw=
|
||||||
|
github.com/openzipkin/zipkin-go v0.4.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ=
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
|
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
|
||||||
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
@@ -324,7 +341,6 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
@@ -339,16 +355,19 @@ github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2
|
|||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
|
|
||||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||||
|
github.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug=
|
||||||
|
github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
|
||||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
|
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||||
|
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=
|
github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
@@ -357,14 +376,12 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
|||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||||
@@ -380,40 +397,49 @@ github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/X
|
|||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||||
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
|
||||||
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da h1:NimzV1aGyq29m5ukMK0AMWEhFaL/lrEOaephfuoiARg=
|
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da h1:NimzV1aGyq29m5ukMK0AMWEhFaL/lrEOaephfuoiARg=
|
||||||
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
|
github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
|
||||||
go.etcd.io/etcd/api/v3 v3.5.1 h1:v28cktvBq+7vGyJXF8G+rWJmj+1XUmMtqcLnH8hDocM=
|
go.etcd.io/etcd/api/v3 v3.5.2 h1:tXok5yLlKyuQ/SXSjtqHc4uzNaMqZi2XsoSPr/LlJXI=
|
||||||
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
go.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1 h1:XIQcHCFSG53bJETYeRJtIxdLv2EWRGxcfzR8lSnTH4E=
|
go.etcd.io/etcd/client/pkg/v3 v3.5.2 h1:4hzqQ6hIb3blLyQ8usCU4h3NghkqcsohEQ3o3VetYxE=
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
go.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||||
go.etcd.io/etcd/client/v3 v3.5.1 h1:oImGuV5LGKjCqXdjkMHCyWa5OO1gYKCnC/1sgdfj1Uk=
|
go.etcd.io/etcd/client/v3 v3.5.2 h1:WdnejrUtQC4nCxK0/dLTMqKOB+U5TP/2Ya0BJL+1otA=
|
||||||
go.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46OtKyd3Q=
|
go.etcd.io/etcd/client/v3 v3.5.2/go.mod h1:kOOaWFFgHygyT0WlSmL8TJiXmMysO/nNUlEsSsN6W4o=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opentelemetry.io/otel v1.1.0 h1:8p0uMLcyyIx0KHNTgO8o3CW8A1aA+dJZJW6PvnMz0Wc=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opentelemetry.io/otel v1.1.0/go.mod h1:7cww0OW51jQ8IaZChIEdqLwgh+44+7uiTdWsAL0wQpA=
|
go.opentelemetry.io/otel v1.3.0 h1:APxLf0eiBwLl+SOXiJJCVYzA1OOJNyAoV8C5RNRyy7Y=
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.1.0 h1:VRF+Hf3EePFO6ab7/wfPoyWzSY4z5X0tTvQtV9/Mq8Y=
|
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
|
||||||
go.opentelemetry.io/otel/exporters/jaeger v1.1.0/go.mod h1:D/GIBwAdrFTTqCy1iITpC9nh5rgJpIbFVgkhlz2vCXk=
|
go.opentelemetry.io/otel/exporters/jaeger v1.3.0 h1:HfydzioALdtcB26H5WHc4K47iTETJCdloL7VN579/L0=
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.1.0 h1:NfP5auMWoVOYnAeQPY+fxNG8UMAu94heSL4rtOL8Bsg=
|
go.opentelemetry.io/otel/exporters/jaeger v1.3.0/go.mod h1:KoYHi1BtkUPncGSRtCe/eh1ijsnePhSkxwzz07vU0Fc=
|
||||||
go.opentelemetry.io/otel/exporters/zipkin v1.1.0/go.mod h1:LZwDnf1mVGTPMq9hdRUHfFBH30SuQvZ1BJaVywpg0VI=
|
go.opentelemetry.io/otel/exporters/zipkin v1.3.0 h1:uOD28dZ7yIKITTcUS6MeAGNHYy3uhP7DTkhcJM6onlQ=
|
||||||
go.opentelemetry.io/otel/sdk v1.1.0 h1:j/1PngUJIDOddkCILQYTevrTIbWd494djgGkSsMit+U=
|
go.opentelemetry.io/otel/exporters/zipkin v1.3.0/go.mod h1:LxGGfHIYbvsFnrJtBcazb0yG24xHdDGrT/H6RB9r3+8=
|
||||||
go.opentelemetry.io/otel/sdk v1.1.0/go.mod h1:3aQvM6uLm6C4wJpHtT8Od3vNzeZ34Pqc6bps8MywWzo=
|
go.opentelemetry.io/otel/sdk v1.3.0 h1:3278edCoH89MEJ0Ky8WQXVmDQv3FX4ZJ3Pp+9fJreAI=
|
||||||
go.opentelemetry.io/otel/trace v1.1.0 h1:N25T9qCL0+7IpOT8RrRy0WYlL7y6U0WiUJzXcVdXY/o=
|
go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs=
|
||||||
go.opentelemetry.io/otel/trace v1.1.0/go.mod h1:i47XtdcBQiktu5IsrPqOHe8w+sBmnLwwHt8wiUsWGTI=
|
go.opentelemetry.io/otel/trace v1.3.0 h1:doy8Hzb1RJ+I3yFhtDmwNc7tIyw1tNMOIsyPzp1NOGY=
|
||||||
|
go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk=
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.uber.org/automaxprocs v1.4.0 h1:CpDZl6aOlLhReez+8S3eEotD7Jx0Os++lemPlMULQP0=
|
go.uber.org/automaxprocs v1.4.0 h1:CpDZl6aOlLhReez+8S3eEotD7Jx0Os++lemPlMULQP0=
|
||||||
go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q=
|
go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q=
|
||||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
|
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||||
|
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
|
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
|
||||||
|
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||||
|
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||||
|
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
@@ -447,6 +473,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
|
|||||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
|
||||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
@@ -456,6 +483,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -470,6 +498,7 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@@ -479,29 +508,39 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
|
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b h1:eB48h3HiRycXNy8E0Gf5e0hv7YT6Kt14L/D73G1fuwo=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
||||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -511,7 +550,6 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -537,8 +575,14 @@ golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -551,10 +595,12 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211106132015-ebca88c72f68 h1:Ywe/f3fNleF8I6F6qv3MeFoSZ6CTf2zBMMa/7qVML8M=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211106132015-ebca88c72f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -568,8 +614,8 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
|
|||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
|
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
|
||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
@@ -600,12 +646,24 @@ golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapK
|
|||||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
|
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
|
||||||
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -620,13 +678,21 @@ google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb
|
|||||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||||
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
@@ -644,12 +710,22 @@ google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvx
|
|||||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||||
google.golang.org/genproto v0.0.0-20210928142010-c7af6a1a74c9 h1:XTH066D35LyHehRwlYhoK3qA+Hcgvg5xREG4kFQEW1Y=
|
google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7 h1:ntPPoHzFW6Xp09ueznmahONZufyoSakK/piXnr2BU3I=
|
||||||
google.golang.org/genproto v0.0.0-20210928142010-c7af6a1a74c9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
@@ -658,14 +734,16 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ
|
|||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||||
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
|
||||||
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
|
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
|
||||||
google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A=
|
google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg=
|
||||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
@@ -674,6 +752,7 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
|||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
@@ -713,6 +792,7 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
|
|||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
k8s.io/api v0.20.12 h1:LfRpmRkJLwPP8eaYehsVVmIIfg1yCBIIUHaSsdqCgHA=
|
k8s.io/api v0.20.12 h1:LfRpmRkJLwPP8eaYehsVVmIIfg1yCBIIUHaSsdqCgHA=
|
||||||
k8s.io/api v0.20.12/go.mod h1:A2brwyEkVLM3wQGNnzoAa5JsQRzHK0uoOQ+bsnv7V68=
|
k8s.io/api v0.20.12/go.mod h1:A2brwyEkVLM3wQGNnzoAa5JsQRzHK0uoOQ+bsnv7V68=
|
||||||
k8s.io/apimachinery v0.20.12 h1:2c0LIVNMvB8k2Ozstmhl2zGeCEcPazznuLYEwxFdNjM=
|
k8s.io/apimachinery v0.20.12 h1:2c0LIVNMvB8k2Ozstmhl2zGeCEcPazznuLYEwxFdNjM=
|
||||||
@@ -721,8 +801,9 @@ k8s.io/client-go v0.20.12 h1:U75SxTC31BHT9i7CbX/hL4v+U1Wkzy/E1vt5ClDPp3I=
|
|||||||
k8s.io/client-go v0.20.12/go.mod h1:NBJj6Evp73Xy/4v/O/RDRaH0+3JoxNfjRxkyRgrdbsA=
|
k8s.io/client-go v0.20.12/go.mod h1:NBJj6Evp73Xy/4v/O/RDRaH0+3JoxNfjRxkyRgrdbsA=
|
||||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||||
k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
|
|
||||||
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||||
|
k8s.io/klog/v2 v2.40.1 h1:P4RRucWk/lFOlDdkAr3mc7iWFkgKrZY9qZMAgek06S4=
|
||||||
|
k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||||
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
|
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
|
||||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
|
||||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||||
|
|||||||
31
readme-cn.md
31
readme-cn.md
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
# go-zero
|
# go-zero
|
||||||
|
|
||||||
|
***缩短从需求到上线的距离***
|
||||||
|
|
||||||
[English](readme.md) | 简体中文
|
[English](readme.md) | 简体中文
|
||||||
|
|
||||||
[](https://github.com/zeromicro/go-zero/actions)
|
[](https://github.com/zeromicro/go-zero/actions)
|
||||||
@@ -11,7 +13,13 @@
|
|||||||
[](https://github.com/zeromicro/go-zero)
|
[](https://github.com/zeromicro/go-zero)
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
**注意:为了满足开源基金会要求,go-zero 从好未来(tal-tech)组织下迁移至中立的 GitHub 组织(zeromicro)。**
|
> ***注意:***
|
||||||
|
>
|
||||||
|
> 从 v1.3.0 之前版本升级请执行以下命令:
|
||||||
|
>
|
||||||
|
> `GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest`
|
||||||
|
>
|
||||||
|
> `goctl migrate —verbose —version v1.3.1`
|
||||||
|
|
||||||
## 0. go-zero 介绍
|
## 0. go-zero 介绍
|
||||||
|
|
||||||
@@ -232,9 +240,18 @@ go-zero 已被许多公司用于生产部署,接入场景如在线教育、电
|
|||||||
>46. 上海游族网络
|
>46. 上海游族网络
|
||||||
>47. 深信服
|
>47. 深信服
|
||||||
>48. 中免日上科技互联有限公司
|
>48. 中免日上科技互联有限公司
|
||||||
>48. ECLOUDVALLEY TECHNOLOGY (HK) LIMITED
|
>49. ECLOUDVALLEY TECHNOLOGY (HK) LIMITED
|
||||||
>48. 馨科智(深圳)科技有限公司
|
>50. 馨科智(深圳)科技有限公司
|
||||||
>48. 成都松珀科技有限公司
|
>51. 成都松珀科技有限公司
|
||||||
|
>52. 亿景智联
|
||||||
|
>53. 上海扩博智能技术有限公司
|
||||||
|
>54. 一犀科技成都有限公司
|
||||||
|
>55. 北京术杰科技有限公司
|
||||||
|
>56. 时代脉搏网络科技(云浮市)有限公司
|
||||||
|
>57. 店有帮
|
||||||
|
>58. 七牛云
|
||||||
|
>59. 费芮网络
|
||||||
|
>60. 51CTO
|
||||||
|
|
||||||
如果贵公司也已使用 go-zero,欢迎在 [登记地址](https://github.com/zeromicro/go-zero/issues/602) 登记,仅仅为了推广,不做其它用途。
|
如果贵公司也已使用 go-zero,欢迎在 [登记地址](https://github.com/zeromicro/go-zero/issues/602) 登记,仅仅为了推广,不做其它用途。
|
||||||
|
|
||||||
@@ -264,3 +281,9 @@ go-zero 收录在 [CNCF Cloud Native 云原生技术全景图](https://landscape
|
|||||||
加群之前有劳点一下 ***star***,一个小小的 ***star*** 是作者们回答海量问题的动力!🤝
|
加群之前有劳点一下 ***star***,一个小小的 ***star*** 是作者们回答海量问题的动力!🤝
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/wechat.jpg" alt="wechat" width="300" />
|
<img src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/wechat.jpg" alt="wechat" width="300" />
|
||||||
|
|
||||||
|
## 12. 赞助一下👍
|
||||||
|
|
||||||
|
如果觉得项目有帮助,可以请作者喝杯咖啡 🍹
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/sponsor.png" alt="wechat" width="300" />
|
||||||
|
|||||||
52
readme.md
52
readme.md
@@ -9,12 +9,20 @@ English | [简体中文](readme-cn.md)
|
|||||||
[](https://goreportcard.com/report/github.com/zeromicro/go-zero)
|
[](https://goreportcard.com/report/github.com/zeromicro/go-zero)
|
||||||
[](https://github.com/zeromicro/go-zero)
|
[](https://github.com/zeromicro/go-zero)
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
[](https://discord.gg/4JQvC5A4Fe)
|
||||||
|
<a href="https://www.producthunt.com/posts/go-zero?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-go-zero" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=334030&theme=light" alt="go-zero - A web & rpc framework written in Go. | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
|
|
||||||
**Note: To meet the requirements of Open Source Foundation, we moved go-zero from tal-tech to zeromicro (a neutral GitHub organization).**
|
> ***Important!***
|
||||||
|
>
|
||||||
|
> To upgrade from versions eariler than v1.3.0, run the following commands.
|
||||||
|
>
|
||||||
|
> `go install github.com/zeromicro/go-zero/tools/goctl@latest`
|
||||||
|
>
|
||||||
|
> `goctl migrate —verbose —version v1.3.1`
|
||||||
|
|
||||||
## 0. what is go-zero
|
## 0. what is go-zero
|
||||||
|
|
||||||
go-zero (listed in CNCF Landscape: [https://landscape.cncf.io/?selected=go-zero](https://landscape.cncf.io/?selected=go-zero)) is a web and rpc framework with lots of builtin engineering practices. It’s born to ensure the stability of the busy services with resilience design, and has been serving sites with tens of millions users for years.
|
go-zero (listed in CNCF Landscape: [https://landscape.cncf.io/?selected=go-zero](https://landscape.cncf.io/?selected=go-zero)) is a web and rpc framework with lots of builtin engineering practices. It’s born to ensure the stability of the busy services with resilience design and has been serving sites with tens of millions of users for years.
|
||||||
|
|
||||||
go-zero contains simple API description syntax and code generation tool called `goctl`. You can generate Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript from .api files with `goctl`.
|
go-zero contains simple API description syntax and code generation tool called `goctl`. You can generate Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript from .api files with `goctl`.
|
||||||
|
|
||||||
@@ -23,7 +31,7 @@ Advantages of go-zero:
|
|||||||
* improve the stability of the services with tens of millions of daily active users
|
* improve the stability of the services with tens of millions of daily active users
|
||||||
* builtin chained timeout control, concurrency control, rate limit, adaptive circuit breaker, adaptive load shedding, even no configuration needed
|
* builtin chained timeout control, concurrency control, rate limit, adaptive circuit breaker, adaptive load shedding, even no configuration needed
|
||||||
* builtin middlewares also can be integrated into your frameworks
|
* builtin middlewares also can be integrated into your frameworks
|
||||||
* simple API syntax, one command to generate couple of different languages
|
* simple API syntax, one command to generate a couple of different languages
|
||||||
* auto validate the request parameters from clients
|
* auto validate the request parameters from clients
|
||||||
* plenty of builtin microservice management and concurrent toolkits
|
* plenty of builtin microservice management and concurrent toolkits
|
||||||
|
|
||||||
@@ -31,7 +39,7 @@ Advantages of go-zero:
|
|||||||
|
|
||||||
## 1. Backgrounds of go-zero
|
## 1. Backgrounds of go-zero
|
||||||
|
|
||||||
At the beginning of 2018, we decided to re-design our system, from monolithic architecture with Java+MongoDB to microservice architecture. After researches and comparison, we chose to:
|
At the beginning of 2018, we decided to re-design our system, from monolithic architecture with Java+MongoDB to microservice architecture. After research and comparison, we chose to:
|
||||||
|
|
||||||
* Golang based
|
* Golang based
|
||||||
* great performance
|
* great performance
|
||||||
@@ -40,13 +48,13 @@ At the beginning of 2018, we decided to re-design our system, from monolithic ar
|
|||||||
* extreme deployment experience
|
* extreme deployment experience
|
||||||
* less server resource consumption
|
* less server resource consumption
|
||||||
* Self-designed microservice architecture
|
* Self-designed microservice architecture
|
||||||
* I have rich experience on designing microservice architectures
|
* I have rich experience in designing microservice architectures
|
||||||
* easy to location the problems
|
* easy to locate the problems
|
||||||
* easy to extend the features
|
* easy to extend the features
|
||||||
|
|
||||||
## 2. Design considerations on go-zero
|
## 2. Design considerations on go-zero
|
||||||
|
|
||||||
By designing the microservice architecture, we expected to ensure the stability, as well as the productivity. And from just the beginning, we have the following design principles:
|
By designing the microservice architecture, we expected to ensure stability, as well as productivity. And from just the beginning, we have the following design principles:
|
||||||
|
|
||||||
* keep it simple
|
* keep it simple
|
||||||
* high availability
|
* high availability
|
||||||
@@ -56,7 +64,7 @@ By designing the microservice architecture, we expected to ensure the stability,
|
|||||||
* try best to be friendly to the business logic development, encapsulate the complexity
|
* try best to be friendly to the business logic development, encapsulate the complexity
|
||||||
* one thing, one way
|
* one thing, one way
|
||||||
|
|
||||||
After almost half a year, we finished the transfer from monolithic system to microservice system, and deployed on August 2018. The new system guaranteed the business growth, and the system stability.
|
After almost half a year, we finished the transfer from a monolithic system to microservice system and deployed on August 2018. The new system guaranteed business growth and system stability.
|
||||||
|
|
||||||
## 3. The implementation and features of go-zero
|
## 3. The implementation and features of go-zero
|
||||||
|
|
||||||
@@ -69,21 +77,21 @@ go-zero is a web and rpc framework that integrates lots of engineering practices
|
|||||||
* high performance
|
* high performance
|
||||||
* failure-oriented programming, resilience design
|
* failure-oriented programming, resilience design
|
||||||
* builtin service discovery, load balancing
|
* builtin service discovery, load balancing
|
||||||
* builtin concurrency control, adaptive circuit breaker, adaptive load shedding, auto trigger, auto recover
|
* builtin concurrency control, adaptive circuit breaker, adaptive load shedding, auto-trigger, auto recover
|
||||||
* auto validation of API request parameters
|
* auto validation of API request parameters
|
||||||
* chained timeout control
|
* chained timeout control
|
||||||
* auto management of data caching
|
* auto management of data caching
|
||||||
* call tracing, metrics and monitoring
|
* call tracing, metrics, and monitoring
|
||||||
* high concurrency protected
|
* high concurrency protected
|
||||||
|
|
||||||
As below, go-zero protects the system with couple layers and mechanisms:
|
As below, go-zero protects the system with a couple of layers and mechanisms:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 4. Future development plans of go-zero
|
## 4. Future development plans of go-zero
|
||||||
|
|
||||||
* auto generate API mock server, make the client debugging easier
|
* auto-generate API mock server, make the client debugging easier
|
||||||
* auto generate the simple integration test for the server side just from the .api files
|
* auto-generate the simple integration test for the server-side just from the .api files
|
||||||
|
|
||||||
## 5. Installation
|
## 5. Installation
|
||||||
|
|
||||||
@@ -103,7 +111,7 @@ go get -u github.com/zeromicro/go-zero
|
|||||||
|
|
||||||
1. install goctl
|
1. install goctl
|
||||||
|
|
||||||
`goctl`can be read as `go control`. `goctl` means not to be controlled by code, instead, we control it. The inside `go` is not `golang`. At the very beginning, I was expecting it to help us improve the productivity, and make our lives easier.
|
`goctl`can be read as `go control`. `goctl` means not to be controlled by code, instead, we control it. The inside `go` is not `golang`. At the very beginning, I was expecting it to help us improve productivity, and make our lives easier.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
# for Go 1.15 and earlier
|
# for Go 1.15 and earlier
|
||||||
@@ -134,13 +142,13 @@ go get -u github.com/zeromicro/go-zero
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
the .api files also can be generate by goctl, like below:
|
the .api files also can be generated by goctl, like below:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
goctl api -o greet.api
|
goctl api -o greet.api
|
||||||
```
|
```
|
||||||
|
|
||||||
3. generate the go server side code
|
3. generate the go server-side code
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
goctl api go -api greet.api -dir greet
|
goctl api go -api greet.api -dir greet
|
||||||
@@ -177,7 +185,7 @@ go get -u github.com/zeromicro/go-zero
|
|||||||
go run greet.go -f etc/greet-api.yaml
|
go run greet.go -f etc/greet-api.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
by default, it’s listening on port 8888, while it can be changed in configuration file.
|
by default, it’s listening on port 8888, while it can be changed in the configuration file.
|
||||||
|
|
||||||
you can check it by curl:
|
you can check it by curl:
|
||||||
|
|
||||||
@@ -185,7 +193,7 @@ go get -u github.com/zeromicro/go-zero
|
|||||||
curl -i http://localhost:8888/greet/from/you
|
curl -i http://localhost:8888/greet/from/you
|
||||||
```
|
```
|
||||||
|
|
||||||
the response looks like:
|
the response looks like below:
|
||||||
|
|
||||||
```http
|
```http
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
@@ -195,10 +203,10 @@ go get -u github.com/zeromicro/go-zero
|
|||||||
|
|
||||||
4. Write the business logic code
|
4. Write the business logic code
|
||||||
|
|
||||||
* the dependencies can be passed into the logic within servicecontext.go, like mysql, reds etc.
|
* the dependencies can be passed into the logic within servicecontext.go, like mysql, reds, etc.
|
||||||
* add the logic code in logic package according to .api file
|
* add the logic code in a logic package according to .api file
|
||||||
|
|
||||||
5. Generate code like Java, TypeScript, Dart, JavaScript etc. just from the api file
|
5. Generate code like Java, TypeScript, Dart, JavaScript, etc. just from the api file
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
goctl api java -api greet.api -dir greet
|
goctl api java -api greet.api -dir greet
|
||||||
@@ -221,7 +229,7 @@ go get -u github.com/zeromicro/go-zero
|
|||||||
|
|
||||||
## 9. Chat group
|
## 9. Chat group
|
||||||
|
|
||||||
Join the chat via https://join.slack.com/t/go-zero/shared_invite/zt-10ruju779-BE4y6lQNB_R21samtyKTgA
|
Join the chat via https://discord.gg/4JQvC5A4Fe
|
||||||
|
|
||||||
## 10. Cloud Native Landscape
|
## 10. Cloud Native Landscape
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/rest/handler"
|
"github.com/zeromicro/go-zero/rest/handler"
|
||||||
"github.com/zeromicro/go-zero/rest/httpx"
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
"github.com/zeromicro/go-zero/rest/internal"
|
"github.com/zeromicro/go-zero/rest/internal"
|
||||||
|
"github.com/zeromicro/go-zero/rest/internal/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
// use 1000m to represent 100%
|
// use 1000m to represent 100%
|
||||||
@@ -34,16 +35,16 @@ type engine struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newEngine(c RestConf) *engine {
|
func newEngine(c RestConf) *engine {
|
||||||
srv := &engine{
|
svr := &engine{
|
||||||
conf: c,
|
conf: c,
|
||||||
}
|
}
|
||||||
if c.CpuThreshold > 0 {
|
if c.CpuThreshold > 0 {
|
||||||
srv.shedder = load.NewAdaptiveShedder(load.WithCpuThreshold(c.CpuThreshold))
|
svr.shedder = load.NewAdaptiveShedder(load.WithCpuThreshold(c.CpuThreshold))
|
||||||
srv.priorityShedder = load.NewAdaptiveShedder(load.WithCpuThreshold(
|
svr.priorityShedder = load.NewAdaptiveShedder(load.WithCpuThreshold(
|
||||||
(c.CpuThreshold + topCpuUsage) >> 1))
|
(c.CpuThreshold + topCpuUsage) >> 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
return srv
|
return svr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ng *engine) addRoutes(r featuredRoutes) {
|
func (ng *engine) addRoutes(r featuredRoutes) {
|
||||||
@@ -93,7 +94,7 @@ func (ng *engine) bindRoute(fr featuredRoutes, router httpx.Router, metrics *sta
|
|||||||
handler.TimeoutHandler(ng.checkedTimeout(fr.timeout)),
|
handler.TimeoutHandler(ng.checkedTimeout(fr.timeout)),
|
||||||
handler.RecoverHandler,
|
handler.RecoverHandler,
|
||||||
handler.MetricHandler(metrics),
|
handler.MetricHandler(metrics),
|
||||||
handler.MaxBytesHandler(ng.conf.MaxBytes),
|
handler.MaxBytesHandler(ng.checkedMaxBytes(fr.maxBytes)),
|
||||||
handler.GunzipHandler,
|
handler.GunzipHandler,
|
||||||
)
|
)
|
||||||
chain = ng.appendAuthHandler(fr, chain, verifier)
|
chain = ng.appendAuthHandler(fr, chain, verifier)
|
||||||
@@ -118,6 +119,14 @@ func (ng *engine) bindRoutes(router httpx.Router) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ng *engine) checkedMaxBytes(bytes int64) int64 {
|
||||||
|
if bytes > 0 {
|
||||||
|
return bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
return ng.conf.MaxBytes
|
||||||
|
}
|
||||||
|
|
||||||
func (ng *engine) checkedTimeout(timeout time.Duration) time.Duration {
|
func (ng *engine) checkedTimeout(timeout time.Duration) time.Duration {
|
||||||
if timeout > 0 {
|
if timeout > 0 {
|
||||||
return timeout
|
return timeout
|
||||||
@@ -154,6 +163,27 @@ func (ng *engine) getShedder(priority bool) load.Shedder {
|
|||||||
return ng.shedder
|
return ng.shedder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notFoundHandler returns a middleware that handles 404 not found requests.
|
||||||
|
func (ng *engine) notFoundHandler(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
chain := alice.New(
|
||||||
|
handler.TracingHandler(ng.conf.Name, ""),
|
||||||
|
ng.getLogHandler(),
|
||||||
|
)
|
||||||
|
|
||||||
|
var h http.Handler
|
||||||
|
if next != nil {
|
||||||
|
h = chain.Then(next)
|
||||||
|
} else {
|
||||||
|
h = chain.Then(http.NotFoundHandler())
|
||||||
|
}
|
||||||
|
|
||||||
|
cw := response.NewHeaderOnceResponseWriter(w)
|
||||||
|
h.ServeHTTP(cw, r)
|
||||||
|
cw.WriteHeader(http.StatusNotFound)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (ng *engine) setTlsConfig(cfg *tls.Config) {
|
func (ng *engine) setTlsConfig(cfg *tls.Config) {
|
||||||
ng.tlsConfig = cfg
|
ng.tlsConfig = cfg
|
||||||
}
|
}
|
||||||
@@ -216,9 +246,9 @@ func (ng *engine) start(router httpx.Router) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return internal.StartHttps(ng.conf.Host, ng.conf.Port, ng.conf.CertFile,
|
return internal.StartHttps(ng.conf.Host, ng.conf.Port, ng.conf.CertFile,
|
||||||
ng.conf.KeyFile, router, func(srv *http.Server) {
|
ng.conf.KeyFile, router, func(svr *http.Server) {
|
||||||
if ng.tlsConfig != nil {
|
if ng.tlsConfig != nil {
|
||||||
srv.TLSConfig = ng.tlsConfig
|
svr.TLSConfig = ng.tlsConfig
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
package rest
|
package rest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/zeromicro/go-zero/core/conf"
|
"github.com/zeromicro/go-zero/core/conf"
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewEngine(t *testing.T) {
|
func TestNewEngine(t *testing.T) {
|
||||||
@@ -190,6 +194,110 @@ func TestEngine_checkedTimeout(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEngine_checkedMaxBytes(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
maxBytes int64
|
||||||
|
expect int64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "not set",
|
||||||
|
expect: 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "less",
|
||||||
|
maxBytes: 500,
|
||||||
|
expect: 500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "equal",
|
||||||
|
maxBytes: 1000,
|
||||||
|
expect: 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "more",
|
||||||
|
maxBytes: 1500,
|
||||||
|
expect: 1500,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ng := newEngine(RestConf{
|
||||||
|
MaxBytes: 1000,
|
||||||
|
})
|
||||||
|
for _, test := range tests {
|
||||||
|
assert.Equal(t, test.expect, ng.checkedMaxBytes(test.maxBytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEngine_notFoundHandler(t *testing.T) {
|
||||||
|
logx.Disable()
|
||||||
|
|
||||||
|
ng := newEngine(RestConf{})
|
||||||
|
ts := httptest.NewServer(ng.notFoundHandler(nil))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
client := ts.Client()
|
||||||
|
err := func(ctx context.Context) error {
|
||||||
|
req, err := http.NewRequest("GET", ts.URL+"/bad", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
res, err := client.Do(req)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, http.StatusNotFound, res.StatusCode)
|
||||||
|
return res.Body.Close()
|
||||||
|
}(context.Background())
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEngine_notFoundHandlerNotNil(t *testing.T) {
|
||||||
|
logx.Disable()
|
||||||
|
|
||||||
|
ng := newEngine(RestConf{})
|
||||||
|
var called int32
|
||||||
|
ts := httptest.NewServer(ng.notFoundHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
atomic.AddInt32(&called, 1)
|
||||||
|
})))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
client := ts.Client()
|
||||||
|
err := func(ctx context.Context) error {
|
||||||
|
req, err := http.NewRequest("GET", ts.URL+"/bad", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
res, err := client.Do(req)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, http.StatusNotFound, res.StatusCode)
|
||||||
|
return res.Body.Close()
|
||||||
|
}(context.Background())
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, int32(1), atomic.LoadInt32(&called))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEngine_notFoundHandlerNotNilWriteHeader(t *testing.T) {
|
||||||
|
logx.Disable()
|
||||||
|
|
||||||
|
ng := newEngine(RestConf{})
|
||||||
|
var called int32
|
||||||
|
ts := httptest.NewServer(ng.notFoundHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
atomic.AddInt32(&called, 1)
|
||||||
|
w.WriteHeader(http.StatusExpectationFailed)
|
||||||
|
})))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
client := ts.Client()
|
||||||
|
err := func(ctx context.Context) error {
|
||||||
|
req, err := http.NewRequest("GET", ts.URL+"/bad", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
res, err := client.Do(req)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, http.StatusExpectationFailed, res.StatusCode)
|
||||||
|
return res.Body.Close()
|
||||||
|
}(context.Background())
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, int32(1), atomic.LoadInt32(&called))
|
||||||
|
}
|
||||||
|
|
||||||
type mockedRouter struct{}
|
type mockedRouter struct{}
|
||||||
|
|
||||||
func (m mockedRouter) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
func (m mockedRouter) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"github.com/zeromicro/go-zero/rest/internal/response"
|
||||||
"github.com/zeromicro/go-zero/rest/token"
|
"github.com/zeromicro/go-zero/rest/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -105,7 +104,7 @@ func detailAuthLog(r *http.Request, reason string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func unauthorized(w http.ResponseWriter, r *http.Request, err error, callback UnauthorizedCallback) {
|
func unauthorized(w http.ResponseWriter, r *http.Request, err error, callback UnauthorizedCallback) {
|
||||||
writer := newGuardedResponseWriter(w)
|
writer := response.NewHeaderOnceResponseWriter(w)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
detailAuthLog(r, err.Error())
|
detailAuthLog(r, err.Error())
|
||||||
@@ -113,53 +112,11 @@ func unauthorized(w http.ResponseWriter, r *http.Request, err error, callback Un
|
|||||||
detailAuthLog(r, noDetailReason)
|
detailAuthLog(r, noDetailReason)
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteHeader(http.StatusUnauthorized)
|
// let callback go first, to make sure we respond with user-defined HTTP header
|
||||||
|
|
||||||
if callback != nil {
|
if callback != nil {
|
||||||
callback(writer, r, err)
|
callback(writer, r, err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// if user not setting HTTP header, we set header with 401
|
||||||
type guardedResponseWriter struct {
|
writer.WriteHeader(http.StatusUnauthorized)
|
||||||
writer http.ResponseWriter
|
|
||||||
wroteHeader bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newGuardedResponseWriter(w http.ResponseWriter) *guardedResponseWriter {
|
|
||||||
return &guardedResponseWriter{
|
|
||||||
writer: w,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (grw *guardedResponseWriter) Flush() {
|
|
||||||
if flusher, ok := grw.writer.(http.Flusher); ok {
|
|
||||||
flusher.Flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (grw *guardedResponseWriter) Header() http.Header {
|
|
||||||
return grw.writer.Header()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hijack implements the http.Hijacker interface.
|
|
||||||
// This expands the Response to fulfill http.Hijacker if the underlying http.ResponseWriter supports it.
|
|
||||||
func (grw *guardedResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
|
||||||
if hijacked, ok := grw.writer.(http.Hijacker); ok {
|
|
||||||
return hijacked.Hijack()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil, errors.New("server doesn't support hijacking")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (grw *guardedResponseWriter) Write(body []byte) (int, error) {
|
|
||||||
return grw.writer.Write(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (grw *guardedResponseWriter) WriteHeader(statusCode int) {
|
|
||||||
if grw.wroteHeader {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
grw.wroteHeader = true
|
|
||||||
grw.writer.WriteHeader(statusCode)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt/v4"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -90,26 +90,6 @@ func TestAuthHandler_NilError(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthHandler_Flush(t *testing.T) {
|
|
||||||
resp := httptest.NewRecorder()
|
|
||||||
handler := newGuardedResponseWriter(resp)
|
|
||||||
handler.Flush()
|
|
||||||
assert.True(t, resp.Flushed)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAuthHandler_Hijack(t *testing.T) {
|
|
||||||
resp := httptest.NewRecorder()
|
|
||||||
writer := newGuardedResponseWriter(resp)
|
|
||||||
assert.NotPanics(t, func() {
|
|
||||||
writer.Hijack()
|
|
||||||
})
|
|
||||||
|
|
||||||
writer = newGuardedResponseWriter(mockedHijackable{resp})
|
|
||||||
assert.NotPanics(t, func() {
|
|
||||||
writer.Hijack()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildToken(secretKey string, payloads map[string]interface{}, seconds int64) (string, error) {
|
func buildToken(secretKey string, payloads map[string]interface{}, seconds int64) (string, error) {
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
claims := make(jwt.MapClaims)
|
claims := make(jwt.MapClaims)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
"github.com/zeromicro/go-zero/core/stat"
|
"github.com/zeromicro/go-zero/core/stat"
|
||||||
"github.com/zeromicro/go-zero/rest/httpx"
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
"github.com/zeromicro/go-zero/rest/internal/security"
|
"github.com/zeromicro/go-zero/rest/internal/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
const breakerSeparator = "://"
|
const breakerSeparator = "://"
|
||||||
@@ -28,7 +28,7 @@ func BreakerHandler(method, path string, metrics *stat.Metrics) func(http.Handle
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cw := &security.WithCodeResponseWriter{Writer: w}
|
cw := &response.WithCodeResponseWriter{Writer: w}
|
||||||
defer func() {
|
defer func() {
|
||||||
if cw.Code < http.StatusInternalServerError {
|
if cw.Code < http.StatusInternalServerError {
|
||||||
promise.Accept()
|
promise.Accept()
|
||||||
|
|||||||
@@ -160,9 +160,9 @@ func dumpRequest(r *http.Request) string {
|
|||||||
func logBrief(r *http.Request, code int, timer *utils.ElapsedTimer, logs *internal.LogCollector) {
|
func logBrief(r *http.Request, code int, timer *utils.ElapsedTimer, logs *internal.LogCollector) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
duration := timer.Duration()
|
duration := timer.Duration()
|
||||||
logger := logx.WithContext(r.Context())
|
logger := logx.WithContext(r.Context()).WithDuration(duration)
|
||||||
buf.WriteString(fmt.Sprintf("[HTTP] %s - %d - %s - %s - %s - %s",
|
buf.WriteString(fmt.Sprintf("[HTTP] %s - %d - %s - %s - %s",
|
||||||
r.Method, code, r.RequestURI, httpx.GetRemoteAddr(r), r.UserAgent(), timex.ReprOfDuration(duration)))
|
r.Method, code, r.RequestURI, httpx.GetRemoteAddr(r), r.UserAgent()))
|
||||||
if duration > slowThreshold.Load() {
|
if duration > slowThreshold.Load() {
|
||||||
logger.Slowf("[HTTP] %s - %d - %s - %s - %s - slowcall(%s)",
|
logger.Slowf("[HTTP] %s - %d - %s - %s - %s - slowcall(%s)",
|
||||||
r.Method, code, r.RequestURI, httpx.GetRemoteAddr(r), r.UserAgent(), timex.ReprOfDuration(duration))
|
r.Method, code, r.RequestURI, httpx.GetRemoteAddr(r), r.UserAgent(), timex.ReprOfDuration(duration))
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/core/metric"
|
"github.com/zeromicro/go-zero/core/metric"
|
||||||
"github.com/zeromicro/go-zero/core/prometheus"
|
"github.com/zeromicro/go-zero/core/prometheus"
|
||||||
"github.com/zeromicro/go-zero/core/timex"
|
"github.com/zeromicro/go-zero/core/timex"
|
||||||
"github.com/zeromicro/go-zero/rest/internal/security"
|
"github.com/zeromicro/go-zero/rest/internal/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
const serverNamespace = "http_server"
|
const serverNamespace = "http_server"
|
||||||
@@ -41,7 +41,7 @@ func PrometheusHandler(path string) func(http.Handler) http.Handler {
|
|||||||
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
startTime := timex.Now()
|
startTime := timex.Now()
|
||||||
cw := &security.WithCodeResponseWriter{Writer: w}
|
cw := &response.WithCodeResponseWriter{Writer: w}
|
||||||
defer func() {
|
defer func() {
|
||||||
metricServerReqDur.Observe(int64(timex.Since(startTime)/time.Millisecond), path)
|
metricServerReqDur.Observe(int64(timex.Since(startTime)/time.Millisecond), path)
|
||||||
metricServerReqCodeTotal.Inc(path, strconv.Itoa(cw.Code))
|
metricServerReqCodeTotal.Inc(path, strconv.Itoa(cw.Code))
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/core/logx"
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
"github.com/zeromicro/go-zero/core/stat"
|
"github.com/zeromicro/go-zero/core/stat"
|
||||||
"github.com/zeromicro/go-zero/rest/httpx"
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
"github.com/zeromicro/go-zero/rest/internal/security"
|
"github.com/zeromicro/go-zero/rest/internal/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
const serviceType = "api"
|
const serviceType = "api"
|
||||||
@@ -41,7 +41,7 @@ func SheddingHandler(shedder load.Shedder, metrics *stat.Metrics) func(http.Hand
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cw := &security.WithCodeResponseWriter{Writer: w}
|
cw := &response.WithCodeResponseWriter{Writer: w}
|
||||||
defer func() {
|
defer func() {
|
||||||
if cw.Code == http.StatusServiceUnavailable {
|
if cw.Code == http.StatusServiceUnavailable {
|
||||||
promise.Fail()
|
promise.Fail()
|
||||||
|
|||||||
@@ -18,21 +18,21 @@ func TracingHandler(serviceName, path string) func(http.Handler) http.Handler {
|
|||||||
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := propagator.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
|
ctx := propagator.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
|
||||||
|
spanName := path
|
||||||
|
if len(spanName) == 0 {
|
||||||
|
spanName = r.URL.Path
|
||||||
|
}
|
||||||
spanCtx, span := tracer.Start(
|
spanCtx, span := tracer.Start(
|
||||||
ctx,
|
ctx,
|
||||||
path,
|
spanName,
|
||||||
oteltrace.WithSpanKind(oteltrace.SpanKindServer),
|
oteltrace.WithSpanKind(oteltrace.SpanKindServer),
|
||||||
oteltrace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest(
|
oteltrace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest(
|
||||||
serviceName, path, r)...),
|
serviceName, spanName, r)...),
|
||||||
)
|
)
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
// convenient for tracking error messages
|
// convenient for tracking error messages
|
||||||
sc := span.SpanContext()
|
propagator.Inject(spanCtx, propagation.HeaderCarrier(w.Header()))
|
||||||
if sc.HasTraceID() {
|
|
||||||
w.Header().Set(trace.TraceIdKey, sc.TraceID().String())
|
|
||||||
}
|
|
||||||
|
|
||||||
next.ServeHTTP(w, r.WithContext(spanCtx))
|
next.ServeHTTP(w, r.WithContext(spanCtx))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/justinas/alice"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
ztrace "github.com/zeromicro/go-zero/core/trace"
|
ztrace "github.com/zeromicro/go-zero/core/trace"
|
||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
@@ -21,28 +22,31 @@ func TestOtelHandler(t *testing.T) {
|
|||||||
Sampler: 1.0,
|
Sampler: 1.0,
|
||||||
})
|
})
|
||||||
|
|
||||||
ts := httptest.NewServer(
|
for _, test := range []string{"", "bar"} {
|
||||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
t.Run(test, func(t *testing.T) {
|
||||||
ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header))
|
h := alice.New(TracingHandler("foo", test)).Then(
|
||||||
spanCtx := trace.SpanContextFromContext(ctx)
|
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
assert.Equal(t, true, spanCtx.IsValid())
|
ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header))
|
||||||
}),
|
spanCtx := trace.SpanContextFromContext(ctx)
|
||||||
)
|
assert.True(t, spanCtx.IsValid())
|
||||||
defer ts.Close()
|
}))
|
||||||
|
ts := httptest.NewServer(h)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
client := ts.Client()
|
client := ts.Client()
|
||||||
err := func(ctx context.Context) error {
|
err := func(ctx context.Context) error {
|
||||||
ctx, span := otel.Tracer("httptrace/client").Start(ctx, "test")
|
ctx, span := otel.Tracer("httptrace/client").Start(ctx, "test")
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
req, _ := http.NewRequest("GET", ts.URL, nil)
|
req, _ := http.NewRequest("GET", ts.URL, nil)
|
||||||
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header))
|
otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header))
|
||||||
|
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
assert.Equal(t, err, nil)
|
assert.Nil(t, err)
|
||||||
_ = res.Body.Close()
|
return res.Body.Close()
|
||||||
return nil
|
}(context.Background())
|
||||||
}(context.Background())
|
|
||||||
|
|
||||||
assert.Equal(t, err, nil)
|
assert.Nil(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
rest/httpc/internal/interceptor.go
Normal file
8
rest/httpc/internal/interceptor.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
type (
|
||||||
|
Interceptor func(r *http.Request) (*http.Request, ResponseHandler)
|
||||||
|
ResponseHandler func(resp *http.Response, err error)
|
||||||
|
)
|
||||||
34
rest/httpc/internal/loginterceptor.go
Normal file
34
rest/httpc/internal/loginterceptor.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
"github.com/zeromicro/go-zero/core/timex"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
)
|
||||||
|
|
||||||
|
func LogInterceptor(r *http.Request) (*http.Request, ResponseHandler) {
|
||||||
|
start := timex.Now()
|
||||||
|
return r, func(resp *http.Response, err error) {
|
||||||
|
duration := timex.Since(start)
|
||||||
|
if err != nil {
|
||||||
|
logger := logx.WithContext(r.Context()).WithDuration(duration)
|
||||||
|
logger.Errorf("[HTTP] %s %s - %v", r.Method, r.URL, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var tc propagation.TraceContext
|
||||||
|
ctx := tc.Extract(r.Context(), propagation.HeaderCarrier(resp.Header))
|
||||||
|
logger := logx.WithContext(ctx).WithDuration(duration)
|
||||||
|
if isOkResponse(resp.StatusCode) {
|
||||||
|
logger.Infof("[HTTP] %d - %s %s", resp.StatusCode, r.Method, r.URL)
|
||||||
|
} else {
|
||||||
|
logger.Errorf("[HTTP] %d - %s %s", resp.StatusCode, r.Method, r.URL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isOkResponse(code int) bool {
|
||||||
|
return code < http.StatusBadRequest
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user