Update xorm to v1 (#323)
Fix limit for databases other than sqlite go mod tidy && go mod vendor Remove unneeded break statements Make everything work with the new xorm version Fix xorm logging Fix lint Fix redis init Fix using id field Fix database init for testing Change default database log level Add xorm logger Use const for postgres go mod tidy Merge branch 'master' into update/xorm # Conflicts: # go.mod # go.sum # vendor/modules.txt go mod vendor Fix loading fixtures for postgres Go mod vendor1 Update xorm to version 1 Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/323
This commit is contained in:
parent
713560702b
commit
d28f005552
430 changed files with 48291 additions and 99915 deletions
|
@ -112,6 +112,8 @@ log:
|
||||||
standard: "stdout"
|
standard: "stdout"
|
||||||
# Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
|
# Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
|
||||||
database: "off"
|
database: "off"
|
||||||
|
# The log level for database log messages. Possible values are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
|
||||||
|
databaselevel: "DEBUG"
|
||||||
# Whether to log http requests or not. Possible values are stdout, stderr, file or off to disable http logging.
|
# Whether to log http requests or not. Possible values are stdout, stderr, file or off to disable http logging.
|
||||||
http: "stdout"
|
http: "stdout"
|
||||||
# Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
|
# Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||||
|
|
|
@ -155,6 +155,8 @@ log:
|
||||||
standard: "stdout"
|
standard: "stdout"
|
||||||
# Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
|
# Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
|
||||||
database: "off"
|
database: "off"
|
||||||
|
# The log level for database log messages. Possible values are CRITICAL, ERROR, WARNING, NOTICE, INFO, DEBUG.
|
||||||
|
databaselevel: "DEBUG"
|
||||||
# Whether to log http requests or not. Possible values are stdout, stderr, file or off to disable http logging.
|
# Whether to log http requests or not. Possible values are stdout, stderr, file or off to disable http logging.
|
||||||
http: "stdout"
|
http: "stdout"
|
||||||
# Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
|
# Echo has its own logging which usually is unnessecary, which is why it is disabled by default. Possible values are stdout, stderr, file or off to disable standard logging.
|
||||||
|
|
29
go.mod
29
go.mod
|
@ -30,20 +30,22 @@ require (
|
||||||
github.com/cweill/gotests v1.5.3
|
github.com/cweill/gotests v1.5.3
|
||||||
github.com/d4l3k/messagediff v1.2.1 // indirect
|
github.com/d4l3k/messagediff v1.2.1 // indirect
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835
|
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835
|
||||||
github.com/garyburd/redigo v1.6.0 // indirect
|
github.com/garyburd/redigo v1.6.0 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.19.3 // indirect
|
github.com/go-openapi/jsonreference v0.19.3 // indirect
|
||||||
github.com/go-openapi/spec v0.19.4 // indirect
|
github.com/go-openapi/spec v0.19.4 // indirect
|
||||||
github.com/go-redis/redis v6.14.0+incompatible
|
github.com/go-redis/redis/v7 v7.2.0
|
||||||
github.com/go-redis/redis/v7 v7.2.0 // indirect
|
|
||||||
github.com/go-sql-driver/mysql v1.5.0
|
github.com/go-sql-driver/mysql v1.5.0
|
||||||
github.com/go-testfixtures/testfixtures/v3 v3.1.1
|
github.com/go-testfixtures/testfixtures/v3 v3.1.1
|
||||||
github.com/go-xorm/core v0.6.2 // indirect
|
github.com/go-xorm/core v0.6.2 // indirect
|
||||||
github.com/go-xorm/xorm v0.7.9 // indirect
|
github.com/go-xorm/xorm v0.7.9 // indirect
|
||||||
|
github.com/golang/protobuf v1.3.5 // indirect
|
||||||
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf
|
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf
|
||||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334
|
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334
|
||||||
github.com/imdario/mergo v0.3.9
|
github.com/imdario/mergo v0.3.9
|
||||||
github.com/jgautheron/goconst v0.0.0-20200227150835-cda7ea3bf591
|
github.com/jgautheron/goconst v0.0.0-20200227150835-cda7ea3bf591
|
||||||
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/labstack/echo/v4 v4.1.16
|
github.com/labstack/echo/v4 v4.1.16
|
||||||
github.com/labstack/gommon v0.3.0
|
github.com/labstack/gommon v0.3.0
|
||||||
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef
|
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef
|
||||||
|
@ -51,7 +53,10 @@ require (
|
||||||
github.com/mailru/easyjson v0.7.0 // indirect
|
github.com/mailru/easyjson v0.7.0 // indirect
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||||
github.com/olekukonko/tablewriter v0.0.4
|
github.com/olekukonko/tablewriter v0.0.4
|
||||||
|
github.com/onsi/ginkgo v1.12.0 // indirect
|
||||||
|
github.com/onsi/gomega v1.9.0 // indirect
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||||
github.com/pelletier/go-toml v1.4.0 // indirect
|
github.com/pelletier/go-toml v1.4.0 // indirect
|
||||||
github.com/prometheus/client_golang v0.9.4
|
github.com/prometheus/client_golang v0.9.4
|
||||||
|
@ -64,21 +69,31 @@ require (
|
||||||
github.com/spf13/viper v1.6.3
|
github.com/spf13/viper v1.6.3
|
||||||
github.com/stretchr/testify v1.5.1
|
github.com/stretchr/testify v1.5.1
|
||||||
github.com/swaggo/swag v1.6.3
|
github.com/swaggo/swag v1.6.3
|
||||||
github.com/ulule/limiter/v3 v3.3.0
|
github.com/ulule/limiter/v3 v3.5.0
|
||||||
github.com/urfave/cli v1.22.2 // indirect
|
github.com/urfave/cli v1.22.2 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71
|
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa // indirect
|
||||||
|
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef // indirect
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||||
gopkg.in/d4l3k/messagediff.v1 v1.2.1
|
gopkg.in/d4l3k/messagediff.v1 v1.2.1
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a
|
||||||
src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273
|
src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273
|
||||||
src.techknowlogick.com/xormigrate v1.1.0
|
src.techknowlogick.com/xormigrate v1.2.0
|
||||||
xorm.io/builder v0.3.6
|
xorm.io/builder v0.3.7
|
||||||
xorm.io/core v0.7.3
|
xorm.io/core v0.7.3
|
||||||
xorm.io/xorm v0.8.1
|
xorm.io/xorm v1.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/samedi/caldav-go => github.com/kolaente/caldav-go v3.0.1-0.20190524174923-9e5cd1688227+incompatible // Branch: feature/dynamic-supported-components, PR: https://github.com/samedi/caldav-go/pull/6 and https://github.com/samedi/caldav-go/pull/7
|
replace (
|
||||||
|
github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.4
|
||||||
|
github.com/coreos/go-systemd => github.com/coreos/go-systemd/v22 v22.0.0
|
||||||
|
github.com/hpcloud/tail => github.com/jeffbean/tail v0.0.0-20180825121900-988c412e1aaa923bd3f9dcb7c8a75b1b58818f4f // See https://github.com/hpcloud/tail/pull/159
|
||||||
|
github.com/samedi/caldav-go => github.com/kolaente/caldav-go v3.0.1-0.20190524174923-9e5cd1688227+incompatible // Branch: feature/dynamic-supported-components, PR: https://github.com/samedi/caldav-go/pull/6 and https://github.com/samedi/caldav-go/pull/7
|
||||||
|
gopkg.in/fsnotify.v1 => github.com/kolaente/fsnotify v1.4.10-0.20200411160148-1bc3c8ff4048 // See https://github.com/fsnotify/fsnotify/issues/328 and https://github.com/golang/go/issues/26904
|
||||||
|
)
|
||||||
|
|
||||||
go 1.13
|
go 1.13
|
||||||
|
|
110
go.sum
110
go.sum
|
@ -11,6 +11,8 @@ cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
|
||||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
||||||
code.vikunja.io/web v0.0.0-20200208214421-c90649369427 h1:6ps5r0OxZNRdmCavh1k/xMwftN27hHauo+EtdTGxLug=
|
code.vikunja.io/web v0.0.0-20200208214421-c90649369427 h1:6ps5r0OxZNRdmCavh1k/xMwftN27hHauo+EtdTGxLug=
|
||||||
code.vikunja.io/web v0.0.0-20200208214421-c90649369427/go.mod h1:cuP1/ieGWAZzgQGw+QPt6Y5F0fVb/8Ol5NV4QSezGdo=
|
code.vikunja.io/web v0.0.0-20200208214421-c90649369427/go.mod h1:cuP1/ieGWAZzgQGw+QPt6Y5F0fVb/8Ol5NV4QSezGdo=
|
||||||
|
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
||||||
|
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||||
gitea.com/xorm/tests v0.5.6 h1:bm5SwZD5B6LI4VinKf5bqOCJ8z3z5l0h43HkVuxZG3k=
|
gitea.com/xorm/tests v0.5.6 h1:bm5SwZD5B6LI4VinKf5bqOCJ8z3z5l0h43HkVuxZG3k=
|
||||||
gitea.com/xorm/tests v0.5.6/go.mod h1:53b8exJwT/5JBCf5n5gMqKrIZAqIErdJCRtzS1AuiMM=
|
gitea.com/xorm/tests v0.5.6/go.mod h1:53b8exJwT/5JBCf5n5gMqKrIZAqIErdJCRtzS1AuiMM=
|
||||||
gitea.com/xorm/xorm-redis-cache v0.0.0-20191113062523-5a6a9e2ab9f2 h1:85jEhrFlzlDrJ+CXoCQ24WtkZ7MEtt8yOYZuC9ewKyk=
|
gitea.com/xorm/xorm-redis-cache v0.0.0-20191113062523-5a6a9e2ab9f2 h1:85jEhrFlzlDrJ+CXoCQ24WtkZ7MEtt8yOYZuC9ewKyk=
|
||||||
|
@ -36,7 +38,6 @@ github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
|
||||||
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/astaxie/beego v1.10.0/go.mod h1:0R4++1tUqERR0WYFWdfkcrsyoVBCG4DgpDGokT3yb+U=
|
|
||||||
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
|
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
|
||||||
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
|
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||||
|
@ -50,15 +51,15 @@ github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJ
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
|
||||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/cweill/gotests v1.5.3 h1:k3t4wW/x/YNixWZJhUIn+mivmK5iV1tJVOwVYkx0UcU=
|
github.com/cweill/gotests v1.5.3 h1:k3t4wW/x/YNixWZJhUIn+mivmK5iV1tJVOwVYkx0UcU=
|
||||||
github.com/cweill/gotests v1.5.3/go.mod h1:XZYOJkGVkCRoymaIzmp9Wyi3rUgfA3oOnkuljYrjFV8=
|
github.com/cweill/gotests v1.5.3/go.mod h1:XZYOJkGVkCRoymaIzmp9Wyi3rUgfA3oOnkuljYrjFV8=
|
||||||
github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U=
|
github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U=
|
||||||
|
@ -79,6 +80,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
|
||||||
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=
|
||||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835 h1:roDmqJ4Qes7hrDOsWsMCce0vQHz3xiMPjJ9m4c2eeNs=
|
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835 h1:roDmqJ4Qes7hrDOsWsMCce0vQHz3xiMPjJ9m4c2eeNs=
|
||||||
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835/go.mod h1:BjL/N0+C+j9uNX+1xcNuM9vdSIcXCZrQZUYbXOFbgN8=
|
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835/go.mod h1:BjL/N0+C+j9uNX+1xcNuM9vdSIcXCZrQZUYbXOFbgN8=
|
||||||
github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc=
|
github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc=
|
||||||
|
@ -91,7 +94,7 @@ github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NB
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
|
||||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||||
github.com/go-chi/chi v3.3.3+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
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=
|
||||||
|
@ -115,10 +118,11 @@ github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/
|
||||||
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 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||||
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.14.0+incompatible h1:AMPZkM7PbsJbilelrJUAyC4xQbGROTOLSuDd7fnMXCI=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-redis/redis v6.14.0+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U=
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||||
|
github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs=
|
||||||
github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
|
github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
|
||||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
|
@ -133,6 +137,7 @@ github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk
|
||||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
|
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
|
||||||
github.com/go-xorm/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0=
|
github.com/go-xorm/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0=
|
||||||
github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ=
|
github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
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.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
@ -147,9 +152,14 @@ github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM
|
||||||
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=
|
||||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
|
||||||
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.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
|
github.com/golang/snappy v0.0.1/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=
|
||||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
|
@ -171,8 +181,6 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
|
||||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8=
|
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8=
|
||||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
|
||||||
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
||||||
|
@ -182,12 +190,13 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
|
||||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
|
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
|
||||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
|
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
|
||||||
github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||||
|
github.com/jeffbean/tail v0.0.0-20180825121900-988c412e1aaa923bd3f9dcb7c8a75b1b58818f4f h1:UgkcqE2r+hkV2Pn+1JTUTaH4iEsrUFTwI2OiDOM//rE=
|
||||||
|
github.com/jeffbean/tail v0.0.0-20180825121900-988c412e1aaa923bd3f9dcb7c8a75b1b58818f4f/go.mod h1:+MhJ+VPZMpv8Ui6WRzpJFuWFKxBCZgVOo5HAmlw1sFc=
|
||||||
github.com/jgautheron/goconst v0.0.0-20200227150835-cda7ea3bf591 h1:x/BpEhm6aL26o4TLtcU0loJ7B3+69jielrGc70V7Yb4=
|
github.com/jgautheron/goconst v0.0.0-20200227150835-cda7ea3bf591 h1:x/BpEhm6aL26o4TLtcU0loJ7B3+69jielrGc70V7Yb4=
|
||||||
github.com/jgautheron/goconst v0.0.0-20200227150835-cda7ea3bf591/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
|
github.com/jgautheron/goconst v0.0.0-20200227150835-cda7ea3bf591/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
|
||||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
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.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
@ -197,8 +206,12 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
|
github.com/klauspost/compress v1.9.6/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
|
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
github.com/kolaente/caldav-go v3.0.1-0.20190524174923-9e5cd1688227+incompatible h1:PkEEpmbrFXlMul8cOplR8nkcIM/NDbx+H6fq2+vaKAA=
|
github.com/kolaente/caldav-go v3.0.1-0.20190524174923-9e5cd1688227+incompatible h1:PkEEpmbrFXlMul8cOplR8nkcIM/NDbx+H6fq2+vaKAA=
|
||||||
github.com/kolaente/caldav-go v3.0.1-0.20190524174923-9e5cd1688227+incompatible/go.mod h1:y1UhTNI4g0hVymJrI6yJ5/ohy09hNBeU8iJEZjgdDOw=
|
github.com/kolaente/caldav-go v3.0.1-0.20190524174923-9e5cd1688227+incompatible/go.mod h1:y1UhTNI4g0hVymJrI6yJ5/ohy09hNBeU8iJEZjgdDOw=
|
||||||
|
github.com/kolaente/fsnotify v1.4.10-0.20200411160148-1bc3c8ff4048/go.mod h1:dv6KyzAg9UuJWiE1pwkvvB2i0TvcQM6QhdsXLZ7K5KI=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
@ -207,8 +220,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/labstack/echo/v4 v4.1.7-0.20190627175217-8fb7b5be270f h1:fNJtR+TNyxTdYCZU40fc8Or8RyBqMOKYNv+Zay5gjvk=
|
github.com/labstack/echo/v4 v4.1.7-0.20190627175217-8fb7b5be270f h1:fNJtR+TNyxTdYCZU40fc8Or8RyBqMOKYNv+Zay5gjvk=
|
||||||
github.com/labstack/echo/v4 v4.1.7-0.20190627175217-8fb7b5be270f/go.mod h1:kU/7PwzgNxZH4das4XNsSpBSOD09XIF5YEPzjpkGnGE=
|
github.com/labstack/echo/v4 v4.1.7-0.20190627175217-8fb7b5be270f/go.mod h1:kU/7PwzgNxZH4das4XNsSpBSOD09XIF5YEPzjpkGnGE=
|
||||||
github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o=
|
github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o=
|
||||||
|
@ -219,6 +232,7 @@ github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0
|
||||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||||
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef h1:RZnRnSID1skF35j/15KJ6hKZkdIC/teQClJK5wP5LU4=
|
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef h1:RZnRnSID1skF35j/15KJ6hKZkdIC/teQClJK5wP5LU4=
|
||||||
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef/go.mod h1:4LATl0uhhtytR6p9n1AlktDyIz4u2iUnWEdI3L/hXiw=
|
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef/go.mod h1:4LATl0uhhtytR6p9n1AlktDyIz4u2iUnWEdI3L/hXiw=
|
||||||
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||||
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.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
|
||||||
|
@ -265,19 +279,24 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||||
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 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||||
github.com/onsi/ginkgo v1.7.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.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
|
|
||||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
|
||||||
|
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
|
||||||
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 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
|
||||||
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
|
github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
|
||||||
|
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||||
|
@ -290,6 +309,8 @@ github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
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/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
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=
|
||||||
|
@ -373,34 +394,40 @@ github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05
|
||||||
github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
|
github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
|
||||||
github.com/swaggo/swag v1.6.3 h1:N+uVPGP4H2hXoss2pt5dctoSUPKKRInr6qcTMOm0usI=
|
github.com/swaggo/swag v1.6.3 h1:N+uVPGP4H2hXoss2pt5dctoSUPKKRInr6qcTMOm0usI=
|
||||||
github.com/swaggo/swag v1.6.3/go.mod h1:wcc83tB4Mb2aNiL/HP4MFeQdpHUrca+Rp/DRNgWAUio=
|
github.com/swaggo/swag v1.6.3/go.mod h1:wcc83tB4Mb2aNiL/HP4MFeQdpHUrca+Rp/DRNgWAUio=
|
||||||
|
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||||
|
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/ugorji/go v1.1.1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=
|
github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=
|
||||||
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=
|
github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=
|
||||||
github.com/ulule/limiter/v3 v3.3.0 h1:DuMRthpkl1wW9Em6xOVw5HMHnbDumSIDydiMqP0PTXs=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
github.com/ulule/limiter/v3 v3.3.0/go.mod h1:E6sfg3hfRgW+yFvkE/rZf6YLqXYFMWTmZaZKvdEiQsA=
|
github.com/ulule/limiter/v3 v3.5.0 h1:QRAebbswjlezHIfiSQgM8+jMxaz/zsrxGRuiUJ43MHo=
|
||||||
|
github.com/ulule/limiter/v3 v3.5.0/go.mod h1:TgOUQZKZ2KHjemqrC8UHUbKPqpTmSY43/2wbQ7YN1h8=
|
||||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
|
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
|
||||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
|
||||||
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
|
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
|
||||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||||
github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
|
github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
|
||||||
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||||
|
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
|
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
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-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
@ -420,12 +447,13 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
||||||
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/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
|
||||||
|
golang.org/x/mod v0.2.0/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=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE=
|
golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE=
|
||||||
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -446,6 +474,8 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||||
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-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
|
||||||
|
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
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/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=
|
||||||
|
@ -454,6 +484,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
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/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -472,10 +503,17 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||||
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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa h1:mQTN3ECqfsViCNBgq+A40vdwhkGykrrQlYe3mPj6BoU=
|
||||||
|
golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
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=
|
||||||
|
@ -492,15 +530,20 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20190628034336-212fb13d595e h1:ZlQjfVdpDxeqxRfmO30CdqWWzTvgRCj0MxaUVfxEG1k=
|
golang.org/x/tools v0.0.0-20190628034336-212fb13d595e h1:ZlQjfVdpDxeqxRfmO30CdqWWzTvgRCj0MxaUVfxEG1k=
|
||||||
golang.org/x/tools v0.0.0-20190628034336-212fb13d595e/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190628034336-212fb13d595e/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY=
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef h1:RHORRhs540cYZYrzgU2CPUyykkwZM78hGdzocOo9P8A=
|
||||||
|
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||||
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.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
|
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
|
||||||
|
@ -508,8 +551,6 @@ google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||||
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.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
|
google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
|
||||||
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
|
||||||
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-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
@ -523,12 +564,11 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/d4l3k/messagediff.v1 v1.2.1 h1:70AthpjunwzUiarMHyED52mj9UwtAnE89l1Gmrt3EU0=
|
gopkg.in/d4l3k/messagediff.v1 v1.2.1 h1:70AthpjunwzUiarMHyED52mj9UwtAnE89l1Gmrt3EU0=
|
||||||
gopkg.in/d4l3k/messagediff.v1 v1.2.1/go.mod h1:EUzikiKadqXWcD1AzJLagx0j/BeeWGtn++04Xniyg44=
|
gopkg.in/d4l3k/messagediff.v1 v1.2.1/go.mod h1:EUzikiKadqXWcD1AzJLagx0j/BeeWGtn++04Xniyg44=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||||
|
@ -546,6 +586,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
@ -553,14 +595,14 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a h1:LJwr7TCTghdatWv40WobzlK
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273 h1:dE6ry9rVwDn3soD4wPCXqEG60AZTuhniZzHdnj3c+74=
|
src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273 h1:dE6ry9rVwDn3soD4wPCXqEG60AZTuhniZzHdnj3c+74=
|
||||||
src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU=
|
src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU=
|
||||||
src.techknowlogick.com/xormigrate v1.1.0 h1:Ob79c1pOO+voMB9roa2eHZByT+TODwC51+Mn9e3HoTI=
|
src.techknowlogick.com/xormigrate v1.2.0 h1:bq9JaI48bxB+OddMghicjmV7sGmBUogJq4HmTN0DOcw=
|
||||||
src.techknowlogick.com/xormigrate v1.1.0/go.mod h1:IMdvIk60uPX+IUsaXbdtqFzl3n7PfRg/cSZxxsiCWf8=
|
src.techknowlogick.com/xormigrate v1.2.0/go.mod h1:7so27LAfBRqAxbma5jKYeL4ykVG1Jhsv9ncSq1KBCs4=
|
||||||
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
|
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
|
||||||
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
|
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
|
||||||
|
xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI=
|
||||||
|
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
|
||||||
xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
||||||
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
|
|
||||||
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
|
||||||
xorm.io/core v0.7.3 h1:W8ws1PlrnkS1CZU1YWaYLMQcQilwAmQXU0BJDJon+H0=
|
xorm.io/core v0.7.3 h1:W8ws1PlrnkS1CZU1YWaYLMQcQilwAmQXU0BJDJon+H0=
|
||||||
xorm.io/core v0.7.3/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
xorm.io/core v0.7.3/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
|
||||||
xorm.io/xorm v0.8.1 h1:4f2KXuQxVdaX3RdI3Fw81NzMiSpZeyCZt8m3sEVeIkQ=
|
xorm.io/xorm v1.0.1 h1:/lITxpJtkZauNpdzj+L9CN/3OQxZaABrbergMcJu+Cw=
|
||||||
xorm.io/xorm v0.8.1/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
|
xorm.io/xorm v1.0.1/go.mod h1:o4vnEsQ5V2F1/WK6w4XTwmiWJeGj82tqjAnHe44wVHY=
|
||||||
|
|
|
@ -82,6 +82,7 @@ const (
|
||||||
LogErrors Key = `log.errors`
|
LogErrors Key = `log.errors`
|
||||||
LogStandard Key = `log.standard`
|
LogStandard Key = `log.standard`
|
||||||
LogDatabase Key = `log.database`
|
LogDatabase Key = `log.database`
|
||||||
|
LogDatabaseLevel Key = `log.databaselevel`
|
||||||
LogHTTP Key = `log.http`
|
LogHTTP Key = `log.http`
|
||||||
LogEcho Key = `log.echo`
|
LogEcho Key = `log.echo`
|
||||||
LogPath Key = `log.path`
|
LogPath Key = `log.path`
|
||||||
|
@ -214,6 +215,7 @@ func InitDefaultConfig() {
|
||||||
LogErrors.setDefault("stdout")
|
LogErrors.setDefault("stdout")
|
||||||
LogStandard.setDefault("stdout")
|
LogStandard.setDefault("stdout")
|
||||||
LogDatabase.setDefault("off")
|
LogDatabase.setDefault("off")
|
||||||
|
LogDatabaseLevel.setDefault("DEBUG")
|
||||||
LogHTTP.setDefault("stdout")
|
LogHTTP.setDefault("stdout")
|
||||||
LogEcho.setDefault("off")
|
LogEcho.setDefault("off")
|
||||||
LogPath.setDefault(ServiceRootpath.GetString() + "/logs")
|
LogPath.setDefault(ServiceRootpath.GetString() + "/logs")
|
||||||
|
|
11
pkg/db/db.go
11
pkg/db/db.go
|
@ -21,14 +21,14 @@ import (
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
xrc "gitea.com/xorm/xorm-redis-cache"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"xorm.io/core"
|
"xorm.io/core"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
|
"xorm.io/xorm/caches"
|
||||||
xrc "gitea.com/xorm/xorm-redis-cache"
|
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql" // Because.
|
_ "github.com/go-sql-driver/mysql" // Because.
|
||||||
_ "github.com/lib/pq" // Because.
|
_ "github.com/lib/pq" // Because.
|
||||||
|
@ -70,8 +70,7 @@ func CreateDBEngine() (engine *xorm.Engine, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.SetMapper(core.GonicMapper{})
|
engine.SetMapper(core.GonicMapper{})
|
||||||
logger := xorm.NewSimpleLogger(log.GetLogWriter("database"))
|
logger := log.NewXormLogger()
|
||||||
logger.ShowSQL(config.LogDatabase.GetString() != "off")
|
|
||||||
engine.SetLogger(logger)
|
engine.SetLogger(logger)
|
||||||
|
|
||||||
// Cache
|
// Cache
|
||||||
|
@ -79,10 +78,10 @@ func CreateDBEngine() (engine *xorm.Engine, err error) {
|
||||||
if config.CacheEnabled.GetBool() {
|
if config.CacheEnabled.GetBool() {
|
||||||
switch config.CacheType.GetString() {
|
switch config.CacheType.GetString() {
|
||||||
case "memory":
|
case "memory":
|
||||||
cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), config.CacheMaxElementSize.GetInt())
|
cacher := caches.NewLRUCacher(caches.NewMemoryStore(), config.CacheMaxElementSize.GetInt())
|
||||||
engine.SetDefaultCacher(cacher)
|
engine.SetDefaultCacher(cacher)
|
||||||
case "redis":
|
case "redis":
|
||||||
cacher := xrc.NewRedisCacher(config.RedisEnabled.GetString(), config.RedisPassword.GetString(), xrc.DEFAULT_EXPIRATION, engine.Logger())
|
cacher := xrc.NewRedisCacher(config.RedisEnabled.GetString(), config.RedisPassword.GetString(), xrc.DEFAULT_EXPIRATION, log.GetXormLoggerForRedis(logger))
|
||||||
engine.SetDefaultCacher(cacher)
|
engine.SetDefaultCacher(cacher)
|
||||||
default:
|
default:
|
||||||
log.Info("Did not find a valid cache type. Caching disabled. Please refer to the docs for poosible cache types.")
|
log.Info("Did not find a valid cache type. Caching disabled. Please refer to the docs for poosible cache types.")
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
- id: 1
|
- id: 1
|
||||||
text: 'task #1'
|
text: 'task #1'
|
||||||
description: 'Lorem Ipsum'
|
description: 'Lorem Ipsum'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 3
|
- id: 3
|
||||||
text: 'task #3 high prio'
|
text: 'task #3 high prio'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 3
|
index: 3
|
||||||
|
@ -24,6 +26,7 @@
|
||||||
priority: 100
|
priority: 100
|
||||||
- id: 4
|
- id: 4
|
||||||
text: 'task #4 low prio'
|
text: 'task #4 low prio'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 4
|
index: 4
|
||||||
|
@ -32,6 +35,7 @@
|
||||||
priority: 1
|
priority: 1
|
||||||
- id: 5
|
- id: 5
|
||||||
text: 'task #5 higher due date'
|
text: 'task #5 higher due date'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 5
|
index: 5
|
||||||
|
@ -40,6 +44,7 @@
|
||||||
due_date_unix: 1543636724
|
due_date_unix: 1543636724
|
||||||
- id: 6
|
- id: 6
|
||||||
text: 'task #6 lower due date'
|
text: 'task #6 lower due date'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 6
|
index: 6
|
||||||
|
@ -48,6 +53,7 @@
|
||||||
due_date_unix: 1543616724
|
due_date_unix: 1543616724
|
||||||
- id: 7
|
- id: 7
|
||||||
text: 'task #7 with start date'
|
text: 'task #7 with start date'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 7
|
index: 7
|
||||||
|
@ -56,6 +62,7 @@
|
||||||
start_date_unix: 1544600000
|
start_date_unix: 1544600000
|
||||||
- id: 8
|
- id: 8
|
||||||
text: 'task #8 with end date'
|
text: 'task #8 with end date'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 8
|
index: 8
|
||||||
|
@ -64,6 +71,7 @@
|
||||||
end_date_unix: 1544700000
|
end_date_unix: 1544700000
|
||||||
- id: 9
|
- id: 9
|
||||||
text: 'task #9 with start and end date'
|
text: 'task #9 with start and end date'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 9
|
index: 9
|
||||||
|
@ -73,6 +81,7 @@
|
||||||
end_date_unix: 1544700000
|
end_date_unix: 1544700000
|
||||||
- id: 10
|
- id: 10
|
||||||
text: 'task #10 basic'
|
text: 'task #10 basic'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 10
|
index: 10
|
||||||
|
@ -80,6 +89,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 11
|
- id: 11
|
||||||
text: 'task #11 basic'
|
text: 'task #11 basic'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 11
|
index: 11
|
||||||
|
@ -87,6 +97,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 12
|
- id: 12
|
||||||
text: 'task #12 basic'
|
text: 'task #12 basic'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 12
|
index: 12
|
||||||
|
@ -94,6 +105,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 13
|
- id: 13
|
||||||
text: 'task #13 basic other list'
|
text: 'task #13 basic other list'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 2
|
list_id: 2
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -101,6 +113,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 14
|
- id: 14
|
||||||
text: 'task #14 basic other list'
|
text: 'task #14 basic other list'
|
||||||
|
done: false
|
||||||
created_by_id: 5
|
created_by_id: 5
|
||||||
list_id: 5
|
list_id: 5
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -108,6 +121,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 15
|
- id: 15
|
||||||
text: 'task #15'
|
text: 'task #15'
|
||||||
|
done: false
|
||||||
created_by_id: 6
|
created_by_id: 6
|
||||||
list_id: 6
|
list_id: 6
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -115,6 +129,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 16
|
- id: 16
|
||||||
text: 'task #16'
|
text: 'task #16'
|
||||||
|
done: false
|
||||||
created_by_id: 6
|
created_by_id: 6
|
||||||
list_id: 7
|
list_id: 7
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -122,6 +137,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 17
|
- id: 17
|
||||||
text: 'task #17'
|
text: 'task #17'
|
||||||
|
done: false
|
||||||
created_by_id: 6
|
created_by_id: 6
|
||||||
list_id: 8
|
list_id: 8
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -129,6 +145,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 18
|
- id: 18
|
||||||
text: 'task #18'
|
text: 'task #18'
|
||||||
|
done: false
|
||||||
created_by_id: 6
|
created_by_id: 6
|
||||||
list_id: 9
|
list_id: 9
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -136,6 +153,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 19
|
- id: 19
|
||||||
text: 'task #19'
|
text: 'task #19'
|
||||||
|
done: false
|
||||||
created_by_id: 6
|
created_by_id: 6
|
||||||
list_id: 10
|
list_id: 10
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -143,6 +161,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 20
|
- id: 20
|
||||||
text: 'task #20'
|
text: 'task #20'
|
||||||
|
done: false
|
||||||
created_by_id: 6
|
created_by_id: 6
|
||||||
list_id: 11
|
list_id: 11
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -150,6 +169,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 21
|
- id: 21
|
||||||
text: 'task #21'
|
text: 'task #21'
|
||||||
|
done: false
|
||||||
created_by_id: 6
|
created_by_id: 6
|
||||||
list_id: 12
|
list_id: 12
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -157,6 +177,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 22
|
- id: 22
|
||||||
text: 'task #22'
|
text: 'task #22'
|
||||||
|
done: false
|
||||||
created_by_id: 6
|
created_by_id: 6
|
||||||
list_id: 13
|
list_id: 13
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -164,6 +185,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 23
|
- id: 23
|
||||||
text: 'task #23'
|
text: 'task #23'
|
||||||
|
done: false
|
||||||
created_by_id: 6
|
created_by_id: 6
|
||||||
list_id: 14
|
list_id: 14
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -171,6 +193,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 24
|
- id: 24
|
||||||
text: 'task #24'
|
text: 'task #24'
|
||||||
|
done: false
|
||||||
created_by_id: 6
|
created_by_id: 6
|
||||||
list_id: 15
|
list_id: 15
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -178,6 +201,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 25
|
- id: 25
|
||||||
text: 'task #25'
|
text: 'task #25'
|
||||||
|
done: false
|
||||||
created_by_id: 6
|
created_by_id: 6
|
||||||
list_id: 16
|
list_id: 16
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -185,6 +209,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 26
|
- id: 26
|
||||||
text: 'task #26'
|
text: 'task #26'
|
||||||
|
done: false
|
||||||
created_by_id: 6
|
created_by_id: 6
|
||||||
list_id: 17
|
list_id: 17
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -192,6 +217,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 27
|
- id: 27
|
||||||
text: 'task #27 with reminders'
|
text: 'task #27 with reminders'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 12
|
index: 12
|
||||||
|
@ -208,6 +234,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 29
|
- id: 29
|
||||||
text: 'task #29 with parent task (1)'
|
text: 'task #29 with parent task (1)'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 14
|
index: 14
|
||||||
|
@ -215,6 +242,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 30
|
- id: 30
|
||||||
text: 'task #30 with assignees'
|
text: 'task #30 with assignees'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 15
|
index: 15
|
||||||
|
@ -222,6 +250,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 31
|
- id: 31
|
||||||
text: 'task #31 with color'
|
text: 'task #31 with color'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 16
|
index: 16
|
||||||
|
@ -230,6 +259,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 32
|
- id: 32
|
||||||
text: 'task #32'
|
text: 'task #32'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 3
|
list_id: 3
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -237,6 +267,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 33
|
- id: 33
|
||||||
text: 'task #33 with percent done'
|
text: 'task #33 with percent done'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 1
|
list_id: 1
|
||||||
index: 17
|
index: 17
|
||||||
|
@ -246,6 +277,7 @@
|
||||||
# This task is forbidden for user1
|
# This task is forbidden for user1
|
||||||
- id: 34
|
- id: 34
|
||||||
text: 'task #34'
|
text: 'task #34'
|
||||||
|
done: false
|
||||||
created_by_id: 13
|
created_by_id: 13
|
||||||
list_id: 20
|
list_id: 20
|
||||||
index: 20
|
index: 20
|
||||||
|
@ -253,6 +285,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 35
|
- id: 35
|
||||||
text: 'task #35'
|
text: 'task #35'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 21
|
list_id: 21
|
||||||
index: 1
|
index: 1
|
||||||
|
@ -260,6 +293,7 @@
|
||||||
updated: 1543626724
|
updated: 1543626724
|
||||||
- id: 36
|
- id: 36
|
||||||
text: 'task #36'
|
text: 'task #36'
|
||||||
|
done: false
|
||||||
created_by_id: 1
|
created_by_id: 1
|
||||||
list_id: 22
|
list_id: 22
|
||||||
index: 1
|
index: 1
|
||||||
|
|
|
@ -46,7 +46,7 @@ func CreateTestEngine() (engine *xorm.Engine, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.SetMapper(core.GonicMapper{})
|
engine.SetMapper(core.GonicMapper{})
|
||||||
logger := xorm.NewSimpleLogger(log.GetLogWriter("database"))
|
logger := log.NewXormLogger()
|
||||||
logger.ShowSQL(os.Getenv("UNIT_TESTS_VERBOSE") == "1")
|
logger.ShowSQL(os.Getenv("UNIT_TESTS_VERBOSE") == "1")
|
||||||
engine.SetLogger(logger)
|
engine.SetLogger(logger)
|
||||||
x = engine
|
x = engine
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
"xorm.io/xorm/schemas"
|
||||||
)
|
)
|
||||||
|
|
||||||
var fixtures *testfixtures.Loader
|
var fixtures *testfixtures.Loader
|
||||||
|
@ -73,7 +74,7 @@ func LoadFixtures() error {
|
||||||
|
|
||||||
// Copied from https://github.com/go-gitea/gitea/blob/master/models/test_fixtures.go#L39
|
// Copied from https://github.com/go-gitea/gitea/blob/master/models/test_fixtures.go#L39
|
||||||
// Now if we're running postgres we need to tell it to update the sequences
|
// Now if we're running postgres we need to tell it to update the sequences
|
||||||
if x.Dialect().DriverName() == "postgres" {
|
if x.Dialect().URI().DBType == schemas.POSTGRES {
|
||||||
results, err := x.QueryString(`SELECT 'SELECT SETVAL(' ||
|
results, err := x.QueryString(`SELECT 'SELECT SETVAL(' ||
|
||||||
quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
|
quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
|
||||||
', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
|
', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
|
||||||
|
|
188
pkg/log/xorm_logger.go
Normal file
188
pkg/log/xorm_logger.go
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
// Vikunja is a to-do list application to facilitate your life.
|
||||||
|
// Copyright 2018-2020 Vikunja and contributors. All rights reserved.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.vikunja.io/api/pkg/config"
|
||||||
|
"github.com/op/go-logging"
|
||||||
|
"time"
|
||||||
|
"xorm.io/core"
|
||||||
|
"xorm.io/xorm/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XormFmt defines the format for xorm logging strings
|
||||||
|
const XormFmt = `%{color}%{time:` + time.RFC3339Nano + `}: %{level}` + "\t" + `▶ [DATABASE] %{id:03x}%{color:reset} %{message}`
|
||||||
|
|
||||||
|
const xormLogModule = `vikunja_database`
|
||||||
|
|
||||||
|
// XormLogger holds an implementation of the xorm logger interface.
|
||||||
|
type XormLogger struct {
|
||||||
|
logger *logging.Logger
|
||||||
|
level log.LogLevel
|
||||||
|
showSQL bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewXormLogger creates and initializes a new xorm logger
|
||||||
|
func NewXormLogger() *XormLogger {
|
||||||
|
level, err := logging.LogLevel(config.LogDatabaseLevel.GetString())
|
||||||
|
if err != nil {
|
||||||
|
Critical("Error setting database log level: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
xormLogger := &XormLogger{
|
||||||
|
logger: logging.MustGetLogger(xormLogModule),
|
||||||
|
}
|
||||||
|
|
||||||
|
logBackend := logging.NewLogBackend(GetLogWriter("database"), "", 0)
|
||||||
|
backend := logging.NewBackendFormatter(logBackend, logging.MustStringFormatter(XormFmt+"\n"))
|
||||||
|
|
||||||
|
backendLeveled := logging.AddModuleLevel(backend)
|
||||||
|
backendLeveled.SetLevel(level, xormLogModule)
|
||||||
|
|
||||||
|
xormLogger.logger.SetBackend(backendLeveled)
|
||||||
|
|
||||||
|
switch level {
|
||||||
|
case logging.CRITICAL:
|
||||||
|
case logging.ERROR:
|
||||||
|
xormLogger.level = log.LOG_ERR
|
||||||
|
case logging.WARNING:
|
||||||
|
case logging.NOTICE:
|
||||||
|
xormLogger.level = log.LOG_WARNING
|
||||||
|
case logging.INFO:
|
||||||
|
xormLogger.level = log.LOG_INFO
|
||||||
|
case logging.DEBUG:
|
||||||
|
xormLogger.level = log.LOG_DEBUG
|
||||||
|
default:
|
||||||
|
xormLogger.level = log.LOG_OFF
|
||||||
|
}
|
||||||
|
|
||||||
|
xormLogger.showSQL = true
|
||||||
|
|
||||||
|
return xormLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug logs a debug string
|
||||||
|
func (x *XormLogger) Debug(v ...interface{}) {
|
||||||
|
x.logger.Debug(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf logs a debug string
|
||||||
|
func (x *XormLogger) Debugf(format string, v ...interface{}) {
|
||||||
|
x.logger.Debugf(format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs a debug string
|
||||||
|
func (x *XormLogger) Error(v ...interface{}) {
|
||||||
|
x.logger.Error(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf logs a debug string
|
||||||
|
func (x *XormLogger) Errorf(format string, v ...interface{}) {
|
||||||
|
x.logger.Errorf(format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info logs an info string
|
||||||
|
func (x *XormLogger) Info(v ...interface{}) {
|
||||||
|
x.logger.Info(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs an info string
|
||||||
|
func (x *XormLogger) Infof(format string, v ...interface{}) {
|
||||||
|
x.logger.Infof(format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn logs a warning string
|
||||||
|
func (x *XormLogger) Warn(v ...interface{}) {
|
||||||
|
x.logger.Warning(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnf logs a warning string
|
||||||
|
func (x *XormLogger) Warnf(format string, v ...interface{}) {
|
||||||
|
x.logger.Warningf(format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Level returns the current set log level
|
||||||
|
func (x *XormLogger) Level() log.LogLevel {
|
||||||
|
return x.level
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLevel sets the log level
|
||||||
|
func (x *XormLogger) SetLevel(l log.LogLevel) {
|
||||||
|
x.level = l
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShowSQL sets whether to show the log level or not
|
||||||
|
func (x *XormLogger) ShowSQL(show ...bool) {
|
||||||
|
if len(show) > 0 {
|
||||||
|
x.showSQL = show[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsShowSQL returns if sql queries should be shown
|
||||||
|
func (x *XormLogger) IsShowSQL() bool {
|
||||||
|
return x.showSQL
|
||||||
|
}
|
||||||
|
|
||||||
|
// XormRedisCacherLogger is used as a compatibility layer to be able to re-use the same logger from xorm until
|
||||||
|
// the redis cacher module accepts the same logger.
|
||||||
|
// See https://gitea.com/xorm/xorm-redis-cache/issues/10
|
||||||
|
type XormRedisCacherLogger struct {
|
||||||
|
XormLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetXormLoggerForRedis creates a new xorm logger which can be used with redis
|
||||||
|
func GetXormLoggerForRedis(x *XormLogger) *XormRedisCacherLogger {
|
||||||
|
return &XormRedisCacherLogger{*x}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Level returns the currently set log level
|
||||||
|
func (x *XormRedisCacherLogger) Level() core.LogLevel {
|
||||||
|
switch x.level {
|
||||||
|
case log.LOG_DEBUG:
|
||||||
|
return core.LOG_DEBUG
|
||||||
|
case log.LOG_INFO:
|
||||||
|
return core.LOG_INFO
|
||||||
|
case log.LOG_WARNING:
|
||||||
|
return core.LOG_WARNING
|
||||||
|
case log.LOG_ERR:
|
||||||
|
return core.LOG_ERR
|
||||||
|
case log.LOG_OFF:
|
||||||
|
return core.LOG_OFF
|
||||||
|
case log.LOG_UNKNOWN:
|
||||||
|
return core.LOG_UNKNOWN
|
||||||
|
default:
|
||||||
|
return core.LOG_UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLevel sets the log level
|
||||||
|
func (x *XormRedisCacherLogger) SetLevel(l core.LogLevel) {
|
||||||
|
switch l {
|
||||||
|
case core.LOG_DEBUG:
|
||||||
|
x.level = log.LOG_DEBUG
|
||||||
|
case core.LOG_INFO:
|
||||||
|
x.level = log.LOG_INFO
|
||||||
|
case core.LOG_WARNING:
|
||||||
|
x.level = log.LOG_WARNING
|
||||||
|
case core.LOG_ERR:
|
||||||
|
x.level = log.LOG_ERR
|
||||||
|
case core.LOG_OFF:
|
||||||
|
x.level = log.LOG_OFF
|
||||||
|
default:
|
||||||
|
x.level = log.LOG_UNKNOWN
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/red"
|
"code.vikunja.io/api/pkg/red"
|
||||||
"github.com/go-redis/redis"
|
"github.com/go-redis/redis/v7"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
)
|
)
|
||||||
|
|
|
@ -60,7 +60,7 @@ func init() {
|
||||||
|
|
||||||
// Get all current subtasks and put them in a new table
|
// Get all current subtasks and put them in a new table
|
||||||
tasks := []*task20190922205826{}
|
tasks := []*task20190922205826{}
|
||||||
err = tx.Where("parent_task_id != null OR parent_task_id != 0").Find(&tasks)
|
err = tx.Where("parent_task_id is not null OR parent_task_id != 0").Find(&tasks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ func (l *Label) hasAccessToLabel(a web.Auth) (bool, error) {
|
||||||
has, err := x.Table("labels").
|
has, err := x.Table("labels").
|
||||||
Select("labels.*").
|
Select("labels.*").
|
||||||
Join("LEFT", "label_task", "label_task.label_id = labels.id").
|
Join("LEFT", "label_task", "label_task.label_id = labels.id").
|
||||||
Where("label_task.label_id != null OR labels.created_by_id = ?", a.GetID()).
|
Where("label_task.label_id is not null OR labels.created_by_id = ?", a.GetID()).
|
||||||
Or(builder.In("label_task.task_id", taskIDs)).
|
Or(builder.In("label_task.task_id", taskIDs)).
|
||||||
And("labels.id = ?", l.ID).
|
And("labels.id = ?", l.ID).
|
||||||
Exist(&labels)
|
Exist(&labels)
|
||||||
|
|
|
@ -162,20 +162,27 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, res
|
||||||
|
|
||||||
// Get all labels associated with these tasks
|
// Get all labels associated with these tasks
|
||||||
var labels []*labelWithTaskID
|
var labels []*labelWithTaskID
|
||||||
cond := builder.And(builder.In("label_task.task_id", opts.TaskIDs), builder.NotNull{"label_task.label_id"})
|
cond := builder.And(builder.NotNull{"label_task.label_id"})
|
||||||
|
if len(opts.TaskIDs) > 0 {
|
||||||
|
cond = builder.And(builder.In("label_task.task_id", opts.TaskIDs), cond)
|
||||||
|
}
|
||||||
if opts.GetUnusedLabels {
|
if opts.GetUnusedLabels {
|
||||||
cond = builder.Or(cond, builder.Eq{"labels.created_by_id": opts.User.ID})
|
cond = builder.Or(cond, builder.Eq{"labels.created_by_id": opts.User.ID})
|
||||||
}
|
}
|
||||||
|
|
||||||
err = x.Table("labels").
|
limit, start := getLimitFromPageIndex(opts.Page, opts.PerPage)
|
||||||
|
|
||||||
|
query := x.Table("labels").
|
||||||
Select(selectStmt).
|
Select(selectStmt).
|
||||||
Join("LEFT", "label_task", "label_task.label_id = labels.id").
|
Join("LEFT", "label_task", "label_task.label_id = labels.id").
|
||||||
Where(cond).
|
Where(cond).
|
||||||
And("labels.title LIKE ?", "%"+opts.Search+"%").
|
And("labels.title LIKE ?", "%"+opts.Search+"%").
|
||||||
GroupBy(groupBy).
|
GroupBy(groupBy).
|
||||||
OrderBy("labels.id ASC").
|
OrderBy("labels.id ASC")
|
||||||
Limit(getLimitFromPageIndex(opts.Page, opts.PerPage)).
|
if limit > 0 {
|
||||||
Find(&labels)
|
query = query.Limit(limit, start)
|
||||||
|
}
|
||||||
|
err = query.Find(&labels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,11 +155,15 @@ func (share *LinkSharing) ReadAll(a web.Auth, search string, page int, perPage i
|
||||||
return nil, 0, 0, ErrGenericForbidden{}
|
return nil, 0, 0, ErrGenericForbidden{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
limit, start := getLimitFromPageIndex(page, perPage)
|
||||||
|
|
||||||
var shares []*LinkSharing
|
var shares []*LinkSharing
|
||||||
err = x.
|
query := x.
|
||||||
Where("list_id = ? AND hash LIKE ?", share.ListID, "%"+search+"%").
|
Where("list_id = ? AND hash LIKE ?", share.ListID, "%"+search+"%")
|
||||||
Limit(getLimitFromPageIndex(page, perPage)).
|
if limit > 0 {
|
||||||
Find(&shares)
|
query = query.Limit(limit, start)
|
||||||
|
}
|
||||||
|
err = query.Find(&shares)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,9 +235,11 @@ func getRawListsForUser(opts *listOptions) (lists []*List, resultCount int, tota
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
limit, start := getLimitFromPageIndex(opts.page, opts.perPage)
|
||||||
|
|
||||||
// Gets all Lists where the user is either owner or in a team which has access to the list
|
// Gets all Lists where the user is either owner or in a team which has access to the list
|
||||||
// Or in a team which has namespace read access
|
// Or in a team which has namespace read access
|
||||||
err = x.Select("l.*").
|
query := x.Select("l.*").
|
||||||
Table("list").
|
Table("list").
|
||||||
Alias("l").
|
Alias("l").
|
||||||
Join("INNER", []string{"namespaces", "n"}, "l.namespace_id = n.id").
|
Join("INNER", []string{"namespaces", "n"}, "l.namespace_id = n.id").
|
||||||
|
@ -247,16 +249,20 @@ func getRawListsForUser(opts *listOptions) (lists []*List, resultCount int, tota
|
||||||
Join("LEFT", []string{"team_members", "tm2"}, "tm2.team_id = tl.team_id").
|
Join("LEFT", []string{"team_members", "tm2"}, "tm2.team_id = tl.team_id").
|
||||||
Join("LEFT", []string{"users_list", "ul"}, "ul.list_id = l.id").
|
Join("LEFT", []string{"users_list", "ul"}, "ul.list_id = l.id").
|
||||||
Join("LEFT", []string{"users_namespace", "un"}, "un.namespace_id = l.namespace_id").
|
Join("LEFT", []string{"users_namespace", "un"}, "un.namespace_id = l.namespace_id").
|
||||||
Where("tm.user_id = ?", fullUser.ID).
|
Where(builder.Or(
|
||||||
Or("tm2.user_id = ?", fullUser.ID).
|
builder.Eq{"tm.user_id": fullUser.ID},
|
||||||
Or("l.owner_id = ?", fullUser.ID).
|
builder.Eq{"tm2.user_id": fullUser.ID},
|
||||||
Or("ul.user_id = ?", fullUser.ID).
|
builder.Eq{"ul.user_id": fullUser.ID},
|
||||||
Or("un.user_id = ?", fullUser.ID).
|
builder.Eq{"un.user_id": fullUser.ID},
|
||||||
|
builder.Eq{"l.owner_id": fullUser.ID},
|
||||||
|
)).
|
||||||
GroupBy("l.id").
|
GroupBy("l.id").
|
||||||
Limit(getLimitFromPageIndex(opts.page, opts.perPage)).
|
|
||||||
Where("l.title LIKE ?", "%"+opts.search+"%").
|
Where("l.title LIKE ?", "%"+opts.search+"%").
|
||||||
Where(isArchivedCond).
|
Where(isArchivedCond)
|
||||||
Find(&lists)
|
if limit > 0 {
|
||||||
|
query = query.Limit(limit, start)
|
||||||
|
}
|
||||||
|
err = query.Find(&lists)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
@ -271,13 +277,14 @@ func getRawListsForUser(opts *listOptions) (lists []*List, resultCount int, tota
|
||||||
Join("LEFT", []string{"team_members", "tm2"}, "tm2.team_id = tl.team_id").
|
Join("LEFT", []string{"team_members", "tm2"}, "tm2.team_id = tl.team_id").
|
||||||
Join("LEFT", []string{"users_list", "ul"}, "ul.list_id = l.id").
|
Join("LEFT", []string{"users_list", "ul"}, "ul.list_id = l.id").
|
||||||
Join("LEFT", []string{"users_namespace", "un"}, "un.namespace_id = l.namespace_id").
|
Join("LEFT", []string{"users_namespace", "un"}, "un.namespace_id = l.namespace_id").
|
||||||
Where("tm.user_id = ?", fullUser.ID).
|
Where(builder.Or(
|
||||||
Or("tm2.user_id = ?", fullUser.ID).
|
builder.Eq{"tm.user_id": fullUser.ID},
|
||||||
Or("l.owner_id = ?", fullUser.ID).
|
builder.Eq{"tm2.user_id": fullUser.ID},
|
||||||
Or("ul.user_id = ?", fullUser.ID).
|
builder.Eq{"ul.user_id": fullUser.ID},
|
||||||
Or("un.user_id = ?", fullUser.ID).
|
builder.Eq{"un.user_id": fullUser.ID},
|
||||||
|
builder.Eq{"l.owner_id": fullUser.ID},
|
||||||
|
)).
|
||||||
GroupBy("l.id").
|
GroupBy("l.id").
|
||||||
Limit(getLimitFromPageIndex(opts.page, opts.perPage)).
|
|
||||||
Where("l.title LIKE ?", "%"+opts.search+"%").
|
Where("l.title LIKE ?", "%"+opts.search+"%").
|
||||||
Where(isArchivedCond).
|
Where(isArchivedCond).
|
||||||
Count(&List{})
|
Count(&List{})
|
||||||
|
|
|
@ -176,15 +176,19 @@ func (tl *TeamList) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
||||||
return nil, 0, 0, ErrNeedToHaveListReadAccess{ListID: tl.ListID, UserID: a.GetID()}
|
return nil, 0, 0, ErrNeedToHaveListReadAccess{ListID: tl.ListID, UserID: a.GetID()}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
limit, start := getLimitFromPageIndex(page, perPage)
|
||||||
|
|
||||||
// Get the teams
|
// Get the teams
|
||||||
all := []*TeamWithRight{}
|
all := []*TeamWithRight{}
|
||||||
err = x.
|
query := x.
|
||||||
Table("teams").
|
Table("teams").
|
||||||
Join("INNER", "team_list", "team_id = teams.id").
|
Join("INNER", "team_list", "team_id = teams.id").
|
||||||
Where("team_list.list_id = ?", tl.ListID).
|
Where("team_list.list_id = ?", tl.ListID).
|
||||||
Limit(getLimitFromPageIndex(page, perPage)).
|
Where("teams.name LIKE ?", "%"+search+"%")
|
||||||
Where("teams.name LIKE ?", "%"+search+"%").
|
if limit > 0 {
|
||||||
Find(&all)
|
query = query.Limit(limit, start)
|
||||||
|
}
|
||||||
|
err = query.Find(&all)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,14 +182,18 @@ func (lu *ListUser) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
||||||
return nil, 0, 0, ErrNeedToHaveListReadAccess{UserID: a.GetID(), ListID: lu.ListID}
|
return nil, 0, 0, ErrNeedToHaveListReadAccess{UserID: a.GetID(), ListID: lu.ListID}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
limit, start := getLimitFromPageIndex(page, perPage)
|
||||||
|
|
||||||
// Get all users
|
// Get all users
|
||||||
all := []*UserWithRight{}
|
all := []*UserWithRight{}
|
||||||
err = x.
|
query := x.
|
||||||
Join("INNER", "users_list", "user_id = users.id").
|
Join("INNER", "users_list", "user_id = users.id").
|
||||||
Where("users_list.list_id = ?", lu.ListID).
|
Where("users_list.list_id = ?", lu.ListID).
|
||||||
Limit(getLimitFromPageIndex(page, perPage)).
|
Where("users.username LIKE ?", "%"+search+"%")
|
||||||
Where("users.username LIKE ?", "%"+search+"%").
|
if limit > 0 {
|
||||||
Find(&all)
|
query = query.Limit(limit, start)
|
||||||
|
}
|
||||||
|
err = query.Find(&all)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,8 +72,8 @@ func SetEngine() (err error) {
|
||||||
|
|
||||||
func getLimitFromPageIndex(page int, perPage int) (limit, start int) {
|
func getLimitFromPageIndex(page int, perPage int) (limit, start int) {
|
||||||
|
|
||||||
// Get everything when page index is -1
|
// Get everything when page index is -1 or 0 (= not set)
|
||||||
if page < 0 {
|
if page < 1 {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,9 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
||||||
[]*List{},
|
[]*List{},
|
||||||
})
|
})
|
||||||
|
|
||||||
err = x.Select("namespaces.*").
|
limit, start := getLimitFromPageIndex(page, perPage)
|
||||||
|
|
||||||
|
query := x.Select("namespaces.*").
|
||||||
Table("namespaces").
|
Table("namespaces").
|
||||||
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
|
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_id").
|
||||||
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").
|
Join("LEFT", "team_members", "team_members.team_id = team_namespaces.team_id").
|
||||||
|
@ -199,10 +201,12 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
|
||||||
Or("namespaces.owner_id = ?", doer.ID).
|
Or("namespaces.owner_id = ?", doer.ID).
|
||||||
Or("users_namespace.user_id = ?", doer.ID).
|
Or("users_namespace.user_id = ?", doer.ID).
|
||||||
GroupBy("namespaces.id").
|
GroupBy("namespaces.id").
|
||||||
Limit(getLimitFromPageIndex(page, perPage)).
|
|
||||||
Where("namespaces.name LIKE ?", "%"+search+"%").
|
Where("namespaces.name LIKE ?", "%"+search+"%").
|
||||||
Where(isArchivedCond).
|
Where(isArchivedCond)
|
||||||
Find(&all)
|
if limit > 0 {
|
||||||
|
query = query.Limit(limit, start)
|
||||||
|
}
|
||||||
|
err = query.Find(&all)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return all, 0, 0, err
|
return all, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,12 +164,16 @@ func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage in
|
||||||
// Get the teams
|
// Get the teams
|
||||||
all := []*TeamWithRight{}
|
all := []*TeamWithRight{}
|
||||||
|
|
||||||
err = x.Table("teams").
|
limit, start := getLimitFromPageIndex(page, perPage)
|
||||||
|
|
||||||
|
query := x.Table("teams").
|
||||||
Join("INNER", "team_namespaces", "team_id = teams.id").
|
Join("INNER", "team_namespaces", "team_id = teams.id").
|
||||||
Where("team_namespaces.namespace_id = ?", tn.NamespaceID).
|
Where("team_namespaces.namespace_id = ?", tn.NamespaceID).
|
||||||
Limit(getLimitFromPageIndex(page, perPage)).
|
Where("teams.name LIKE ?", "%"+search+"%")
|
||||||
Where("teams.name LIKE ?", "%"+search+"%").
|
if limit > 0 {
|
||||||
Find(&all)
|
query = query.Limit(limit, start)
|
||||||
|
}
|
||||||
|
err = query.Find(&all)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,12 +170,17 @@ func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage in
|
||||||
|
|
||||||
// Get all users
|
// Get all users
|
||||||
all := []*UserWithRight{}
|
all := []*UserWithRight{}
|
||||||
err = x.
|
|
||||||
|
limit, start := getLimitFromPageIndex(page, perPage)
|
||||||
|
|
||||||
|
query := x.
|
||||||
Join("INNER", "users_namespace", "user_id = users.id").
|
Join("INNER", "users_namespace", "user_id = users.id").
|
||||||
Where("users_namespace.namespace_id = ?", nu.NamespaceID).
|
Where("users_namespace.namespace_id = ?", nu.NamespaceID).
|
||||||
Limit(getLimitFromPageIndex(page, perPage)).
|
Where("users.username LIKE ?", "%"+search+"%")
|
||||||
Where("users.username LIKE ?", "%"+search+"%").
|
if limit > 0 {
|
||||||
Find(&all)
|
query = query.Limit(limit, start)
|
||||||
|
}
|
||||||
|
err = query.Find(&all)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,14 +253,17 @@ func (la *TaskAssginee) ReadAll(a web.Auth, search string, page int, perPage int
|
||||||
if !can {
|
if !can {
|
||||||
return nil, 0, 0, ErrGenericForbidden{}
|
return nil, 0, 0, ErrGenericForbidden{}
|
||||||
}
|
}
|
||||||
|
limit, start := getLimitFromPageIndex(page, perPage)
|
||||||
|
|
||||||
var taskAssignees []*user.User
|
var taskAssignees []*user.User
|
||||||
err = x.Table("task_assignees").
|
query := x.Table("task_assignees").
|
||||||
Select("users.*").
|
Select("users.*").
|
||||||
Join("INNER", "users", "task_assignees.user_id = users.id").
|
Join("INNER", "users", "task_assignees.user_id = users.id").
|
||||||
Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%").
|
Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%")
|
||||||
Limit(getLimitFromPageIndex(page, perPage)).
|
if limit > 0 {
|
||||||
Find(&taskAssignees)
|
query = query.Limit(limit, start)
|
||||||
|
}
|
||||||
|
err = query.Find(&taskAssignees)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,10 +112,14 @@ func (ta *TaskAttachment) ReadOne() (err error) {
|
||||||
func (ta *TaskAttachment) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
func (ta *TaskAttachment) ReadAll(a web.Auth, search string, page int, perPage int) (result interface{}, resultCount int, numberOfTotalItems int64, err error) {
|
||||||
attachments := []*TaskAttachment{}
|
attachments := []*TaskAttachment{}
|
||||||
|
|
||||||
err = x.
|
limit, start := getLimitFromPageIndex(page, perPage)
|
||||||
Limit(getLimitFromPageIndex(page, perPage)).
|
|
||||||
Where("task_id = ?", ta.TaskID).
|
query := x.
|
||||||
Find(&attachments)
|
Where("task_id = ?", ta.TaskID)
|
||||||
|
if limit > 0 {
|
||||||
|
query = query.Limit(limit, start)
|
||||||
|
}
|
||||||
|
err = query.Find(&attachments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,12 +180,16 @@ func (tc *TaskComment) ReadAll(auth web.Auth, search string, page int, perPage i
|
||||||
AuthorFromDB *user.User `xorm:"extends" json:"-"`
|
AuthorFromDB *user.User `xorm:"extends" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
limit, start := getLimitFromPageIndex(page, perPage)
|
||||||
|
|
||||||
comments := []*TaskComment{}
|
comments := []*TaskComment{}
|
||||||
err = x.
|
query := x.
|
||||||
Where("task_id = ? AND comment like ?", tc.TaskID, "%"+search+"%").
|
Where("task_id = ? AND comment like ?", tc.TaskID, "%"+search+"%").
|
||||||
Join("LEFT", "users", "users.id = task_comments.author_id").
|
Join("LEFT", "users", "users.id = task_comments.author_id")
|
||||||
Limit(getLimitFromPageIndex(page, perPage)).
|
if limit > 0 {
|
||||||
Find(&comments)
|
query = query.Limit(limit, start)
|
||||||
|
}
|
||||||
|
err = query.Find(&comments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ type Task struct {
|
||||||
// The task description.
|
// The task description.
|
||||||
Description string `xorm:"longtext null" json:"description"`
|
Description string `xorm:"longtext null" json:"description"`
|
||||||
// Whether a task is done or not.
|
// Whether a task is done or not.
|
||||||
Done bool `xorm:"INDEX null default false" json:"done"`
|
Done bool `xorm:"INDEX null" json:"done"`
|
||||||
// The time when a task was marked as done.
|
// The time when a task was marked as done.
|
||||||
DoneAt timeutil.TimeStamp `xorm:"INDEX null 'done_at_unix'" json:"doneAt"`
|
DoneAt timeutil.TimeStamp `xorm:"INDEX null 'done_at_unix'" json:"doneAt"`
|
||||||
// The time when the task is due.
|
// The time when the task is due.
|
||||||
|
@ -184,41 +184,38 @@ func getRawTasksForLists(lists []*List, opts *taskOptions) (taskMap map[int64]*T
|
||||||
taskMap = make(map[int64]*Task)
|
taskMap = make(map[int64]*Task)
|
||||||
|
|
||||||
// Then return all tasks for that lists
|
// Then return all tasks for that lists
|
||||||
|
query := x.
|
||||||
|
OrderBy(orderby)
|
||||||
|
|
||||||
|
if len(opts.search) > 0 {
|
||||||
|
query = query.Where("text LIKE ?", "%"+opts.search+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(listIDs) > 0 {
|
||||||
|
query = query.In("list_id", listIDs)
|
||||||
|
}
|
||||||
|
|
||||||
if len(filters) > 0 {
|
if len(filters) > 0 {
|
||||||
|
query = query.Where(builder.Or(filters...))
|
||||||
|
}
|
||||||
|
|
||||||
err := x.In("list_id", listIDs).
|
limit, start := getLimitFromPageIndex(opts.page, opts.perPage)
|
||||||
Where("text LIKE ?", "%"+opts.search+"%").
|
|
||||||
Where(builder.Or(filters...)).
|
if limit > 0 {
|
||||||
OrderBy(orderby).
|
query = query.Limit(limit, start)
|
||||||
Limit(getLimitFromPageIndex(opts.page, opts.perPage)).
|
}
|
||||||
Find(&taskMap)
|
|
||||||
|
err = query.Find(&taskMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
totalItems, err = x.In("list_id", listIDs).
|
totalItems, err = query.
|
||||||
Where("text LIKE ?", "%"+opts.search+"%").
|
|
||||||
Where(builder.Or(filters...)).
|
|
||||||
Count(&Task{})
|
Count(&Task{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err := x.In("list_id", listIDs).
|
|
||||||
Where("text LIKE ?", "%"+opts.search+"%").
|
|
||||||
OrderBy(orderby).
|
|
||||||
Limit(getLimitFromPageIndex(opts.page, opts.perPage)).
|
|
||||||
Find(&taskMap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, 0, err
|
|
||||||
}
|
|
||||||
totalItems, err = x.In("list_id", listIDs).
|
|
||||||
Where("text LIKE ?", "%"+opts.search+"%").
|
|
||||||
Count(&Task{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, 0, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return taskMap, len(taskMap), totalItems, nil
|
return taskMap, len(taskMap), totalItems, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -203,14 +203,18 @@ func (t *Team) ReadAll(a web.Auth, search string, page int, perPage int) (result
|
||||||
return nil, 0, 0, ErrGenericForbidden{}
|
return nil, 0, 0, ErrGenericForbidden{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
limit, start := getLimitFromPageIndex(page, perPage)
|
||||||
|
|
||||||
all := []*Team{}
|
all := []*Team{}
|
||||||
err = x.Select("teams.*").
|
query := x.Select("teams.*").
|
||||||
Table("teams").
|
Table("teams").
|
||||||
Join("INNER", "team_members", "team_members.team_id = teams.id").
|
Join("INNER", "team_members", "team_members.team_id = teams.id").
|
||||||
Where("team_members.user_id = ?", a.GetID()).
|
Where("team_members.user_id = ?", a.GetID()).
|
||||||
Limit(getLimitFromPageIndex(page, perPage)).
|
Where("teams.name LIKE ?", "%"+search+"%")
|
||||||
Where("teams.name LIKE ?", "%"+search+"%").
|
if limit > 0 {
|
||||||
Find(&all)
|
query = query.Limit(limit, start)
|
||||||
|
}
|
||||||
|
err = query.Find(&all)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, err
|
return nil, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ package red
|
||||||
import (
|
import (
|
||||||
"code.vikunja.io/api/pkg/config"
|
"code.vikunja.io/api/pkg/config"
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"github.com/go-redis/redis"
|
"github.com/go-redis/redis/v7"
|
||||||
)
|
)
|
||||||
|
|
||||||
var r *redis.Client
|
var r *redis.Client
|
||||||
|
|
|
@ -309,7 +309,7 @@ func UpdateUser(user *User) (updatedUser *User, err error) {
|
||||||
user.Password = theUser.Password // set the password to the one in the database to not accedently resetting it
|
user.Password = theUser.Password // set the password to the one in the database to not accedently resetting it
|
||||||
|
|
||||||
// Update it
|
// Update it
|
||||||
_, err = x.Id(user.ID).Update(user)
|
_, err = x.ID(user.ID).Update(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &User{}, err
|
return &User{}, err
|
||||||
}
|
}
|
||||||
|
@ -344,7 +344,7 @@ func UpdateUserPassword(user *User, newPassword string) (err error) {
|
||||||
theUser.Password = hashed
|
theUser.Password = hashed
|
||||||
|
|
||||||
// Update it
|
// Update it
|
||||||
_, err = x.Id(user.ID).Update(theUser)
|
_, err = x.ID(user.ID).Update(theUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
9
vendor/github.com/fsnotify/fsnotify/.editorconfig
generated
vendored
9
vendor/github.com/fsnotify/fsnotify/.editorconfig
generated
vendored
|
@ -1,5 +1,12 @@
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
[*]
|
[*.go]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{yml,yaml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
1
vendor/github.com/fsnotify/fsnotify/.gitattributes
generated
vendored
Normal file
1
vendor/github.com/fsnotify/fsnotify/.gitattributes
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
go.sum linguist-generated
|
20
vendor/github.com/fsnotify/fsnotify/.travis.yml
generated
vendored
20
vendor/github.com/fsnotify/fsnotify/.travis.yml
generated
vendored
|
@ -2,29 +2,35 @@ sudo: false
|
||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.8.x
|
- "stable"
|
||||||
- 1.9.x
|
- "1.11.x"
|
||||||
- tip
|
- "1.10.x"
|
||||||
|
- "1.9.x"
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
|
include:
|
||||||
|
- go: "stable"
|
||||||
|
env: GOLINT=true
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- go: tip
|
- go: tip
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
|
|
||||||
before_script:
|
|
||||||
- go get -u github.com/golang/lint/golint
|
before_install:
|
||||||
|
- if [ ! -z "${GOLINT}" ]; then go get -u golang.org/x/lint/golint; fi
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- go test -v --race ./...
|
- go test --race ./...
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
- test -z "$(gofmt -s -l -w . | tee /dev/stderr)"
|
- test -z "$(gofmt -s -l -w . | tee /dev/stderr)"
|
||||||
- test -z "$(golint ./... | tee /dev/stderr)"
|
- if [ ! -z "${GOLINT}" ]; then echo running golint; golint --set_exit_status ./...; else echo skipping golint; fi
|
||||||
- go vet ./...
|
- go vet ./...
|
||||||
|
|
||||||
os:
|
os:
|
||||||
- linux
|
- linux
|
||||||
- osx
|
- osx
|
||||||
|
- windows
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
|
2
vendor/github.com/fsnotify/fsnotify/LICENSE
generated
vendored
2
vendor/github.com/fsnotify/fsnotify/LICENSE
generated
vendored
|
@ -1,5 +1,5 @@
|
||||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||||
Copyright (c) 2012 fsnotify Authors. All rights reserved.
|
Copyright (c) 2012-2019 fsnotify Authors. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
modification, are permitted provided that the following conditions are
|
||||||
|
|
57
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
57
vendor/github.com/fsnotify/fsnotify/README.md
generated
vendored
|
@ -11,13 +11,13 @@ go get -u golang.org/x/sys/...
|
||||||
Cross platform: Windows, Linux, BSD and macOS.
|
Cross platform: Windows, Linux, BSD and macOS.
|
||||||
|
|
||||||
| Adapter | OS | Status |
|
| Adapter | OS | Status |
|
||||||
|----------|----------|----------|
|
| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| inotify | Linux 2.6.27 or later, Android\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
|
| inotify | Linux 2.6.27 or later, Android\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
|
||||||
| kqueue | BSD, macOS, iOS\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
|
| kqueue | BSD, macOS, iOS\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
|
||||||
|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)|
|
| ReadDirectoryChangesW | Windows | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
|
||||||
| FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) |
|
| FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) |
|
||||||
| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/issues/12) |
|
| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/issues/12) |
|
||||||
|fanotify |Linux 2.6.37+ | |
|
| fanotify | Linux 2.6.37+ | [Planned](https://github.com/fsnotify/fsnotify/issues/114) |
|
||||||
| USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) |
|
| USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) |
|
||||||
| Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) |
|
| Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) |
|
||||||
|
|
||||||
|
@ -33,6 +33,53 @@ All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based o
|
||||||
|
|
||||||
Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`.
|
Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
watcher, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer watcher.Close()
|
||||||
|
|
||||||
|
done := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event, ok := <-watcher.Events:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("event:", event)
|
||||||
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||||
|
log.Println("modified file:", event.Name)
|
||||||
|
}
|
||||||
|
case err, ok := <-watcher.Errors:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("error:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = watcher.Add("/tmp/foo")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Please refer to [CONTRIBUTING][] before opening an issue or pull request.
|
Please refer to [CONTRIBUTING][] before opening an issue or pull request.
|
||||||
|
@ -65,6 +112,10 @@ There are OS-specific limits as to how many watches can be created:
|
||||||
* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
|
* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
|
||||||
* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
|
* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
|
||||||
|
|
||||||
|
**Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?**
|
||||||
|
|
||||||
|
fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications.
|
||||||
|
|
||||||
[#62]: https://github.com/howeyc/fsnotify/issues/62
|
[#62]: https://github.com/howeyc/fsnotify/issues/62
|
||||||
[#18]: https://github.com/fsnotify/fsnotify/issues/18
|
[#18]: https://github.com/fsnotify/fsnotify/issues/18
|
||||||
[#11]: https://github.com/fsnotify/fsnotify/issues/11
|
[#11]: https://github.com/fsnotify/fsnotify/issues/11
|
||||||
|
|
4
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
4
vendor/github.com/fsnotify/fsnotify/fsnotify.go
generated
vendored
|
@ -63,4 +63,6 @@ func (e Event) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common errors that can be reported by a watcher
|
// Common errors that can be reported by a watcher
|
||||||
var ErrEventOverflow = errors.New("fsnotify queue overflow")
|
var (
|
||||||
|
ErrEventOverflow = errors.New("fsnotify queue overflow")
|
||||||
|
)
|
||||||
|
|
5
vendor/github.com/fsnotify/fsnotify/go.mod
generated
vendored
Normal file
5
vendor/github.com/fsnotify/fsnotify/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module github.com/fsnotify/fsnotify
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9
|
2
vendor/github.com/fsnotify/fsnotify/go.sum
generated
vendored
Normal file
2
vendor/github.com/fsnotify/fsnotify/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
4
vendor/github.com/fsnotify/fsnotify/inotify_poller.go
generated
vendored
4
vendor/github.com/fsnotify/fsnotify/inotify_poller.go
generated
vendored
|
@ -40,12 +40,12 @@ func newFdPoller(fd int) (*fdPoller, error) {
|
||||||
poller.fd = fd
|
poller.fd = fd
|
||||||
|
|
||||||
// Create epoll fd
|
// Create epoll fd
|
||||||
poller.epfd, errno = unix.EpollCreate1(0)
|
poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
|
||||||
if poller.epfd == -1 {
|
if poller.epfd == -1 {
|
||||||
return nil, errno
|
return nil, errno
|
||||||
}
|
}
|
||||||
// Create pipe; pipe[0] is the read end, pipe[1] the write end.
|
// Create pipe; pipe[0] is the read end, pipe[1] the write end.
|
||||||
errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK)
|
errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC)
|
||||||
if errno != nil {
|
if errno != nil {
|
||||||
return nil, errno
|
return nil, errno
|
||||||
}
|
}
|
||||||
|
|
2
vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
generated
vendored
2
vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
generated
vendored
|
@ -8,4 +8,4 @@ package fsnotify
|
||||||
|
|
||||||
import "golang.org/x/sys/unix"
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
const openMode = unix.O_NONBLOCK | unix.O_RDONLY
|
const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC
|
||||||
|
|
2
vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
generated
vendored
2
vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
generated
vendored
|
@ -9,4 +9,4 @@ package fsnotify
|
||||||
import "golang.org/x/sys/unix"
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
// note: this constant is not defined on BSD
|
// note: this constant is not defined on BSD
|
||||||
const openMode = unix.O_EVTONLY
|
const openMode = unix.O_EVTONLY | unix.O_CLOEXEC
|
||||||
|
|
20
vendor/github.com/go-redis/redis/.travis.yml
generated
vendored
20
vendor/github.com/go-redis/redis/.travis.yml
generated
vendored
|
@ -1,20 +0,0 @@
|
||||||
sudo: false
|
|
||||||
language: go
|
|
||||||
|
|
||||||
services:
|
|
||||||
- redis-server
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
|
||||||
- 1.9.x
|
|
||||||
- 1.10.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get github.com/onsi/ginkgo
|
|
||||||
- go get github.com/onsi/gomega
|
|
21
vendor/github.com/go-redis/redis/CHANGELOG.md
generated
vendored
21
vendor/github.com/go-redis/redis/CHANGELOG.md
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
## 6.14
|
|
||||||
|
|
||||||
- Added Options.MinIdleConns.
|
|
||||||
- Added Options.MaxConnAge.
|
|
||||||
- PoolStats.FreeConns is renamed to PoolStats.IdleConns.
|
|
||||||
- Add Client.Do to simplify creating custom commands.
|
|
||||||
- Add Cmd.String, Cmd.Int, Cmd.Int64, Cmd.Uint64, Cmd.Float64, and Cmd.Bool helpers.
|
|
||||||
- Lower memory usage.
|
|
||||||
|
|
||||||
## v6.13
|
|
||||||
|
|
||||||
- Ring got new options called `HashReplicas` and `Hash`. It is recommended to set `HashReplicas = 1000` for better keys distribution between shards.
|
|
||||||
- Cluster client was optimized to use much less memory when reloading cluster state.
|
|
||||||
- PubSub.ReceiveMessage is re-worked to not use ReceiveTimeout so it does not lose data when timeout occurres. In most cases it is recommended to use PubSub.Channel instead.
|
|
||||||
- Dialer.KeepAlive is set to 5 minutes by default.
|
|
||||||
|
|
||||||
## v6.12
|
|
||||||
|
|
||||||
- ClusterClient got new option called `ClusterSlots` which allows to build cluster of normal Redis Servers that don't have cluster mode enabled. See https://godoc.org/github.com/go-redis/redis#example-NewClusterClient--ManualSetup
|
|
15
vendor/github.com/go-redis/redis/internal/log.go
generated
vendored
15
vendor/github.com/go-redis/redis/internal/log.go
generated
vendored
|
@ -1,15 +0,0 @@
|
||||||
package internal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
var Logger *log.Logger
|
|
||||||
|
|
||||||
func Logf(s string, args ...interface{}) {
|
|
||||||
if Logger == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Logger.Output(2, fmt.Sprintf(s, args...))
|
|
||||||
}
|
|
93
vendor/github.com/go-redis/redis/internal/pool/conn.go
generated
vendored
93
vendor/github.com/go-redis/redis/internal/pool/conn.go
generated
vendored
|
@ -1,93 +0,0 @@
|
||||||
package pool
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-redis/redis/internal/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
var noDeadline = time.Time{}
|
|
||||||
|
|
||||||
type Conn struct {
|
|
||||||
netConn net.Conn
|
|
||||||
|
|
||||||
rd *proto.Reader
|
|
||||||
rdLocked bool
|
|
||||||
wr *proto.Writer
|
|
||||||
|
|
||||||
InitedAt time.Time
|
|
||||||
pooled bool
|
|
||||||
usedAt atomic.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConn(netConn net.Conn) *Conn {
|
|
||||||
cn := &Conn{
|
|
||||||
netConn: netConn,
|
|
||||||
}
|
|
||||||
cn.rd = proto.NewReader(netConn)
|
|
||||||
cn.wr = proto.NewWriter(netConn)
|
|
||||||
cn.SetUsedAt(time.Now())
|
|
||||||
return cn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cn *Conn) UsedAt() time.Time {
|
|
||||||
return cn.usedAt.Load().(time.Time)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cn *Conn) SetUsedAt(tm time.Time) {
|
|
||||||
cn.usedAt.Store(tm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cn *Conn) SetNetConn(netConn net.Conn) {
|
|
||||||
cn.netConn = netConn
|
|
||||||
cn.rd.Reset(netConn)
|
|
||||||
cn.wr.Reset(netConn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cn *Conn) setReadTimeout(timeout time.Duration) error {
|
|
||||||
now := time.Now()
|
|
||||||
cn.SetUsedAt(now)
|
|
||||||
if timeout > 0 {
|
|
||||||
return cn.netConn.SetReadDeadline(now.Add(timeout))
|
|
||||||
}
|
|
||||||
return cn.netConn.SetReadDeadline(noDeadline)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cn *Conn) setWriteTimeout(timeout time.Duration) error {
|
|
||||||
now := time.Now()
|
|
||||||
cn.SetUsedAt(now)
|
|
||||||
if timeout > 0 {
|
|
||||||
return cn.netConn.SetWriteDeadline(now.Add(timeout))
|
|
||||||
}
|
|
||||||
return cn.netConn.SetWriteDeadline(noDeadline)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cn *Conn) Write(b []byte) (int, error) {
|
|
||||||
return cn.netConn.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cn *Conn) RemoteAddr() net.Addr {
|
|
||||||
return cn.netConn.RemoteAddr()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cn *Conn) WithReader(timeout time.Duration, fn func(rd *proto.Reader) error) error {
|
|
||||||
_ = cn.setReadTimeout(timeout)
|
|
||||||
return fn(cn.rd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cn *Conn) WithWriter(timeout time.Duration, fn func(wr *proto.Writer) error) error {
|
|
||||||
_ = cn.setWriteTimeout(timeout)
|
|
||||||
|
|
||||||
firstErr := fn(cn.wr)
|
|
||||||
err := cn.wr.Flush()
|
|
||||||
if err != nil && firstErr == nil {
|
|
||||||
firstErr = err
|
|
||||||
}
|
|
||||||
return firstErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cn *Conn) Close() error {
|
|
||||||
return cn.netConn.Close()
|
|
||||||
}
|
|
53
vendor/github.com/go-redis/redis/internal/pool/pool_single.go
generated
vendored
53
vendor/github.com/go-redis/redis/internal/pool/pool_single.go
generated
vendored
|
@ -1,53 +0,0 @@
|
||||||
package pool
|
|
||||||
|
|
||||||
type SingleConnPool struct {
|
|
||||||
cn *Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Pooler = (*SingleConnPool)(nil)
|
|
||||||
|
|
||||||
func NewSingleConnPool(cn *Conn) *SingleConnPool {
|
|
||||||
return &SingleConnPool{
|
|
||||||
cn: cn,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SingleConnPool) NewConn() (*Conn, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SingleConnPool) CloseConn(*Conn) error {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SingleConnPool) Get() (*Conn, error) {
|
|
||||||
return p.cn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SingleConnPool) Put(cn *Conn) {
|
|
||||||
if p.cn != cn {
|
|
||||||
panic("p.cn != cn")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SingleConnPool) Remove(cn *Conn) {
|
|
||||||
if p.cn != cn {
|
|
||||||
panic("p.cn != cn")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SingleConnPool) Len() int {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SingleConnPool) IdleLen() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SingleConnPool) Stats() *Stats {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SingleConnPool) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
64
vendor/github.com/go-redis/redis/internal/singleflight/singleflight.go
generated
vendored
64
vendor/github.com/go-redis/redis/internal/singleflight/singleflight.go
generated
vendored
|
@ -1,64 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2013 Google Inc.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Package singleflight provides a duplicate function call suppression
|
|
||||||
// mechanism.
|
|
||||||
package singleflight
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
// call is an in-flight or completed Do call
|
|
||||||
type call struct {
|
|
||||||
wg sync.WaitGroup
|
|
||||||
val interface{}
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group represents a class of work and forms a namespace in which
|
|
||||||
// units of work can be executed with duplicate suppression.
|
|
||||||
type Group struct {
|
|
||||||
mu sync.Mutex // protects m
|
|
||||||
m map[string]*call // lazily initialized
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do executes and returns the results of the given function, making
|
|
||||||
// sure that only one execution is in-flight for a given key at a
|
|
||||||
// time. If a duplicate comes in, the duplicate caller waits for the
|
|
||||||
// original to complete and receives the same results.
|
|
||||||
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
|
|
||||||
g.mu.Lock()
|
|
||||||
if g.m == nil {
|
|
||||||
g.m = make(map[string]*call)
|
|
||||||
}
|
|
||||||
if c, ok := g.m[key]; ok {
|
|
||||||
g.mu.Unlock()
|
|
||||||
c.wg.Wait()
|
|
||||||
return c.val, c.err
|
|
||||||
}
|
|
||||||
c := new(call)
|
|
||||||
c.wg.Add(1)
|
|
||||||
g.m[key] = c
|
|
||||||
g.mu.Unlock()
|
|
||||||
|
|
||||||
c.val, c.err = fn()
|
|
||||||
c.wg.Done()
|
|
||||||
|
|
||||||
g.mu.Lock()
|
|
||||||
delete(g.m, key)
|
|
||||||
g.mu.Unlock()
|
|
||||||
|
|
||||||
return c.val, c.err
|
|
||||||
}
|
|
29
vendor/github.com/go-redis/redis/internal/util.go
generated
vendored
29
vendor/github.com/go-redis/redis/internal/util.go
generated
vendored
|
@ -1,29 +0,0 @@
|
||||||
package internal
|
|
||||||
|
|
||||||
import "github.com/go-redis/redis/internal/util"
|
|
||||||
|
|
||||||
func ToLower(s string) string {
|
|
||||||
if isLower(s) {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
b := make([]byte, len(s))
|
|
||||||
for i := range b {
|
|
||||||
c := s[i]
|
|
||||||
if c >= 'A' && c <= 'Z' {
|
|
||||||
c += 'a' - 'A'
|
|
||||||
}
|
|
||||||
b[i] = c
|
|
||||||
}
|
|
||||||
return util.BytesToString(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isLower(s string) bool {
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
c := s[i]
|
|
||||||
if c >= 'A' && c <= 'Z' {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
519
vendor/github.com/go-redis/redis/redis.go
generated
vendored
519
vendor/github.com/go-redis/redis/redis.go
generated
vendored
|
@ -1,519 +0,0 @@
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-redis/redis/internal"
|
|
||||||
"github.com/go-redis/redis/internal/pool"
|
|
||||||
"github.com/go-redis/redis/internal/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Nil reply Redis returns when key does not exist.
|
|
||||||
const Nil = proto.Nil
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SetLogger(log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile))
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetLogger(logger *log.Logger) {
|
|
||||||
internal.Logger = logger
|
|
||||||
}
|
|
||||||
|
|
||||||
type baseClient struct {
|
|
||||||
opt *Options
|
|
||||||
connPool pool.Pooler
|
|
||||||
|
|
||||||
process func(Cmder) error
|
|
||||||
processPipeline func([]Cmder) error
|
|
||||||
processTxPipeline func([]Cmder) error
|
|
||||||
|
|
||||||
onClose func() error // hook called when client is closed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) init() {
|
|
||||||
c.process = c.defaultProcess
|
|
||||||
c.processPipeline = c.defaultProcessPipeline
|
|
||||||
c.processTxPipeline = c.defaultProcessTxPipeline
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) String() string {
|
|
||||||
return fmt.Sprintf("Redis<%s db:%d>", c.getAddr(), c.opt.DB)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) newConn() (*pool.Conn, error) {
|
|
||||||
cn, err := c.connPool.NewConn()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cn.InitedAt.IsZero() {
|
|
||||||
if err := c.initConn(cn); err != nil {
|
|
||||||
_ = c.connPool.CloseConn(cn)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) getConn() (*pool.Conn, error) {
|
|
||||||
cn, err := c.connPool.Get()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cn.InitedAt.IsZero() {
|
|
||||||
err := c.initConn(cn)
|
|
||||||
if err != nil {
|
|
||||||
c.connPool.Remove(cn)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) releaseConn(cn *pool.Conn, err error) bool {
|
|
||||||
if internal.IsBadConn(err, false) {
|
|
||||||
c.connPool.Remove(cn)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
c.connPool.Put(cn)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) initConn(cn *pool.Conn) error {
|
|
||||||
cn.InitedAt = time.Now()
|
|
||||||
|
|
||||||
if c.opt.Password == "" &&
|
|
||||||
c.opt.DB == 0 &&
|
|
||||||
!c.opt.readOnly &&
|
|
||||||
c.opt.OnConnect == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
conn := newConn(c.opt, cn)
|
|
||||||
_, err := conn.Pipelined(func(pipe Pipeliner) error {
|
|
||||||
if c.opt.Password != "" {
|
|
||||||
pipe.Auth(c.opt.Password)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.opt.DB > 0 {
|
|
||||||
pipe.Select(c.opt.DB)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.opt.readOnly {
|
|
||||||
pipe.ReadOnly()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.opt.OnConnect != nil {
|
|
||||||
return c.opt.OnConnect(conn)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do creates a Cmd from the args and processes the cmd.
|
|
||||||
func (c *baseClient) Do(args ...interface{}) *Cmd {
|
|
||||||
cmd := NewCmd(args...)
|
|
||||||
c.Process(cmd)
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
// WrapProcess wraps function that processes Redis commands.
|
|
||||||
func (c *baseClient) WrapProcess(
|
|
||||||
fn func(oldProcess func(cmd Cmder) error) func(cmd Cmder) error,
|
|
||||||
) {
|
|
||||||
c.process = fn(c.process)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) Process(cmd Cmder) error {
|
|
||||||
return c.process(cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) defaultProcess(cmd Cmder) error {
|
|
||||||
for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
|
|
||||||
if attempt > 0 {
|
|
||||||
time.Sleep(c.retryBackoff(attempt))
|
|
||||||
}
|
|
||||||
|
|
||||||
cn, err := c.getConn()
|
|
||||||
if err != nil {
|
|
||||||
cmd.setErr(err)
|
|
||||||
if internal.IsRetryableError(err, true) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
|
|
||||||
return writeCmd(wr, cmd)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
c.releaseConn(cn, err)
|
|
||||||
cmd.setErr(err)
|
|
||||||
if internal.IsRetryableError(err, true) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cn.WithReader(c.cmdTimeout(cmd), func(rd *proto.Reader) error {
|
|
||||||
return cmd.readReply(rd)
|
|
||||||
})
|
|
||||||
c.releaseConn(cn, err)
|
|
||||||
if err != nil && internal.IsRetryableError(err, cmd.readTimeout() == nil) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) retryBackoff(attempt int) time.Duration {
|
|
||||||
return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) cmdTimeout(cmd Cmder) time.Duration {
|
|
||||||
if timeout := cmd.readTimeout(); timeout != nil {
|
|
||||||
return readTimeout(*timeout)
|
|
||||||
}
|
|
||||||
return c.opt.ReadTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the client, releasing any open resources.
|
|
||||||
//
|
|
||||||
// It is rare to Close a Client, as the Client is meant to be
|
|
||||||
// long-lived and shared between many goroutines.
|
|
||||||
func (c *baseClient) Close() error {
|
|
||||||
var firstErr error
|
|
||||||
if c.onClose != nil {
|
|
||||||
if err := c.onClose(); err != nil && firstErr == nil {
|
|
||||||
firstErr = err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := c.connPool.Close(); err != nil && firstErr == nil {
|
|
||||||
firstErr = err
|
|
||||||
}
|
|
||||||
return firstErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) getAddr() string {
|
|
||||||
return c.opt.Addr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) WrapProcessPipeline(
|
|
||||||
fn func(oldProcess func([]Cmder) error) func([]Cmder) error,
|
|
||||||
) {
|
|
||||||
c.processPipeline = fn(c.processPipeline)
|
|
||||||
c.processTxPipeline = fn(c.processTxPipeline)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) defaultProcessPipeline(cmds []Cmder) error {
|
|
||||||
return c.generalProcessPipeline(cmds, c.pipelineProcessCmds)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) defaultProcessTxPipeline(cmds []Cmder) error {
|
|
||||||
return c.generalProcessPipeline(cmds, c.txPipelineProcessCmds)
|
|
||||||
}
|
|
||||||
|
|
||||||
type pipelineProcessor func(*pool.Conn, []Cmder) (bool, error)
|
|
||||||
|
|
||||||
func (c *baseClient) generalProcessPipeline(cmds []Cmder, p pipelineProcessor) error {
|
|
||||||
for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
|
|
||||||
if attempt > 0 {
|
|
||||||
time.Sleep(c.retryBackoff(attempt))
|
|
||||||
}
|
|
||||||
|
|
||||||
cn, err := c.getConn()
|
|
||||||
if err != nil {
|
|
||||||
setCmdsErr(cmds, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
canRetry, err := p(cn, cmds)
|
|
||||||
|
|
||||||
if err == nil || internal.IsRedisError(err) {
|
|
||||||
c.connPool.Put(cn)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
c.connPool.Remove(cn)
|
|
||||||
|
|
||||||
if !canRetry || !internal.IsRetryableError(err, true) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cmdsFirstErr(cmds)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) pipelineProcessCmds(cn *pool.Conn, cmds []Cmder) (bool, error) {
|
|
||||||
err := cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
|
|
||||||
return writeCmd(wr, cmds...)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
setCmdsErr(cmds, err)
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cn.WithReader(c.opt.ReadTimeout, func(rd *proto.Reader) error {
|
|
||||||
return pipelineReadCmds(rd, cmds)
|
|
||||||
})
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func pipelineReadCmds(rd *proto.Reader, cmds []Cmder) error {
|
|
||||||
for _, cmd := range cmds {
|
|
||||||
err := cmd.readReply(rd)
|
|
||||||
if err != nil && !internal.IsRedisError(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *baseClient) txPipelineProcessCmds(cn *pool.Conn, cmds []Cmder) (bool, error) {
|
|
||||||
err := cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
|
|
||||||
return txPipelineWriteMulti(wr, cmds)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
setCmdsErr(cmds, err)
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cn.WithReader(c.opt.ReadTimeout, func(rd *proto.Reader) error {
|
|
||||||
err := txPipelineReadQueued(rd, cmds)
|
|
||||||
if err != nil {
|
|
||||||
setCmdsErr(cmds, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return pipelineReadCmds(rd, cmds)
|
|
||||||
})
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func txPipelineWriteMulti(wr *proto.Writer, cmds []Cmder) error {
|
|
||||||
multiExec := make([]Cmder, 0, len(cmds)+2)
|
|
||||||
multiExec = append(multiExec, NewStatusCmd("MULTI"))
|
|
||||||
multiExec = append(multiExec, cmds...)
|
|
||||||
multiExec = append(multiExec, NewSliceCmd("EXEC"))
|
|
||||||
return writeCmd(wr, multiExec...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func txPipelineReadQueued(rd *proto.Reader, cmds []Cmder) error {
|
|
||||||
// Parse queued replies.
|
|
||||||
var statusCmd StatusCmd
|
|
||||||
err := statusCmd.readReply(rd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _ = range cmds {
|
|
||||||
err = statusCmd.readReply(rd)
|
|
||||||
if err != nil && !internal.IsRedisError(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse number of replies.
|
|
||||||
line, err := rd.ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
if err == Nil {
|
|
||||||
err = TxFailedErr
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch line[0] {
|
|
||||||
case proto.ErrorReply:
|
|
||||||
return proto.ParseErrorReply(line)
|
|
||||||
case proto.ArrayReply:
|
|
||||||
// ok
|
|
||||||
default:
|
|
||||||
err := fmt.Errorf("redis: expected '*', but got line %q", line)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Client is a Redis client representing a pool of zero or more
|
|
||||||
// underlying connections. It's safe for concurrent use by multiple
|
|
||||||
// goroutines.
|
|
||||||
type Client struct {
|
|
||||||
baseClient
|
|
||||||
cmdable
|
|
||||||
|
|
||||||
ctx context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClient returns a client to the Redis Server specified by Options.
|
|
||||||
func NewClient(opt *Options) *Client {
|
|
||||||
opt.init()
|
|
||||||
|
|
||||||
c := Client{
|
|
||||||
baseClient: baseClient{
|
|
||||||
opt: opt,
|
|
||||||
connPool: newConnPool(opt),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
c.baseClient.init()
|
|
||||||
c.init()
|
|
||||||
|
|
||||||
return &c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) init() {
|
|
||||||
c.cmdable.setProcessor(c.Process)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) Context() context.Context {
|
|
||||||
if c.ctx != nil {
|
|
||||||
return c.ctx
|
|
||||||
}
|
|
||||||
return context.Background()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) WithContext(ctx context.Context) *Client {
|
|
||||||
if ctx == nil {
|
|
||||||
panic("nil context")
|
|
||||||
}
|
|
||||||
c2 := c.copy()
|
|
||||||
c2.ctx = ctx
|
|
||||||
return c2
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) copy() *Client {
|
|
||||||
cp := *c
|
|
||||||
cp.init()
|
|
||||||
return &cp
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options returns read-only Options that were used to create the client.
|
|
||||||
func (c *Client) Options() *Options {
|
|
||||||
return c.opt
|
|
||||||
}
|
|
||||||
|
|
||||||
type PoolStats pool.Stats
|
|
||||||
|
|
||||||
// PoolStats returns connection pool stats.
|
|
||||||
func (c *Client) PoolStats() *PoolStats {
|
|
||||||
stats := c.connPool.Stats()
|
|
||||||
return (*PoolStats)(stats)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
|
||||||
return c.Pipeline().Pipelined(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) Pipeline() Pipeliner {
|
|
||||||
pipe := Pipeline{
|
|
||||||
exec: c.processPipeline,
|
|
||||||
}
|
|
||||||
pipe.statefulCmdable.setProcessor(pipe.Process)
|
|
||||||
return &pipe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
|
||||||
return c.TxPipeline().Pipelined(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.
|
|
||||||
func (c *Client) TxPipeline() Pipeliner {
|
|
||||||
pipe := Pipeline{
|
|
||||||
exec: c.processTxPipeline,
|
|
||||||
}
|
|
||||||
pipe.statefulCmdable.setProcessor(pipe.Process)
|
|
||||||
return &pipe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) pubSub() *PubSub {
|
|
||||||
pubsub := &PubSub{
|
|
||||||
opt: c.opt,
|
|
||||||
|
|
||||||
newConn: func(channels []string) (*pool.Conn, error) {
|
|
||||||
return c.newConn()
|
|
||||||
},
|
|
||||||
closeConn: c.connPool.CloseConn,
|
|
||||||
}
|
|
||||||
pubsub.init()
|
|
||||||
return pubsub
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe subscribes the client to the specified channels.
|
|
||||||
// Channels can be omitted to create empty subscription.
|
|
||||||
func (c *Client) Subscribe(channels ...string) *PubSub {
|
|
||||||
pubsub := c.pubSub()
|
|
||||||
if len(channels) > 0 {
|
|
||||||
_ = pubsub.Subscribe(channels...)
|
|
||||||
}
|
|
||||||
return pubsub
|
|
||||||
}
|
|
||||||
|
|
||||||
// PSubscribe subscribes the client to the given patterns.
|
|
||||||
// Patterns can be omitted to create empty subscription.
|
|
||||||
func (c *Client) PSubscribe(channels ...string) *PubSub {
|
|
||||||
pubsub := c.pubSub()
|
|
||||||
if len(channels) > 0 {
|
|
||||||
_ = pubsub.PSubscribe(channels...)
|
|
||||||
}
|
|
||||||
return pubsub
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Conn is like Client, but its pool contains single connection.
|
|
||||||
type Conn struct {
|
|
||||||
baseClient
|
|
||||||
statefulCmdable
|
|
||||||
}
|
|
||||||
|
|
||||||
func newConn(opt *Options, cn *pool.Conn) *Conn {
|
|
||||||
c := Conn{
|
|
||||||
baseClient: baseClient{
|
|
||||||
opt: opt,
|
|
||||||
connPool: pool.NewSingleConnPool(cn),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
c.baseClient.init()
|
|
||||||
c.statefulCmdable.setProcessor(c.Process)
|
|
||||||
return &c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
|
||||||
return c.Pipeline().Pipelined(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Pipeline() Pipeliner {
|
|
||||||
pipe := Pipeline{
|
|
||||||
exec: c.processPipeline,
|
|
||||||
}
|
|
||||||
pipe.statefulCmdable.setProcessor(pipe.Process)
|
|
||||||
return &pipe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
|
||||||
return c.TxPipeline().Pipelined(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.
|
|
||||||
func (c *Conn) TxPipeline() Pipeliner {
|
|
||||||
pipe := Pipeline{
|
|
||||||
exec: c.processTxPipeline,
|
|
||||||
}
|
|
||||||
pipe.statefulCmdable.setProcessor(pipe.Process)
|
|
||||||
return &pipe
|
|
||||||
}
|
|
347
vendor/github.com/go-redis/redis/sentinel.go
generated
vendored
347
vendor/github.com/go-redis/redis/sentinel.go
generated
vendored
|
@ -1,347 +0,0 @@
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-redis/redis/internal"
|
|
||||||
"github.com/go-redis/redis/internal/pool"
|
|
||||||
)
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// FailoverOptions are used to configure a failover client and should
|
|
||||||
// be passed to NewFailoverClient.
|
|
||||||
type FailoverOptions struct {
|
|
||||||
// The master name.
|
|
||||||
MasterName string
|
|
||||||
// A seed list of host:port addresses of sentinel nodes.
|
|
||||||
SentinelAddrs []string
|
|
||||||
|
|
||||||
// Following options are copied from Options struct.
|
|
||||||
|
|
||||||
OnConnect func(*Conn) error
|
|
||||||
|
|
||||||
Password string
|
|
||||||
DB int
|
|
||||||
|
|
||||||
MaxRetries int
|
|
||||||
MinRetryBackoff time.Duration
|
|
||||||
MaxRetryBackoff time.Duration
|
|
||||||
|
|
||||||
DialTimeout time.Duration
|
|
||||||
ReadTimeout time.Duration
|
|
||||||
WriteTimeout time.Duration
|
|
||||||
|
|
||||||
PoolSize int
|
|
||||||
MinIdleConns int
|
|
||||||
MaxConnAge time.Duration
|
|
||||||
PoolTimeout time.Duration
|
|
||||||
IdleTimeout time.Duration
|
|
||||||
IdleCheckFrequency time.Duration
|
|
||||||
|
|
||||||
TLSConfig *tls.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func (opt *FailoverOptions) options() *Options {
|
|
||||||
return &Options{
|
|
||||||
Addr: "FailoverClient",
|
|
||||||
|
|
||||||
OnConnect: opt.OnConnect,
|
|
||||||
|
|
||||||
DB: opt.DB,
|
|
||||||
Password: opt.Password,
|
|
||||||
|
|
||||||
MaxRetries: opt.MaxRetries,
|
|
||||||
|
|
||||||
DialTimeout: opt.DialTimeout,
|
|
||||||
ReadTimeout: opt.ReadTimeout,
|
|
||||||
WriteTimeout: opt.WriteTimeout,
|
|
||||||
|
|
||||||
PoolSize: opt.PoolSize,
|
|
||||||
PoolTimeout: opt.PoolTimeout,
|
|
||||||
IdleTimeout: opt.IdleTimeout,
|
|
||||||
IdleCheckFrequency: opt.IdleCheckFrequency,
|
|
||||||
|
|
||||||
TLSConfig: opt.TLSConfig,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFailoverClient returns a Redis client that uses Redis Sentinel
|
|
||||||
// for automatic failover. It's safe for concurrent use by multiple
|
|
||||||
// goroutines.
|
|
||||||
func NewFailoverClient(failoverOpt *FailoverOptions) *Client {
|
|
||||||
opt := failoverOpt.options()
|
|
||||||
opt.init()
|
|
||||||
|
|
||||||
failover := &sentinelFailover{
|
|
||||||
masterName: failoverOpt.MasterName,
|
|
||||||
sentinelAddrs: failoverOpt.SentinelAddrs,
|
|
||||||
|
|
||||||
opt: opt,
|
|
||||||
}
|
|
||||||
|
|
||||||
c := Client{
|
|
||||||
baseClient: baseClient{
|
|
||||||
opt: opt,
|
|
||||||
connPool: failover.Pool(),
|
|
||||||
|
|
||||||
onClose: func() error {
|
|
||||||
return failover.Close()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
c.baseClient.init()
|
|
||||||
c.cmdable.setProcessor(c.Process)
|
|
||||||
|
|
||||||
return &c
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
type SentinelClient struct {
|
|
||||||
baseClient
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSentinelClient(opt *Options) *SentinelClient {
|
|
||||||
opt.init()
|
|
||||||
c := &SentinelClient{
|
|
||||||
baseClient: baseClient{
|
|
||||||
opt: opt,
|
|
||||||
connPool: newConnPool(opt),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
c.baseClient.init()
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *SentinelClient) PubSub() *PubSub {
|
|
||||||
pubsub := &PubSub{
|
|
||||||
opt: c.opt,
|
|
||||||
|
|
||||||
newConn: func(channels []string) (*pool.Conn, error) {
|
|
||||||
return c.newConn()
|
|
||||||
},
|
|
||||||
closeConn: c.connPool.CloseConn,
|
|
||||||
}
|
|
||||||
pubsub.init()
|
|
||||||
return pubsub
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *SentinelClient) GetMasterAddrByName(name string) *StringSliceCmd {
|
|
||||||
cmd := NewStringSliceCmd("SENTINEL", "get-master-addr-by-name", name)
|
|
||||||
c.Process(cmd)
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *SentinelClient) Sentinels(name string) *SliceCmd {
|
|
||||||
cmd := NewSliceCmd("SENTINEL", "sentinels", name)
|
|
||||||
c.Process(cmd)
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
type sentinelFailover struct {
|
|
||||||
sentinelAddrs []string
|
|
||||||
|
|
||||||
opt *Options
|
|
||||||
|
|
||||||
pool *pool.ConnPool
|
|
||||||
poolOnce sync.Once
|
|
||||||
|
|
||||||
mu sync.RWMutex
|
|
||||||
masterName string
|
|
||||||
_masterAddr string
|
|
||||||
sentinel *SentinelClient
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *sentinelFailover) Close() error {
|
|
||||||
return d.resetSentinel()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *sentinelFailover) Pool() *pool.ConnPool {
|
|
||||||
d.poolOnce.Do(func() {
|
|
||||||
d.opt.Dialer = d.dial
|
|
||||||
d.pool = newConnPool(d.opt)
|
|
||||||
})
|
|
||||||
return d.pool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *sentinelFailover) dial() (net.Conn, error) {
|
|
||||||
addr, err := d.MasterAddr()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return net.DialTimeout("tcp", addr, d.opt.DialTimeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *sentinelFailover) MasterAddr() (string, error) {
|
|
||||||
d.mu.Lock()
|
|
||||||
defer d.mu.Unlock()
|
|
||||||
|
|
||||||
addr, err := d.masterAddr()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
d._switchMaster(addr)
|
|
||||||
|
|
||||||
return addr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *sentinelFailover) masterAddr() (string, error) {
|
|
||||||
// Try last working sentinel.
|
|
||||||
if d.sentinel != nil {
|
|
||||||
addr, err := d.sentinel.GetMasterAddrByName(d.masterName).Result()
|
|
||||||
if err == nil {
|
|
||||||
addr := net.JoinHostPort(addr[0], addr[1])
|
|
||||||
return addr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
internal.Logf("sentinel: GetMasterAddrByName name=%q failed: %s",
|
|
||||||
d.masterName, err)
|
|
||||||
d._resetSentinel()
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, sentinelAddr := range d.sentinelAddrs {
|
|
||||||
sentinel := NewSentinelClient(&Options{
|
|
||||||
Addr: sentinelAddr,
|
|
||||||
|
|
||||||
DialTimeout: d.opt.DialTimeout,
|
|
||||||
ReadTimeout: d.opt.ReadTimeout,
|
|
||||||
WriteTimeout: d.opt.WriteTimeout,
|
|
||||||
|
|
||||||
PoolSize: d.opt.PoolSize,
|
|
||||||
PoolTimeout: d.opt.PoolTimeout,
|
|
||||||
IdleTimeout: d.opt.IdleTimeout,
|
|
||||||
})
|
|
||||||
|
|
||||||
masterAddr, err := sentinel.GetMasterAddrByName(d.masterName).Result()
|
|
||||||
if err != nil {
|
|
||||||
internal.Logf("sentinel: GetMasterAddrByName master=%q failed: %s",
|
|
||||||
d.masterName, err)
|
|
||||||
sentinel.Close()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push working sentinel to the top.
|
|
||||||
d.sentinelAddrs[0], d.sentinelAddrs[i] = d.sentinelAddrs[i], d.sentinelAddrs[0]
|
|
||||||
d.setSentinel(sentinel)
|
|
||||||
|
|
||||||
addr := net.JoinHostPort(masterAddr[0], masterAddr[1])
|
|
||||||
return addr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", errors.New("redis: all sentinels are unreachable")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *sentinelFailover) switchMaster(addr string) {
|
|
||||||
c.mu.Lock()
|
|
||||||
c._switchMaster(addr)
|
|
||||||
c.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *sentinelFailover) _switchMaster(addr string) {
|
|
||||||
if c._masterAddr == addr {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
internal.Logf("sentinel: new master=%q addr=%q",
|
|
||||||
c.masterName, addr)
|
|
||||||
_ = c.Pool().Filter(func(cn *pool.Conn) bool {
|
|
||||||
return cn.RemoteAddr().String() != addr
|
|
||||||
})
|
|
||||||
c._masterAddr = addr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *sentinelFailover) setSentinel(sentinel *SentinelClient) {
|
|
||||||
d.discoverSentinels(sentinel)
|
|
||||||
d.sentinel = sentinel
|
|
||||||
go d.listen(sentinel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *sentinelFailover) resetSentinel() error {
|
|
||||||
var err error
|
|
||||||
d.mu.Lock()
|
|
||||||
if d.sentinel != nil {
|
|
||||||
err = d._resetSentinel()
|
|
||||||
}
|
|
||||||
d.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *sentinelFailover) _resetSentinel() error {
|
|
||||||
err := d.sentinel.Close()
|
|
||||||
d.sentinel = nil
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *sentinelFailover) discoverSentinels(sentinel *SentinelClient) {
|
|
||||||
sentinels, err := sentinel.Sentinels(d.masterName).Result()
|
|
||||||
if err != nil {
|
|
||||||
internal.Logf("sentinel: Sentinels master=%q failed: %s", d.masterName, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, sentinel := range sentinels {
|
|
||||||
vals := sentinel.([]interface{})
|
|
||||||
for i := 0; i < len(vals); i += 2 {
|
|
||||||
key := vals[i].(string)
|
|
||||||
if key == "name" {
|
|
||||||
sentinelAddr := vals[i+1].(string)
|
|
||||||
if !contains(d.sentinelAddrs, sentinelAddr) {
|
|
||||||
internal.Logf(
|
|
||||||
"sentinel: discovered new sentinel=%q for master=%q",
|
|
||||||
sentinelAddr, d.masterName,
|
|
||||||
)
|
|
||||||
d.sentinelAddrs = append(d.sentinelAddrs, sentinelAddr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *sentinelFailover) listen(sentinel *SentinelClient) {
|
|
||||||
pubsub := sentinel.PubSub()
|
|
||||||
defer pubsub.Close()
|
|
||||||
|
|
||||||
err := pubsub.Subscribe("+switch-master")
|
|
||||||
if err != nil {
|
|
||||||
internal.Logf("sentinel: Subscribe failed: %s", err)
|
|
||||||
d.resetSentinel()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
msg, err := pubsub.ReceiveMessage()
|
|
||||||
if err != nil {
|
|
||||||
if err == pool.ErrClosed {
|
|
||||||
d.resetSentinel()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
internal.Logf("sentinel: ReceiveMessage failed: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch msg.Channel {
|
|
||||||
case "+switch-master":
|
|
||||||
parts := strings.Split(msg.Payload, " ")
|
|
||||||
if parts[0] != d.masterName {
|
|
||||||
internal.Logf("sentinel: ignore addr for master=%q", parts[0])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
addr := net.JoinHostPort(parts[3], parts[4])
|
|
||||||
d.switchMaster(addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func contains(slice []string, str string) bool {
|
|
||||||
for _, s := range slice {
|
|
||||||
if s == str {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
15
vendor/github.com/go-redis/redis/v7/.golangci.yml
generated
vendored
Normal file
15
vendor/github.com/go-redis/redis/v7/.golangci.yml
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
run:
|
||||||
|
concurrency: 8
|
||||||
|
deadline: 5m
|
||||||
|
tests: false
|
||||||
|
linters:
|
||||||
|
enable-all: true
|
||||||
|
disable:
|
||||||
|
- funlen
|
||||||
|
- gochecknoglobals
|
||||||
|
- gocognit
|
||||||
|
- goconst
|
||||||
|
- godox
|
||||||
|
- gosec
|
||||||
|
- maligned
|
||||||
|
- wsl
|
24
vendor/github.com/go-redis/redis/v7/.travis.yml
generated
vendored
Normal file
24
vendor/github.com/go-redis/redis/v7/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
dist: xenial
|
||||||
|
sudo: false
|
||||||
|
language: go
|
||||||
|
|
||||||
|
services:
|
||||||
|
- redis-server
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.11.x
|
||||||
|
- 1.12.x
|
||||||
|
- 1.13.x
|
||||||
|
- tip
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
|
|
||||||
|
go_import_path: github.com/go-redis/redis
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.21.0
|
42
vendor/github.com/go-redis/redis/v7/CHANGELOG.md
generated
vendored
Normal file
42
vendor/github.com/go-redis/redis/v7/CHANGELOG.md
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## v7.2
|
||||||
|
|
||||||
|
- Existing `HMSet` is renamed to `HSet` and old deprecated `HMSet` is restored for Redis 3 users.
|
||||||
|
|
||||||
|
## v7
|
||||||
|
|
||||||
|
- *Important*. Tx.Pipeline now returns a non-transactional pipeline. Use Tx.TxPipeline for a transactional pipeline.
|
||||||
|
- WrapProcess is replaced with more convenient AddHook that has access to context.Context.
|
||||||
|
- WithContext now can not be used to create a shallow copy of the client.
|
||||||
|
- New methods ProcessContext, DoContext, and ExecContext.
|
||||||
|
- Client respects Context.Deadline when setting net.Conn deadline.
|
||||||
|
- Client listens on Context.Done while waiting for a connection from the pool and returns an error when context context is cancelled.
|
||||||
|
- Add PubSub.ChannelWithSubscriptions that sends `*Subscription` in addition to `*Message` to allow detecting reconnections.
|
||||||
|
- `time.Time` is now marshalled in RFC3339 format. `rdb.Get("foo").Time()` helper is added to parse the time.
|
||||||
|
- `SetLimiter` is removed and added `Options.Limiter` instead.
|
||||||
|
- `HMSet` is deprecated as of Redis v4.
|
||||||
|
|
||||||
|
## v6.15
|
||||||
|
|
||||||
|
- Cluster and Ring pipelines process commands for each node in its own goroutine.
|
||||||
|
|
||||||
|
## 6.14
|
||||||
|
|
||||||
|
- Added Options.MinIdleConns.
|
||||||
|
- Added Options.MaxConnAge.
|
||||||
|
- PoolStats.FreeConns is renamed to PoolStats.IdleConns.
|
||||||
|
- Add Client.Do to simplify creating custom commands.
|
||||||
|
- Add Cmd.String, Cmd.Int, Cmd.Int64, Cmd.Uint64, Cmd.Float64, and Cmd.Bool helpers.
|
||||||
|
- Lower memory usage.
|
||||||
|
|
||||||
|
## v6.13
|
||||||
|
|
||||||
|
- Ring got new options called `HashReplicas` and `Hash`. It is recommended to set `HashReplicas = 1000` for better keys distribution between shards.
|
||||||
|
- Cluster client was optimized to use much less memory when reloading cluster state.
|
||||||
|
- PubSub.ReceiveMessage is re-worked to not use ReceiveTimeout so it does not lose data when timeout occurres. In most cases it is recommended to use PubSub.Channel instead.
|
||||||
|
- Dialer.KeepAlive is set to 5 minutes by default.
|
||||||
|
|
||||||
|
## v6.12
|
||||||
|
|
||||||
|
- ClusterClient got new option called `ClusterSlots` which allows to build cluster of normal Redis Servers that don't have cluster mode enabled. See https://godoc.org/github.com/go-redis/redis#example-NewClusterClient--ManualSetup
|
|
@ -1,8 +1,9 @@
|
||||||
all: testdeps
|
all: testdeps
|
||||||
go test ./...
|
go test ./...
|
||||||
go test ./... -short -race
|
go test ./... -short -race
|
||||||
|
go test ./... -run=NONE -bench=. -benchmem
|
||||||
env GOOS=linux GOARCH=386 go test ./...
|
env GOOS=linux GOARCH=386 go test ./...
|
||||||
go vet
|
golangci-lint run
|
||||||
|
|
||||||
testdeps: testdata/redis/src/redis-server
|
testdeps: testdata/redis/src/redis-server
|
||||||
|
|
||||||
|
@ -13,8 +14,7 @@ bench: testdeps
|
||||||
|
|
||||||
testdata/redis:
|
testdata/redis:
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
wget -qO- https://github.com/antirez/redis/archive/unstable.tar.gz | tar xvz --strip-components=1 -C $@
|
wget -qO- http://download.redis.io/releases/redis-5.0.7.tar.gz | tar xvz --strip-components=1 -C $@
|
||||||
|
|
||||||
testdata/redis/src/redis-server: testdata/redis
|
testdata/redis/src/redis-server: testdata/redis
|
||||||
sed -i.bak 's/libjemalloc.a/libjemalloc.a -lrt/g' $</src/Makefile
|
|
||||||
cd $< && make all
|
cd $< && make all
|
|
@ -9,7 +9,7 @@ Supports:
|
||||||
- Redis 3 commands except QUIT, MONITOR, SLOWLOG and SYNC.
|
- Redis 3 commands except QUIT, MONITOR, SLOWLOG and SYNC.
|
||||||
- Automatic connection pooling with [circuit breaker](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) support.
|
- Automatic connection pooling with [circuit breaker](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) support.
|
||||||
- [Pub/Sub](https://godoc.org/github.com/go-redis/redis#PubSub).
|
- [Pub/Sub](https://godoc.org/github.com/go-redis/redis#PubSub).
|
||||||
- [Transactions](https://godoc.org/github.com/go-redis/redis#Multi).
|
- [Transactions](https://godoc.org/github.com/go-redis/redis#example-Client-TxPipeline).
|
||||||
- [Pipeline](https://godoc.org/github.com/go-redis/redis#example-Client-Pipeline) and [TxPipeline](https://godoc.org/github.com/go-redis/redis#example-Client-TxPipeline).
|
- [Pipeline](https://godoc.org/github.com/go-redis/redis#example-Client-Pipeline) and [TxPipeline](https://godoc.org/github.com/go-redis/redis#example-Client-TxPipeline).
|
||||||
- [Scripting](https://godoc.org/github.com/go-redis/redis#Script).
|
- [Scripting](https://godoc.org/github.com/go-redis/redis#Script).
|
||||||
- [Timeouts](https://godoc.org/github.com/go-redis/redis#Options).
|
- [Timeouts](https://godoc.org/github.com/go-redis/redis#Options).
|
||||||
|
@ -20,23 +20,24 @@ Supports:
|
||||||
- [Instrumentation](https://godoc.org/github.com/go-redis/redis#ex-package--Instrumentation).
|
- [Instrumentation](https://godoc.org/github.com/go-redis/redis#ex-package--Instrumentation).
|
||||||
- [Cache friendly](https://github.com/go-redis/cache).
|
- [Cache friendly](https://github.com/go-redis/cache).
|
||||||
- [Rate limiting](https://github.com/go-redis/redis_rate).
|
- [Rate limiting](https://github.com/go-redis/redis_rate).
|
||||||
- [Distributed Locks](https://github.com/bsm/redis-lock).
|
- [Distributed Locks](https://github.com/bsm/redislock).
|
||||||
|
|
||||||
API docs: https://godoc.org/github.com/go-redis/redis.
|
API docs: https://godoc.org/github.com/go-redis/redis.
|
||||||
Examples: https://godoc.org/github.com/go-redis/redis#pkg-examples.
|
Examples: https://godoc.org/github.com/go-redis/redis#pkg-examples.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Install:
|
go-redis requires a Go version with [Modules](https://github.com/golang/go/wiki/Modules) support and uses import versioning. So please make sure to initialize a Go module before installing go-redis:
|
||||||
|
|
||||||
``` shell
|
``` shell
|
||||||
go get -u github.com/go-redis/redis
|
go mod init github.com/my/repo
|
||||||
|
go get github.com/go-redis/redis/v7
|
||||||
```
|
```
|
||||||
|
|
||||||
Import:
|
Import:
|
||||||
|
|
||||||
``` go
|
``` go
|
||||||
import "github.com/go-redis/redis"
|
import "github.com/go-redis/redis/v7"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
@ -55,6 +56,11 @@ func ExampleNewClient() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleClient() {
|
func ExampleClient() {
|
||||||
|
client := redis.NewClient(&redis.Options{
|
||||||
|
Addr: "localhost:6379",
|
||||||
|
Password: "", // no password set
|
||||||
|
DB: 0, // use default DB
|
||||||
|
})
|
||||||
err := client.Set("key", "value", 0).Err()
|
err := client.Set("key", "value", 0).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -92,7 +98,7 @@ Some corner cases:
|
||||||
set, err := client.SetNX("key", "value", 10*time.Second).Result()
|
set, err := client.SetNX("key", "value", 10*time.Second).Result()
|
||||||
|
|
||||||
// SORT list LIMIT 0 2 ASC
|
// SORT list LIMIT 0 2 ASC
|
||||||
vals, err := client.Sort("list", redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result()
|
vals, err := client.Sort("list", &redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result()
|
||||||
|
|
||||||
// ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2
|
// ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2
|
||||||
vals, err := client.ZRangeByScoreWithScores("zset", redis.ZRangeBy{
|
vals, err := client.ZRangeByScoreWithScores("zset", redis.ZRangeBy{
|
||||||
|
@ -107,40 +113,13 @@ vals, err := client.ZInterStore("out", redis.ZStore{Weights: []int64{2, 3}}, "zs
|
||||||
|
|
||||||
// EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello"
|
// EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello"
|
||||||
vals, err := client.Eval("return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result()
|
vals, err := client.Eval("return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result()
|
||||||
```
|
|
||||||
|
|
||||||
## Benchmark
|
// custom command
|
||||||
|
res, err := client.Do("set", "key", "value").Result()
|
||||||
go-redis vs redigo:
|
|
||||||
|
|
||||||
```
|
|
||||||
BenchmarkSetGoRedis10Conns64Bytes-4 200000 7621 ns/op 210 B/op 6 allocs/op
|
|
||||||
BenchmarkSetGoRedis100Conns64Bytes-4 200000 7554 ns/op 210 B/op 6 allocs/op
|
|
||||||
BenchmarkSetGoRedis10Conns1KB-4 200000 7697 ns/op 210 B/op 6 allocs/op
|
|
||||||
BenchmarkSetGoRedis100Conns1KB-4 200000 7688 ns/op 210 B/op 6 allocs/op
|
|
||||||
BenchmarkSetGoRedis10Conns10KB-4 200000 9214 ns/op 210 B/op 6 allocs/op
|
|
||||||
BenchmarkSetGoRedis100Conns10KB-4 200000 9181 ns/op 210 B/op 6 allocs/op
|
|
||||||
BenchmarkSetGoRedis10Conns1MB-4 2000 583242 ns/op 2337 B/op 6 allocs/op
|
|
||||||
BenchmarkSetGoRedis100Conns1MB-4 2000 583089 ns/op 2338 B/op 6 allocs/op
|
|
||||||
BenchmarkSetRedigo10Conns64Bytes-4 200000 7576 ns/op 208 B/op 7 allocs/op
|
|
||||||
BenchmarkSetRedigo100Conns64Bytes-4 200000 7782 ns/op 208 B/op 7 allocs/op
|
|
||||||
BenchmarkSetRedigo10Conns1KB-4 200000 7958 ns/op 208 B/op 7 allocs/op
|
|
||||||
BenchmarkSetRedigo100Conns1KB-4 200000 7725 ns/op 208 B/op 7 allocs/op
|
|
||||||
BenchmarkSetRedigo10Conns10KB-4 100000 18442 ns/op 208 B/op 7 allocs/op
|
|
||||||
BenchmarkSetRedigo100Conns10KB-4 100000 18818 ns/op 208 B/op 7 allocs/op
|
|
||||||
BenchmarkSetRedigo10Conns1MB-4 2000 668829 ns/op 226 B/op 7 allocs/op
|
|
||||||
BenchmarkSetRedigo100Conns1MB-4 2000 679542 ns/op 226 B/op 7 allocs/op
|
|
||||||
```
|
|
||||||
|
|
||||||
Redis Cluster:
|
|
||||||
|
|
||||||
```
|
|
||||||
BenchmarkRedisPing-4 200000 6983 ns/op 116 B/op 4 allocs/op
|
|
||||||
BenchmarkRedisClusterPing-4 100000 11535 ns/op 117 B/op 4 allocs/op
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [Golang PostgreSQL ORM](https://github.com/go-pg/pg)
|
- [Golang PostgreSQL ORM](https://github.com/go-pg/pg)
|
||||||
- [Golang msgpack](https://github.com/vmihailenco/msgpack)
|
- [Golang msgpack](https://github.com/vmihailenco/msgpack)
|
||||||
- [Golang message task queue](https://github.com/go-msgqueue/msgqueue)
|
- [Golang message task queue](https://github.com/vmihailenco/taskq)
|
968
vendor/github.com/go-redis/redis/cluster.go → vendor/github.com/go-redis/redis/v7/cluster.go
generated
vendored
968
vendor/github.com/go-redis/redis/cluster.go → vendor/github.com/go-redis/redis/v7/cluster.go
generated
vendored
File diff suppressed because it is too large
Load diff
|
@ -14,7 +14,7 @@ func (c *ClusterClient) DBSize() *IntCmd {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
cmd.val = size
|
cmd.val = size
|
809
vendor/github.com/go-redis/redis/command.go → vendor/github.com/go-redis/redis/v7/command.go
generated
vendored
809
vendor/github.com/go-redis/redis/command.go → vendor/github.com/go-redis/redis/v7/command.go
generated
vendored
File diff suppressed because it is too large
Load diff
1290
vendor/github.com/go-redis/redis/commands.go → vendor/github.com/go-redis/redis/v7/commands.go
generated
vendored
1290
vendor/github.com/go-redis/redis/commands.go → vendor/github.com/go-redis/redis/v7/commands.go
generated
vendored
File diff suppressed because it is too large
Load diff
0
vendor/github.com/go-redis/redis/doc.go → vendor/github.com/go-redis/redis/v7/doc.go
generated
vendored
0
vendor/github.com/go-redis/redis/doc.go → vendor/github.com/go-redis/redis/v7/doc.go
generated
vendored
|
@ -1,15 +1,19 @@
|
||||||
package internal
|
package redis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-redis/redis/internal/proto"
|
"github.com/go-redis/redis/v7/internal/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IsRetryableError(err error, retryTimeout bool) bool {
|
func isRetryableError(err error, retryTimeout bool) bool {
|
||||||
if err == io.EOF {
|
switch err {
|
||||||
|
case nil, context.Canceled, context.DeadlineExceeded:
|
||||||
|
return false
|
||||||
|
case io.EOF:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if netErr, ok := err.(net.Error); ok {
|
if netErr, ok := err.(net.Error); ok {
|
||||||
|
@ -18,6 +22,7 @@ func IsRetryableError(err error, retryTimeout bool) bool {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
s := err.Error()
|
s := err.Error()
|
||||||
if s == "ERR max number of clients reached" {
|
if s == "ERR max number of clients reached" {
|
||||||
return true
|
return true
|
||||||
|
@ -34,17 +39,19 @@ func IsRetryableError(err error, retryTimeout bool) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsRedisError(err error) bool {
|
func isRedisError(err error) bool {
|
||||||
_, ok := err.(proto.RedisError)
|
_, ok := err.(proto.RedisError)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsBadConn(err error, allowTimeout bool) bool {
|
func isBadConn(err error, allowTimeout bool) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if IsRedisError(err) {
|
if isRedisError(err) {
|
||||||
return strings.HasPrefix(err.Error(), "READONLY ")
|
// Close connections in read only state in case domain addr is used
|
||||||
|
// and domain resolves to a different Redis Server. See #790.
|
||||||
|
return isReadOnlyError(err)
|
||||||
}
|
}
|
||||||
if allowTimeout {
|
if allowTimeout {
|
||||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||||
|
@ -54,17 +61,18 @@ func IsBadConn(err error, allowTimeout bool) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsMovedError(err error) (moved bool, ask bool, addr string) {
|
func isMovedError(err error) (moved bool, ask bool, addr string) {
|
||||||
if !IsRedisError(err) {
|
if !isRedisError(err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s := err.Error()
|
s := err.Error()
|
||||||
if strings.HasPrefix(s, "MOVED ") {
|
switch {
|
||||||
|
case strings.HasPrefix(s, "MOVED "):
|
||||||
moved = true
|
moved = true
|
||||||
} else if strings.HasPrefix(s, "ASK ") {
|
case strings.HasPrefix(s, "ASK "):
|
||||||
ask = true
|
ask = true
|
||||||
} else {
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,6 +84,10 @@ func IsMovedError(err error) (moved bool, ask bool, addr string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsLoadingError(err error) bool {
|
func isLoadingError(err error) bool {
|
||||||
return strings.HasPrefix(err.Error(), "LOADING ")
|
return strings.HasPrefix(err.Error(), "LOADING ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isReadOnlyError(err error) bool {
|
||||||
|
return strings.HasPrefix(err.Error(), "READONLY ")
|
||||||
|
}
|
15
vendor/github.com/go-redis/redis/v7/go.mod
generated
vendored
Normal file
15
vendor/github.com/go-redis/redis/v7/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
module github.com/go-redis/redis/v7
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/golang/protobuf v1.3.2 // indirect
|
||||||
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
|
github.com/onsi/ginkgo v1.10.1
|
||||||
|
github.com/onsi/gomega v1.7.0
|
||||||
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
|
||||||
|
golang.org/x/text v0.3.2 // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.2.4 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
go 1.11
|
47
vendor/github.com/go-redis/redis/v7/go.sum
generated
vendored
Normal file
47
vendor/github.com/go-redis/redis/v7/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
|
||||||
|
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||||
|
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
||||||
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
|
||||||
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
8
vendor/github.com/go-redis/redis/v7/internal/log.go
generated
vendored
Normal file
8
vendor/github.com/go-redis/redis/v7/internal/log.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Logger = log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile)
|
118
vendor/github.com/go-redis/redis/v7/internal/pool/conn.go
generated
vendored
Normal file
118
vendor/github.com/go-redis/redis/v7/internal/pool/conn.go
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
package pool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis/v7/internal/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var noDeadline = time.Time{}
|
||||||
|
|
||||||
|
type Conn struct {
|
||||||
|
netConn net.Conn
|
||||||
|
|
||||||
|
rd *proto.Reader
|
||||||
|
wr *proto.Writer
|
||||||
|
|
||||||
|
Inited bool
|
||||||
|
pooled bool
|
||||||
|
createdAt time.Time
|
||||||
|
usedAt int64 // atomic
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConn(netConn net.Conn) *Conn {
|
||||||
|
cn := &Conn{
|
||||||
|
netConn: netConn,
|
||||||
|
createdAt: time.Now(),
|
||||||
|
}
|
||||||
|
cn.rd = proto.NewReader(netConn)
|
||||||
|
cn.wr = proto.NewWriter(netConn)
|
||||||
|
cn.SetUsedAt(time.Now())
|
||||||
|
return cn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *Conn) UsedAt() time.Time {
|
||||||
|
unix := atomic.LoadInt64(&cn.usedAt)
|
||||||
|
return time.Unix(unix, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *Conn) SetUsedAt(tm time.Time) {
|
||||||
|
atomic.StoreInt64(&cn.usedAt, tm.Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *Conn) SetNetConn(netConn net.Conn) {
|
||||||
|
cn.netConn = netConn
|
||||||
|
cn.rd.Reset(netConn)
|
||||||
|
cn.wr.Reset(netConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *Conn) Write(b []byte) (int, error) {
|
||||||
|
return cn.netConn.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *Conn) RemoteAddr() net.Addr {
|
||||||
|
return cn.netConn.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *Conn) WithReader(ctx context.Context, timeout time.Duration, fn func(rd *proto.Reader) error) error {
|
||||||
|
err := cn.netConn.SetReadDeadline(cn.deadline(ctx, timeout))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fn(cn.rd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *Conn) WithWriter(
|
||||||
|
ctx context.Context, timeout time.Duration, fn func(wr *proto.Writer) error,
|
||||||
|
) error {
|
||||||
|
err := cn.netConn.SetWriteDeadline(cn.deadline(ctx, timeout))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cn.wr.Buffered() > 0 {
|
||||||
|
cn.wr.Reset(cn.netConn)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fn(cn.wr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cn.wr.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *Conn) Close() error {
|
||||||
|
return cn.netConn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cn *Conn) deadline(ctx context.Context, timeout time.Duration) time.Time {
|
||||||
|
tm := time.Now()
|
||||||
|
cn.SetUsedAt(tm)
|
||||||
|
|
||||||
|
if timeout > 0 {
|
||||||
|
tm = tm.Add(timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx != nil {
|
||||||
|
deadline, ok := ctx.Deadline()
|
||||||
|
if ok {
|
||||||
|
if timeout == 0 {
|
||||||
|
return deadline
|
||||||
|
}
|
||||||
|
if deadline.Before(tm) {
|
||||||
|
return deadline
|
||||||
|
}
|
||||||
|
return tm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeout > 0 {
|
||||||
|
return tm
|
||||||
|
}
|
||||||
|
|
||||||
|
return noDeadline
|
||||||
|
}
|
|
@ -1,13 +1,14 @@
|
||||||
package pool
|
package pool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-redis/redis/internal"
|
"github.com/go-redis/redis/v7/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrClosed = errors.New("redis: client is closed")
|
var ErrClosed = errors.New("redis: client is closed")
|
||||||
|
@ -33,12 +34,12 @@ type Stats struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Pooler interface {
|
type Pooler interface {
|
||||||
NewConn() (*Conn, error)
|
NewConn(context.Context) (*Conn, error)
|
||||||
CloseConn(*Conn) error
|
CloseConn(*Conn) error
|
||||||
|
|
||||||
Get() (*Conn, error)
|
Get(context.Context) (*Conn, error)
|
||||||
Put(*Conn)
|
Put(*Conn)
|
||||||
Remove(*Conn)
|
Remove(*Conn, error)
|
||||||
|
|
||||||
Len() int
|
Len() int
|
||||||
IdleLen() int
|
IdleLen() int
|
||||||
|
@ -48,7 +49,7 @@ type Pooler interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Dialer func() (net.Conn, error)
|
Dialer func(context.Context) (net.Conn, error)
|
||||||
OnClose func(*Conn) error
|
OnClose func(*Conn) error
|
||||||
|
|
||||||
PoolSize int
|
PoolSize int
|
||||||
|
@ -78,6 +79,7 @@ type ConnPool struct {
|
||||||
stats Stats
|
stats Stats
|
||||||
|
|
||||||
_closed uint32 // atomic
|
_closed uint32 // atomic
|
||||||
|
closedCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Pooler = (*ConnPool)(nil)
|
var _ Pooler = (*ConnPool)(nil)
|
||||||
|
@ -89,11 +91,10 @@ func NewConnPool(opt *Options) *ConnPool {
|
||||||
queue: make(chan struct{}, opt.PoolSize),
|
queue: make(chan struct{}, opt.PoolSize),
|
||||||
conns: make([]*Conn, 0, opt.PoolSize),
|
conns: make([]*Conn, 0, opt.PoolSize),
|
||||||
idleConns: make([]*Conn, 0, opt.PoolSize),
|
idleConns: make([]*Conn, 0, opt.PoolSize),
|
||||||
|
closedCh: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < opt.MinIdleConns; i++ {
|
|
||||||
p.checkMinIdleConns()
|
p.checkMinIdleConns()
|
||||||
}
|
|
||||||
|
|
||||||
if opt.IdleTimeout > 0 && opt.IdleCheckFrequency > 0 {
|
if opt.IdleTimeout > 0 && opt.IdleCheckFrequency > 0 {
|
||||||
go p.reaper(opt.IdleCheckFrequency)
|
go p.reaper(opt.IdleCheckFrequency)
|
||||||
|
@ -106,31 +107,40 @@ func (p *ConnPool) checkMinIdleConns() {
|
||||||
if p.opt.MinIdleConns == 0 {
|
if p.opt.MinIdleConns == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if p.poolSize < p.opt.PoolSize && p.idleConnsLen < p.opt.MinIdleConns {
|
for p.poolSize < p.opt.PoolSize && p.idleConnsLen < p.opt.MinIdleConns {
|
||||||
p.poolSize++
|
p.poolSize++
|
||||||
p.idleConnsLen++
|
p.idleConnsLen++
|
||||||
go p.addIdleConn()
|
go func() {
|
||||||
|
err := p.addIdleConn()
|
||||||
|
if err != nil {
|
||||||
|
p.connsMu.Lock()
|
||||||
|
p.poolSize--
|
||||||
|
p.idleConnsLen--
|
||||||
|
p.connsMu.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConnPool) addIdleConn() {
|
func (p *ConnPool) addIdleConn() error {
|
||||||
cn, err := p.newConn(true)
|
cn, err := p.dialConn(context.TODO(), true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
p.connsMu.Lock()
|
p.connsMu.Lock()
|
||||||
p.conns = append(p.conns, cn)
|
p.conns = append(p.conns, cn)
|
||||||
p.idleConns = append(p.idleConns, cn)
|
p.idleConns = append(p.idleConns, cn)
|
||||||
p.connsMu.Unlock()
|
p.connsMu.Unlock()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConnPool) NewConn() (*Conn, error) {
|
func (p *ConnPool) NewConn(ctx context.Context) (*Conn, error) {
|
||||||
return p._NewConn(false)
|
return p.newConn(ctx, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConnPool) _NewConn(pooled bool) (*Conn, error) {
|
func (p *ConnPool) newConn(ctx context.Context, pooled bool) (*Conn, error) {
|
||||||
cn, err := p.newConn(pooled)
|
cn, err := p.dialConn(ctx, pooled)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -138,17 +148,18 @@ func (p *ConnPool) _NewConn(pooled bool) (*Conn, error) {
|
||||||
p.connsMu.Lock()
|
p.connsMu.Lock()
|
||||||
p.conns = append(p.conns, cn)
|
p.conns = append(p.conns, cn)
|
||||||
if pooled {
|
if pooled {
|
||||||
if p.poolSize < p.opt.PoolSize {
|
// If pool is full remove the cn on next Put.
|
||||||
p.poolSize++
|
if p.poolSize >= p.opt.PoolSize {
|
||||||
} else {
|
|
||||||
cn.pooled = false
|
cn.pooled = false
|
||||||
|
} else {
|
||||||
|
p.poolSize++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.connsMu.Unlock()
|
p.connsMu.Unlock()
|
||||||
return cn, nil
|
return cn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConnPool) newConn(pooled bool) (*Conn, error) {
|
func (p *ConnPool) dialConn(ctx context.Context, pooled bool) (*Conn, error) {
|
||||||
if p.closed() {
|
if p.closed() {
|
||||||
return nil, ErrClosed
|
return nil, ErrClosed
|
||||||
}
|
}
|
||||||
|
@ -157,7 +168,7 @@ func (p *ConnPool) newConn(pooled bool) (*Conn, error) {
|
||||||
return nil, p.getLastDialError()
|
return nil, p.getLastDialError()
|
||||||
}
|
}
|
||||||
|
|
||||||
netConn, err := p.opt.Dialer()
|
netConn, err := p.opt.Dialer(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.setLastDialError(err)
|
p.setLastDialError(err)
|
||||||
if atomic.AddUint32(&p.dialErrorsNum, 1) == uint32(p.opt.PoolSize) {
|
if atomic.AddUint32(&p.dialErrorsNum, 1) == uint32(p.opt.PoolSize) {
|
||||||
|
@ -177,7 +188,7 @@ func (p *ConnPool) tryDial() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := p.opt.Dialer()
|
conn, err := p.opt.Dialer(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.setLastDialError(err)
|
p.setLastDialError(err)
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
@ -204,12 +215,12 @@ func (p *ConnPool) getLastDialError() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns existed connection from the pool or creates a new one.
|
// Get returns existed connection from the pool or creates a new one.
|
||||||
func (p *ConnPool) Get() (*Conn, error) {
|
func (p *ConnPool) Get(ctx context.Context) (*Conn, error) {
|
||||||
if p.closed() {
|
if p.closed() {
|
||||||
return nil, ErrClosed
|
return nil, ErrClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
err := p.waitTurn()
|
err := p.waitTurn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -234,7 +245,7 @@ func (p *ConnPool) Get() (*Conn, error) {
|
||||||
|
|
||||||
atomic.AddUint32(&p.stats.Misses, 1)
|
atomic.AddUint32(&p.stats.Misses, 1)
|
||||||
|
|
||||||
newcn, err := p._NewConn(true)
|
newcn, err := p.newConn(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.freeTurn()
|
p.freeTurn()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -247,15 +258,29 @@ func (p *ConnPool) getTurn() {
|
||||||
p.queue <- struct{}{}
|
p.queue <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConnPool) waitTurn() error {
|
func (p *ConnPool) waitTurn(ctx context.Context) error {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case p.queue <- struct{}{}:
|
case p.queue <- struct{}{}:
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
timer := timers.Get().(*time.Timer)
|
timer := timers.Get().(*time.Timer)
|
||||||
timer.Reset(p.opt.PoolTimeout)
|
timer.Reset(p.opt.PoolTimeout)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
if !timer.Stop() {
|
||||||
|
<-timer.C
|
||||||
|
}
|
||||||
|
timers.Put(timer)
|
||||||
|
return ctx.Err()
|
||||||
case p.queue <- struct{}{}:
|
case p.queue <- struct{}{}:
|
||||||
if !timer.Stop() {
|
if !timer.Stop() {
|
||||||
<-timer.C
|
<-timer.C
|
||||||
|
@ -268,7 +293,6 @@ func (p *ConnPool) waitTurn() error {
|
||||||
return ErrPoolTimeout
|
return ErrPoolTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ConnPool) freeTurn() {
|
func (p *ConnPool) freeTurn() {
|
||||||
<-p.queue
|
<-p.queue
|
||||||
|
@ -288,8 +312,14 @@ func (p *ConnPool) popIdle() *Conn {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConnPool) Put(cn *Conn) {
|
func (p *ConnPool) Put(cn *Conn) {
|
||||||
|
if cn.rd.Buffered() > 0 {
|
||||||
|
internal.Logger.Printf("Conn has unread data")
|
||||||
|
p.Remove(cn, BadConnError{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !cn.pooled {
|
if !cn.pooled {
|
||||||
p.Remove(cn)
|
p.Remove(cn, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,19 +330,24 @@ func (p *ConnPool) Put(cn *Conn) {
|
||||||
p.freeTurn()
|
p.freeTurn()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConnPool) Remove(cn *Conn) {
|
func (p *ConnPool) Remove(cn *Conn, reason error) {
|
||||||
p.removeConn(cn)
|
p.removeConnWithLock(cn)
|
||||||
p.freeTurn()
|
p.freeTurn()
|
||||||
_ = p.closeConn(cn)
|
_ = p.closeConn(cn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConnPool) CloseConn(cn *Conn) error {
|
func (p *ConnPool) CloseConn(cn *Conn) error {
|
||||||
p.removeConn(cn)
|
p.removeConnWithLock(cn)
|
||||||
return p.closeConn(cn)
|
return p.closeConn(cn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConnPool) removeConn(cn *Conn) {
|
func (p *ConnPool) removeConnWithLock(cn *Conn) {
|
||||||
p.connsMu.Lock()
|
p.connsMu.Lock()
|
||||||
|
p.removeConn(cn)
|
||||||
|
p.connsMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConnPool) removeConn(cn *Conn) {
|
||||||
for i, c := range p.conns {
|
for i, c := range p.conns {
|
||||||
if c == cn {
|
if c == cn {
|
||||||
p.conns = append(p.conns[:i], p.conns[i+1:]...)
|
p.conns = append(p.conns[:i], p.conns[i+1:]...)
|
||||||
|
@ -320,10 +355,9 @@ func (p *ConnPool) removeConn(cn *Conn) {
|
||||||
p.poolSize--
|
p.poolSize--
|
||||||
p.checkMinIdleConns()
|
p.checkMinIdleConns()
|
||||||
}
|
}
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.connsMu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConnPool) closeConn(cn *Conn) error {
|
func (p *ConnPool) closeConn(cn *Conn) error {
|
||||||
|
@ -384,6 +418,7 @@ func (p *ConnPool) Close() error {
|
||||||
if !atomic.CompareAndSwapUint32(&p._closed, 0, 1) {
|
if !atomic.CompareAndSwapUint32(&p._closed, 0, 1) {
|
||||||
return ErrClosed
|
return ErrClosed
|
||||||
}
|
}
|
||||||
|
close(p.closedCh)
|
||||||
|
|
||||||
var firstErr error
|
var firstErr error
|
||||||
p.connsMu.Lock()
|
p.connsMu.Lock()
|
||||||
|
@ -401,6 +436,51 @@ func (p *ConnPool) Close() error {
|
||||||
return firstErr
|
return firstErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *ConnPool) reaper(frequency time.Duration) {
|
||||||
|
ticker := time.NewTicker(frequency)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
// It is possible that ticker and closedCh arrive together,
|
||||||
|
// and select pseudo-randomly pick ticker case, we double
|
||||||
|
// check here to prevent being executed after closed.
|
||||||
|
if p.closed() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err := p.ReapStaleConns()
|
||||||
|
if err != nil {
|
||||||
|
internal.Logger.Printf("ReapStaleConns failed: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case <-p.closedCh:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConnPool) ReapStaleConns() (int, error) {
|
||||||
|
var n int
|
||||||
|
for {
|
||||||
|
p.getTurn()
|
||||||
|
|
||||||
|
p.connsMu.Lock()
|
||||||
|
cn := p.reapStaleConn()
|
||||||
|
p.connsMu.Unlock()
|
||||||
|
p.freeTurn()
|
||||||
|
|
||||||
|
if cn != nil {
|
||||||
|
_ = p.closeConn(cn)
|
||||||
|
n++
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
atomic.AddUint32(&p.stats.StaleConns, uint32(n))
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *ConnPool) reapStaleConn() *Conn {
|
func (p *ConnPool) reapStaleConn() *Conn {
|
||||||
if len(p.idleConns) == 0 {
|
if len(p.idleConns) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
@ -413,52 +493,11 @@ func (p *ConnPool) reapStaleConn() *Conn {
|
||||||
|
|
||||||
p.idleConns = append(p.idleConns[:0], p.idleConns[1:]...)
|
p.idleConns = append(p.idleConns[:0], p.idleConns[1:]...)
|
||||||
p.idleConnsLen--
|
p.idleConnsLen--
|
||||||
|
p.removeConn(cn)
|
||||||
|
|
||||||
return cn
|
return cn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ConnPool) ReapStaleConns() (int, error) {
|
|
||||||
var n int
|
|
||||||
for {
|
|
||||||
p.getTurn()
|
|
||||||
|
|
||||||
p.connsMu.Lock()
|
|
||||||
cn := p.reapStaleConn()
|
|
||||||
p.connsMu.Unlock()
|
|
||||||
|
|
||||||
if cn != nil {
|
|
||||||
p.removeConn(cn)
|
|
||||||
}
|
|
||||||
|
|
||||||
p.freeTurn()
|
|
||||||
|
|
||||||
if cn != nil {
|
|
||||||
p.closeConn(cn)
|
|
||||||
n++
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ConnPool) reaper(frequency time.Duration) {
|
|
||||||
ticker := time.NewTicker(frequency)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
for range ticker.C {
|
|
||||||
if p.closed() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
n, err := p.ReapStaleConns()
|
|
||||||
if err != nil {
|
|
||||||
internal.Logf("ReapStaleConns failed: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
atomic.AddUint32(&p.stats.StaleConns, uint32(n))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ConnPool) isStaleConn(cn *Conn) bool {
|
func (p *ConnPool) isStaleConn(cn *Conn) bool {
|
||||||
if p.opt.IdleTimeout == 0 && p.opt.MaxConnAge == 0 {
|
if p.opt.IdleTimeout == 0 && p.opt.MaxConnAge == 0 {
|
||||||
return false
|
return false
|
||||||
|
@ -468,7 +507,7 @@ func (p *ConnPool) isStaleConn(cn *Conn) bool {
|
||||||
if p.opt.IdleTimeout > 0 && now.Sub(cn.UsedAt()) >= p.opt.IdleTimeout {
|
if p.opt.IdleTimeout > 0 && now.Sub(cn.UsedAt()) >= p.opt.IdleTimeout {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if p.opt.MaxConnAge > 0 && now.Sub(cn.InitedAt) >= p.opt.MaxConnAge {
|
if p.opt.MaxConnAge > 0 && now.Sub(cn.createdAt) >= p.opt.MaxConnAge {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
208
vendor/github.com/go-redis/redis/v7/internal/pool/pool_single.go
generated
vendored
Normal file
208
vendor/github.com/go-redis/redis/v7/internal/pool/pool_single.go
generated
vendored
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
package pool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
stateDefault = 0
|
||||||
|
stateInited = 1
|
||||||
|
stateClosed = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type BadConnError struct {
|
||||||
|
wrapped error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ error = (*BadConnError)(nil)
|
||||||
|
|
||||||
|
func (e BadConnError) Error() string {
|
||||||
|
s := "redis: Conn is in a bad state"
|
||||||
|
if e.wrapped != nil {
|
||||||
|
s += ": " + e.wrapped.Error()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e BadConnError) Unwrap() error {
|
||||||
|
return e.wrapped
|
||||||
|
}
|
||||||
|
|
||||||
|
type SingleConnPool struct {
|
||||||
|
pool Pooler
|
||||||
|
level int32 // atomic
|
||||||
|
|
||||||
|
state uint32 // atomic
|
||||||
|
ch chan *Conn
|
||||||
|
|
||||||
|
_badConnError atomic.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Pooler = (*SingleConnPool)(nil)
|
||||||
|
|
||||||
|
func NewSingleConnPool(pool Pooler) *SingleConnPool {
|
||||||
|
p, ok := pool.(*SingleConnPool)
|
||||||
|
if !ok {
|
||||||
|
p = &SingleConnPool{
|
||||||
|
pool: pool,
|
||||||
|
ch: make(chan *Conn, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
atomic.AddInt32(&p.level, 1)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) SetConn(cn *Conn) {
|
||||||
|
if atomic.CompareAndSwapUint32(&p.state, stateDefault, stateInited) {
|
||||||
|
p.ch <- cn
|
||||||
|
} else {
|
||||||
|
panic("not reached")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) NewConn(ctx context.Context) (*Conn, error) {
|
||||||
|
return p.pool.NewConn(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) CloseConn(cn *Conn) error {
|
||||||
|
return p.pool.CloseConn(cn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) Get(ctx context.Context) (*Conn, error) {
|
||||||
|
// In worst case this races with Close which is not a very common operation.
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
switch atomic.LoadUint32(&p.state) {
|
||||||
|
case stateDefault:
|
||||||
|
cn, err := p.pool.Get(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if atomic.CompareAndSwapUint32(&p.state, stateDefault, stateInited) {
|
||||||
|
return cn, nil
|
||||||
|
}
|
||||||
|
p.pool.Remove(cn, ErrClosed)
|
||||||
|
case stateInited:
|
||||||
|
if err := p.badConnError(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cn, ok := <-p.ch
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrClosed
|
||||||
|
}
|
||||||
|
return cn, nil
|
||||||
|
case stateClosed:
|
||||||
|
return nil, ErrClosed
|
||||||
|
default:
|
||||||
|
panic("not reached")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("redis: SingleConnPool.Get: infinite loop")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) Put(cn *Conn) {
|
||||||
|
defer func() {
|
||||||
|
if recover() != nil {
|
||||||
|
p.freeConn(cn)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
p.ch <- cn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) freeConn(cn *Conn) {
|
||||||
|
if err := p.badConnError(); err != nil {
|
||||||
|
p.pool.Remove(cn, err)
|
||||||
|
} else {
|
||||||
|
p.pool.Put(cn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) Remove(cn *Conn, reason error) {
|
||||||
|
defer func() {
|
||||||
|
if recover() != nil {
|
||||||
|
p.pool.Remove(cn, ErrClosed)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
p._badConnError.Store(BadConnError{wrapped: reason})
|
||||||
|
p.ch <- cn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) Len() int {
|
||||||
|
switch atomic.LoadUint32(&p.state) {
|
||||||
|
case stateDefault:
|
||||||
|
return 0
|
||||||
|
case stateInited:
|
||||||
|
return 1
|
||||||
|
case stateClosed:
|
||||||
|
return 0
|
||||||
|
default:
|
||||||
|
panic("not reached")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) IdleLen() int {
|
||||||
|
return len(p.ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) Stats() *Stats {
|
||||||
|
return &Stats{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) Close() error {
|
||||||
|
level := atomic.AddInt32(&p.level, -1)
|
||||||
|
if level > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
state := atomic.LoadUint32(&p.state)
|
||||||
|
if state == stateClosed {
|
||||||
|
return ErrClosed
|
||||||
|
}
|
||||||
|
if atomic.CompareAndSwapUint32(&p.state, state, stateClosed) {
|
||||||
|
close(p.ch)
|
||||||
|
cn, ok := <-p.ch
|
||||||
|
if ok {
|
||||||
|
p.freeConn(cn)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("redis: SingleConnPool.Close: infinite loop")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) Reset() error {
|
||||||
|
if p.badConnError() == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case cn, ok := <-p.ch:
|
||||||
|
if !ok {
|
||||||
|
return ErrClosed
|
||||||
|
}
|
||||||
|
p.pool.Remove(cn, ErrClosed)
|
||||||
|
p._badConnError.Store(BadConnError{wrapped: nil})
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("redis: SingleConnPool does not have a Conn")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !atomic.CompareAndSwapUint32(&p.state, stateInited, stateDefault) {
|
||||||
|
state := atomic.LoadUint32(&p.state)
|
||||||
|
return fmt.Errorf("redis: invalid SingleConnPool state: %d", state)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SingleConnPool) badConnError() error {
|
||||||
|
if v := p._badConnError.Load(); v != nil {
|
||||||
|
err := v.(BadConnError)
|
||||||
|
if err.wrapped != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
package pool
|
package pool
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
type StickyConnPool struct {
|
type StickyConnPool struct {
|
||||||
pool *ConnPool
|
pool *ConnPool
|
||||||
|
@ -20,7 +23,7 @@ func NewStickyConnPool(pool *ConnPool, reusable bool) *StickyConnPool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *StickyConnPool) NewConn() (*Conn, error) {
|
func (p *StickyConnPool) NewConn(context.Context) (*Conn, error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +31,7 @@ func (p *StickyConnPool) CloseConn(*Conn) error {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *StickyConnPool) Get() (*Conn, error) {
|
func (p *StickyConnPool) Get(ctx context.Context) (*Conn, error) {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
|
|
||||||
|
@ -39,7 +42,7 @@ func (p *StickyConnPool) Get() (*Conn, error) {
|
||||||
return p.cn, nil
|
return p.cn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cn, err := p.pool.Get()
|
cn, err := p.pool.Get(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -55,13 +58,13 @@ func (p *StickyConnPool) putUpstream() {
|
||||||
|
|
||||||
func (p *StickyConnPool) Put(cn *Conn) {}
|
func (p *StickyConnPool) Put(cn *Conn) {}
|
||||||
|
|
||||||
func (p *StickyConnPool) removeUpstream() {
|
func (p *StickyConnPool) removeUpstream(reason error) {
|
||||||
p.pool.Remove(p.cn)
|
p.pool.Remove(p.cn, reason)
|
||||||
p.cn = nil
|
p.cn = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *StickyConnPool) Remove(cn *Conn) {
|
func (p *StickyConnPool) Remove(cn *Conn, reason error) {
|
||||||
p.removeUpstream()
|
p.removeUpstream(reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *StickyConnPool) Len() int {
|
func (p *StickyConnPool) Len() int {
|
||||||
|
@ -101,7 +104,7 @@ func (p *StickyConnPool) Close() error {
|
||||||
if p.reusable {
|
if p.reusable {
|
||||||
p.putUpstream()
|
p.putUpstream()
|
||||||
} else {
|
} else {
|
||||||
p.removeUpstream()
|
p.removeUpstream(ErrClosed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,8 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/go-redis/redis/internal/util"
|
"github.com/go-redis/redis/v7/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -41,27 +40,44 @@ func NewReader(rd io.Reader) *Reader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Reader) Buffered() int {
|
||||||
|
return r.rd.Buffered()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) Peek(n int) ([]byte, error) {
|
||||||
|
return r.rd.Peek(n)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Reader) Reset(rd io.Reader) {
|
func (r *Reader) Reset(rd io.Reader) {
|
||||||
r.rd.Reset(rd)
|
r.rd.Reset(rd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) ReadLine() ([]byte, error) {
|
func (r *Reader) ReadLine() ([]byte, error) {
|
||||||
line, isPrefix, err := r.rd.ReadLine()
|
line, err := r.readLine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if isPrefix {
|
|
||||||
return nil, bufio.ErrBufferFull
|
|
||||||
}
|
|
||||||
if len(line) == 0 {
|
|
||||||
return nil, fmt.Errorf("redis: reply is empty")
|
|
||||||
}
|
|
||||||
if isNilReply(line) {
|
if isNilReply(line) {
|
||||||
return nil, Nil
|
return nil, Nil
|
||||||
}
|
}
|
||||||
return line, nil
|
return line, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readLine that returns an error if:
|
||||||
|
// - there is a pending read error;
|
||||||
|
// - or line does not end with \r\n.
|
||||||
|
func (r *Reader) readLine() ([]byte, error) {
|
||||||
|
b, err := r.rd.ReadSlice('\n')
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(b) <= 2 || b[len(b)-1] != '\n' || b[len(b)-2] != '\r' {
|
||||||
|
return nil, fmt.Errorf("redis: invalid reply: %q", b)
|
||||||
|
}
|
||||||
|
b = b[:len(b)-2]
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Reader) ReadReply(m MultiBulkParse) (interface{}, error) {
|
func (r *Reader) ReadReply(m MultiBulkParse) (interface{}, error) {
|
||||||
line, err := r.ReadLine()
|
line, err := r.ReadLine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -82,6 +98,10 @@ func (r *Reader) ReadReply(m MultiBulkParse) (interface{}, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if m == nil {
|
||||||
|
err := fmt.Errorf("redis: got %.100q, but multi bulk parser is nil", line)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return m(r, n)
|
return m(r, n)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("redis: can't parse %.100q", line)
|
return nil, fmt.Errorf("redis: can't parse %.100q", line)
|
||||||
|
@ -126,7 +146,7 @@ func (r *Reader) readStringReply(line []byte) (string, error) {
|
||||||
return "", Nil
|
return "", Nil
|
||||||
}
|
}
|
||||||
|
|
||||||
replyLen, err := strconv.Atoi(string(line[1:]))
|
replyLen, err := util.Atoi(line[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -251,7 +271,7 @@ func (r *Reader) _readTmpBytesReply(line []byte) ([]byte, error) {
|
||||||
return nil, Nil
|
return nil, Nil
|
||||||
}
|
}
|
||||||
|
|
||||||
replyLen, err := strconv.Atoi(string(line[1:]))
|
replyLen, err := util.Atoi(line[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -266,11 +286,13 @@ func (r *Reader) _readTmpBytesReply(line []byte) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Reader) buf(n int) []byte {
|
func (r *Reader) buf(n int) []byte {
|
||||||
if d := n - cap(r._buf); d > 0 {
|
if n <= cap(r._buf) {
|
||||||
r._buf = append(r._buf, make([]byte, d)...)
|
|
||||||
}
|
|
||||||
return r._buf[:n]
|
return r._buf[:n]
|
||||||
}
|
}
|
||||||
|
d := n - cap(r._buf)
|
||||||
|
r._buf = append(r._buf, make([]byte, d)...)
|
||||||
|
return r._buf
|
||||||
|
}
|
||||||
|
|
||||||
func isNilReply(b []byte) bool {
|
func isNilReply(b []byte) bool {
|
||||||
return len(b) == 3 &&
|
return len(b) == 3 &&
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/go-redis/redis/internal/util"
|
"github.com/go-redis/redis/v7/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Scan(b []byte, v interface{}) error {
|
func Scan(b []byte, v interface{}) error {
|
|
@ -6,8 +6,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-redis/redis/internal/util"
|
"github.com/go-redis/redis/v7/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Writer struct {
|
type Writer struct {
|
||||||
|
@ -89,9 +90,10 @@ func (w *Writer) writeArg(v interface{}) error {
|
||||||
case bool:
|
case bool:
|
||||||
if v {
|
if v {
|
||||||
return w.int(1)
|
return w.int(1)
|
||||||
} else {
|
|
||||||
return w.int(0)
|
|
||||||
}
|
}
|
||||||
|
return w.int(0)
|
||||||
|
case time.Time:
|
||||||
|
return w.string(v.Format(time.RFC3339))
|
||||||
case encoding.BinaryMarshaler:
|
case encoding.BinaryMarshaler:
|
||||||
b, err := v.MarshalBinary()
|
b, err := v.MarshalBinary()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -150,6 +152,10 @@ func (w *Writer) crlf() error {
|
||||||
return w.wr.WriteByte('\n')
|
return w.wr.WriteByte('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Buffered() int {
|
||||||
|
return w.wr.Buffered()
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Writer) Reset(wr io.Writer) {
|
func (w *Writer) Reset(wr io.Writer) {
|
||||||
w.wr.Reset(wr)
|
w.wr.Reset(wr)
|
||||||
}
|
}
|
56
vendor/github.com/go-redis/redis/v7/internal/util.go
generated
vendored
Normal file
56
vendor/github.com/go-redis/redis/v7/internal/util.go
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis/v7/internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Sleep(ctx context.Context, dur time.Duration) error {
|
||||||
|
t := time.NewTimer(dur)
|
||||||
|
defer t.Stop()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
return nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToLower(s string) string {
|
||||||
|
if isLower(s) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]byte, len(s))
|
||||||
|
for i := range b {
|
||||||
|
c := s[i]
|
||||||
|
if c >= 'A' && c <= 'Z' {
|
||||||
|
c += 'a' - 'A'
|
||||||
|
}
|
||||||
|
b[i] = c
|
||||||
|
}
|
||||||
|
return util.BytesToString(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLower(s string) bool {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
c := s[i]
|
||||||
|
if c >= 'A' && c <= 'Z' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unwrap(err error) error {
|
||||||
|
u, ok := err.(interface {
|
||||||
|
Unwrap() error
|
||||||
|
})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return u.Unwrap()
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
package redis
|
package redis
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
// ScanIterator is used to incrementally iterate over a collection of elements.
|
// ScanIterator is used to incrementally iterate over a collection of elements.
|
||||||
// It's safe for concurrent use by multiple goroutines.
|
// It's safe for concurrent use by multiple goroutines.
|
||||||
|
@ -41,10 +43,10 @@ func (it *ScanIterator) Next() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch next page.
|
// Fetch next page.
|
||||||
if it.cmd._args[0] == "scan" {
|
if it.cmd.args[0] == "scan" {
|
||||||
it.cmd._args[1] = it.cmd.cursor
|
it.cmd.args[1] = it.cmd.cursor
|
||||||
} else {
|
} else {
|
||||||
it.cmd._args[2] = it.cmd.cursor
|
it.cmd.args[2] = it.cmd.cursor
|
||||||
}
|
}
|
||||||
|
|
||||||
err := it.cmd.process(it.cmd)
|
err := it.cmd.process(it.cmd)
|
|
@ -1,6 +1,7 @@
|
||||||
package redis
|
package redis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -11,9 +12,20 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-redis/redis/internal/pool"
|
"github.com/go-redis/redis/v7/internal/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Limiter is the interface of a rate limiter or a circuit breaker.
|
||||||
|
type Limiter interface {
|
||||||
|
// Allow returns nil if operation is allowed or an error otherwise.
|
||||||
|
// If operation is allowed client must ReportResult of the operation
|
||||||
|
// whether it is a success or a failure.
|
||||||
|
Allow() error
|
||||||
|
// ReportResult reports the result of the previously allowed operation.
|
||||||
|
// nil indicates a success, non-nil error usually indicates a failure.
|
||||||
|
ReportResult(result error)
|
||||||
|
}
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
// The network type, either tcp or unix.
|
// The network type, either tcp or unix.
|
||||||
// Default is tcp.
|
// Default is tcp.
|
||||||
|
@ -23,7 +35,7 @@ type Options struct {
|
||||||
|
|
||||||
// Dialer creates new network connection and has priority over
|
// Dialer creates new network connection and has priority over
|
||||||
// Network and Addr options.
|
// Network and Addr options.
|
||||||
Dialer func() (net.Conn, error)
|
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||||
|
|
||||||
// Hook that is called when new connection is established.
|
// Hook that is called when new connection is established.
|
||||||
OnConnect func(*Conn) error
|
OnConnect func(*Conn) error
|
||||||
|
@ -48,7 +60,7 @@ type Options struct {
|
||||||
// Default is 5 seconds.
|
// Default is 5 seconds.
|
||||||
DialTimeout time.Duration
|
DialTimeout time.Duration
|
||||||
// Timeout for socket reads. If reached, commands will fail
|
// Timeout for socket reads. If reached, commands will fail
|
||||||
// with a timeout instead of blocking.
|
// with a timeout instead of blocking. Use value -1 for no timeout and 0 for default.
|
||||||
// Default is 3 seconds.
|
// Default is 3 seconds.
|
||||||
ReadTimeout time.Duration
|
ReadTimeout time.Duration
|
||||||
// Timeout for socket writes. If reached, commands will fail
|
// Timeout for socket writes. If reached, commands will fail
|
||||||
|
@ -84,23 +96,32 @@ type Options struct {
|
||||||
|
|
||||||
// TLS Config to use. When set TLS will be negotiated.
|
// TLS Config to use. When set TLS will be negotiated.
|
||||||
TLSConfig *tls.Config
|
TLSConfig *tls.Config
|
||||||
|
|
||||||
|
// Limiter interface used to implemented circuit breaker or rate limiter.
|
||||||
|
Limiter Limiter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opt *Options) init() {
|
func (opt *Options) init() {
|
||||||
|
if opt.Addr == "" {
|
||||||
|
opt.Addr = "localhost:6379"
|
||||||
|
}
|
||||||
if opt.Network == "" {
|
if opt.Network == "" {
|
||||||
|
if strings.HasPrefix(opt.Addr, "/") {
|
||||||
|
opt.Network = "unix"
|
||||||
|
} else {
|
||||||
opt.Network = "tcp"
|
opt.Network = "tcp"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if opt.Dialer == nil {
|
if opt.Dialer == nil {
|
||||||
opt.Dialer = func() (net.Conn, error) {
|
opt.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
netDialer := &net.Dialer{
|
netDialer := &net.Dialer{
|
||||||
Timeout: opt.DialTimeout,
|
Timeout: opt.DialTimeout,
|
||||||
KeepAlive: 5 * time.Minute,
|
KeepAlive: 5 * time.Minute,
|
||||||
}
|
}
|
||||||
if opt.TLSConfig == nil {
|
if opt.TLSConfig == nil {
|
||||||
return netDialer.Dial(opt.Network, opt.Addr)
|
return netDialer.DialContext(ctx, network, addr)
|
||||||
} else {
|
|
||||||
return tls.DialWithDialer(netDialer, opt.Network, opt.Addr, opt.TLSConfig)
|
|
||||||
}
|
}
|
||||||
|
return tls.DialWithDialer(netDialer, network, addr, opt.TLSConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if opt.PoolSize == 0 {
|
if opt.PoolSize == 0 {
|
||||||
|
@ -131,6 +152,9 @@ func (opt *Options) init() {
|
||||||
opt.IdleCheckFrequency = time.Minute
|
opt.IdleCheckFrequency = time.Minute
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opt.MaxRetries == -1 {
|
||||||
|
opt.MaxRetries = 0
|
||||||
|
}
|
||||||
switch opt.MinRetryBackoff {
|
switch opt.MinRetryBackoff {
|
||||||
case -1:
|
case -1:
|
||||||
opt.MinRetryBackoff = 0
|
opt.MinRetryBackoff = 0
|
||||||
|
@ -145,6 +169,11 @@ func (opt *Options) init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (opt *Options) clone() *Options {
|
||||||
|
clone := *opt
|
||||||
|
return &clone
|
||||||
|
}
|
||||||
|
|
||||||
// ParseURL parses an URL into Options that can be used to connect to Redis.
|
// ParseURL parses an URL into Options that can be used to connect to Redis.
|
||||||
func ParseURL(redisURL string) (*Options, error) {
|
func ParseURL(redisURL string) (*Options, error) {
|
||||||
o := &Options{Network: "tcp"}
|
o := &Options{Network: "tcp"}
|
||||||
|
@ -201,7 +230,9 @@ func ParseURL(redisURL string) (*Options, error) {
|
||||||
|
|
||||||
func newConnPool(opt *Options) *pool.ConnPool {
|
func newConnPool(opt *Options) *pool.ConnPool {
|
||||||
return pool.NewConnPool(&pool.Options{
|
return pool.NewConnPool(&pool.Options{
|
||||||
Dialer: opt.Dialer,
|
Dialer: func(ctx context.Context) (net.Conn, error) {
|
||||||
|
return opt.Dialer(ctx, opt.Network, opt.Addr)
|
||||||
|
},
|
||||||
PoolSize: opt.PoolSize,
|
PoolSize: opt.PoolSize,
|
||||||
MinIdleConns: opt.MinIdleConns,
|
MinIdleConns: opt.MinIdleConns,
|
||||||
MaxConnAge: opt.MaxConnAge,
|
MaxConnAge: opt.MaxConnAge,
|
|
@ -1,19 +1,35 @@
|
||||||
package redis
|
package redis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/go-redis/redis/internal/pool"
|
"github.com/go-redis/redis/v7/internal/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
type pipelineExecer func([]Cmder) error
|
type pipelineExecer func(context.Context, []Cmder) error
|
||||||
|
|
||||||
|
// Pipeliner is an mechanism to realise Redis Pipeline technique.
|
||||||
|
//
|
||||||
|
// Pipelining is a technique to extremely speed up processing by packing
|
||||||
|
// operations to batches, send them at once to Redis and read a replies in a
|
||||||
|
// singe step.
|
||||||
|
// See https://redis.io/topics/pipelining
|
||||||
|
//
|
||||||
|
// Pay attention, that Pipeline is not a transaction, so you can get unexpected
|
||||||
|
// results in case of big pipelines and small read/write timeouts.
|
||||||
|
// Redis client has retransmission logic in case of timeouts, pipeline
|
||||||
|
// can be retransmitted and commands can be executed more then once.
|
||||||
|
// To avoid this: it is good idea to use reasonable bigger read/write timeouts
|
||||||
|
// depends of your batch size and/or use TxPipeline.
|
||||||
type Pipeliner interface {
|
type Pipeliner interface {
|
||||||
StatefulCmdable
|
StatefulCmdable
|
||||||
|
Do(args ...interface{}) *Cmd
|
||||||
Process(cmd Cmder) error
|
Process(cmd Cmder) error
|
||||||
Close() error
|
Close() error
|
||||||
Discard() error
|
Discard() error
|
||||||
Exec() ([]Cmder, error)
|
Exec() ([]Cmder, error)
|
||||||
|
ExecContext(ctx context.Context) ([]Cmder, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Pipeliner = (*Pipeline)(nil)
|
var _ Pipeliner = (*Pipeline)(nil)
|
||||||
|
@ -22,8 +38,10 @@ var _ Pipeliner = (*Pipeline)(nil)
|
||||||
// http://redis.io/topics/pipelining. It's safe for concurrent use
|
// http://redis.io/topics/pipelining. It's safe for concurrent use
|
||||||
// by multiple goroutines.
|
// by multiple goroutines.
|
||||||
type Pipeline struct {
|
type Pipeline struct {
|
||||||
|
cmdable
|
||||||
statefulCmdable
|
statefulCmdable
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
exec pipelineExecer
|
exec pipelineExecer
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
@ -31,6 +49,17 @@ type Pipeline struct {
|
||||||
closed bool
|
closed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Pipeline) init() {
|
||||||
|
c.cmdable = c.Process
|
||||||
|
c.statefulCmdable = c.Process
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Pipeline) Do(args ...interface{}) *Cmd {
|
||||||
|
cmd := NewCmd(args...)
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
// Process queues the cmd for later execution.
|
// Process queues the cmd for later execution.
|
||||||
func (c *Pipeline) Process(cmd Cmder) error {
|
func (c *Pipeline) Process(cmd Cmder) error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
|
@ -42,7 +71,7 @@ func (c *Pipeline) Process(cmd Cmder) error {
|
||||||
// Close closes the pipeline, releasing any open resources.
|
// Close closes the pipeline, releasing any open resources.
|
||||||
func (c *Pipeline) Close() error {
|
func (c *Pipeline) Close() error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
c.discard()
|
_ = c.discard()
|
||||||
c.closed = true
|
c.closed = true
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
|
@ -70,6 +99,10 @@ func (c *Pipeline) discard() error {
|
||||||
// Exec always returns list of commands and error of the first failed
|
// Exec always returns list of commands and error of the first failed
|
||||||
// command if any.
|
// command if any.
|
||||||
func (c *Pipeline) Exec() ([]Cmder, error) {
|
func (c *Pipeline) Exec() ([]Cmder, error) {
|
||||||
|
return c.ExecContext(c.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Pipeline) ExecContext(ctx context.Context) ([]Cmder, error) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
@ -84,10 +117,10 @@ func (c *Pipeline) Exec() ([]Cmder, error) {
|
||||||
cmds := c.cmds
|
cmds := c.cmds
|
||||||
c.cmds = nil
|
c.cmds = nil
|
||||||
|
|
||||||
return cmds, c.exec(cmds)
|
return cmds, c.exec(ctx, cmds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Pipeline) pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
func (c *Pipeline) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||||
if err := fn(c); err != nil {
|
if err := fn(c); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -96,16 +129,12 @@ func (c *Pipeline) pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||||
return cmds, err
|
return cmds, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Pipeline) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
|
||||||
return c.pipelined(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Pipeline) Pipeline() Pipeliner {
|
func (c *Pipeline) Pipeline() Pipeliner {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Pipeline) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
func (c *Pipeline) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||||
return c.pipelined(fn)
|
return c.Pipelined(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Pipeline) TxPipeline() Pipeliner {
|
func (c *Pipeline) TxPipeline() Pipeliner {
|
270
vendor/github.com/go-redis/redis/pubsub.go → vendor/github.com/go-redis/redis/v7/pubsub.go
generated
vendored
270
vendor/github.com/go-redis/redis/pubsub.go → vendor/github.com/go-redis/redis/v7/pubsub.go
generated
vendored
|
@ -1,16 +1,23 @@
|
||||||
package redis
|
package redis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-redis/redis/internal"
|
"github.com/go-redis/redis/v7/internal"
|
||||||
"github.com/go-redis/redis/internal/pool"
|
"github.com/go-redis/redis/v7/internal/pool"
|
||||||
"github.com/go-redis/redis/internal/proto"
|
"github.com/go-redis/redis/v7/internal/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PubSub implements Pub/Sub commands bas described in
|
const pingTimeout = 30 * time.Second
|
||||||
|
|
||||||
|
var errPingTimeout = errors.New("redis: ping timeout")
|
||||||
|
|
||||||
|
// PubSub implements Pub/Sub commands as described in
|
||||||
// http://redis.io/topics/pubsub. Message receiving is NOT safe
|
// http://redis.io/topics/pubsub. Message receiving is NOT safe
|
||||||
// for concurrent use by multiple goroutines.
|
// for concurrent use by multiple goroutines.
|
||||||
//
|
//
|
||||||
|
@ -26,32 +33,39 @@ type PubSub struct {
|
||||||
cn *pool.Conn
|
cn *pool.Conn
|
||||||
channels map[string]struct{}
|
channels map[string]struct{}
|
||||||
patterns map[string]struct{}
|
patterns map[string]struct{}
|
||||||
|
|
||||||
closed bool
|
closed bool
|
||||||
exit chan struct{}
|
exit chan struct{}
|
||||||
|
|
||||||
cmd *Cmd
|
cmd *Cmd
|
||||||
|
|
||||||
chOnce sync.Once
|
chOnce sync.Once
|
||||||
ch chan *Message
|
msgCh chan *Message
|
||||||
|
allCh chan interface{}
|
||||||
ping chan struct{}
|
ping chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *PubSub) String() string {
|
||||||
|
channels := mapKeys(c.channels)
|
||||||
|
channels = append(channels, mapKeys(c.patterns)...)
|
||||||
|
return fmt.Sprintf("PubSub(%s)", strings.Join(channels, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
func (c *PubSub) init() {
|
func (c *PubSub) init() {
|
||||||
c.exit = make(chan struct{})
|
c.exit = make(chan struct{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PubSub) conn() (*pool.Conn, error) {
|
func (c *PubSub) connWithLock() (*pool.Conn, error) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
cn, err := c._conn(nil)
|
cn, err := c.conn(nil)
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
return cn, err
|
return cn, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PubSub) _conn(newChannels []string) (*pool.Conn, error) {
|
func (c *PubSub) conn(newChannels []string) (*pool.Conn, error) {
|
||||||
if c.closed {
|
if c.closed {
|
||||||
return nil, pool.ErrClosed
|
return nil, pool.ErrClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.cn != nil {
|
if c.cn != nil {
|
||||||
return c.cn, nil
|
return c.cn, nil
|
||||||
}
|
}
|
||||||
|
@ -73,8 +87,8 @@ func (c *PubSub) _conn(newChannels []string) (*pool.Conn, error) {
|
||||||
return cn, nil
|
return cn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PubSub) writeCmd(cn *pool.Conn, cmd Cmder) error {
|
func (c *PubSub) writeCmd(ctx context.Context, cn *pool.Conn, cmd Cmder) error {
|
||||||
return cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
|
return cn.WithWriter(ctx, c.opt.WriteTimeout, func(wr *proto.Writer) error {
|
||||||
return writeCmd(wr, cmd)
|
return writeCmd(wr, cmd)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -83,10 +97,7 @@ func (c *PubSub) resubscribe(cn *pool.Conn) error {
|
||||||
var firstErr error
|
var firstErr error
|
||||||
|
|
||||||
if len(c.channels) > 0 {
|
if len(c.channels) > 0 {
|
||||||
err := c._subscribe(cn, "subscribe", mapKeys(c.channels))
|
firstErr = c._subscribe(cn, "subscribe", mapKeys(c.channels))
|
||||||
if err != nil && firstErr == nil {
|
|
||||||
firstErr = err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.patterns) > 0 {
|
if len(c.patterns) > 0 {
|
||||||
|
@ -118,35 +129,35 @@ func (c *PubSub) _subscribe(
|
||||||
args = append(args, channel)
|
args = append(args, channel)
|
||||||
}
|
}
|
||||||
cmd := NewSliceCmd(args...)
|
cmd := NewSliceCmd(args...)
|
||||||
return c.writeCmd(cn, cmd)
|
return c.writeCmd(context.TODO(), cn, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PubSub) releaseConn(cn *pool.Conn, err error, allowTimeout bool) {
|
func (c *PubSub) releaseConnWithLock(cn *pool.Conn, err error, allowTimeout bool) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
c._releaseConn(cn, err, allowTimeout)
|
c.releaseConn(cn, err, allowTimeout)
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PubSub) _releaseConn(cn *pool.Conn, err error, allowTimeout bool) {
|
func (c *PubSub) releaseConn(cn *pool.Conn, err error, allowTimeout bool) {
|
||||||
if c.cn != cn {
|
if c.cn != cn {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if internal.IsBadConn(err, allowTimeout) {
|
if isBadConn(err, allowTimeout) {
|
||||||
c._reconnect(err)
|
c.reconnect(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PubSub) _reconnect(reason error) {
|
func (c *PubSub) reconnect(reason error) {
|
||||||
_ = c._closeTheCn(reason)
|
_ = c.closeTheCn(reason)
|
||||||
_, _ = c._conn(nil)
|
_, _ = c.conn(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PubSub) _closeTheCn(reason error) error {
|
func (c *PubSub) closeTheCn(reason error) error {
|
||||||
if c.cn == nil {
|
if c.cn == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !c.closed {
|
if !c.closed {
|
||||||
internal.Logf("redis: discarding bad PubSub connection: %s", reason)
|
internal.Logger.Printf("redis: discarding bad PubSub connection: %s", reason)
|
||||||
}
|
}
|
||||||
err := c.closeConn(c.cn)
|
err := c.closeConn(c.cn)
|
||||||
c.cn = nil
|
c.cn = nil
|
||||||
|
@ -163,8 +174,7 @@ func (c *PubSub) Close() error {
|
||||||
c.closed = true
|
c.closed = true
|
||||||
close(c.exit)
|
close(c.exit)
|
||||||
|
|
||||||
err := c._closeTheCn(pool.ErrClosed)
|
return c.closeTheCn(pool.ErrClosed)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe the client to the specified channels. It returns
|
// Subscribe the client to the specified channels. It returns
|
||||||
|
@ -226,13 +236,13 @@ func (c *PubSub) PUnsubscribe(patterns ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PubSub) subscribe(redisCmd string, channels ...string) error {
|
func (c *PubSub) subscribe(redisCmd string, channels ...string) error {
|
||||||
cn, err := c._conn(channels)
|
cn, err := c.conn(channels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c._subscribe(cn, redisCmd, channels)
|
err = c._subscribe(cn, redisCmd, channels)
|
||||||
c._releaseConn(cn, err, false)
|
c.releaseConn(cn, err, false)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,13 +253,13 @@ func (c *PubSub) Ping(payload ...string) error {
|
||||||
}
|
}
|
||||||
cmd := NewCmd(args...)
|
cmd := NewCmd(args...)
|
||||||
|
|
||||||
cn, err := c.conn()
|
cn, err := c.connWithLock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.writeCmd(cn, cmd)
|
err = c.writeCmd(context.TODO(), cn, cmd)
|
||||||
c.releaseConn(cn, err, false)
|
c.releaseConnWithLock(cn, err, false)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,16 +345,16 @@ func (c *PubSub) ReceiveTimeout(timeout time.Duration) (interface{}, error) {
|
||||||
c.cmd = NewCmd()
|
c.cmd = NewCmd()
|
||||||
}
|
}
|
||||||
|
|
||||||
cn, err := c.conn()
|
cn, err := c.connWithLock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cn.WithReader(timeout, func(rd *proto.Reader) error {
|
err = cn.WithReader(context.TODO(), timeout, func(rd *proto.Reader) error {
|
||||||
return c.cmd.readReply(rd)
|
return c.cmd.readReply(rd)
|
||||||
})
|
})
|
||||||
|
|
||||||
c.releaseConn(cn, err, timeout > 0)
|
c.releaseConnWithLock(cn, err, timeout > 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -384,25 +394,103 @@ func (c *PubSub) ReceiveMessage() (*Message, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Channel returns a Go channel for concurrently receiving messages.
|
// Channel returns a Go channel for concurrently receiving messages.
|
||||||
// It periodically sends Ping messages to test connection health.
|
// The channel is closed together with the PubSub. If the Go channel
|
||||||
// The channel is closed with PubSub. Receive* APIs can not be used
|
// is blocked full for 30 seconds the message is dropped.
|
||||||
// after channel is created.
|
// Receive* APIs can not be used after channel is created.
|
||||||
|
//
|
||||||
|
// go-redis periodically sends ping messages to test connection health
|
||||||
|
// and re-subscribes if ping can not not received for 30 seconds.
|
||||||
func (c *PubSub) Channel() <-chan *Message {
|
func (c *PubSub) Channel() <-chan *Message {
|
||||||
c.chOnce.Do(c.initChannel)
|
return c.ChannelSize(100)
|
||||||
return c.ch
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PubSub) initChannel() {
|
// ChannelSize is like Channel, but creates a Go channel
|
||||||
c.ch = make(chan *Message, 100)
|
// with specified buffer size.
|
||||||
c.ping = make(chan struct{}, 10)
|
func (c *PubSub) ChannelSize(size int) <-chan *Message {
|
||||||
|
c.chOnce.Do(func() {
|
||||||
|
c.initPing()
|
||||||
|
c.initMsgChan(size)
|
||||||
|
})
|
||||||
|
if c.msgCh == nil {
|
||||||
|
err := fmt.Errorf("redis: Channel can't be called after ChannelWithSubscriptions")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if cap(c.msgCh) != size {
|
||||||
|
err := fmt.Errorf("redis: PubSub.Channel size can not be changed once created")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c.msgCh
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelWithSubscriptions is like Channel, but message type can be either
|
||||||
|
// *Subscription or *Message. Subscription messages can be used to detect
|
||||||
|
// reconnections.
|
||||||
|
//
|
||||||
|
// ChannelWithSubscriptions can not be used together with Channel or ChannelSize.
|
||||||
|
func (c *PubSub) ChannelWithSubscriptions(size int) <-chan interface{} {
|
||||||
|
c.chOnce.Do(func() {
|
||||||
|
c.initPing()
|
||||||
|
c.initAllChan(size)
|
||||||
|
})
|
||||||
|
if c.allCh == nil {
|
||||||
|
err := fmt.Errorf("redis: ChannelWithSubscriptions can't be called after Channel")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if cap(c.allCh) != size {
|
||||||
|
err := fmt.Errorf("redis: PubSub.Channel size can not be changed once created")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return c.allCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PubSub) initPing() {
|
||||||
|
c.ping = make(chan struct{}, 1)
|
||||||
go func() {
|
go func() {
|
||||||
|
timer := time.NewTimer(pingTimeout)
|
||||||
|
timer.Stop()
|
||||||
|
|
||||||
|
healthy := true
|
||||||
|
for {
|
||||||
|
timer.Reset(pingTimeout)
|
||||||
|
select {
|
||||||
|
case <-c.ping:
|
||||||
|
healthy = true
|
||||||
|
if !timer.Stop() {
|
||||||
|
<-timer.C
|
||||||
|
}
|
||||||
|
case <-timer.C:
|
||||||
|
pingErr := c.Ping()
|
||||||
|
if healthy {
|
||||||
|
healthy = false
|
||||||
|
} else {
|
||||||
|
if pingErr == nil {
|
||||||
|
pingErr = errPingTimeout
|
||||||
|
}
|
||||||
|
c.mu.Lock()
|
||||||
|
c.reconnect(pingErr)
|
||||||
|
healthy = true
|
||||||
|
c.mu.Unlock()
|
||||||
|
}
|
||||||
|
case <-c.exit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// initMsgChan must be in sync with initAllChan.
|
||||||
|
func (c *PubSub) initMsgChan(size int) {
|
||||||
|
c.msgCh = make(chan *Message, size)
|
||||||
|
go func() {
|
||||||
|
timer := time.NewTimer(pingTimeout)
|
||||||
|
timer.Stop()
|
||||||
|
|
||||||
var errCount int
|
var errCount int
|
||||||
for {
|
for {
|
||||||
msg, err := c.Receive()
|
msg, err := c.Receive()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == pool.ErrClosed {
|
if err == pool.ErrClosed {
|
||||||
close(c.ch)
|
close(c.msgCh)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if errCount > 0 {
|
if errCount > 0 {
|
||||||
|
@ -411,6 +499,7 @@ func (c *PubSub) initChannel() {
|
||||||
errCount++
|
errCount++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
errCount = 0
|
errCount = 0
|
||||||
|
|
||||||
// Any message is as good as a ping.
|
// Any message is as good as a ping.
|
||||||
|
@ -425,45 +514,80 @@ func (c *PubSub) initChannel() {
|
||||||
case *Pong:
|
case *Pong:
|
||||||
// Ignore.
|
// Ignore.
|
||||||
case *Message:
|
case *Message:
|
||||||
c.ch <- msg
|
timer.Reset(pingTimeout)
|
||||||
default:
|
|
||||||
internal.Logf("redis: unknown message: %T", msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
const timeout = 5 * time.Second
|
|
||||||
|
|
||||||
timer := time.NewTimer(timeout)
|
|
||||||
timer.Stop()
|
|
||||||
|
|
||||||
healthy := true
|
|
||||||
var pingErr error
|
|
||||||
for {
|
|
||||||
timer.Reset(timeout)
|
|
||||||
select {
|
select {
|
||||||
case <-c.ping:
|
case c.msgCh <- msg:
|
||||||
healthy = true
|
|
||||||
if !timer.Stop() {
|
if !timer.Stop() {
|
||||||
<-timer.C
|
<-timer.C
|
||||||
}
|
}
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
pingErr = c.Ping()
|
internal.Logger.Printf(
|
||||||
if healthy {
|
"redis: %s channel is full for %s (message is dropped)", c, pingTimeout)
|
||||||
healthy = false
|
|
||||||
} else {
|
|
||||||
c.mu.Lock()
|
|
||||||
c._reconnect(pingErr)
|
|
||||||
c.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
case <-c.exit:
|
default:
|
||||||
return
|
internal.Logger.Printf("redis: unknown message type: %T", msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initAllChan must be in sync with initMsgChan.
|
||||||
|
func (c *PubSub) initAllChan(size int) {
|
||||||
|
c.allCh = make(chan interface{}, size)
|
||||||
|
go func() {
|
||||||
|
timer := time.NewTimer(pingTimeout)
|
||||||
|
timer.Stop()
|
||||||
|
|
||||||
|
var errCount int
|
||||||
|
for {
|
||||||
|
msg, err := c.Receive()
|
||||||
|
if err != nil {
|
||||||
|
if err == pool.ErrClosed {
|
||||||
|
close(c.allCh)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if errCount > 0 {
|
||||||
|
time.Sleep(c.retryBackoff(errCount))
|
||||||
|
}
|
||||||
|
errCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
errCount = 0
|
||||||
|
|
||||||
|
// Any message is as good as a ping.
|
||||||
|
select {
|
||||||
|
case c.ping <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case *Subscription:
|
||||||
|
c.sendMessage(msg, timer)
|
||||||
|
case *Pong:
|
||||||
|
// Ignore.
|
||||||
|
case *Message:
|
||||||
|
c.sendMessage(msg, timer)
|
||||||
|
default:
|
||||||
|
internal.Logger.Printf("redis: unknown message type: %T", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *PubSub) sendMessage(msg interface{}, timer *time.Timer) {
|
||||||
|
timer.Reset(pingTimeout)
|
||||||
|
select {
|
||||||
|
case c.allCh <- msg:
|
||||||
|
if !timer.Stop() {
|
||||||
|
<-timer.C
|
||||||
|
}
|
||||||
|
case <-timer.C:
|
||||||
|
internal.Logger.Printf(
|
||||||
|
"redis: %s channel is full for %s (message is dropped)", c, pingTimeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *PubSub) retryBackoff(attempt int) time.Duration {
|
func (c *PubSub) retryBackoff(attempt int) time.Duration {
|
||||||
return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
|
return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
|
||||||
}
|
}
|
754
vendor/github.com/go-redis/redis/v7/redis.go
generated
vendored
Normal file
754
vendor/github.com/go-redis/redis/v7/redis.go
generated
vendored
Normal file
|
@ -0,0 +1,754 @@
|
||||||
|
package redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis/v7/internal"
|
||||||
|
"github.com/go-redis/redis/v7/internal/pool"
|
||||||
|
"github.com/go-redis/redis/v7/internal/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Nil reply returned by Redis when key does not exist.
|
||||||
|
const Nil = proto.Nil
|
||||||
|
|
||||||
|
func SetLogger(logger *log.Logger) {
|
||||||
|
internal.Logger = logger
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type Hook interface {
|
||||||
|
BeforeProcess(ctx context.Context, cmd Cmder) (context.Context, error)
|
||||||
|
AfterProcess(ctx context.Context, cmd Cmder) error
|
||||||
|
|
||||||
|
BeforeProcessPipeline(ctx context.Context, cmds []Cmder) (context.Context, error)
|
||||||
|
AfterProcessPipeline(ctx context.Context, cmds []Cmder) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type hooks struct {
|
||||||
|
hooks []Hook
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs *hooks) lock() {
|
||||||
|
hs.hooks = hs.hooks[:len(hs.hooks):len(hs.hooks)]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs hooks) clone() hooks {
|
||||||
|
clone := hs
|
||||||
|
clone.lock()
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs *hooks) AddHook(hook Hook) {
|
||||||
|
hs.hooks = append(hs.hooks, hook)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs hooks) process(
|
||||||
|
ctx context.Context, cmd Cmder, fn func(context.Context, Cmder) error,
|
||||||
|
) error {
|
||||||
|
ctx, err := hs.beforeProcess(ctx, cmd)
|
||||||
|
if err != nil {
|
||||||
|
cmd.SetErr(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdErr := fn(ctx, cmd)
|
||||||
|
|
||||||
|
if err := hs.afterProcess(ctx, cmd); err != nil {
|
||||||
|
cmd.SetErr(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmdErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs hooks) beforeProcess(ctx context.Context, cmd Cmder) (context.Context, error) {
|
||||||
|
for _, h := range hs.hooks {
|
||||||
|
var err error
|
||||||
|
ctx, err = h.BeforeProcess(ctx, cmd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs hooks) afterProcess(ctx context.Context, cmd Cmder) error {
|
||||||
|
var firstErr error
|
||||||
|
for _, h := range hs.hooks {
|
||||||
|
err := h.AfterProcess(ctx, cmd)
|
||||||
|
if err != nil && firstErr == nil {
|
||||||
|
firstErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firstErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs hooks) processPipeline(
|
||||||
|
ctx context.Context, cmds []Cmder, fn func(context.Context, []Cmder) error,
|
||||||
|
) error {
|
||||||
|
ctx, err := hs.beforeProcessPipeline(ctx, cmds)
|
||||||
|
if err != nil {
|
||||||
|
setCmdsErr(cmds, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdsErr := fn(ctx, cmds)
|
||||||
|
|
||||||
|
if err := hs.afterProcessPipeline(ctx, cmds); err != nil {
|
||||||
|
setCmdsErr(cmds, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmdsErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs hooks) beforeProcessPipeline(ctx context.Context, cmds []Cmder) (context.Context, error) {
|
||||||
|
for _, h := range hs.hooks {
|
||||||
|
var err error
|
||||||
|
ctx, err = h.BeforeProcessPipeline(ctx, cmds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs hooks) afterProcessPipeline(ctx context.Context, cmds []Cmder) error {
|
||||||
|
var firstErr error
|
||||||
|
for _, h := range hs.hooks {
|
||||||
|
err := h.AfterProcessPipeline(ctx, cmds)
|
||||||
|
if err != nil && firstErr == nil {
|
||||||
|
firstErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firstErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs hooks) processTxPipeline(
|
||||||
|
ctx context.Context, cmds []Cmder, fn func(context.Context, []Cmder) error,
|
||||||
|
) error {
|
||||||
|
cmds = wrapMultiExec(cmds)
|
||||||
|
return hs.processPipeline(ctx, cmds, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type baseClient struct {
|
||||||
|
opt *Options
|
||||||
|
connPool pool.Pooler
|
||||||
|
|
||||||
|
onClose func() error // hook called when client is closed
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBaseClient(opt *Options, connPool pool.Pooler) *baseClient {
|
||||||
|
return &baseClient{
|
||||||
|
opt: opt,
|
||||||
|
connPool: connPool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) clone() *baseClient {
|
||||||
|
clone := *c
|
||||||
|
return &clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) withTimeout(timeout time.Duration) *baseClient {
|
||||||
|
opt := c.opt.clone()
|
||||||
|
opt.ReadTimeout = timeout
|
||||||
|
opt.WriteTimeout = timeout
|
||||||
|
|
||||||
|
clone := c.clone()
|
||||||
|
clone.opt = opt
|
||||||
|
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) String() string {
|
||||||
|
return fmt.Sprintf("Redis<%s db:%d>", c.getAddr(), c.opt.DB)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) newConn(ctx context.Context) (*pool.Conn, error) {
|
||||||
|
cn, err := c.connPool.NewConn(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.initConn(ctx, cn)
|
||||||
|
if err != nil {
|
||||||
|
_ = c.connPool.CloseConn(cn)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) getConn(ctx context.Context) (*pool.Conn, error) {
|
||||||
|
if c.opt.Limiter != nil {
|
||||||
|
err := c.opt.Limiter.Allow()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cn, err := c._getConn(ctx)
|
||||||
|
if err != nil {
|
||||||
|
if c.opt.Limiter != nil {
|
||||||
|
c.opt.Limiter.ReportResult(err)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) _getConn(ctx context.Context) (*pool.Conn, error) {
|
||||||
|
cn, err := c.connPool.Get(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.initConn(ctx, cn)
|
||||||
|
if err != nil {
|
||||||
|
c.connPool.Remove(cn, err)
|
||||||
|
if err := internal.Unwrap(err); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
|
||||||
|
if cn.Inited {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cn.Inited = true
|
||||||
|
|
||||||
|
if c.opt.Password == "" &&
|
||||||
|
c.opt.DB == 0 &&
|
||||||
|
!c.opt.readOnly &&
|
||||||
|
c.opt.OnConnect == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
connPool := pool.NewSingleConnPool(nil)
|
||||||
|
connPool.SetConn(cn)
|
||||||
|
conn := newConn(ctx, c.opt, connPool)
|
||||||
|
|
||||||
|
_, err := conn.Pipelined(func(pipe Pipeliner) error {
|
||||||
|
if c.opt.Password != "" {
|
||||||
|
pipe.Auth(c.opt.Password)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.opt.DB > 0 {
|
||||||
|
pipe.Select(c.opt.DB)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.opt.readOnly {
|
||||||
|
pipe.ReadOnly()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.opt.OnConnect != nil {
|
||||||
|
return c.opt.OnConnect(conn)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) releaseConn(cn *pool.Conn, err error) {
|
||||||
|
if c.opt.Limiter != nil {
|
||||||
|
c.opt.Limiter.ReportResult(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isBadConn(err, false) {
|
||||||
|
c.connPool.Remove(cn, err)
|
||||||
|
} else {
|
||||||
|
c.connPool.Put(cn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) withConn(
|
||||||
|
ctx context.Context, fn func(context.Context, *pool.Conn) error,
|
||||||
|
) error {
|
||||||
|
cn, err := c.getConn(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
c.releaseConn(cn, err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = fn(ctx, cn)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) process(ctx context.Context, cmd Cmder) error {
|
||||||
|
err := c._process(ctx, cmd)
|
||||||
|
if err != nil {
|
||||||
|
cmd.SetErr(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) _process(ctx context.Context, cmd Cmder) error {
|
||||||
|
var lastErr error
|
||||||
|
for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
|
||||||
|
if attempt > 0 {
|
||||||
|
if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retryTimeout := true
|
||||||
|
lastErr = c.withConn(ctx, func(ctx context.Context, cn *pool.Conn) error {
|
||||||
|
err := cn.WithWriter(ctx, c.opt.WriteTimeout, func(wr *proto.Writer) error {
|
||||||
|
return writeCmd(wr, cmd)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cn.WithReader(ctx, c.cmdTimeout(cmd), cmd.readReply)
|
||||||
|
if err != nil {
|
||||||
|
retryTimeout = cmd.readTimeout() == nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if lastErr == nil || !isRetryableError(lastErr, retryTimeout) {
|
||||||
|
return lastErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lastErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) retryBackoff(attempt int) time.Duration {
|
||||||
|
return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) cmdTimeout(cmd Cmder) time.Duration {
|
||||||
|
if timeout := cmd.readTimeout(); timeout != nil {
|
||||||
|
t := *timeout
|
||||||
|
if t == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return t + 10*time.Second
|
||||||
|
}
|
||||||
|
return c.opt.ReadTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the client, releasing any open resources.
|
||||||
|
//
|
||||||
|
// It is rare to Close a Client, as the Client is meant to be
|
||||||
|
// long-lived and shared between many goroutines.
|
||||||
|
func (c *baseClient) Close() error {
|
||||||
|
var firstErr error
|
||||||
|
if c.onClose != nil {
|
||||||
|
if err := c.onClose(); err != nil {
|
||||||
|
firstErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := c.connPool.Close(); err != nil && firstErr == nil {
|
||||||
|
firstErr = err
|
||||||
|
}
|
||||||
|
return firstErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) getAddr() string {
|
||||||
|
return c.opt.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) processPipeline(ctx context.Context, cmds []Cmder) error {
|
||||||
|
return c.generalProcessPipeline(ctx, cmds, c.pipelineProcessCmds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) processTxPipeline(ctx context.Context, cmds []Cmder) error {
|
||||||
|
return c.generalProcessPipeline(ctx, cmds, c.txPipelineProcessCmds)
|
||||||
|
}
|
||||||
|
|
||||||
|
type pipelineProcessor func(context.Context, *pool.Conn, []Cmder) (bool, error)
|
||||||
|
|
||||||
|
func (c *baseClient) generalProcessPipeline(
|
||||||
|
ctx context.Context, cmds []Cmder, p pipelineProcessor,
|
||||||
|
) error {
|
||||||
|
err := c._generalProcessPipeline(ctx, cmds, p)
|
||||||
|
if err != nil {
|
||||||
|
setCmdsErr(cmds, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return cmdsFirstErr(cmds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) _generalProcessPipeline(
|
||||||
|
ctx context.Context, cmds []Cmder, p pipelineProcessor,
|
||||||
|
) error {
|
||||||
|
var lastErr error
|
||||||
|
for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
|
||||||
|
if attempt > 0 {
|
||||||
|
if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var canRetry bool
|
||||||
|
lastErr = c.withConn(ctx, func(ctx context.Context, cn *pool.Conn) error {
|
||||||
|
var err error
|
||||||
|
canRetry, err = p(ctx, cn, cmds)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if lastErr == nil || !canRetry || !isRetryableError(lastErr, true) {
|
||||||
|
return lastErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lastErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) pipelineProcessCmds(
|
||||||
|
ctx context.Context, cn *pool.Conn, cmds []Cmder,
|
||||||
|
) (bool, error) {
|
||||||
|
err := cn.WithWriter(ctx, c.opt.WriteTimeout, func(wr *proto.Writer) error {
|
||||||
|
return writeCmds(wr, cmds)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cn.WithReader(ctx, c.opt.ReadTimeout, func(rd *proto.Reader) error {
|
||||||
|
return pipelineReadCmds(rd, cmds)
|
||||||
|
})
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func pipelineReadCmds(rd *proto.Reader, cmds []Cmder) error {
|
||||||
|
for _, cmd := range cmds {
|
||||||
|
err := cmd.readReply(rd)
|
||||||
|
if err != nil && !isRedisError(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) txPipelineProcessCmds(
|
||||||
|
ctx context.Context, cn *pool.Conn, cmds []Cmder,
|
||||||
|
) (bool, error) {
|
||||||
|
err := cn.WithWriter(ctx, c.opt.WriteTimeout, func(wr *proto.Writer) error {
|
||||||
|
return writeCmds(wr, cmds)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cn.WithReader(ctx, c.opt.ReadTimeout, func(rd *proto.Reader) error {
|
||||||
|
statusCmd := cmds[0].(*StatusCmd)
|
||||||
|
// Trim multi and exec.
|
||||||
|
cmds = cmds[1 : len(cmds)-1]
|
||||||
|
|
||||||
|
err := txPipelineReadQueued(rd, statusCmd, cmds)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pipelineReadCmds(rd, cmds)
|
||||||
|
})
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapMultiExec(cmds []Cmder) []Cmder {
|
||||||
|
if len(cmds) == 0 {
|
||||||
|
panic("not reached")
|
||||||
|
}
|
||||||
|
cmds = append(cmds, make([]Cmder, 2)...)
|
||||||
|
copy(cmds[1:], cmds[:len(cmds)-2])
|
||||||
|
cmds[0] = NewStatusCmd("multi")
|
||||||
|
cmds[len(cmds)-1] = NewSliceCmd("exec")
|
||||||
|
return cmds
|
||||||
|
}
|
||||||
|
|
||||||
|
func txPipelineReadQueued(rd *proto.Reader, statusCmd *StatusCmd, cmds []Cmder) error {
|
||||||
|
// Parse queued replies.
|
||||||
|
if err := statusCmd.readReply(rd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for range cmds {
|
||||||
|
if err := statusCmd.readReply(rd); err != nil && !isRedisError(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse number of replies.
|
||||||
|
line, err := rd.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
if err == Nil {
|
||||||
|
err = TxFailedErr
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch line[0] {
|
||||||
|
case proto.ErrorReply:
|
||||||
|
return proto.ParseErrorReply(line)
|
||||||
|
case proto.ArrayReply:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
err := fmt.Errorf("redis: expected '*', but got line %q", line)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Client is a Redis client representing a pool of zero or more
|
||||||
|
// underlying connections. It's safe for concurrent use by multiple
|
||||||
|
// goroutines.
|
||||||
|
type Client struct {
|
||||||
|
*baseClient
|
||||||
|
cmdable
|
||||||
|
hooks
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a client to the Redis Server specified by Options.
|
||||||
|
func NewClient(opt *Options) *Client {
|
||||||
|
opt.init()
|
||||||
|
|
||||||
|
c := Client{
|
||||||
|
baseClient: newBaseClient(opt, newConnPool(opt)),
|
||||||
|
ctx: context.Background(),
|
||||||
|
}
|
||||||
|
c.cmdable = c.Process
|
||||||
|
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) clone() *Client {
|
||||||
|
clone := *c
|
||||||
|
clone.cmdable = clone.Process
|
||||||
|
clone.hooks.lock()
|
||||||
|
return &clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) WithTimeout(timeout time.Duration) *Client {
|
||||||
|
clone := c.clone()
|
||||||
|
clone.baseClient = c.baseClient.withTimeout(timeout)
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Context() context.Context {
|
||||||
|
return c.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) WithContext(ctx context.Context) *Client {
|
||||||
|
if ctx == nil {
|
||||||
|
panic("nil context")
|
||||||
|
}
|
||||||
|
clone := c.clone()
|
||||||
|
clone.ctx = ctx
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Conn() *Conn {
|
||||||
|
return newConn(c.ctx, c.opt, pool.NewSingleConnPool(c.connPool))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do creates a Cmd from the args and processes the cmd.
|
||||||
|
func (c *Client) Do(args ...interface{}) *Cmd {
|
||||||
|
return c.DoContext(c.ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) DoContext(ctx context.Context, args ...interface{}) *Cmd {
|
||||||
|
cmd := NewCmd(args...)
|
||||||
|
_ = c.ProcessContext(ctx, cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Process(cmd Cmder) error {
|
||||||
|
return c.ProcessContext(c.ctx, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) ProcessContext(ctx context.Context, cmd Cmder) error {
|
||||||
|
return c.hooks.process(ctx, cmd, c.baseClient.process)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) processPipeline(ctx context.Context, cmds []Cmder) error {
|
||||||
|
return c.hooks.processPipeline(ctx, cmds, c.baseClient.processPipeline)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) processTxPipeline(ctx context.Context, cmds []Cmder) error {
|
||||||
|
return c.hooks.processTxPipeline(ctx, cmds, c.baseClient.processTxPipeline)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options returns read-only Options that were used to create the client.
|
||||||
|
func (c *Client) Options() *Options {
|
||||||
|
return c.opt
|
||||||
|
}
|
||||||
|
|
||||||
|
type PoolStats pool.Stats
|
||||||
|
|
||||||
|
// PoolStats returns connection pool stats.
|
||||||
|
func (c *Client) PoolStats() *PoolStats {
|
||||||
|
stats := c.connPool.Stats()
|
||||||
|
return (*PoolStats)(stats)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||||
|
return c.Pipeline().Pipelined(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Pipeline() Pipeliner {
|
||||||
|
pipe := Pipeline{
|
||||||
|
ctx: c.ctx,
|
||||||
|
exec: c.processPipeline,
|
||||||
|
}
|
||||||
|
pipe.init()
|
||||||
|
return &pipe
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||||
|
return c.TxPipeline().Pipelined(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.
|
||||||
|
func (c *Client) TxPipeline() Pipeliner {
|
||||||
|
pipe := Pipeline{
|
||||||
|
ctx: c.ctx,
|
||||||
|
exec: c.processTxPipeline,
|
||||||
|
}
|
||||||
|
pipe.init()
|
||||||
|
return &pipe
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) pubSub() *PubSub {
|
||||||
|
pubsub := &PubSub{
|
||||||
|
opt: c.opt,
|
||||||
|
|
||||||
|
newConn: func(channels []string) (*pool.Conn, error) {
|
||||||
|
return c.newConn(context.TODO())
|
||||||
|
},
|
||||||
|
closeConn: c.connPool.CloseConn,
|
||||||
|
}
|
||||||
|
pubsub.init()
|
||||||
|
return pubsub
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe subscribes the client to the specified channels.
|
||||||
|
// Channels can be omitted to create empty subscription.
|
||||||
|
// Note that this method does not wait on a response from Redis, so the
|
||||||
|
// subscription may not be active immediately. To force the connection to wait,
|
||||||
|
// you may call the Receive() method on the returned *PubSub like so:
|
||||||
|
//
|
||||||
|
// sub := client.Subscribe(queryResp)
|
||||||
|
// iface, err := sub.Receive()
|
||||||
|
// if err != nil {
|
||||||
|
// // handle error
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Should be *Subscription, but others are possible if other actions have been
|
||||||
|
// // taken on sub since it was created.
|
||||||
|
// switch iface.(type) {
|
||||||
|
// case *Subscription:
|
||||||
|
// // subscribe succeeded
|
||||||
|
// case *Message:
|
||||||
|
// // received first message
|
||||||
|
// case *Pong:
|
||||||
|
// // pong received
|
||||||
|
// default:
|
||||||
|
// // handle error
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ch := sub.Channel()
|
||||||
|
func (c *Client) Subscribe(channels ...string) *PubSub {
|
||||||
|
pubsub := c.pubSub()
|
||||||
|
if len(channels) > 0 {
|
||||||
|
_ = pubsub.Subscribe(channels...)
|
||||||
|
}
|
||||||
|
return pubsub
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSubscribe subscribes the client to the given patterns.
|
||||||
|
// Patterns can be omitted to create empty subscription.
|
||||||
|
func (c *Client) PSubscribe(channels ...string) *PubSub {
|
||||||
|
pubsub := c.pubSub()
|
||||||
|
if len(channels) > 0 {
|
||||||
|
_ = pubsub.PSubscribe(channels...)
|
||||||
|
}
|
||||||
|
return pubsub
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type conn struct {
|
||||||
|
baseClient
|
||||||
|
cmdable
|
||||||
|
statefulCmdable
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conn is like Client, but its pool contains single connection.
|
||||||
|
type Conn struct {
|
||||||
|
*conn
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConn(ctx context.Context, opt *Options, connPool pool.Pooler) *Conn {
|
||||||
|
c := Conn{
|
||||||
|
conn: &conn{
|
||||||
|
baseClient: baseClient{
|
||||||
|
opt: opt,
|
||||||
|
connPool: connPool,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
c.cmdable = c.Process
|
||||||
|
c.statefulCmdable = c.Process
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) Process(cmd Cmder) error {
|
||||||
|
return c.ProcessContext(c.ctx, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) ProcessContext(ctx context.Context, cmd Cmder) error {
|
||||||
|
return c.baseClient.process(ctx, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||||
|
return c.Pipeline().Pipelined(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) Pipeline() Pipeliner {
|
||||||
|
pipe := Pipeline{
|
||||||
|
ctx: c.ctx,
|
||||||
|
exec: c.processPipeline,
|
||||||
|
}
|
||||||
|
pipe.init()
|
||||||
|
return &pipe
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||||
|
return c.TxPipeline().Pipelined(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.
|
||||||
|
func (c *Conn) TxPipeline() Pipeliner {
|
||||||
|
pipe := Pipeline{
|
||||||
|
ctx: c.ctx,
|
||||||
|
exec: c.processTxPipeline,
|
||||||
|
}
|
||||||
|
pipe.init()
|
||||||
|
return &pipe
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import "time"
|
||||||
func NewCmdResult(val interface{}, err error) *Cmd {
|
func NewCmdResult(val interface{}, err error) *Cmd {
|
||||||
var cmd Cmd
|
var cmd Cmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ func NewCmdResult(val interface{}, err error) *Cmd {
|
||||||
func NewSliceResult(val []interface{}, err error) *SliceCmd {
|
func NewSliceResult(val []interface{}, err error) *SliceCmd {
|
||||||
var cmd SliceCmd
|
var cmd SliceCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ func NewSliceResult(val []interface{}, err error) *SliceCmd {
|
||||||
func NewStatusResult(val string, err error) *StatusCmd {
|
func NewStatusResult(val string, err error) *StatusCmd {
|
||||||
var cmd StatusCmd
|
var cmd StatusCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ func NewStatusResult(val string, err error) *StatusCmd {
|
||||||
func NewIntResult(val int64, err error) *IntCmd {
|
func NewIntResult(val int64, err error) *IntCmd {
|
||||||
var cmd IntCmd
|
var cmd IntCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ func NewIntResult(val int64, err error) *IntCmd {
|
||||||
func NewDurationResult(val time.Duration, err error) *DurationCmd {
|
func NewDurationResult(val time.Duration, err error) *DurationCmd {
|
||||||
var cmd DurationCmd
|
var cmd DurationCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ func NewDurationResult(val time.Duration, err error) *DurationCmd {
|
||||||
func NewBoolResult(val bool, err error) *BoolCmd {
|
func NewBoolResult(val bool, err error) *BoolCmd {
|
||||||
var cmd BoolCmd
|
var cmd BoolCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ func NewBoolResult(val bool, err error) *BoolCmd {
|
||||||
func NewStringResult(val string, err error) *StringCmd {
|
func NewStringResult(val string, err error) *StringCmd {
|
||||||
var cmd StringCmd
|
var cmd StringCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ func NewStringResult(val string, err error) *StringCmd {
|
||||||
func NewFloatResult(val float64, err error) *FloatCmd {
|
func NewFloatResult(val float64, err error) *FloatCmd {
|
||||||
var cmd FloatCmd
|
var cmd FloatCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ func NewFloatResult(val float64, err error) *FloatCmd {
|
||||||
func NewStringSliceResult(val []string, err error) *StringSliceCmd {
|
func NewStringSliceResult(val []string, err error) *StringSliceCmd {
|
||||||
var cmd StringSliceCmd
|
var cmd StringSliceCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ func NewStringSliceResult(val []string, err error) *StringSliceCmd {
|
||||||
func NewBoolSliceResult(val []bool, err error) *BoolSliceCmd {
|
func NewBoolSliceResult(val []bool, err error) *BoolSliceCmd {
|
||||||
var cmd BoolSliceCmd
|
var cmd BoolSliceCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ func NewBoolSliceResult(val []bool, err error) *BoolSliceCmd {
|
||||||
func NewStringStringMapResult(val map[string]string, err error) *StringStringMapCmd {
|
func NewStringStringMapResult(val map[string]string, err error) *StringStringMapCmd {
|
||||||
var cmd StringStringMapCmd
|
var cmd StringStringMapCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ func NewStringStringMapResult(val map[string]string, err error) *StringStringMap
|
||||||
func NewStringIntMapCmdResult(val map[string]int64, err error) *StringIntMapCmd {
|
func NewStringIntMapCmdResult(val map[string]int64, err error) *StringIntMapCmd {
|
||||||
var cmd StringIntMapCmd
|
var cmd StringIntMapCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,15 @@ func NewStringIntMapCmdResult(val map[string]int64, err error) *StringIntMapCmd
|
||||||
func NewZSliceCmdResult(val []Z, err error) *ZSliceCmd {
|
func NewZSliceCmdResult(val []Z, err error) *ZSliceCmd {
|
||||||
var cmd ZSliceCmd
|
var cmd ZSliceCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
|
return &cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewZWithKeyCmdResult returns a NewZWithKeyCmd initialised with val and err for testing
|
||||||
|
func NewZWithKeyCmdResult(val *ZWithKey, err error) *ZWithKeyCmd {
|
||||||
|
var cmd ZWithKeyCmd
|
||||||
|
cmd.val = val
|
||||||
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +119,7 @@ func NewScanCmdResult(keys []string, cursor uint64, err error) *ScanCmd {
|
||||||
var cmd ScanCmd
|
var cmd ScanCmd
|
||||||
cmd.page = keys
|
cmd.page = keys
|
||||||
cmd.cursor = cursor
|
cmd.cursor = cursor
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +127,7 @@ func NewScanCmdResult(keys []string, cursor uint64, err error) *ScanCmd {
|
||||||
func NewClusterSlotsCmdResult(val []ClusterSlot, err error) *ClusterSlotsCmd {
|
func NewClusterSlotsCmdResult(val []ClusterSlot, err error) *ClusterSlotsCmd {
|
||||||
var cmd ClusterSlotsCmd
|
var cmd ClusterSlotsCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +135,15 @@ func NewClusterSlotsCmdResult(val []ClusterSlot, err error) *ClusterSlotsCmd {
|
||||||
func NewGeoLocationCmdResult(val []GeoLocation, err error) *GeoLocationCmd {
|
func NewGeoLocationCmdResult(val []GeoLocation, err error) *GeoLocationCmd {
|
||||||
var cmd GeoLocationCmd
|
var cmd GeoLocationCmd
|
||||||
cmd.locations = val
|
cmd.locations = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
|
return &cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGeoPosCmdResult returns a GeoPosCmd initialised with val and err for testing
|
||||||
|
func NewGeoPosCmdResult(val []*GeoPos, err error) *GeoPosCmd {
|
||||||
|
var cmd GeoPosCmd
|
||||||
|
cmd.val = val
|
||||||
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +151,22 @@ func NewGeoLocationCmdResult(val []GeoLocation, err error) *GeoLocationCmd {
|
||||||
func NewCommandsInfoCmdResult(val map[string]*CommandInfo, err error) *CommandsInfoCmd {
|
func NewCommandsInfoCmdResult(val map[string]*CommandInfo, err error) *CommandsInfoCmd {
|
||||||
var cmd CommandsInfoCmd
|
var cmd CommandsInfoCmd
|
||||||
cmd.val = val
|
cmd.val = val
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
|
return &cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewXMessageSliceCmdResult returns a XMessageSliceCmd initialised with val and err for testing
|
||||||
|
func NewXMessageSliceCmdResult(val []XMessage, err error) *XMessageSliceCmd {
|
||||||
|
var cmd XMessageSliceCmd
|
||||||
|
cmd.val = val
|
||||||
|
cmd.SetErr(err)
|
||||||
|
return &cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewXStreamSliceCmdResult returns a XStreamSliceCmd initialised with val and err for testing
|
||||||
|
func NewXStreamSliceCmdResult(val []XStream, err error) *XStreamSliceCmd {
|
||||||
|
var cmd XStreamSliceCmd
|
||||||
|
cmd.val = val
|
||||||
|
cmd.SetErr(err)
|
||||||
return &cmd
|
return &cmd
|
||||||
}
|
}
|
297
vendor/github.com/go-redis/redis/ring.go → vendor/github.com/go-redis/redis/v7/ring.go
generated
vendored
297
vendor/github.com/go-redis/redis/ring.go → vendor/github.com/go-redis/redis/v7/ring.go
generated
vendored
|
@ -10,10 +10,10 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-redis/redis/internal"
|
"github.com/go-redis/redis/v7/internal"
|
||||||
"github.com/go-redis/redis/internal/consistenthash"
|
"github.com/go-redis/redis/v7/internal/consistenthash"
|
||||||
"github.com/go-redis/redis/internal/hashtag"
|
"github.com/go-redis/redis/v7/internal/hashtag"
|
||||||
"github.com/go-redis/redis/internal/pool"
|
"github.com/go-redis/redis/v7/internal/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hash is type of hash function used in consistent hash.
|
// Hash is type of hash function used in consistent hash.
|
||||||
|
@ -27,6 +27,10 @@ type RingOptions struct {
|
||||||
// Map of name => host:port addresses of ring shards.
|
// Map of name => host:port addresses of ring shards.
|
||||||
Addrs map[string]string
|
Addrs map[string]string
|
||||||
|
|
||||||
|
// Map of name => password of ring shards, to allow different shards to have
|
||||||
|
// different passwords. It will be ignored if the Password field is set.
|
||||||
|
Passwords map[string]string
|
||||||
|
|
||||||
// Frequency of PING commands sent to check shards availability.
|
// Frequency of PING commands sent to check shards availability.
|
||||||
// Shard is considered down after 3 subsequent failed checks.
|
// Shard is considered down after 3 subsequent failed checks.
|
||||||
HeartbeatFrequency time.Duration
|
HeartbeatFrequency time.Duration
|
||||||
|
@ -52,6 +56,9 @@ type RingOptions struct {
|
||||||
// See https://arxiv.org/abs/1406.2294 for reference
|
// See https://arxiv.org/abs/1406.2294 for reference
|
||||||
HashReplicas int
|
HashReplicas int
|
||||||
|
|
||||||
|
// Optional hook that is called when a new shard is created.
|
||||||
|
OnNewShard func(*Client)
|
||||||
|
|
||||||
// Following options are copied from Options struct.
|
// Following options are copied from Options struct.
|
||||||
|
|
||||||
OnConnect func(*Conn) error
|
OnConnect func(*Conn) error
|
||||||
|
@ -98,12 +105,12 @@ func (opt *RingOptions) init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (opt *RingOptions) clientOptions() *Options {
|
func (opt *RingOptions) clientOptions(shard string) *Options {
|
||||||
return &Options{
|
return &Options{
|
||||||
OnConnect: opt.OnConnect,
|
OnConnect: opt.OnConnect,
|
||||||
|
|
||||||
DB: opt.DB,
|
DB: opt.DB,
|
||||||
Password: opt.Password,
|
Password: opt.getPassword(shard),
|
||||||
|
|
||||||
DialTimeout: opt.DialTimeout,
|
DialTimeout: opt.DialTimeout,
|
||||||
ReadTimeout: opt.ReadTimeout,
|
ReadTimeout: opt.ReadTimeout,
|
||||||
|
@ -118,6 +125,13 @@ func (opt *RingOptions) clientOptions() *Options {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (opt *RingOptions) getPassword(shard string) string {
|
||||||
|
if opt.Password == "" {
|
||||||
|
return opt.Passwords[shard]
|
||||||
|
}
|
||||||
|
return opt.Password
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
type ringShard struct {
|
type ringShard struct {
|
||||||
|
@ -260,7 +274,7 @@ func (c *ringShards) Heartbeat(frequency time.Duration) {
|
||||||
for _, shard := range shards {
|
for _, shard := range shards {
|
||||||
err := shard.Client.Ping().Err()
|
err := shard.Client.Ping().Err()
|
||||||
if shard.Vote(err == nil || err == pool.ErrPoolTimeout) {
|
if shard.Vote(err == nil || err == pool.ErrPoolTimeout) {
|
||||||
internal.Logf("ring shard state changed: %s", shard)
|
internal.Logger.Printf("ring shard state changed: %s", shard)
|
||||||
rebalance = true
|
rebalance = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,9 +287,13 @@ func (c *ringShards) Heartbeat(frequency time.Duration) {
|
||||||
|
|
||||||
// rebalance removes dead shards from the Ring.
|
// rebalance removes dead shards from the Ring.
|
||||||
func (c *ringShards) rebalance() {
|
func (c *ringShards) rebalance() {
|
||||||
|
c.mu.RLock()
|
||||||
|
shards := c.shards
|
||||||
|
c.mu.RUnlock()
|
||||||
|
|
||||||
hash := newConsistentHash(c.opt)
|
hash := newConsistentHash(c.opt)
|
||||||
var shardsNum int
|
var shardsNum int
|
||||||
for name, shard := range c.shards {
|
for name, shard := range shards {
|
||||||
if shard.IsUp() {
|
if shard.IsUp() {
|
||||||
hash.Add(name)
|
hash.Add(name)
|
||||||
shardsNum++
|
shardsNum++
|
||||||
|
@ -319,12 +337,18 @@ func (c *ringShards) Close() error {
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Ring is a Redis client that uses constistent hashing to distribute
|
type ring struct {
|
||||||
|
opt *RingOptions
|
||||||
|
shards *ringShards
|
||||||
|
cmdsInfoCache *cmdsInfoCache //nolint:structcheck
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ring is a Redis client that uses consistent hashing to distribute
|
||||||
// keys across multiple Redis servers (shards). It's safe for
|
// keys across multiple Redis servers (shards). It's safe for
|
||||||
// concurrent use by multiple goroutines.
|
// concurrent use by multiple goroutines.
|
||||||
//
|
//
|
||||||
// Ring monitors the state of each shard and removes dead shards from
|
// Ring monitors the state of each shard and removes dead shards from
|
||||||
// the ring. When shard comes online it is added back to the ring. This
|
// the ring. When a shard comes online it is added back to the ring. This
|
||||||
// gives you maximum availability and partition tolerance, but no
|
// gives you maximum availability and partition tolerance, but no
|
||||||
// consistency between different shards or even clients. Each client
|
// consistency between different shards or even clients. Each client
|
||||||
// uses shards that are available to the client and does not do any
|
// uses shards that are available to the client and does not do any
|
||||||
|
@ -334,59 +358,77 @@ func (c *ringShards) Close() error {
|
||||||
// and can tolerate losing data when one of the servers dies.
|
// and can tolerate losing data when one of the servers dies.
|
||||||
// Otherwise you should use Redis Cluster.
|
// Otherwise you should use Redis Cluster.
|
||||||
type Ring struct {
|
type Ring struct {
|
||||||
|
*ring
|
||||||
cmdable
|
cmdable
|
||||||
|
hooks
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
|
||||||
opt *RingOptions
|
|
||||||
shards *ringShards
|
|
||||||
cmdsInfoCache *cmdsInfoCache
|
|
||||||
|
|
||||||
processPipeline func([]Cmder) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRing(opt *RingOptions) *Ring {
|
func NewRing(opt *RingOptions) *Ring {
|
||||||
opt.init()
|
opt.init()
|
||||||
|
|
||||||
ring := &Ring{
|
ring := Ring{
|
||||||
|
ring: &ring{
|
||||||
opt: opt,
|
opt: opt,
|
||||||
shards: newRingShards(opt),
|
shards: newRingShards(opt),
|
||||||
|
},
|
||||||
|
ctx: context.Background(),
|
||||||
}
|
}
|
||||||
ring.cmdsInfoCache = newCmdsInfoCache(ring.cmdsInfo)
|
ring.cmdsInfoCache = newCmdsInfoCache(ring.cmdsInfo)
|
||||||
|
ring.cmdable = ring.Process
|
||||||
ring.processPipeline = ring.defaultProcessPipeline
|
|
||||||
ring.cmdable.setProcessor(ring.Process)
|
|
||||||
|
|
||||||
for name, addr := range opt.Addrs {
|
for name, addr := range opt.Addrs {
|
||||||
clopt := opt.clientOptions()
|
shard := newRingShard(opt, name, addr)
|
||||||
clopt.Addr = addr
|
ring.shards.Add(name, shard)
|
||||||
ring.shards.Add(name, NewClient(clopt))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
go ring.shards.Heartbeat(opt.HeartbeatFrequency)
|
go ring.shards.Heartbeat(opt.HeartbeatFrequency)
|
||||||
|
|
||||||
return ring
|
return &ring
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRingShard(opt *RingOptions, name, addr string) *Client {
|
||||||
|
clopt := opt.clientOptions(name)
|
||||||
|
clopt.Addr = addr
|
||||||
|
shard := NewClient(clopt)
|
||||||
|
if opt.OnNewShard != nil {
|
||||||
|
opt.OnNewShard(shard)
|
||||||
|
}
|
||||||
|
return shard
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Ring) Context() context.Context {
|
func (c *Ring) Context() context.Context {
|
||||||
if c.ctx != nil {
|
|
||||||
return c.ctx
|
return c.ctx
|
||||||
}
|
}
|
||||||
return context.Background()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Ring) WithContext(ctx context.Context) *Ring {
|
func (c *Ring) WithContext(ctx context.Context) *Ring {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
panic("nil context")
|
panic("nil context")
|
||||||
}
|
}
|
||||||
c2 := c.copy()
|
clone := *c
|
||||||
c2.ctx = ctx
|
clone.cmdable = clone.Process
|
||||||
return c2
|
clone.hooks.lock()
|
||||||
|
clone.ctx = ctx
|
||||||
|
return &clone
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Ring) copy() *Ring {
|
// Do creates a Cmd from the args and processes the cmd.
|
||||||
cp := *c
|
func (c *Ring) Do(args ...interface{}) *Cmd {
|
||||||
return &cp
|
return c.DoContext(c.ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Ring) DoContext(ctx context.Context, args ...interface{}) *Cmd {
|
||||||
|
cmd := NewCmd(args...)
|
||||||
|
_ = c.ProcessContext(ctx, cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Ring) Process(cmd Cmder) error {
|
||||||
|
return c.ProcessContext(c.ctx, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Ring) ProcessContext(ctx context.Context, cmd Cmder) error {
|
||||||
|
return c.hooks.process(ctx, cmd, c.process)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options returns read-only Options that were used to create the client.
|
// Options returns read-only Options that were used to create the client.
|
||||||
|
@ -501,7 +543,7 @@ func (c *Ring) cmdInfo(name string) *CommandInfo {
|
||||||
}
|
}
|
||||||
info := cmdsInfo[name]
|
info := cmdsInfo[name]
|
||||||
if info == nil {
|
if info == nil {
|
||||||
internal.Logf("info for cmd=%s not found", name)
|
internal.Logger.Printf("info for cmd=%s not found", name)
|
||||||
}
|
}
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
@ -516,50 +558,78 @@ func (c *Ring) cmdShard(cmd Cmder) (*ringShard, error) {
|
||||||
return c.shards.GetByKey(firstKey)
|
return c.shards.GetByKey(firstKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do creates a Cmd from the args and processes the cmd.
|
func (c *Ring) process(ctx context.Context, cmd Cmder) error {
|
||||||
func (c *Ring) Do(args ...interface{}) *Cmd {
|
err := c._process(ctx, cmd)
|
||||||
cmd := NewCmd(args...)
|
|
||||||
c.Process(cmd)
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Ring) WrapProcess(
|
|
||||||
fn func(oldProcess func(cmd Cmder) error) func(cmd Cmder) error,
|
|
||||||
) {
|
|
||||||
c.ForEachShard(func(c *Client) error {
|
|
||||||
c.WrapProcess(fn)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Ring) Process(cmd Cmder) error {
|
|
||||||
shard, err := c.cmdShard(cmd)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmd.setErr(err)
|
cmd.SetErr(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return shard.Client.Process(cmd)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Ring) Pipeline() Pipeliner {
|
func (c *Ring) _process(ctx context.Context, cmd Cmder) error {
|
||||||
pipe := Pipeline{
|
var lastErr error
|
||||||
exec: c.processPipeline,
|
for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
|
||||||
|
if attempt > 0 {
|
||||||
|
if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
pipe.cmdable.setProcessor(pipe.Process)
|
}
|
||||||
return &pipe
|
|
||||||
|
shard, err := c.cmdShard(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
lastErr = shard.Client.ProcessContext(ctx, cmd)
|
||||||
|
if lastErr == nil || !isRetryableError(lastErr, cmd.readTimeout() == nil) {
|
||||||
|
return lastErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lastErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Ring) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
func (c *Ring) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||||
return c.Pipeline().Pipelined(fn)
|
return c.Pipeline().Pipelined(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Ring) WrapProcessPipeline(
|
func (c *Ring) Pipeline() Pipeliner {
|
||||||
fn func(oldProcess func([]Cmder) error) func([]Cmder) error,
|
pipe := Pipeline{
|
||||||
) {
|
ctx: c.ctx,
|
||||||
c.processPipeline = fn(c.processPipeline)
|
exec: c.processPipeline,
|
||||||
|
}
|
||||||
|
pipe.init()
|
||||||
|
return &pipe
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Ring) defaultProcessPipeline(cmds []Cmder) error {
|
func (c *Ring) processPipeline(ctx context.Context, cmds []Cmder) error {
|
||||||
|
return c.hooks.processPipeline(ctx, cmds, func(ctx context.Context, cmds []Cmder) error {
|
||||||
|
return c.generalProcessPipeline(ctx, cmds, false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Ring) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||||
|
return c.TxPipeline().Pipelined(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Ring) TxPipeline() Pipeliner {
|
||||||
|
pipe := Pipeline{
|
||||||
|
ctx: c.ctx,
|
||||||
|
exec: c.processTxPipeline,
|
||||||
|
}
|
||||||
|
pipe.init()
|
||||||
|
return &pipe
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Ring) processTxPipeline(ctx context.Context, cmds []Cmder) error {
|
||||||
|
return c.hooks.processPipeline(ctx, cmds, func(ctx context.Context, cmds []Cmder) error {
|
||||||
|
return c.generalProcessPipeline(ctx, cmds, true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Ring) generalProcessPipeline(
|
||||||
|
ctx context.Context, cmds []Cmder, tx bool,
|
||||||
|
) error {
|
||||||
cmdsMap := make(map[string][]Cmder)
|
cmdsMap := make(map[string][]Cmder)
|
||||||
for _, cmd := range cmds {
|
for _, cmd := range cmds {
|
||||||
cmdInfo := c.cmdInfo(cmd.Name())
|
cmdInfo := c.cmdInfo(cmd.Name())
|
||||||
|
@ -570,56 +640,36 @@ func (c *Ring) defaultProcessPipeline(cmds []Cmder) error {
|
||||||
cmdsMap[hash] = append(cmdsMap[hash], cmd)
|
cmdsMap[hash] = append(cmdsMap[hash], cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
|
var wg sync.WaitGroup
|
||||||
if attempt > 0 {
|
|
||||||
time.Sleep(c.retryBackoff(attempt))
|
|
||||||
}
|
|
||||||
|
|
||||||
var failedCmdsMap map[string][]Cmder
|
|
||||||
|
|
||||||
for hash, cmds := range cmdsMap {
|
for hash, cmds := range cmdsMap {
|
||||||
shard, err := c.shards.GetByHash(hash)
|
wg.Add(1)
|
||||||
if err != nil {
|
go func(hash string, cmds []Cmder) {
|
||||||
setCmdsErr(cmds, err)
|
defer wg.Done()
|
||||||
continue
|
|
||||||
}
|
_ = c.processShardPipeline(ctx, hash, cmds, tx)
|
||||||
|
}(hash, cmds)
|
||||||
cn, err := shard.Client.getConn()
|
|
||||||
if err != nil {
|
|
||||||
setCmdsErr(cmds, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
canRetry, err := shard.Client.pipelineProcessCmds(cn, cmds)
|
|
||||||
if err == nil || internal.IsRedisError(err) {
|
|
||||||
shard.Client.connPool.Put(cn)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
shard.Client.connPool.Remove(cn)
|
|
||||||
|
|
||||||
if canRetry && internal.IsRetryableError(err, true) {
|
|
||||||
if failedCmdsMap == nil {
|
|
||||||
failedCmdsMap = make(map[string][]Cmder)
|
|
||||||
}
|
|
||||||
failedCmdsMap[hash] = cmds
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(failedCmdsMap) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
cmdsMap = failedCmdsMap
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
return cmdsFirstErr(cmds)
|
return cmdsFirstErr(cmds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Ring) TxPipeline() Pipeliner {
|
func (c *Ring) processShardPipeline(
|
||||||
panic("not implemented")
|
ctx context.Context, hash string, cmds []Cmder, tx bool,
|
||||||
|
) error {
|
||||||
|
//TODO: retry?
|
||||||
|
shard, err := c.shards.GetByHash(hash)
|
||||||
|
if err != nil {
|
||||||
|
setCmdsErr(cmds, err)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Ring) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
if tx {
|
||||||
panic("not implemented")
|
err = shard.Client.processTxPipeline(ctx, cmds)
|
||||||
|
} else {
|
||||||
|
err = shard.Client.processPipeline(ctx, cmds)
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the ring client, releasing any open resources.
|
// Close closes the ring client, releasing any open resources.
|
||||||
|
@ -630,6 +680,39 @@ func (c *Ring) Close() error {
|
||||||
return c.shards.Close()
|
return c.shards.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Ring) Watch(fn func(*Tx) error, keys ...string) error {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return fmt.Errorf("redis: Watch requires at least one key")
|
||||||
|
}
|
||||||
|
|
||||||
|
var shards []*ringShard
|
||||||
|
for _, key := range keys {
|
||||||
|
if key != "" {
|
||||||
|
shard, err := c.shards.GetByKey(hashtag.Key(key))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
shards = append(shards, shard)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(shards) == 0 {
|
||||||
|
return fmt.Errorf("redis: Watch requires at least one shard")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(shards) > 1 {
|
||||||
|
for _, shard := range shards[1:] {
|
||||||
|
if shard.Client != shards[0].Client {
|
||||||
|
err := fmt.Errorf("redis: Watch requires all keys to be in the same shard")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return shards[0].Client.Watch(fn, keys...)
|
||||||
|
}
|
||||||
|
|
||||||
func newConsistentHash(opt *RingOptions) *consistenthash.Map {
|
func newConsistentHash(opt *RingOptions) *consistenthash.Map {
|
||||||
return consistenthash.New(opt.HashReplicas, consistenthash.Hash(opt.Hash))
|
return consistenthash.New(opt.HashReplicas, consistenthash.Hash(opt.Hash))
|
||||||
}
|
}
|
|
@ -24,7 +24,7 @@ type Script struct {
|
||||||
|
|
||||||
func NewScript(src string) *Script {
|
func NewScript(src string) *Script {
|
||||||
h := sha1.New()
|
h := sha1.New()
|
||||||
io.WriteString(h, src)
|
_, _ = io.WriteString(h, src)
|
||||||
return &Script{
|
return &Script{
|
||||||
src: src,
|
src: src,
|
||||||
hash: hex.EncodeToString(h.Sum(nil)),
|
hash: hex.EncodeToString(h.Sum(nil)),
|
503
vendor/github.com/go-redis/redis/v7/sentinel.go
generated
vendored
Normal file
503
vendor/github.com/go-redis/redis/v7/sentinel.go
generated
vendored
Normal file
|
@ -0,0 +1,503 @@
|
||||||
|
package redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis/v7/internal"
|
||||||
|
"github.com/go-redis/redis/v7/internal/pool"
|
||||||
|
)
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// FailoverOptions are used to configure a failover client and should
|
||||||
|
// be passed to NewFailoverClient.
|
||||||
|
type FailoverOptions struct {
|
||||||
|
// The master name.
|
||||||
|
MasterName string
|
||||||
|
// A seed list of host:port addresses of sentinel nodes.
|
||||||
|
SentinelAddrs []string
|
||||||
|
SentinelPassword string
|
||||||
|
|
||||||
|
// Following options are copied from Options struct.
|
||||||
|
|
||||||
|
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||||
|
OnConnect func(*Conn) error
|
||||||
|
|
||||||
|
Password string
|
||||||
|
DB int
|
||||||
|
|
||||||
|
MaxRetries int
|
||||||
|
MinRetryBackoff time.Duration
|
||||||
|
MaxRetryBackoff time.Duration
|
||||||
|
|
||||||
|
DialTimeout time.Duration
|
||||||
|
ReadTimeout time.Duration
|
||||||
|
WriteTimeout time.Duration
|
||||||
|
|
||||||
|
PoolSize int
|
||||||
|
MinIdleConns int
|
||||||
|
MaxConnAge time.Duration
|
||||||
|
PoolTimeout time.Duration
|
||||||
|
IdleTimeout time.Duration
|
||||||
|
IdleCheckFrequency time.Duration
|
||||||
|
|
||||||
|
TLSConfig *tls.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opt *FailoverOptions) options() *Options {
|
||||||
|
return &Options{
|
||||||
|
Addr: "FailoverClient",
|
||||||
|
Dialer: opt.Dialer,
|
||||||
|
OnConnect: opt.OnConnect,
|
||||||
|
|
||||||
|
DB: opt.DB,
|
||||||
|
Password: opt.Password,
|
||||||
|
|
||||||
|
MaxRetries: opt.MaxRetries,
|
||||||
|
MinRetryBackoff: opt.MinRetryBackoff,
|
||||||
|
MaxRetryBackoff: opt.MaxRetryBackoff,
|
||||||
|
|
||||||
|
DialTimeout: opt.DialTimeout,
|
||||||
|
ReadTimeout: opt.ReadTimeout,
|
||||||
|
WriteTimeout: opt.WriteTimeout,
|
||||||
|
|
||||||
|
PoolSize: opt.PoolSize,
|
||||||
|
PoolTimeout: opt.PoolTimeout,
|
||||||
|
IdleTimeout: opt.IdleTimeout,
|
||||||
|
IdleCheckFrequency: opt.IdleCheckFrequency,
|
||||||
|
MinIdleConns: opt.MinIdleConns,
|
||||||
|
MaxConnAge: opt.MaxConnAge,
|
||||||
|
|
||||||
|
TLSConfig: opt.TLSConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFailoverClient returns a Redis client that uses Redis Sentinel
|
||||||
|
// for automatic failover. It's safe for concurrent use by multiple
|
||||||
|
// goroutines.
|
||||||
|
func NewFailoverClient(failoverOpt *FailoverOptions) *Client {
|
||||||
|
opt := failoverOpt.options()
|
||||||
|
opt.init()
|
||||||
|
|
||||||
|
failover := &sentinelFailover{
|
||||||
|
masterName: failoverOpt.MasterName,
|
||||||
|
sentinelAddrs: failoverOpt.SentinelAddrs,
|
||||||
|
password: failoverOpt.SentinelPassword,
|
||||||
|
|
||||||
|
opt: opt,
|
||||||
|
}
|
||||||
|
|
||||||
|
c := Client{
|
||||||
|
baseClient: newBaseClient(opt, failover.Pool()),
|
||||||
|
ctx: context.Background(),
|
||||||
|
}
|
||||||
|
c.cmdable = c.Process
|
||||||
|
c.onClose = failover.Close
|
||||||
|
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type SentinelClient struct {
|
||||||
|
*baseClient
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSentinelClient(opt *Options) *SentinelClient {
|
||||||
|
opt.init()
|
||||||
|
c := &SentinelClient{
|
||||||
|
baseClient: &baseClient{
|
||||||
|
opt: opt,
|
||||||
|
connPool: newConnPool(opt),
|
||||||
|
},
|
||||||
|
ctx: context.Background(),
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SentinelClient) Context() context.Context {
|
||||||
|
return c.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SentinelClient) WithContext(ctx context.Context) *SentinelClient {
|
||||||
|
if ctx == nil {
|
||||||
|
panic("nil context")
|
||||||
|
}
|
||||||
|
clone := *c
|
||||||
|
clone.ctx = ctx
|
||||||
|
return &clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SentinelClient) Process(cmd Cmder) error {
|
||||||
|
return c.ProcessContext(c.ctx, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SentinelClient) ProcessContext(ctx context.Context, cmd Cmder) error {
|
||||||
|
return c.baseClient.process(ctx, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SentinelClient) pubSub() *PubSub {
|
||||||
|
pubsub := &PubSub{
|
||||||
|
opt: c.opt,
|
||||||
|
|
||||||
|
newConn: func(channels []string) (*pool.Conn, error) {
|
||||||
|
return c.newConn(context.TODO())
|
||||||
|
},
|
||||||
|
closeConn: c.connPool.CloseConn,
|
||||||
|
}
|
||||||
|
pubsub.init()
|
||||||
|
return pubsub
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping is used to test if a connection is still alive, or to
|
||||||
|
// measure latency.
|
||||||
|
func (c *SentinelClient) Ping() *StringCmd {
|
||||||
|
cmd := NewStringCmd("ping")
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe subscribes the client to the specified channels.
|
||||||
|
// Channels can be omitted to create empty subscription.
|
||||||
|
func (c *SentinelClient) Subscribe(channels ...string) *PubSub {
|
||||||
|
pubsub := c.pubSub()
|
||||||
|
if len(channels) > 0 {
|
||||||
|
_ = pubsub.Subscribe(channels...)
|
||||||
|
}
|
||||||
|
return pubsub
|
||||||
|
}
|
||||||
|
|
||||||
|
// PSubscribe subscribes the client to the given patterns.
|
||||||
|
// Patterns can be omitted to create empty subscription.
|
||||||
|
func (c *SentinelClient) PSubscribe(channels ...string) *PubSub {
|
||||||
|
pubsub := c.pubSub()
|
||||||
|
if len(channels) > 0 {
|
||||||
|
_ = pubsub.PSubscribe(channels...)
|
||||||
|
}
|
||||||
|
return pubsub
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SentinelClient) GetMasterAddrByName(name string) *StringSliceCmd {
|
||||||
|
cmd := NewStringSliceCmd("sentinel", "get-master-addr-by-name", name)
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SentinelClient) Sentinels(name string) *SliceCmd {
|
||||||
|
cmd := NewSliceCmd("sentinel", "sentinels", name)
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failover forces a failover as if the master was not reachable, and without
|
||||||
|
// asking for agreement to other Sentinels.
|
||||||
|
func (c *SentinelClient) Failover(name string) *StatusCmd {
|
||||||
|
cmd := NewStatusCmd("sentinel", "failover", name)
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset resets all the masters with matching name. The pattern argument is a
|
||||||
|
// glob-style pattern. The reset process clears any previous state in a master
|
||||||
|
// (including a failover in progress), and removes every slave and sentinel
|
||||||
|
// already discovered and associated with the master.
|
||||||
|
func (c *SentinelClient) Reset(pattern string) *IntCmd {
|
||||||
|
cmd := NewIntCmd("sentinel", "reset", pattern)
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlushConfig forces Sentinel to rewrite its configuration on disk, including
|
||||||
|
// the current Sentinel state.
|
||||||
|
func (c *SentinelClient) FlushConfig() *StatusCmd {
|
||||||
|
cmd := NewStatusCmd("sentinel", "flushconfig")
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Master shows the state and info of the specified master.
|
||||||
|
func (c *SentinelClient) Master(name string) *StringStringMapCmd {
|
||||||
|
cmd := NewStringStringMapCmd("sentinel", "master", name)
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Masters shows a list of monitored masters and their state.
|
||||||
|
func (c *SentinelClient) Masters() *SliceCmd {
|
||||||
|
cmd := NewSliceCmd("sentinel", "masters")
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slaves shows a list of slaves for the specified master and their state.
|
||||||
|
func (c *SentinelClient) Slaves(name string) *SliceCmd {
|
||||||
|
cmd := NewSliceCmd("sentinel", "slaves", name)
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// CkQuorum checks if the current Sentinel configuration is able to reach the
|
||||||
|
// quorum needed to failover a master, and the majority needed to authorize the
|
||||||
|
// failover. This command should be used in monitoring systems to check if a
|
||||||
|
// Sentinel deployment is ok.
|
||||||
|
func (c *SentinelClient) CkQuorum(name string) *StringCmd {
|
||||||
|
cmd := NewStringCmd("sentinel", "ckquorum", name)
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monitor tells the Sentinel to start monitoring a new master with the specified
|
||||||
|
// name, ip, port, and quorum.
|
||||||
|
func (c *SentinelClient) Monitor(name, ip, port, quorum string) *StringCmd {
|
||||||
|
cmd := NewStringCmd("sentinel", "monitor", name, ip, port, quorum)
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set is used in order to change configuration parameters of a specific master.
|
||||||
|
func (c *SentinelClient) Set(name, option, value string) *StringCmd {
|
||||||
|
cmd := NewStringCmd("sentinel", "set", name, option, value)
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove is used in order to remove the specified master: the master will no
|
||||||
|
// longer be monitored, and will totally be removed from the internal state of
|
||||||
|
// the Sentinel.
|
||||||
|
func (c *SentinelClient) Remove(name string) *StringCmd {
|
||||||
|
cmd := NewStringCmd("sentinel", "remove", name)
|
||||||
|
_ = c.Process(cmd)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
type sentinelFailover struct {
|
||||||
|
sentinelAddrs []string
|
||||||
|
|
||||||
|
opt *Options
|
||||||
|
password string
|
||||||
|
|
||||||
|
pool *pool.ConnPool
|
||||||
|
poolOnce sync.Once
|
||||||
|
|
||||||
|
mu sync.RWMutex
|
||||||
|
masterName string
|
||||||
|
_masterAddr string
|
||||||
|
sentinel *SentinelClient
|
||||||
|
pubsub *PubSub
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sentinelFailover) Close() error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if c.sentinel != nil {
|
||||||
|
return c.closeSentinel()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sentinelFailover) closeSentinel() error {
|
||||||
|
firstErr := c.pubsub.Close()
|
||||||
|
c.pubsub = nil
|
||||||
|
|
||||||
|
err := c.sentinel.Close()
|
||||||
|
if err != nil && firstErr == nil {
|
||||||
|
firstErr = err
|
||||||
|
}
|
||||||
|
c.sentinel = nil
|
||||||
|
|
||||||
|
return firstErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sentinelFailover) Pool() *pool.ConnPool {
|
||||||
|
c.poolOnce.Do(func() {
|
||||||
|
opt := *c.opt
|
||||||
|
opt.Dialer = c.dial
|
||||||
|
c.pool = newConnPool(&opt)
|
||||||
|
})
|
||||||
|
return c.pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sentinelFailover) dial(ctx context.Context, network, _ string) (net.Conn, error) {
|
||||||
|
addr, err := c.MasterAddr()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if c.opt.Dialer != nil {
|
||||||
|
return c.opt.Dialer(ctx, network, addr)
|
||||||
|
}
|
||||||
|
return net.DialTimeout("tcp", addr, c.opt.DialTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sentinelFailover) MasterAddr() (string, error) {
|
||||||
|
addr, err := c.masterAddr()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
c.switchMaster(addr)
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sentinelFailover) masterAddr() (string, error) {
|
||||||
|
c.mu.RLock()
|
||||||
|
sentinel := c.sentinel
|
||||||
|
c.mu.RUnlock()
|
||||||
|
|
||||||
|
if sentinel != nil {
|
||||||
|
addr := c.getMasterAddr(sentinel)
|
||||||
|
if addr != "" {
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
if c.sentinel != nil {
|
||||||
|
addr := c.getMasterAddr(c.sentinel)
|
||||||
|
if addr != "" {
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
_ = c.closeSentinel()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, sentinelAddr := range c.sentinelAddrs {
|
||||||
|
sentinel := NewSentinelClient(&Options{
|
||||||
|
Addr: sentinelAddr,
|
||||||
|
Dialer: c.opt.Dialer,
|
||||||
|
|
||||||
|
Password: c.password,
|
||||||
|
|
||||||
|
MaxRetries: c.opt.MaxRetries,
|
||||||
|
|
||||||
|
DialTimeout: c.opt.DialTimeout,
|
||||||
|
ReadTimeout: c.opt.ReadTimeout,
|
||||||
|
WriteTimeout: c.opt.WriteTimeout,
|
||||||
|
|
||||||
|
PoolSize: c.opt.PoolSize,
|
||||||
|
PoolTimeout: c.opt.PoolTimeout,
|
||||||
|
IdleTimeout: c.opt.IdleTimeout,
|
||||||
|
IdleCheckFrequency: c.opt.IdleCheckFrequency,
|
||||||
|
|
||||||
|
TLSConfig: c.opt.TLSConfig,
|
||||||
|
})
|
||||||
|
|
||||||
|
masterAddr, err := sentinel.GetMasterAddrByName(c.masterName).Result()
|
||||||
|
if err != nil {
|
||||||
|
internal.Logger.Printf("sentinel: GetMasterAddrByName master=%q failed: %s",
|
||||||
|
c.masterName, err)
|
||||||
|
_ = sentinel.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push working sentinel to the top.
|
||||||
|
c.sentinelAddrs[0], c.sentinelAddrs[i] = c.sentinelAddrs[i], c.sentinelAddrs[0]
|
||||||
|
c.setSentinel(sentinel)
|
||||||
|
|
||||||
|
addr := net.JoinHostPort(masterAddr[0], masterAddr[1])
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("redis: all sentinels are unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sentinelFailover) getMasterAddr(sentinel *SentinelClient) string {
|
||||||
|
addr, err := sentinel.GetMasterAddrByName(c.masterName).Result()
|
||||||
|
if err != nil {
|
||||||
|
internal.Logger.Printf("sentinel: GetMasterAddrByName name=%q failed: %s",
|
||||||
|
c.masterName, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return net.JoinHostPort(addr[0], addr[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sentinelFailover) switchMaster(addr string) {
|
||||||
|
c.mu.RLock()
|
||||||
|
masterAddr := c._masterAddr
|
||||||
|
c.mu.RUnlock()
|
||||||
|
if masterAddr == addr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
if c._masterAddr == addr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
internal.Logger.Printf("sentinel: new master=%q addr=%q",
|
||||||
|
c.masterName, addr)
|
||||||
|
_ = c.Pool().Filter(func(cn *pool.Conn) bool {
|
||||||
|
return cn.RemoteAddr().String() != addr
|
||||||
|
})
|
||||||
|
c._masterAddr = addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sentinelFailover) setSentinel(sentinel *SentinelClient) {
|
||||||
|
if c.sentinel != nil {
|
||||||
|
panic("not reached")
|
||||||
|
}
|
||||||
|
c.sentinel = sentinel
|
||||||
|
c.discoverSentinels()
|
||||||
|
|
||||||
|
c.pubsub = sentinel.Subscribe("+switch-master")
|
||||||
|
go c.listen(c.pubsub)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sentinelFailover) discoverSentinels() {
|
||||||
|
sentinels, err := c.sentinel.Sentinels(c.masterName).Result()
|
||||||
|
if err != nil {
|
||||||
|
internal.Logger.Printf("sentinel: Sentinels master=%q failed: %s", c.masterName, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, sentinel := range sentinels {
|
||||||
|
vals := sentinel.([]interface{})
|
||||||
|
for i := 0; i < len(vals); i += 2 {
|
||||||
|
key := vals[i].(string)
|
||||||
|
if key == "name" {
|
||||||
|
sentinelAddr := vals[i+1].(string)
|
||||||
|
if !contains(c.sentinelAddrs, sentinelAddr) {
|
||||||
|
internal.Logger.Printf("sentinel: discovered new sentinel=%q for master=%q",
|
||||||
|
sentinelAddr, c.masterName)
|
||||||
|
c.sentinelAddrs = append(c.sentinelAddrs, sentinelAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sentinelFailover) listen(pubsub *PubSub) {
|
||||||
|
ch := pubsub.Channel()
|
||||||
|
for {
|
||||||
|
msg, ok := <-ch
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.Channel == "+switch-master" {
|
||||||
|
parts := strings.Split(msg.Payload, " ")
|
||||||
|
if parts[0] != c.masterName {
|
||||||
|
internal.Logger.Printf("sentinel: ignore addr for master=%q", parts[0])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addr := net.JoinHostPort(parts[3], parts[4])
|
||||||
|
c.switchMaster(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(slice []string, str string) bool {
|
||||||
|
for _, s := range slice {
|
||||||
|
if s == str {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
95
vendor/github.com/go-redis/redis/tx.go → vendor/github.com/go-redis/redis/v7/tx.go
generated
vendored
95
vendor/github.com/go-redis/redis/tx.go → vendor/github.com/go-redis/redis/v7/tx.go
generated
vendored
|
@ -1,8 +1,10 @@
|
||||||
package redis
|
package redis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/go-redis/redis/internal/pool"
|
"context"
|
||||||
"github.com/go-redis/redis/internal/proto"
|
|
||||||
|
"github.com/go-redis/redis/v7/internal/pool"
|
||||||
|
"github.com/go-redis/redis/v7/internal/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TxFailedErr transaction redis failed.
|
// TxFailedErr transaction redis failed.
|
||||||
|
@ -13,28 +15,64 @@ const TxFailedErr = proto.RedisError("redis: transaction failed")
|
||||||
// by multiple goroutines, because Exec resets list of watched keys.
|
// by multiple goroutines, because Exec resets list of watched keys.
|
||||||
// If you don't need WATCH it is better to use Pipeline.
|
// If you don't need WATCH it is better to use Pipeline.
|
||||||
type Tx struct {
|
type Tx struct {
|
||||||
statefulCmdable
|
|
||||||
baseClient
|
baseClient
|
||||||
|
cmdable
|
||||||
|
statefulCmdable
|
||||||
|
hooks
|
||||||
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) newTx() *Tx {
|
func (c *Client) newTx(ctx context.Context) *Tx {
|
||||||
tx := Tx{
|
tx := Tx{
|
||||||
baseClient: baseClient{
|
baseClient: baseClient{
|
||||||
opt: c.opt,
|
opt: c.opt,
|
||||||
connPool: pool.NewStickyConnPool(c.connPool.(*pool.ConnPool), true),
|
connPool: pool.NewStickyConnPool(c.connPool.(*pool.ConnPool), true),
|
||||||
},
|
},
|
||||||
|
hooks: c.hooks.clone(),
|
||||||
|
ctx: ctx,
|
||||||
}
|
}
|
||||||
tx.baseClient.init()
|
tx.init()
|
||||||
tx.statefulCmdable.setProcessor(tx.Process)
|
|
||||||
return &tx
|
return &tx
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch prepares a transcaction and marks the keys to be watched
|
func (c *Tx) init() {
|
||||||
|
c.cmdable = c.Process
|
||||||
|
c.statefulCmdable = c.Process
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Tx) Context() context.Context {
|
||||||
|
return c.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Tx) WithContext(ctx context.Context) *Tx {
|
||||||
|
if ctx == nil {
|
||||||
|
panic("nil context")
|
||||||
|
}
|
||||||
|
clone := *c
|
||||||
|
clone.init()
|
||||||
|
clone.hooks.lock()
|
||||||
|
clone.ctx = ctx
|
||||||
|
return &clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Tx) Process(cmd Cmder) error {
|
||||||
|
return c.ProcessContext(c.ctx, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Tx) ProcessContext(ctx context.Context, cmd Cmder) error {
|
||||||
|
return c.hooks.process(ctx, cmd, c.baseClient.process)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch prepares a transaction and marks the keys to be watched
|
||||||
// for conditional execution if there are any keys.
|
// for conditional execution if there are any keys.
|
||||||
//
|
//
|
||||||
// The transaction is automatically closed when the fn exits.
|
// The transaction is automatically closed when fn exits.
|
||||||
func (c *Client) Watch(fn func(*Tx) error, keys ...string) error {
|
func (c *Client) Watch(fn func(*Tx) error, keys ...string) error {
|
||||||
tx := c.newTx()
|
return c.WatchContext(c.ctx, fn, keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) WatchContext(ctx context.Context, fn func(*Tx) error, keys ...string) error {
|
||||||
|
tx := c.newTx(ctx)
|
||||||
if len(keys) > 0 {
|
if len(keys) > 0 {
|
||||||
if err := tx.Watch(keys...).Err(); err != nil {
|
if err := tx.Watch(keys...).Err(); err != nil {
|
||||||
_ = tx.Close()
|
_ = tx.Close()
|
||||||
|
@ -62,7 +100,7 @@ func (c *Tx) Watch(keys ...string) *StatusCmd {
|
||||||
args[1+i] = key
|
args[1+i] = key
|
||||||
}
|
}
|
||||||
cmd := NewStatusCmd(args...)
|
cmd := NewStatusCmd(args...)
|
||||||
c.Process(cmd)
|
_ = c.Process(cmd)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,20 +112,29 @@ func (c *Tx) Unwatch(keys ...string) *StatusCmd {
|
||||||
args[1+i] = key
|
args[1+i] = key
|
||||||
}
|
}
|
||||||
cmd := NewStatusCmd(args...)
|
cmd := NewStatusCmd(args...)
|
||||||
c.Process(cmd)
|
_ = c.Process(cmd)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pipeline creates a new pipeline. It is more convenient to use Pipelined.
|
// Pipeline creates a pipeline. Usually it is more convenient to use Pipelined.
|
||||||
func (c *Tx) Pipeline() Pipeliner {
|
func (c *Tx) Pipeline() Pipeliner {
|
||||||
pipe := Pipeline{
|
pipe := Pipeline{
|
||||||
exec: c.processTxPipeline,
|
ctx: c.ctx,
|
||||||
|
exec: func(ctx context.Context, cmds []Cmder) error {
|
||||||
|
return c.hooks.processPipeline(ctx, cmds, c.baseClient.processPipeline)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
pipe.statefulCmdable.setProcessor(pipe.Process)
|
pipe.init()
|
||||||
return &pipe
|
return &pipe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pipelined executes commands queued in the fn in a transaction.
|
// Pipelined executes commands queued in the fn outside of the transaction.
|
||||||
|
// Use TxPipelined if you need transactional behavior.
|
||||||
|
func (c *Tx) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||||
|
return c.Pipeline().Pipelined(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxPipelined executes commands queued in the fn in the transaction.
|
||||||
//
|
//
|
||||||
// When using WATCH, EXEC will execute commands only if the watched keys
|
// When using WATCH, EXEC will execute commands only if the watched keys
|
||||||
// were not modified, allowing for a check-and-set mechanism.
|
// were not modified, allowing for a check-and-set mechanism.
|
||||||
|
@ -95,16 +142,18 @@ func (c *Tx) Pipeline() Pipeliner {
|
||||||
// Exec always returns list of commands. If transaction fails
|
// Exec always returns list of commands. If transaction fails
|
||||||
// TxFailedErr is returned. Otherwise Exec returns an error of the first
|
// TxFailedErr is returned. Otherwise Exec returns an error of the first
|
||||||
// failed command or nil.
|
// failed command or nil.
|
||||||
func (c *Tx) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
|
||||||
return c.Pipeline().Pipelined(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TxPipelined is an alias for Pipelined.
|
|
||||||
func (c *Tx) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
func (c *Tx) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
|
||||||
return c.Pipelined(fn)
|
return c.TxPipeline().Pipelined(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxPipeline is an alias for Pipeline.
|
// TxPipeline creates a pipeline. Usually it is more convenient to use TxPipelined.
|
||||||
func (c *Tx) TxPipeline() Pipeliner {
|
func (c *Tx) TxPipeline() Pipeliner {
|
||||||
return c.Pipeline()
|
pipe := Pipeline{
|
||||||
|
ctx: c.ctx,
|
||||||
|
exec: func(ctx context.Context, cmds []Cmder) error {
|
||||||
|
return c.hooks.processTxPipeline(ctx, cmds, c.baseClient.processTxPipeline)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
pipe.init()
|
||||||
|
return &pipe
|
||||||
}
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
package redis
|
package redis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,6 +20,7 @@ type UniversalOptions struct {
|
||||||
|
|
||||||
// Common options.
|
// Common options.
|
||||||
|
|
||||||
|
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||||
OnConnect func(*Conn) error
|
OnConnect func(*Conn) error
|
||||||
Password string
|
Password string
|
||||||
MaxRetries int
|
MaxRetries int
|
||||||
|
@ -46,13 +49,15 @@ type UniversalOptions struct {
|
||||||
MasterName string
|
MasterName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *UniversalOptions) cluster() *ClusterOptions {
|
// Cluster returns cluster options created from the universal options.
|
||||||
|
func (o *UniversalOptions) Cluster() *ClusterOptions {
|
||||||
if len(o.Addrs) == 0 {
|
if len(o.Addrs) == 0 {
|
||||||
o.Addrs = []string{"127.0.0.1:6379"}
|
o.Addrs = []string{"127.0.0.1:6379"}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ClusterOptions{
|
return &ClusterOptions{
|
||||||
Addrs: o.Addrs,
|
Addrs: o.Addrs,
|
||||||
|
Dialer: o.Dialer,
|
||||||
OnConnect: o.OnConnect,
|
OnConnect: o.OnConnect,
|
||||||
|
|
||||||
Password: o.Password,
|
Password: o.Password,
|
||||||
|
@ -80,7 +85,8 @@ func (o *UniversalOptions) cluster() *ClusterOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *UniversalOptions) failover() *FailoverOptions {
|
// Failover returns failover options created from the universal options.
|
||||||
|
func (o *UniversalOptions) Failover() *FailoverOptions {
|
||||||
if len(o.Addrs) == 0 {
|
if len(o.Addrs) == 0 {
|
||||||
o.Addrs = []string{"127.0.0.1:26379"}
|
o.Addrs = []string{"127.0.0.1:26379"}
|
||||||
}
|
}
|
||||||
|
@ -88,6 +94,8 @@ func (o *UniversalOptions) failover() *FailoverOptions {
|
||||||
return &FailoverOptions{
|
return &FailoverOptions{
|
||||||
SentinelAddrs: o.Addrs,
|
SentinelAddrs: o.Addrs,
|
||||||
MasterName: o.MasterName,
|
MasterName: o.MasterName,
|
||||||
|
|
||||||
|
Dialer: o.Dialer,
|
||||||
OnConnect: o.OnConnect,
|
OnConnect: o.OnConnect,
|
||||||
|
|
||||||
DB: o.DB,
|
DB: o.DB,
|
||||||
|
@ -112,7 +120,8 @@ func (o *UniversalOptions) failover() *FailoverOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *UniversalOptions) simple() *Options {
|
// Simple returns basic options created from the universal options.
|
||||||
|
func (o *UniversalOptions) Simple() *Options {
|
||||||
addr := "127.0.0.1:6379"
|
addr := "127.0.0.1:6379"
|
||||||
if len(o.Addrs) > 0 {
|
if len(o.Addrs) > 0 {
|
||||||
addr = o.Addrs[0]
|
addr = o.Addrs[0]
|
||||||
|
@ -120,6 +129,7 @@ func (o *UniversalOptions) simple() *Options {
|
||||||
|
|
||||||
return &Options{
|
return &Options{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
|
Dialer: o.Dialer,
|
||||||
OnConnect: o.OnConnect,
|
OnConnect: o.OnConnect,
|
||||||
|
|
||||||
DB: o.DB,
|
DB: o.DB,
|
||||||
|
@ -147,14 +157,18 @@ func (o *UniversalOptions) simple() *Options {
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
// UniversalClient is an abstract client which - based on the provided options -
|
// UniversalClient is an abstract client which - based on the provided options -
|
||||||
// can connect to either clusters, or sentinel-backed failover instances or simple
|
// can connect to either clusters, or sentinel-backed failover instances
|
||||||
// single-instance servers. This can be useful for testing cluster-specific
|
// or simple single-instance servers. This can be useful for testing
|
||||||
// applications locally.
|
// cluster-specific applications locally.
|
||||||
type UniversalClient interface {
|
type UniversalClient interface {
|
||||||
Cmdable
|
Cmdable
|
||||||
|
Context() context.Context
|
||||||
|
AddHook(Hook)
|
||||||
Watch(fn func(*Tx) error, keys ...string) error
|
Watch(fn func(*Tx) error, keys ...string) error
|
||||||
|
Do(args ...interface{}) *Cmd
|
||||||
|
DoContext(ctx context.Context, args ...interface{}) *Cmd
|
||||||
Process(cmd Cmder) error
|
Process(cmd Cmder) error
|
||||||
WrapProcess(fn func(oldProcess func(cmd Cmder) error) func(cmd Cmder) error)
|
ProcessContext(ctx context.Context, cmd Cmder) error
|
||||||
Subscribe(channels ...string) *PubSub
|
Subscribe(channels ...string) *PubSub
|
||||||
PSubscribe(channels ...string) *PubSub
|
PSubscribe(channels ...string) *PubSub
|
||||||
Close() error
|
Close() error
|
||||||
|
@ -162,6 +176,7 @@ type UniversalClient interface {
|
||||||
|
|
||||||
var _ UniversalClient = (*Client)(nil)
|
var _ UniversalClient = (*Client)(nil)
|
||||||
var _ UniversalClient = (*ClusterClient)(nil)
|
var _ UniversalClient = (*ClusterClient)(nil)
|
||||||
|
var _ UniversalClient = (*Ring)(nil)
|
||||||
|
|
||||||
// NewUniversalClient returns a new multi client. The type of client returned depends
|
// NewUniversalClient returns a new multi client. The type of client returned depends
|
||||||
// on the following three conditions:
|
// on the following three conditions:
|
||||||
|
@ -171,9 +186,9 @@ var _ UniversalClient = (*ClusterClient)(nil)
|
||||||
// 3. otherwise, a single-node redis Client will be returned.
|
// 3. otherwise, a single-node redis Client will be returned.
|
||||||
func NewUniversalClient(opts *UniversalOptions) UniversalClient {
|
func NewUniversalClient(opts *UniversalOptions) UniversalClient {
|
||||||
if opts.MasterName != "" {
|
if opts.MasterName != "" {
|
||||||
return NewFailoverClient(opts.failover())
|
return NewFailoverClient(opts.Failover())
|
||||||
} else if len(opts.Addrs) > 1 {
|
} else if len(opts.Addrs) > 1 {
|
||||||
return NewClusterClient(opts.cluster())
|
return NewClusterClient(opts.Cluster())
|
||||||
}
|
}
|
||||||
return NewClient(opts.simple())
|
return NewClient(opts.Simple())
|
||||||
}
|
}
|
2
vendor/github.com/golang/protobuf/proto/lib.go
generated
vendored
2
vendor/github.com/golang/protobuf/proto/lib.go
generated
vendored
|
@ -393,7 +393,7 @@ func (p *Buffer) Bytes() []byte { return p.buf }
|
||||||
// than relying on this API.
|
// than relying on this API.
|
||||||
//
|
//
|
||||||
// If deterministic serialization is requested, map entries will be sorted
|
// If deterministic serialization is requested, map entries will be sorted
|
||||||
// by keys in lexographical order. This is an implementation detail and
|
// by keys in lexicographical order. This is an implementation detail and
|
||||||
// subject to change.
|
// subject to change.
|
||||||
func (p *Buffer) SetDeterministic(deterministic bool) {
|
func (p *Buffer) SetDeterministic(deterministic bool) {
|
||||||
p.deterministic = deterministic
|
p.deterministic = deterministic
|
||||||
|
|
6
vendor/github.com/golang/protobuf/proto/text.go
generated
vendored
6
vendor/github.com/golang/protobuf/proto/text.go
generated
vendored
|
@ -456,6 +456,8 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||||
|
|
||||||
// writeAny writes an arbitrary field.
|
// writeAny writes an arbitrary field.
|
||||||
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
||||||
v = reflect.Indirect(v)
|
v = reflect.Indirect(v)
|
||||||
|
@ -519,8 +521,8 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert
|
||||||
// mutating this value.
|
// mutating this value.
|
||||||
v = v.Addr()
|
v = v.Addr()
|
||||||
}
|
}
|
||||||
if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
if v.Type().Implements(textMarshalerType) {
|
||||||
text, err := etm.MarshalText()
|
text, err := v.Interface().(encoding.TextMarshaler).MarshalText()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
16
vendor/github.com/golang/snappy/.gitignore
generated
vendored
Normal file
16
vendor/github.com/golang/snappy/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
cmd/snappytool/snappytool
|
||||||
|
testdata/bench
|
||||||
|
|
||||||
|
# These explicitly listed benchmark data files are for an obsolete version of
|
||||||
|
# snappy_test.go.
|
||||||
|
testdata/alice29.txt
|
||||||
|
testdata/asyoulik.txt
|
||||||
|
testdata/fireworks.jpeg
|
||||||
|
testdata/geo.protodata
|
||||||
|
testdata/html
|
||||||
|
testdata/html_x_4
|
||||||
|
testdata/kppkn.gtb
|
||||||
|
testdata/lcet10.txt
|
||||||
|
testdata/paper-100k.pdf
|
||||||
|
testdata/plrabn12.txt
|
||||||
|
testdata/urls.10K
|
15
vendor/github.com/golang/snappy/AUTHORS
generated
vendored
Normal file
15
vendor/github.com/golang/snappy/AUTHORS
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# This is the official list of Snappy-Go authors for copyright purposes.
|
||||||
|
# This file is distinct from the CONTRIBUTORS files.
|
||||||
|
# See the latter for an explanation.
|
||||||
|
|
||||||
|
# Names should be added to this file as
|
||||||
|
# Name or Organization <email address>
|
||||||
|
# The email address is not required for organizations.
|
||||||
|
|
||||||
|
# Please keep the list sorted.
|
||||||
|
|
||||||
|
Damian Gryski <dgryski@gmail.com>
|
||||||
|
Google Inc.
|
||||||
|
Jan Mercl <0xjnml@gmail.com>
|
||||||
|
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||||
|
Sebastien Binet <seb.binet@gmail.com>
|
37
vendor/github.com/golang/snappy/CONTRIBUTORS
generated
vendored
Normal file
37
vendor/github.com/golang/snappy/CONTRIBUTORS
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# This is the official list of people who can contribute
|
||||||
|
# (and typically have contributed) code to the Snappy-Go repository.
|
||||||
|
# The AUTHORS file lists the copyright holders; this file
|
||||||
|
# lists people. For example, Google employees are listed here
|
||||||
|
# but not in AUTHORS, because Google holds the copyright.
|
||||||
|
#
|
||||||
|
# The submission process automatically checks to make sure
|
||||||
|
# that people submitting code are listed in this file (by email address).
|
||||||
|
#
|
||||||
|
# Names should be added to this file only after verifying that
|
||||||
|
# the individual or the individual's organization has agreed to
|
||||||
|
# the appropriate Contributor License Agreement, found here:
|
||||||
|
#
|
||||||
|
# http://code.google.com/legal/individual-cla-v1.0.html
|
||||||
|
# http://code.google.com/legal/corporate-cla-v1.0.html
|
||||||
|
#
|
||||||
|
# The agreement for individuals can be filled out on the web.
|
||||||
|
#
|
||||||
|
# When adding J Random Contributor's name to this file,
|
||||||
|
# either J's name or J's organization's name should be
|
||||||
|
# added to the AUTHORS file, depending on whether the
|
||||||
|
# individual or corporate CLA was used.
|
||||||
|
|
||||||
|
# Names should be added to this file like so:
|
||||||
|
# Name <email address>
|
||||||
|
|
||||||
|
# Please keep the list sorted.
|
||||||
|
|
||||||
|
Damian Gryski <dgryski@gmail.com>
|
||||||
|
Jan Mercl <0xjnml@gmail.com>
|
||||||
|
Kai Backman <kaib@golang.org>
|
||||||
|
Marc-Antoine Ruel <maruel@chromium.org>
|
||||||
|
Nigel Tao <nigeltao@golang.org>
|
||||||
|
Rob Pike <r@golang.org>
|
||||||
|
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||||
|
Russ Cox <rsc@golang.org>
|
||||||
|
Sebastien Binet <seb.binet@gmail.com>
|
27
vendor/github.com/golang/snappy/LICENSE
generated
vendored
Normal file
27
vendor/github.com/golang/snappy/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2011 The Snappy-Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
107
vendor/github.com/golang/snappy/README
generated
vendored
Normal file
107
vendor/github.com/golang/snappy/README
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
The Snappy compression format in the Go programming language.
|
||||||
|
|
||||||
|
To download and install from source:
|
||||||
|
$ go get github.com/golang/snappy
|
||||||
|
|
||||||
|
Unless otherwise noted, the Snappy-Go source files are distributed
|
||||||
|
under the BSD-style license found in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Benchmarks.
|
||||||
|
|
||||||
|
The golang/snappy benchmarks include compressing (Z) and decompressing (U) ten
|
||||||
|
or so files, the same set used by the C++ Snappy code (github.com/google/snappy
|
||||||
|
and note the "google", not "golang"). On an "Intel(R) Core(TM) i7-3770 CPU @
|
||||||
|
3.40GHz", Go's GOARCH=amd64 numbers as of 2016-05-29:
|
||||||
|
|
||||||
|
"go test -test.bench=."
|
||||||
|
|
||||||
|
_UFlat0-8 2.19GB/s ± 0% html
|
||||||
|
_UFlat1-8 1.41GB/s ± 0% urls
|
||||||
|
_UFlat2-8 23.5GB/s ± 2% jpg
|
||||||
|
_UFlat3-8 1.91GB/s ± 0% jpg_200
|
||||||
|
_UFlat4-8 14.0GB/s ± 1% pdf
|
||||||
|
_UFlat5-8 1.97GB/s ± 0% html4
|
||||||
|
_UFlat6-8 814MB/s ± 0% txt1
|
||||||
|
_UFlat7-8 785MB/s ± 0% txt2
|
||||||
|
_UFlat8-8 857MB/s ± 0% txt3
|
||||||
|
_UFlat9-8 719MB/s ± 1% txt4
|
||||||
|
_UFlat10-8 2.84GB/s ± 0% pb
|
||||||
|
_UFlat11-8 1.05GB/s ± 0% gaviota
|
||||||
|
|
||||||
|
_ZFlat0-8 1.04GB/s ± 0% html
|
||||||
|
_ZFlat1-8 534MB/s ± 0% urls
|
||||||
|
_ZFlat2-8 15.7GB/s ± 1% jpg
|
||||||
|
_ZFlat3-8 740MB/s ± 3% jpg_200
|
||||||
|
_ZFlat4-8 9.20GB/s ± 1% pdf
|
||||||
|
_ZFlat5-8 991MB/s ± 0% html4
|
||||||
|
_ZFlat6-8 379MB/s ± 0% txt1
|
||||||
|
_ZFlat7-8 352MB/s ± 0% txt2
|
||||||
|
_ZFlat8-8 396MB/s ± 1% txt3
|
||||||
|
_ZFlat9-8 327MB/s ± 1% txt4
|
||||||
|
_ZFlat10-8 1.33GB/s ± 1% pb
|
||||||
|
_ZFlat11-8 605MB/s ± 1% gaviota
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"go test -test.bench=. -tags=noasm"
|
||||||
|
|
||||||
|
_UFlat0-8 621MB/s ± 2% html
|
||||||
|
_UFlat1-8 494MB/s ± 1% urls
|
||||||
|
_UFlat2-8 23.2GB/s ± 1% jpg
|
||||||
|
_UFlat3-8 1.12GB/s ± 1% jpg_200
|
||||||
|
_UFlat4-8 4.35GB/s ± 1% pdf
|
||||||
|
_UFlat5-8 609MB/s ± 0% html4
|
||||||
|
_UFlat6-8 296MB/s ± 0% txt1
|
||||||
|
_UFlat7-8 288MB/s ± 0% txt2
|
||||||
|
_UFlat8-8 309MB/s ± 1% txt3
|
||||||
|
_UFlat9-8 280MB/s ± 1% txt4
|
||||||
|
_UFlat10-8 753MB/s ± 0% pb
|
||||||
|
_UFlat11-8 400MB/s ± 0% gaviota
|
||||||
|
|
||||||
|
_ZFlat0-8 409MB/s ± 1% html
|
||||||
|
_ZFlat1-8 250MB/s ± 1% urls
|
||||||
|
_ZFlat2-8 12.3GB/s ± 1% jpg
|
||||||
|
_ZFlat3-8 132MB/s ± 0% jpg_200
|
||||||
|
_ZFlat4-8 2.92GB/s ± 0% pdf
|
||||||
|
_ZFlat5-8 405MB/s ± 1% html4
|
||||||
|
_ZFlat6-8 179MB/s ± 1% txt1
|
||||||
|
_ZFlat7-8 170MB/s ± 1% txt2
|
||||||
|
_ZFlat8-8 189MB/s ± 1% txt3
|
||||||
|
_ZFlat9-8 164MB/s ± 1% txt4
|
||||||
|
_ZFlat10-8 479MB/s ± 1% pb
|
||||||
|
_ZFlat11-8 270MB/s ± 1% gaviota
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
For comparison (Go's encoded output is byte-for-byte identical to C++'s), here
|
||||||
|
are the numbers from C++ Snappy's
|
||||||
|
|
||||||
|
make CXXFLAGS="-O2 -DNDEBUG -g" clean snappy_unittest.log && cat snappy_unittest.log
|
||||||
|
|
||||||
|
BM_UFlat/0 2.4GB/s html
|
||||||
|
BM_UFlat/1 1.4GB/s urls
|
||||||
|
BM_UFlat/2 21.8GB/s jpg
|
||||||
|
BM_UFlat/3 1.5GB/s jpg_200
|
||||||
|
BM_UFlat/4 13.3GB/s pdf
|
||||||
|
BM_UFlat/5 2.1GB/s html4
|
||||||
|
BM_UFlat/6 1.0GB/s txt1
|
||||||
|
BM_UFlat/7 959.4MB/s txt2
|
||||||
|
BM_UFlat/8 1.0GB/s txt3
|
||||||
|
BM_UFlat/9 864.5MB/s txt4
|
||||||
|
BM_UFlat/10 2.9GB/s pb
|
||||||
|
BM_UFlat/11 1.2GB/s gaviota
|
||||||
|
|
||||||
|
BM_ZFlat/0 944.3MB/s html (22.31 %)
|
||||||
|
BM_ZFlat/1 501.6MB/s urls (47.78 %)
|
||||||
|
BM_ZFlat/2 14.3GB/s jpg (99.95 %)
|
||||||
|
BM_ZFlat/3 538.3MB/s jpg_200 (73.00 %)
|
||||||
|
BM_ZFlat/4 8.3GB/s pdf (83.30 %)
|
||||||
|
BM_ZFlat/5 903.5MB/s html4 (22.52 %)
|
||||||
|
BM_ZFlat/6 336.0MB/s txt1 (57.88 %)
|
||||||
|
BM_ZFlat/7 312.3MB/s txt2 (61.91 %)
|
||||||
|
BM_ZFlat/8 353.1MB/s txt3 (54.99 %)
|
||||||
|
BM_ZFlat/9 289.9MB/s txt4 (66.26 %)
|
||||||
|
BM_ZFlat/10 1.2GB/s pb (19.68 %)
|
||||||
|
BM_ZFlat/11 527.4MB/s gaviota (37.72 %)
|
237
vendor/github.com/golang/snappy/decode.go
generated
vendored
Normal file
237
vendor/github.com/golang/snappy/decode.go
generated
vendored
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package snappy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrCorrupt reports that the input is invalid.
|
||||||
|
ErrCorrupt = errors.New("snappy: corrupt input")
|
||||||
|
// ErrTooLarge reports that the uncompressed length is too large.
|
||||||
|
ErrTooLarge = errors.New("snappy: decoded block is too large")
|
||||||
|
// ErrUnsupported reports that the input isn't supported.
|
||||||
|
ErrUnsupported = errors.New("snappy: unsupported input")
|
||||||
|
|
||||||
|
errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length")
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecodedLen returns the length of the decoded block.
|
||||||
|
func DecodedLen(src []byte) (int, error) {
|
||||||
|
v, _, err := decodedLen(src)
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodedLen returns the length of the decoded block and the number of bytes
|
||||||
|
// that the length header occupied.
|
||||||
|
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
|
||||||
|
v, n := binary.Uvarint(src)
|
||||||
|
if n <= 0 || v > 0xffffffff {
|
||||||
|
return 0, 0, ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
const wordSize = 32 << (^uint(0) >> 32 & 1)
|
||||||
|
if wordSize == 32 && v > 0x7fffffff {
|
||||||
|
return 0, 0, ErrTooLarge
|
||||||
|
}
|
||||||
|
return int(v), n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
decodeErrCodeCorrupt = 1
|
||||||
|
decodeErrCodeUnsupportedLiteralLength = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decode returns the decoded form of src. The returned slice may be a sub-
|
||||||
|
// slice of dst if dst was large enough to hold the entire decoded block.
|
||||||
|
// Otherwise, a newly allocated slice will be returned.
|
||||||
|
//
|
||||||
|
// The dst and src must not overlap. It is valid to pass a nil dst.
|
||||||
|
func Decode(dst, src []byte) ([]byte, error) {
|
||||||
|
dLen, s, err := decodedLen(src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if dLen <= len(dst) {
|
||||||
|
dst = dst[:dLen]
|
||||||
|
} else {
|
||||||
|
dst = make([]byte, dLen)
|
||||||
|
}
|
||||||
|
switch decode(dst, src[s:]) {
|
||||||
|
case 0:
|
||||||
|
return dst, nil
|
||||||
|
case decodeErrCodeUnsupportedLiteralLength:
|
||||||
|
return nil, errUnsupportedLiteralLength
|
||||||
|
}
|
||||||
|
return nil, ErrCorrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReader returns a new Reader that decompresses from r, using the framing
|
||||||
|
// format described at
|
||||||
|
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||||
|
func NewReader(r io.Reader) *Reader {
|
||||||
|
return &Reader{
|
||||||
|
r: r,
|
||||||
|
decoded: make([]byte, maxBlockSize),
|
||||||
|
buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reader is an io.Reader that can read Snappy-compressed bytes.
|
||||||
|
type Reader struct {
|
||||||
|
r io.Reader
|
||||||
|
err error
|
||||||
|
decoded []byte
|
||||||
|
buf []byte
|
||||||
|
// decoded[i:j] contains decoded bytes that have not yet been passed on.
|
||||||
|
i, j int
|
||||||
|
readHeader bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset discards any buffered data, resets all state, and switches the Snappy
|
||||||
|
// reader to read from r. This permits reusing a Reader rather than allocating
|
||||||
|
// a new one.
|
||||||
|
func (r *Reader) Reset(reader io.Reader) {
|
||||||
|
r.r = reader
|
||||||
|
r.err = nil
|
||||||
|
r.i = 0
|
||||||
|
r.j = 0
|
||||||
|
r.readHeader = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) {
|
||||||
|
if _, r.err = io.ReadFull(r.r, p); r.err != nil {
|
||||||
|
if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read satisfies the io.Reader interface.
|
||||||
|
func (r *Reader) Read(p []byte) (int, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
if r.i < r.j {
|
||||||
|
n := copy(p, r.decoded[r.i:r.j])
|
||||||
|
r.i += n
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
if !r.readFull(r.buf[:4], true) {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
chunkType := r.buf[0]
|
||||||
|
if !r.readHeader {
|
||||||
|
if chunkType != chunkTypeStreamIdentifier {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
r.readHeader = true
|
||||||
|
}
|
||||||
|
chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
|
||||||
|
if chunkLen > len(r.buf) {
|
||||||
|
r.err = ErrUnsupported
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The chunk types are specified at
|
||||||
|
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||||
|
switch chunkType {
|
||||||
|
case chunkTypeCompressedData:
|
||||||
|
// Section 4.2. Compressed data (chunk type 0x00).
|
||||||
|
if chunkLen < checksumSize {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
buf := r.buf[:chunkLen]
|
||||||
|
if !r.readFull(buf, false) {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||||
|
buf = buf[checksumSize:]
|
||||||
|
|
||||||
|
n, err := DecodedLen(buf)
|
||||||
|
if err != nil {
|
||||||
|
r.err = err
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
if n > len(r.decoded) {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
if _, err := Decode(r.decoded, buf); err != nil {
|
||||||
|
r.err = err
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
if crc(r.decoded[:n]) != checksum {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
r.i, r.j = 0, n
|
||||||
|
continue
|
||||||
|
|
||||||
|
case chunkTypeUncompressedData:
|
||||||
|
// Section 4.3. Uncompressed data (chunk type 0x01).
|
||||||
|
if chunkLen < checksumSize {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
buf := r.buf[:checksumSize]
|
||||||
|
if !r.readFull(buf, false) {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||||
|
// Read directly into r.decoded instead of via r.buf.
|
||||||
|
n := chunkLen - checksumSize
|
||||||
|
if n > len(r.decoded) {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
if !r.readFull(r.decoded[:n], false) {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
if crc(r.decoded[:n]) != checksum {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
r.i, r.j = 0, n
|
||||||
|
continue
|
||||||
|
|
||||||
|
case chunkTypeStreamIdentifier:
|
||||||
|
// Section 4.1. Stream identifier (chunk type 0xff).
|
||||||
|
if chunkLen != len(magicBody) {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
if !r.readFull(r.buf[:len(magicBody)], false) {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
for i := 0; i < len(magicBody); i++ {
|
||||||
|
if r.buf[i] != magicBody[i] {
|
||||||
|
r.err = ErrCorrupt
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if chunkType <= 0x7f {
|
||||||
|
// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
|
||||||
|
r.err = ErrUnsupported
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
// Section 4.4 Padding (chunk type 0xfe).
|
||||||
|
// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
|
||||||
|
if !r.readFull(r.buf[:chunkLen], false) {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
vendor/github.com/golang/snappy/decode_amd64.go
generated
vendored
Normal file
14
vendor/github.com/golang/snappy/decode_amd64.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !appengine
|
||||||
|
// +build gc
|
||||||
|
// +build !noasm
|
||||||
|
|
||||||
|
package snappy
|
||||||
|
|
||||||
|
// decode has the same semantics as in decode_other.go.
|
||||||
|
//
|
||||||
|
//go:noescape
|
||||||
|
func decode(dst, src []byte) int
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue