diff --git a/config.yml.sample b/config.yml.sample
index 30b149a5..e6175b32 100644
--- a/config.yml.sample
+++ b/config.yml.sample
@@ -112,6 +112,8 @@ log:
standard: "stdout"
# Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
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.
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.
diff --git a/docs/content/doc/setup/config.md b/docs/content/doc/setup/config.md
index c78dedc4..5519d39f 100644
--- a/docs/content/doc/setup/config.md
+++ b/docs/content/doc/setup/config.md
@@ -155,6 +155,8 @@ log:
standard: "stdout"
# Whether or not to log database queries. Useful for debugging. Possible values are stdout, stderr, file or off to disable database logging.
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.
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.
diff --git a/go.mod b/go.mod
index 156947f2..4fee4cb4 100644
--- a/go.mod
+++ b/go.mod
@@ -30,20 +30,22 @@ require (
github.com/cweill/gotests v1.5.3
github.com/d4l3k/messagediff v1.2.1 // indirect
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/garyburd/redigo v1.6.0 // indirect
github.com/go-openapi/jsonreference v0.19.3 // 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 // indirect
+ github.com/go-redis/redis/v7 v7.2.0
github.com/go-sql-driver/mysql v1.5.0
github.com/go-testfixtures/testfixtures/v3 v3.1.1
github.com/go-xorm/core v0.6.2 // 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/iancoleman/strcase v0.0.0-20191112232945-16388991a334
github.com/imdario/mergo v0.3.9
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/gommon v0.3.0
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/mattn/go-sqlite3 v2.0.3+incompatible
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/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/pelletier/go-toml v1.4.0 // indirect
github.com/prometheus/client_golang v0.9.4
@@ -64,21 +69,31 @@ require (
github.com/spf13/viper v1.6.3
github.com/stretchr/testify v1.5.1
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
golang.org/x/crypto v0.0.0-20200406173513-056763e48d71
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/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/d4l3k/messagediff.v1 v1.2.1
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a
src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273
- src.techknowlogick.com/xormigrate v1.1.0
- xorm.io/builder v0.3.6
+ src.techknowlogick.com/xormigrate v1.2.0
+ xorm.io/builder v0.3.7
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
diff --git a/go.sum b/go.sum
index eaf918cc..38411e1c 100644
--- a/go.sum
+++ b/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=
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=
+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/go.mod h1:53b8exJwT/5JBCf5n5gMqKrIZAqIErdJCRtzS1AuiMM=
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/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/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/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
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/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
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.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-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/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/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/go.mod h1:XZYOJkGVkCRoymaIzmp9Wyi3rUgfA3oOnkuljYrjFV8=
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/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.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/go.mod h1:BjL/N0+C+j9uNX+1xcNuM9vdSIcXCZrQZUYbXOFbgN8=
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-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/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-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
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.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
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-redis/redis v6.14.0+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
-github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U=
-github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+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-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
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/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0=
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/gogo/protobuf v1.1.1/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.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
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.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.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 v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
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/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
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/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
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/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
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/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
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/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.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
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/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/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/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/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=
@@ -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/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
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/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+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/go.mod h1:kU/7PwzgNxZH4das4XNsSpBSOD09XIF5YEPzjpkGnGE=
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/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/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/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
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/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
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/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/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/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.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/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.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/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
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.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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.6.3 h1:N+uVPGP4H2hXoss2pt5dctoSUPKKRInr6qcTMOm0usI=
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/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.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 v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=
-github.com/ulule/limiter/v3 v3.3.0 h1:DuMRthpkl1wW9Em6xOVw5HMHnbDumSIDydiMqP0PTXs=
-github.com/ulule/limiter/v3 v3.3.0/go.mod h1:E6sfg3hfRgW+yFvkE/rZf6YLqXYFMWTmZaZKvdEiQsA=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
+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/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
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/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/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
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/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/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
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.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/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-20190308221718-c2843e01d9a2/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/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
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-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-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-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-20181220203305-927f97764cc3/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-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-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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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-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-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-20180905080454-ebe1bf3edb33/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/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-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-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/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/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-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-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-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-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-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/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-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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
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.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
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-20190307195333-5fe7a883aa19/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 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-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-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/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/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=
@@ -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.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
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-20190102054323-c2f93a96b099/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=
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/xormigrate v1.1.0 h1:Ob79c1pOO+voMB9roa2eHZByT+TODwC51+Mn9e3HoTI=
-src.techknowlogick.com/xormigrate v1.1.0/go.mod h1:IMdvIk60uPX+IUsaXbdtqFzl3n7PfRg/cSZxxsiCWf8=
+src.techknowlogick.com/xormigrate v1.2.0 h1:bq9JaI48bxB+OddMghicjmV7sGmBUogJq4HmTN0DOcw=
+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/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 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/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
-xorm.io/xorm v0.8.1 h1:4f2KXuQxVdaX3RdI3Fw81NzMiSpZeyCZt8m3sEVeIkQ=
-xorm.io/xorm v0.8.1/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
+xorm.io/xorm v1.0.1 h1:/lITxpJtkZauNpdzj+L9CN/3OQxZaABrbergMcJu+Cw=
+xorm.io/xorm v1.0.1/go.mod h1:o4vnEsQ5V2F1/WK6w4XTwmiWJeGj82tqjAnHe44wVHY=
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 6c092a9a..2f122ce1 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -78,13 +78,14 @@ const (
RedisPassword Key = `redis.password`
RedisDB Key = `redis.db`
- LogEnabled Key = `log.enabled`
- LogErrors Key = `log.errors`
- LogStandard Key = `log.standard`
- LogDatabase Key = `log.database`
- LogHTTP Key = `log.http`
- LogEcho Key = `log.echo`
- LogPath Key = `log.path`
+ LogEnabled Key = `log.enabled`
+ LogErrors Key = `log.errors`
+ LogStandard Key = `log.standard`
+ LogDatabase Key = `log.database`
+ LogDatabaseLevel Key = `log.databaselevel`
+ LogHTTP Key = `log.http`
+ LogEcho Key = `log.echo`
+ LogPath Key = `log.path`
RateLimitEnabled Key = `ratelimit.enabled`
RateLimitKind Key = `ratelimit.kind`
@@ -214,6 +215,7 @@ func InitDefaultConfig() {
LogErrors.setDefault("stdout")
LogStandard.setDefault("stdout")
LogDatabase.setDefault("off")
+ LogDatabaseLevel.setDefault("DEBUG")
LogHTTP.setDefault("stdout")
LogEcho.setDefault("off")
LogPath.setDefault(ServiceRootpath.GetString() + "/logs")
diff --git a/pkg/db/db.go b/pkg/db/db.go
index aee4a8df..bf54d533 100644
--- a/pkg/db/db.go
+++ b/pkg/db/db.go
@@ -21,14 +21,14 @@ import (
"code.vikunja.io/api/pkg/log"
"encoding/gob"
"fmt"
+ xrc "gitea.com/xorm/xorm-redis-cache"
"net/url"
"strconv"
"strings"
"time"
"xorm.io/core"
"xorm.io/xorm"
-
- xrc "gitea.com/xorm/xorm-redis-cache"
+ "xorm.io/xorm/caches"
_ "github.com/go-sql-driver/mysql" // Because.
_ "github.com/lib/pq" // Because.
@@ -70,8 +70,7 @@ func CreateDBEngine() (engine *xorm.Engine, err error) {
}
engine.SetMapper(core.GonicMapper{})
- logger := xorm.NewSimpleLogger(log.GetLogWriter("database"))
- logger.ShowSQL(config.LogDatabase.GetString() != "off")
+ logger := log.NewXormLogger()
engine.SetLogger(logger)
// Cache
@@ -79,10 +78,10 @@ func CreateDBEngine() (engine *xorm.Engine, err error) {
if config.CacheEnabled.GetBool() {
switch config.CacheType.GetString() {
case "memory":
- cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), config.CacheMaxElementSize.GetInt())
+ cacher := caches.NewLRUCacher(caches.NewMemoryStore(), config.CacheMaxElementSize.GetInt())
engine.SetDefaultCacher(cacher)
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)
default:
log.Info("Did not find a valid cache type. Caching disabled. Please refer to the docs for poosible cache types.")
diff --git a/pkg/db/fixtures/tasks.yml b/pkg/db/fixtures/tasks.yml
index 40742e33..72eb3018 100644
--- a/pkg/db/fixtures/tasks.yml
+++ b/pkg/db/fixtures/tasks.yml
@@ -1,6 +1,7 @@
- id: 1
text: 'task #1'
description: 'Lorem Ipsum'
+ done: false
created_by_id: 1
list_id: 1
index: 1
@@ -16,6 +17,7 @@
updated: 1543626724
- id: 3
text: 'task #3 high prio'
+ done: false
created_by_id: 1
list_id: 1
index: 3
@@ -24,6 +26,7 @@
priority: 100
- id: 4
text: 'task #4 low prio'
+ done: false
created_by_id: 1
list_id: 1
index: 4
@@ -32,6 +35,7 @@
priority: 1
- id: 5
text: 'task #5 higher due date'
+ done: false
created_by_id: 1
list_id: 1
index: 5
@@ -40,6 +44,7 @@
due_date_unix: 1543636724
- id: 6
text: 'task #6 lower due date'
+ done: false
created_by_id: 1
list_id: 1
index: 6
@@ -48,6 +53,7 @@
due_date_unix: 1543616724
- id: 7
text: 'task #7 with start date'
+ done: false
created_by_id: 1
list_id: 1
index: 7
@@ -56,6 +62,7 @@
start_date_unix: 1544600000
- id: 8
text: 'task #8 with end date'
+ done: false
created_by_id: 1
list_id: 1
index: 8
@@ -64,6 +71,7 @@
end_date_unix: 1544700000
- id: 9
text: 'task #9 with start and end date'
+ done: false
created_by_id: 1
list_id: 1
index: 9
@@ -73,6 +81,7 @@
end_date_unix: 1544700000
- id: 10
text: 'task #10 basic'
+ done: false
created_by_id: 1
list_id: 1
index: 10
@@ -80,6 +89,7 @@
updated: 1543626724
- id: 11
text: 'task #11 basic'
+ done: false
created_by_id: 1
list_id: 1
index: 11
@@ -87,6 +97,7 @@
updated: 1543626724
- id: 12
text: 'task #12 basic'
+ done: false
created_by_id: 1
list_id: 1
index: 12
@@ -94,6 +105,7 @@
updated: 1543626724
- id: 13
text: 'task #13 basic other list'
+ done: false
created_by_id: 1
list_id: 2
index: 1
@@ -101,6 +113,7 @@
updated: 1543626724
- id: 14
text: 'task #14 basic other list'
+ done: false
created_by_id: 5
list_id: 5
index: 1
@@ -108,6 +121,7 @@
updated: 1543626724
- id: 15
text: 'task #15'
+ done: false
created_by_id: 6
list_id: 6
index: 1
@@ -115,6 +129,7 @@
updated: 1543626724
- id: 16
text: 'task #16'
+ done: false
created_by_id: 6
list_id: 7
index: 1
@@ -122,6 +137,7 @@
updated: 1543626724
- id: 17
text: 'task #17'
+ done: false
created_by_id: 6
list_id: 8
index: 1
@@ -129,6 +145,7 @@
updated: 1543626724
- id: 18
text: 'task #18'
+ done: false
created_by_id: 6
list_id: 9
index: 1
@@ -136,6 +153,7 @@
updated: 1543626724
- id: 19
text: 'task #19'
+ done: false
created_by_id: 6
list_id: 10
index: 1
@@ -143,6 +161,7 @@
updated: 1543626724
- id: 20
text: 'task #20'
+ done: false
created_by_id: 6
list_id: 11
index: 1
@@ -150,6 +169,7 @@
updated: 1543626724
- id: 21
text: 'task #21'
+ done: false
created_by_id: 6
list_id: 12
index: 1
@@ -157,6 +177,7 @@
updated: 1543626724
- id: 22
text: 'task #22'
+ done: false
created_by_id: 6
list_id: 13
index: 1
@@ -164,6 +185,7 @@
updated: 1543626724
- id: 23
text: 'task #23'
+ done: false
created_by_id: 6
list_id: 14
index: 1
@@ -171,6 +193,7 @@
updated: 1543626724
- id: 24
text: 'task #24'
+ done: false
created_by_id: 6
list_id: 15
index: 1
@@ -178,6 +201,7 @@
updated: 1543626724
- id: 25
text: 'task #25'
+ done: false
created_by_id: 6
list_id: 16
index: 1
@@ -185,6 +209,7 @@
updated: 1543626724
- id: 26
text: 'task #26'
+ done: false
created_by_id: 6
list_id: 17
index: 1
@@ -192,6 +217,7 @@
updated: 1543626724
- id: 27
text: 'task #27 with reminders'
+ done: false
created_by_id: 1
list_id: 1
index: 12
@@ -208,6 +234,7 @@
updated: 1543626724
- id: 29
text: 'task #29 with parent task (1)'
+ done: false
created_by_id: 1
list_id: 1
index: 14
@@ -215,6 +242,7 @@
updated: 1543626724
- id: 30
text: 'task #30 with assignees'
+ done: false
created_by_id: 1
list_id: 1
index: 15
@@ -222,6 +250,7 @@
updated: 1543626724
- id: 31
text: 'task #31 with color'
+ done: false
created_by_id: 1
list_id: 1
index: 16
@@ -230,6 +259,7 @@
updated: 1543626724
- id: 32
text: 'task #32'
+ done: false
created_by_id: 1
list_id: 3
index: 1
@@ -237,6 +267,7 @@
updated: 1543626724
- id: 33
text: 'task #33 with percent done'
+ done: false
created_by_id: 1
list_id: 1
index: 17
@@ -246,6 +277,7 @@
# This task is forbidden for user1
- id: 34
text: 'task #34'
+ done: false
created_by_id: 13
list_id: 20
index: 20
@@ -253,6 +285,7 @@
updated: 1543626724
- id: 35
text: 'task #35'
+ done: false
created_by_id: 1
list_id: 21
index: 1
@@ -260,6 +293,7 @@
updated: 1543626724
- id: 36
text: 'task #36'
+ done: false
created_by_id: 1
list_id: 22
index: 1
diff --git a/pkg/db/test.go b/pkg/db/test.go
index 27a54b55..8752dd47 100644
--- a/pkg/db/test.go
+++ b/pkg/db/test.go
@@ -46,7 +46,7 @@ func CreateTestEngine() (engine *xorm.Engine, err error) {
}
engine.SetMapper(core.GonicMapper{})
- logger := xorm.NewSimpleLogger(log.GetLogWriter("database"))
+ logger := log.NewXormLogger()
logger.ShowSQL(os.Getenv("UNIT_TESTS_VERBOSE") == "1")
engine.SetLogger(logger)
x = engine
diff --git a/pkg/db/test_fixtures.go b/pkg/db/test_fixtures.go
index 90c6c4d5..0808b9db 100644
--- a/pkg/db/test_fixtures.go
+++ b/pkg/db/test_fixtures.go
@@ -24,6 +24,7 @@ import (
"github.com/stretchr/testify/assert"
"path/filepath"
"testing"
+ "xorm.io/xorm/schemas"
)
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
// 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(' ||
quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
diff --git a/pkg/log/xorm_logger.go b/pkg/log/xorm_logger.go
new file mode 100644
index 00000000..da97e1d7
--- /dev/null
+++ b/pkg/log/xorm_logger.go
@@ -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 .
+
+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
+ }
+}
diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go
index e336652f..335da531 100644
--- a/pkg/metrics/metrics.go
+++ b/pkg/metrics/metrics.go
@@ -20,7 +20,7 @@ import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/log"
"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/promauto"
)
diff --git a/pkg/migration/20190922205826.go b/pkg/migration/20190922205826.go
index 01b09660..4babe919 100644
--- a/pkg/migration/20190922205826.go
+++ b/pkg/migration/20190922205826.go
@@ -60,7 +60,7 @@ func init() {
// Get all current subtasks and put them in a new table
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 {
return err
}
diff --git a/pkg/models/label_rights.go b/pkg/models/label_rights.go
index de1b9404..d3ced45a 100644
--- a/pkg/models/label_rights.go
+++ b/pkg/models/label_rights.go
@@ -76,7 +76,7 @@ func (l *Label) hasAccessToLabel(a web.Auth) (bool, error) {
has, err := x.Table("labels").
Select("labels.*").
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)).
And("labels.id = ?", l.ID).
Exist(&labels)
diff --git a/pkg/models/label_task.go b/pkg/models/label_task.go
index c9705a45..f8ade2d1 100644
--- a/pkg/models/label_task.go
+++ b/pkg/models/label_task.go
@@ -162,20 +162,27 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, res
// Get all labels associated with these tasks
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 {
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).
Join("LEFT", "label_task", "label_task.label_id = labels.id").
Where(cond).
And("labels.title LIKE ?", "%"+opts.Search+"%").
GroupBy(groupBy).
- OrderBy("labels.id ASC").
- Limit(getLimitFromPageIndex(opts.Page, opts.PerPage)).
- Find(&labels)
+ OrderBy("labels.id ASC")
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+ err = query.Find(&labels)
if err != nil {
return nil, 0, 0, err
}
diff --git a/pkg/models/link_sharing.go b/pkg/models/link_sharing.go
index 7501872a..4e6e9104 100644
--- a/pkg/models/link_sharing.go
+++ b/pkg/models/link_sharing.go
@@ -155,11 +155,15 @@ func (share *LinkSharing) ReadAll(a web.Auth, search string, page int, perPage i
return nil, 0, 0, ErrGenericForbidden{}
}
+ limit, start := getLimitFromPageIndex(page, perPage)
+
var shares []*LinkSharing
- err = x.
- Where("list_id = ? AND hash LIKE ?", share.ListID, "%"+search+"%").
- Limit(getLimitFromPageIndex(page, perPage)).
- Find(&shares)
+ query := x.
+ Where("list_id = ? AND hash LIKE ?", share.ListID, "%"+search+"%")
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+ err = query.Find(&shares)
if err != nil {
return nil, 0, 0, err
}
diff --git a/pkg/models/list.go b/pkg/models/list.go
index 36594944..23b1ccec 100644
--- a/pkg/models/list.go
+++ b/pkg/models/list.go
@@ -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
// Or in a team which has namespace read access
- err = x.Select("l.*").
+ query := x.Select("l.*").
Table("list").
Alias("l").
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{"users_list", "ul"}, "ul.list_id = l.id").
Join("LEFT", []string{"users_namespace", "un"}, "un.namespace_id = l.namespace_id").
- Where("tm.user_id = ?", fullUser.ID).
- Or("tm2.user_id = ?", fullUser.ID).
- Or("l.owner_id = ?", fullUser.ID).
- Or("ul.user_id = ?", fullUser.ID).
- Or("un.user_id = ?", fullUser.ID).
+ Where(builder.Or(
+ builder.Eq{"tm.user_id": fullUser.ID},
+ builder.Eq{"tm2.user_id": fullUser.ID},
+ builder.Eq{"ul.user_id": fullUser.ID},
+ builder.Eq{"un.user_id": fullUser.ID},
+ builder.Eq{"l.owner_id": fullUser.ID},
+ )).
GroupBy("l.id").
- Limit(getLimitFromPageIndex(opts.page, opts.perPage)).
Where("l.title LIKE ?", "%"+opts.search+"%").
- Where(isArchivedCond).
- Find(&lists)
+ Where(isArchivedCond)
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+ err = query.Find(&lists)
if err != nil {
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{"users_list", "ul"}, "ul.list_id = l.id").
Join("LEFT", []string{"users_namespace", "un"}, "un.namespace_id = l.namespace_id").
- Where("tm.user_id = ?", fullUser.ID).
- Or("tm2.user_id = ?", fullUser.ID).
- Or("l.owner_id = ?", fullUser.ID).
- Or("ul.user_id = ?", fullUser.ID).
- Or("un.user_id = ?", fullUser.ID).
+ Where(builder.Or(
+ builder.Eq{"tm.user_id": fullUser.ID},
+ builder.Eq{"tm2.user_id": fullUser.ID},
+ builder.Eq{"ul.user_id": fullUser.ID},
+ builder.Eq{"un.user_id": fullUser.ID},
+ builder.Eq{"l.owner_id": fullUser.ID},
+ )).
GroupBy("l.id").
- Limit(getLimitFromPageIndex(opts.page, opts.perPage)).
Where("l.title LIKE ?", "%"+opts.search+"%").
Where(isArchivedCond).
Count(&List{})
diff --git a/pkg/models/list_team.go b/pkg/models/list_team.go
index f3896fe5..e78f8c6f 100644
--- a/pkg/models/list_team.go
+++ b/pkg/models/list_team.go
@@ -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()}
}
+ limit, start := getLimitFromPageIndex(page, perPage)
+
// Get the teams
all := []*TeamWithRight{}
- err = x.
+ query := x.
Table("teams").
Join("INNER", "team_list", "team_id = teams.id").
Where("team_list.list_id = ?", tl.ListID).
- Limit(getLimitFromPageIndex(page, perPage)).
- Where("teams.name LIKE ?", "%"+search+"%").
- Find(&all)
+ Where("teams.name LIKE ?", "%"+search+"%")
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+ err = query.Find(&all)
if err != nil {
return nil, 0, 0, err
}
diff --git a/pkg/models/list_users.go b/pkg/models/list_users.go
index fb3edda3..d322d366 100644
--- a/pkg/models/list_users.go
+++ b/pkg/models/list_users.go
@@ -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}
}
+ limit, start := getLimitFromPageIndex(page, perPage)
+
// Get all users
all := []*UserWithRight{}
- err = x.
+ query := x.
Join("INNER", "users_list", "user_id = users.id").
Where("users_list.list_id = ?", lu.ListID).
- Limit(getLimitFromPageIndex(page, perPage)).
- Where("users.username LIKE ?", "%"+search+"%").
- Find(&all)
+ Where("users.username LIKE ?", "%"+search+"%")
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+ err = query.Find(&all)
if err != nil {
return nil, 0, 0, err
}
diff --git a/pkg/models/models.go b/pkg/models/models.go
index 7a0e22ef..5ad208bf 100644
--- a/pkg/models/models.go
+++ b/pkg/models/models.go
@@ -72,8 +72,8 @@ func SetEngine() (err error) {
func getLimitFromPageIndex(page int, perPage int) (limit, start int) {
- // Get everything when page index is -1
- if page < 0 {
+ // Get everything when page index is -1 or 0 (= not set)
+ if page < 1 {
return 0, 0
}
diff --git a/pkg/models/namespace.go b/pkg/models/namespace.go
index 552f8005..977df99b 100644
--- a/pkg/models/namespace.go
+++ b/pkg/models/namespace.go
@@ -190,7 +190,9 @@ func (n *Namespace) ReadAll(a web.Auth, search string, page int, perPage int) (r
[]*List{},
})
- err = x.Select("namespaces.*").
+ limit, start := getLimitFromPageIndex(page, perPage)
+
+ query := x.Select("namespaces.*").
Table("namespaces").
Join("LEFT", "team_namespaces", "namespaces.id = team_namespaces.namespace_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("users_namespace.user_id = ?", doer.ID).
GroupBy("namespaces.id").
- Limit(getLimitFromPageIndex(page, perPage)).
Where("namespaces.name LIKE ?", "%"+search+"%").
- Where(isArchivedCond).
- Find(&all)
+ Where(isArchivedCond)
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+ err = query.Find(&all)
if err != nil {
return all, 0, 0, err
}
diff --git a/pkg/models/namespace_team.go b/pkg/models/namespace_team.go
index 3a498cd2..f7f3852a 100644
--- a/pkg/models/namespace_team.go
+++ b/pkg/models/namespace_team.go
@@ -164,12 +164,16 @@ func (tn *TeamNamespace) ReadAll(a web.Auth, search string, page int, perPage in
// Get the teams
all := []*TeamWithRight{}
- err = x.Table("teams").
+ limit, start := getLimitFromPageIndex(page, perPage)
+
+ query := x.Table("teams").
Join("INNER", "team_namespaces", "team_id = teams.id").
Where("team_namespaces.namespace_id = ?", tn.NamespaceID).
- Limit(getLimitFromPageIndex(page, perPage)).
- Where("teams.name LIKE ?", "%"+search+"%").
- Find(&all)
+ Where("teams.name LIKE ?", "%"+search+"%")
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+ err = query.Find(&all)
if err != nil {
return nil, 0, 0, err
}
diff --git a/pkg/models/namespace_users.go b/pkg/models/namespace_users.go
index 1f988ad3..5885d05e 100644
--- a/pkg/models/namespace_users.go
+++ b/pkg/models/namespace_users.go
@@ -170,12 +170,17 @@ func (nu *NamespaceUser) ReadAll(a web.Auth, search string, page int, perPage in
// Get all users
all := []*UserWithRight{}
- err = x.
+
+ limit, start := getLimitFromPageIndex(page, perPage)
+
+ query := x.
Join("INNER", "users_namespace", "user_id = users.id").
Where("users_namespace.namespace_id = ?", nu.NamespaceID).
- Limit(getLimitFromPageIndex(page, perPage)).
- Where("users.username LIKE ?", "%"+search+"%").
- Find(&all)
+ Where("users.username LIKE ?", "%"+search+"%")
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+ err = query.Find(&all)
if err != nil {
return nil, 0, 0, err
}
diff --git a/pkg/models/task_assignees.go b/pkg/models/task_assignees.go
index ba3e9be0..1d924730 100644
--- a/pkg/models/task_assignees.go
+++ b/pkg/models/task_assignees.go
@@ -253,14 +253,17 @@ func (la *TaskAssginee) ReadAll(a web.Auth, search string, page int, perPage int
if !can {
return nil, 0, 0, ErrGenericForbidden{}
}
+ limit, start := getLimitFromPageIndex(page, perPage)
var taskAssignees []*user.User
- err = x.Table("task_assignees").
+ query := x.Table("task_assignees").
Select("users.*").
Join("INNER", "users", "task_assignees.user_id = users.id").
- Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%").
- Limit(getLimitFromPageIndex(page, perPage)).
- Find(&taskAssignees)
+ Where("task_id = ? AND users.username LIKE ?", la.TaskID, "%"+search+"%")
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+ err = query.Find(&taskAssignees)
if err != nil {
return nil, 0, 0, err
}
diff --git a/pkg/models/task_attachment.go b/pkg/models/task_attachment.go
index b351b6bc..eccbc89e 100644
--- a/pkg/models/task_attachment.go
+++ b/pkg/models/task_attachment.go
@@ -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) {
attachments := []*TaskAttachment{}
- err = x.
- Limit(getLimitFromPageIndex(page, perPage)).
- Where("task_id = ?", ta.TaskID).
- Find(&attachments)
+ limit, start := getLimitFromPageIndex(page, perPage)
+
+ query := x.
+ Where("task_id = ?", ta.TaskID)
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+ err = query.Find(&attachments)
if err != nil {
return nil, 0, 0, err
}
diff --git a/pkg/models/task_comments.go b/pkg/models/task_comments.go
index aa689f80..1c2a4b43 100644
--- a/pkg/models/task_comments.go
+++ b/pkg/models/task_comments.go
@@ -180,12 +180,16 @@ func (tc *TaskComment) ReadAll(auth web.Auth, search string, page int, perPage i
AuthorFromDB *user.User `xorm:"extends" json:"-"`
}
+ limit, start := getLimitFromPageIndex(page, perPage)
+
comments := []*TaskComment{}
- err = x.
+ query := x.
Where("task_id = ? AND comment like ?", tc.TaskID, "%"+search+"%").
- Join("LEFT", "users", "users.id = task_comments.author_id").
- Limit(getLimitFromPageIndex(page, perPage)).
- Find(&comments)
+ Join("LEFT", "users", "users.id = task_comments.author_id")
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+ err = query.Find(&comments)
if err != nil {
return
}
diff --git a/pkg/models/tasks.go b/pkg/models/tasks.go
index e88231f6..ba071797 100644
--- a/pkg/models/tasks.go
+++ b/pkg/models/tasks.go
@@ -39,7 +39,7 @@ type Task struct {
// The task description.
Description string `xorm:"longtext null" json:"description"`
// 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.
DoneAt timeutil.TimeStamp `xorm:"INDEX null 'done_at_unix'" json:"doneAt"`
// 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)
// Then return all tasks for that lists
- if len(filters) > 0 {
+ query := x.
+ OrderBy(orderby)
- err := x.In("list_id", listIDs).
- Where("text LIKE ?", "%"+opts.search+"%").
- Where(builder.Or(filters...)).
- 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+"%").
- Where(builder.Or(filters...)).
- Count(&Task{})
- if err != nil {
- 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
- }
+ 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 {
+ query = query.Where(builder.Or(filters...))
+ }
+
+ limit, start := getLimitFromPageIndex(opts.page, opts.perPage)
+
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+
+ err = query.Find(&taskMap)
+ if err != nil {
+ return nil, 0, 0, err
+ }
+
+ totalItems, err = query.
+ Count(&Task{})
+ if err != nil {
+ return nil, 0, 0, err
+ }
+
return taskMap, len(taskMap), totalItems, nil
}
diff --git a/pkg/models/teams.go b/pkg/models/teams.go
index 4c515c50..9f79877f 100644
--- a/pkg/models/teams.go
+++ b/pkg/models/teams.go
@@ -203,14 +203,18 @@ func (t *Team) ReadAll(a web.Auth, search string, page int, perPage int) (result
return nil, 0, 0, ErrGenericForbidden{}
}
+ limit, start := getLimitFromPageIndex(page, perPage)
+
all := []*Team{}
- err = x.Select("teams.*").
+ query := x.Select("teams.*").
Table("teams").
Join("INNER", "team_members", "team_members.team_id = teams.id").
Where("team_members.user_id = ?", a.GetID()).
- Limit(getLimitFromPageIndex(page, perPage)).
- Where("teams.name LIKE ?", "%"+search+"%").
- Find(&all)
+ Where("teams.name LIKE ?", "%"+search+"%")
+ if limit > 0 {
+ query = query.Limit(limit, start)
+ }
+ err = query.Find(&all)
if err != nil {
return nil, 0, 0, err
}
diff --git a/pkg/red/redis.go b/pkg/red/redis.go
index 2ac0e613..016fb587 100644
--- a/pkg/red/redis.go
+++ b/pkg/red/redis.go
@@ -19,7 +19,7 @@ package red
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/log"
- "github.com/go-redis/redis"
+ "github.com/go-redis/redis/v7"
)
var r *redis.Client
diff --git a/pkg/user/user.go b/pkg/user/user.go
index 9df167e7..fa1bba9f 100644
--- a/pkg/user/user.go
+++ b/pkg/user/user.go
@@ -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
// Update it
- _, err = x.Id(user.ID).Update(user)
+ _, err = x.ID(user.ID).Update(user)
if err != nil {
return &User{}, err
}
@@ -344,7 +344,7 @@ func UpdateUserPassword(user *User, newPassword string) (err error) {
theUser.Password = hashed
// Update it
- _, err = x.Id(user.ID).Update(theUser)
+ _, err = x.ID(user.ID).Update(theUser)
if err != nil {
return err
}
diff --git a/vendor/github.com/fsnotify/fsnotify/.editorconfig b/vendor/github.com/fsnotify/fsnotify/.editorconfig
index ba49e3c2..fad89585 100644
--- a/vendor/github.com/fsnotify/fsnotify/.editorconfig
+++ b/vendor/github.com/fsnotify/fsnotify/.editorconfig
@@ -1,5 +1,12 @@
root = true
-[*]
+[*.go]
indent_style = tab
indent_size = 4
+insert_final_newline = true
+
+[*.{yml,yaml}]
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/vendor/github.com/fsnotify/fsnotify/.gitattributes b/vendor/github.com/fsnotify/fsnotify/.gitattributes
new file mode 100644
index 00000000..32f1001b
--- /dev/null
+++ b/vendor/github.com/fsnotify/fsnotify/.gitattributes
@@ -0,0 +1 @@
+go.sum linguist-generated
diff --git a/vendor/github.com/fsnotify/fsnotify/.travis.yml b/vendor/github.com/fsnotify/fsnotify/.travis.yml
index 981d1bb8..a9c30165 100644
--- a/vendor/github.com/fsnotify/fsnotify/.travis.yml
+++ b/vendor/github.com/fsnotify/fsnotify/.travis.yml
@@ -2,29 +2,35 @@ sudo: false
language: go
go:
- - 1.8.x
- - 1.9.x
- - tip
+ - "stable"
+ - "1.11.x"
+ - "1.10.x"
+ - "1.9.x"
matrix:
+ include:
+ - go: "stable"
+ env: GOLINT=true
allow_failures:
- go: tip
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:
- - go test -v --race ./...
+ - go test --race ./...
after_script:
- 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 ./...
os:
- linux
- osx
+ - windows
notifications:
email: false
diff --git a/vendor/github.com/fsnotify/fsnotify/LICENSE b/vendor/github.com/fsnotify/fsnotify/LICENSE
index f21e5408..e180c8fb 100644
--- a/vendor/github.com/fsnotify/fsnotify/LICENSE
+++ b/vendor/github.com/fsnotify/fsnotify/LICENSE
@@ -1,5 +1,5 @@
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
modification, are permitted provided that the following conditions are
diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md
index 39932074..b2629e52 100644
--- a/vendor/github.com/fsnotify/fsnotify/README.md
+++ b/vendor/github.com/fsnotify/fsnotify/README.md
@@ -10,16 +10,16 @@ go get -u golang.org/x/sys/...
Cross platform: Windows, Linux, BSD and macOS.
-|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)|
-|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)|
-|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)|
-|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)|
-|fanotify |Linux 2.6.37+ | |
-|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)|
-|Polling |*All* |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)|
+| 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) |
+| 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://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
+| FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) |
+| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/issues/12) |
+| fanotify | Linux 2.6.37+ | [Planned](https://github.com/fsnotify/fsnotify/issues/114) |
+| USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) |
+| Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) |
\* Android and iOS are untested.
@@ -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`.
+## 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
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.
* 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
[#18]: https://github.com/fsnotify/fsnotify/issues/18
[#11]: https://github.com/fsnotify/fsnotify/issues/11
diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go
index 190bf0de..89cab046 100644
--- a/vendor/github.com/fsnotify/fsnotify/fsnotify.go
+++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go
@@ -63,4 +63,6 @@ func (e Event) String() string {
}
// Common errors that can be reported by a watcher
-var ErrEventOverflow = errors.New("fsnotify queue overflow")
+var (
+ ErrEventOverflow = errors.New("fsnotify queue overflow")
+)
diff --git a/vendor/github.com/fsnotify/fsnotify/go.mod b/vendor/github.com/fsnotify/fsnotify/go.mod
new file mode 100644
index 00000000..ff11e13f
--- /dev/null
+++ b/vendor/github.com/fsnotify/fsnotify/go.mod
@@ -0,0 +1,5 @@
+module github.com/fsnotify/fsnotify
+
+go 1.13
+
+require golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9
diff --git a/vendor/github.com/fsnotify/fsnotify/go.sum b/vendor/github.com/fsnotify/fsnotify/go.sum
new file mode 100644
index 00000000..f60af985
--- /dev/null
+++ b/vendor/github.com/fsnotify/fsnotify/go.sum
@@ -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=
diff --git a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go
index cc7db4b2..b33f2b4d 100644
--- a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go
+++ b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go
@@ -40,12 +40,12 @@ func newFdPoller(fd int) (*fdPoller, error) {
poller.fd = fd
// Create epoll fd
- poller.epfd, errno = unix.EpollCreate1(0)
+ poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
if poller.epfd == -1 {
return nil, errno
}
// 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 {
return nil, errno
}
diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go b/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
index 7d8de145..2306c462 100644
--- a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
+++ b/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
@@ -8,4 +8,4 @@ package fsnotify
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
diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go b/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
index 9139e171..870c4d6d 100644
--- a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
+++ b/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
@@ -9,4 +9,4 @@ package fsnotify
import "golang.org/x/sys/unix"
// note: this constant is not defined on BSD
-const openMode = unix.O_EVTONLY
+const openMode = unix.O_EVTONLY | unix.O_CLOEXEC
diff --git a/vendor/github.com/go-redis/redis/.travis.yml b/vendor/github.com/go-redis/redis/.travis.yml
deleted file mode 100644
index 39ffc2be..00000000
--- a/vendor/github.com/go-redis/redis/.travis.yml
+++ /dev/null
@@ -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
diff --git a/vendor/github.com/go-redis/redis/CHANGELOG.md b/vendor/github.com/go-redis/redis/CHANGELOG.md
deleted file mode 100644
index 7c40d5e3..00000000
--- a/vendor/github.com/go-redis/redis/CHANGELOG.md
+++ /dev/null
@@ -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
diff --git a/vendor/github.com/go-redis/redis/internal/log.go b/vendor/github.com/go-redis/redis/internal/log.go
deleted file mode 100644
index fd14222e..00000000
--- a/vendor/github.com/go-redis/redis/internal/log.go
+++ /dev/null
@@ -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...))
-}
diff --git a/vendor/github.com/go-redis/redis/internal/pool/conn.go b/vendor/github.com/go-redis/redis/internal/pool/conn.go
deleted file mode 100644
index 1095bfe5..00000000
--- a/vendor/github.com/go-redis/redis/internal/pool/conn.go
+++ /dev/null
@@ -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()
-}
diff --git a/vendor/github.com/go-redis/redis/internal/pool/pool_single.go b/vendor/github.com/go-redis/redis/internal/pool/pool_single.go
deleted file mode 100644
index b35b78af..00000000
--- a/vendor/github.com/go-redis/redis/internal/pool/pool_single.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/github.com/go-redis/redis/internal/singleflight/singleflight.go b/vendor/github.com/go-redis/redis/internal/singleflight/singleflight.go
deleted file mode 100644
index 3b174172..00000000
--- a/vendor/github.com/go-redis/redis/internal/singleflight/singleflight.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/github.com/go-redis/redis/internal/util.go b/vendor/github.com/go-redis/redis/internal/util.go
deleted file mode 100644
index ffd2353e..00000000
--- a/vendor/github.com/go-redis/redis/internal/util.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/github.com/go-redis/redis/redis.go b/vendor/github.com/go-redis/redis/redis.go
deleted file mode 100644
index 3e72bf06..00000000
--- a/vendor/github.com/go-redis/redis/redis.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/github.com/go-redis/redis/sentinel.go b/vendor/github.com/go-redis/redis/sentinel.go
deleted file mode 100644
index c5f71493..00000000
--- a/vendor/github.com/go-redis/redis/sentinel.go
+++ /dev/null
@@ -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
-}
diff --git a/vendor/github.com/go-redis/redis/.gitignore b/vendor/github.com/go-redis/redis/v7/.gitignore
similarity index 100%
rename from vendor/github.com/go-redis/redis/.gitignore
rename to vendor/github.com/go-redis/redis/v7/.gitignore
diff --git a/vendor/github.com/go-redis/redis/v7/.golangci.yml b/vendor/github.com/go-redis/redis/v7/.golangci.yml
new file mode 100644
index 00000000..912dab1e
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v7/.golangci.yml
@@ -0,0 +1,15 @@
+run:
+ concurrency: 8
+ deadline: 5m
+ tests: false
+linters:
+ enable-all: true
+ disable:
+ - funlen
+ - gochecknoglobals
+ - gocognit
+ - goconst
+ - godox
+ - gosec
+ - maligned
+ - wsl
diff --git a/vendor/github.com/go-redis/redis/v7/.travis.yml b/vendor/github.com/go-redis/redis/v7/.travis.yml
new file mode 100644
index 00000000..0c1eef91
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v7/.travis.yml
@@ -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
diff --git a/vendor/github.com/go-redis/redis/v7/CHANGELOG.md b/vendor/github.com/go-redis/redis/v7/CHANGELOG.md
new file mode 100644
index 00000000..88701cfd
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v7/CHANGELOG.md
@@ -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
diff --git a/vendor/github.com/go-redis/redis/LICENSE b/vendor/github.com/go-redis/redis/v7/LICENSE
similarity index 100%
rename from vendor/github.com/go-redis/redis/LICENSE
rename to vendor/github.com/go-redis/redis/v7/LICENSE
diff --git a/vendor/github.com/go-redis/redis/Makefile b/vendor/github.com/go-redis/redis/v7/Makefile
similarity index 65%
rename from vendor/github.com/go-redis/redis/Makefile
rename to vendor/github.com/go-redis/redis/v7/Makefile
index 1fbdac91..421993ef 100644
--- a/vendor/github.com/go-redis/redis/Makefile
+++ b/vendor/github.com/go-redis/redis/v7/Makefile
@@ -1,8 +1,9 @@
all: testdeps
go test ./...
go test ./... -short -race
+ go test ./... -run=NONE -bench=. -benchmem
env GOOS=linux GOARCH=386 go test ./...
- go vet
+ golangci-lint run
testdeps: testdata/redis/src/redis-server
@@ -13,8 +14,7 @@ bench: testdeps
testdata/redis:
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
- sed -i.bak 's/libjemalloc.a/libjemalloc.a -lrt/g' $ threshold {
@@ -560,10 +562,13 @@ func (c *clusterState) slotClosestNode(slot int) (*clusterNode, error) {
return node, nil
}
-func (c *clusterState) slotRandomNode(slot int) *clusterNode {
+func (c *clusterState) slotRandomNode(slot int) (*clusterNode, error) {
nodes := c.slotNodes(slot)
+ if len(nodes) == 0 {
+ return c.nodes.Random()
+ }
n := rand.Intn(len(nodes))
- return nodes[n]
+ return nodes[n], nil
}
func (c *clusterState) slotNodes(slot int) []*clusterNode {
@@ -580,23 +585,12 @@ func (c *clusterState) slotNodes(slot int) []*clusterNode {
return nil
}
-func (c *clusterState) IsConsistent() bool {
- if c.nodes.opt.ClusterSlots != nil {
- return true
- }
- return len(c.Masters) <= len(c.Slaves)
-}
-
//------------------------------------------------------------------------------
type clusterStateHolder struct {
load func() (*clusterState, error)
- state atomic.Value
-
- firstErrMu sync.RWMutex
- firstErr error
-
+ state atomic.Value
reloading uint32 // atomic
}
@@ -607,24 +601,8 @@ func newClusterStateHolder(fn func() (*clusterState, error)) *clusterStateHolder
}
func (c *clusterStateHolder) Reload() (*clusterState, error) {
- state, err := c.reload()
- if err != nil {
- return nil, err
- }
- if !state.IsConsistent() {
- time.AfterFunc(time.Second, c.LazyReload)
- }
- return state, nil
-}
-
-func (c *clusterStateHolder) reload() (*clusterState, error) {
state, err := c.load()
if err != nil {
- c.firstErrMu.Lock()
- if c.firstErr == nil {
- c.firstErr = err
- }
- c.firstErrMu.Unlock()
return nil, err
}
c.state.Store(state)
@@ -638,16 +616,11 @@ func (c *clusterStateHolder) LazyReload() {
go func() {
defer atomic.StoreUint32(&c.reloading, 0)
- for {
- state, err := c.reload()
- if err != nil {
- return
- }
- time.Sleep(100 * time.Millisecond)
- if state.IsConsistent() {
- return
- }
+ _, err := c.Reload()
+ if err != nil {
+ return
}
+ time.Sleep(100 * time.Millisecond)
}()
}
@@ -660,15 +633,7 @@ func (c *clusterStateHolder) Get() (*clusterState, error) {
}
return state, nil
}
-
- c.firstErrMu.RLock()
- err := c.firstErr
- c.firstErrMu.RUnlock()
- if err != nil {
- return nil, err
- }
-
- return nil, errors.New("redis: cluster has no state")
+ return c.Reload()
}
func (c *clusterStateHolder) ReloadOrGet() (*clusterState, error) {
@@ -681,22 +646,21 @@ func (c *clusterStateHolder) ReloadOrGet() (*clusterState, error) {
//------------------------------------------------------------------------------
+type clusterClient struct {
+ opt *ClusterOptions
+ nodes *clusterNodes
+ state *clusterStateHolder //nolint:structcheck
+ cmdsInfoCache *cmdsInfoCache //nolint:structcheck
+}
+
// ClusterClient is a Redis Cluster client representing a pool of zero
// or more underlying connections. It's safe for concurrent use by
// multiple goroutines.
type ClusterClient struct {
+ *clusterClient
cmdable
-
+ hooks
ctx context.Context
-
- opt *ClusterOptions
- nodes *clusterNodes
- state *clusterStateHolder
- cmdsInfoCache *cmdsInfoCache
-
- process func(Cmder) error
- processPipeline func([]Cmder) error
- processTxPipeline func([]Cmder) error
}
// NewClusterClient returns a Redis Cluster client as described in
@@ -705,20 +669,15 @@ func NewClusterClient(opt *ClusterOptions) *ClusterClient {
opt.init()
c := &ClusterClient{
- opt: opt,
- nodes: newClusterNodes(opt),
+ clusterClient: &clusterClient{
+ opt: opt,
+ nodes: newClusterNodes(opt),
+ },
+ ctx: context.Background(),
}
c.state = newClusterStateHolder(c.loadState)
c.cmdsInfoCache = newCmdsInfoCache(c.cmdsInfo)
-
- c.process = c.defaultProcess
- c.processPipeline = c.defaultProcessPipeline
- c.processTxPipeline = c.defaultProcessTxPipeline
-
- c.init()
-
- _, _ = c.state.Reload()
- _, _ = c.cmdsInfoCache.Get()
+ c.cmdable = c.Process
if opt.IdleCheckFrequency > 0 {
go c.reaper(opt.IdleCheckFrequency)
@@ -727,37 +686,19 @@ func NewClusterClient(opt *ClusterOptions) *ClusterClient {
return c
}
-// ReloadState reloads cluster state. It calls ClusterSlots func
-// to get cluster slots information.
-func (c *ClusterClient) ReloadState() error {
- _, err := c.state.Reload()
- return err
-}
-
-func (c *ClusterClient) init() {
- c.cmdable.setProcessor(c.Process)
-}
-
func (c *ClusterClient) Context() context.Context {
- if c.ctx != nil {
- return c.ctx
- }
- return context.Background()
+ return c.ctx
}
func (c *ClusterClient) WithContext(ctx context.Context) *ClusterClient {
if ctx == nil {
panic("nil context")
}
- c2 := c.copy()
- c2.ctx = ctx
- return c2
-}
-
-func (c *ClusterClient) copy() *ClusterClient {
- cp := *c
- cp.init()
- return &cp
+ clone := *c
+ clone.cmdable = clone.Process
+ clone.hooks.lock()
+ clone.ctx = ctx
+ return &clone
}
// Options returns read-only Options that were used to create the client.
@@ -765,158 +706,10 @@ func (c *ClusterClient) Options() *ClusterOptions {
return c.opt
}
-func (c *ClusterClient) retryBackoff(attempt int) time.Duration {
- return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
-}
-
-func (c *ClusterClient) cmdsInfo() (map[string]*CommandInfo, error) {
- addrs, err := c.nodes.Addrs()
- if err != nil {
- return nil, err
- }
-
- var firstErr error
- for _, addr := range addrs {
- node, err := c.nodes.Get(addr)
- if err != nil {
- return nil, err
- }
- if node == nil {
- continue
- }
-
- info, err := node.Client.Command().Result()
- if err == nil {
- return info, nil
- }
- if firstErr == nil {
- firstErr = err
- }
- }
- return nil, firstErr
-}
-
-func (c *ClusterClient) cmdInfo(name string) *CommandInfo {
- cmdsInfo, err := c.cmdsInfoCache.Get()
- if err != nil {
- return nil
- }
-
- info := cmdsInfo[name]
- if info == nil {
- internal.Logf("info for cmd=%s not found", name)
- }
- return info
-}
-
-func cmdSlot(cmd Cmder, pos int) int {
- if pos == 0 {
- return hashtag.RandomSlot()
- }
- firstKey := cmd.stringArg(pos)
- return hashtag.Slot(firstKey)
-}
-
-func (c *ClusterClient) cmdSlot(cmd Cmder) int {
- cmdInfo := c.cmdInfo(cmd.Name())
- return cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo))
-}
-
-func (c *ClusterClient) cmdSlotAndNode(cmd Cmder) (int, *clusterNode, error) {
- state, err := c.state.Get()
- if err != nil {
- return 0, nil, err
- }
-
- cmdInfo := c.cmdInfo(cmd.Name())
- slot := cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo))
-
- if cmdInfo != nil && cmdInfo.ReadOnly && c.opt.ReadOnly {
- if c.opt.RouteByLatency {
- node, err := state.slotClosestNode(slot)
- return slot, node, err
- }
-
- if c.opt.RouteRandomly {
- node := state.slotRandomNode(slot)
- return slot, node, nil
- }
-
- node, err := state.slotSlaveNode(slot)
- return slot, node, err
- }
-
- node, err := state.slotMasterNode(slot)
- return slot, node, err
-}
-
-func (c *ClusterClient) slotMasterNode(slot int) (*clusterNode, error) {
- state, err := c.state.Get()
- if err != nil {
- return nil, err
- }
-
- nodes := state.slotNodes(slot)
- if len(nodes) > 0 {
- return nodes[0], nil
- }
- return c.nodes.Random()
-}
-
-func (c *ClusterClient) Watch(fn func(*Tx) error, keys ...string) error {
- if len(keys) == 0 {
- return fmt.Errorf("redis: Watch requires at least one key")
- }
-
- slot := hashtag.Slot(keys[0])
- for _, key := range keys[1:] {
- if hashtag.Slot(key) != slot {
- err := fmt.Errorf("redis: Watch requires all keys to be in the same slot")
- return err
- }
- }
-
- node, err := c.slotMasterNode(slot)
- if err != nil {
- return err
- }
-
- for attempt := 0; attempt <= c.opt.MaxRedirects; attempt++ {
- if attempt > 0 {
- time.Sleep(c.retryBackoff(attempt))
- }
-
- err = node.Client.Watch(fn, keys...)
- if err == nil {
- break
- }
-
- if internal.IsRetryableError(err, true) {
- c.state.LazyReload()
- continue
- }
-
- moved, ask, addr := internal.IsMovedError(err)
- if moved || ask {
- c.state.LazyReload()
- node, err = c.nodes.GetOrCreate(addr)
- if err != nil {
- return err
- }
- continue
- }
-
- if err == pool.ErrClosed {
- node, err = c.slotMasterNode(slot)
- if err != nil {
- return err
- }
- continue
- }
-
- return err
- }
-
+// ReloadState reloads cluster state. If available it calls ClusterSlots func
+// to get cluster slots information.
+func (c *ClusterClient) ReloadState() error {
+ _, err := c.state.Reload()
return err
}
@@ -930,99 +723,111 @@ func (c *ClusterClient) Close() error {
// Do creates a Cmd from the args and processes the cmd.
func (c *ClusterClient) Do(args ...interface{}) *Cmd {
+ return c.DoContext(c.ctx, args...)
+}
+
+func (c *ClusterClient) DoContext(ctx context.Context, args ...interface{}) *Cmd {
cmd := NewCmd(args...)
- c.Process(cmd)
+ _ = c.ProcessContext(ctx, cmd)
return cmd
}
-func (c *ClusterClient) WrapProcess(
- fn func(oldProcess func(Cmder) error) func(Cmder) error,
-) {
- c.process = fn(c.process)
-}
-
func (c *ClusterClient) Process(cmd Cmder) error {
- return c.process(cmd)
+ return c.ProcessContext(c.ctx, cmd)
}
-func (c *ClusterClient) defaultProcess(cmd Cmder) error {
+func (c *ClusterClient) ProcessContext(ctx context.Context, cmd Cmder) error {
+ return c.hooks.process(ctx, cmd, c.process)
+}
+
+func (c *ClusterClient) process(ctx context.Context, cmd Cmder) error {
+ err := c._process(ctx, cmd)
+ if err != nil {
+ cmd.SetErr(err)
+ return err
+ }
+ return nil
+}
+
+func (c *ClusterClient) _process(ctx context.Context, cmd Cmder) error {
+ cmdInfo := c.cmdInfo(cmd.Name())
+ slot := c.cmdSlot(cmd)
+
var node *clusterNode
var ask bool
+ var lastErr error
for attempt := 0; attempt <= c.opt.MaxRedirects; attempt++ {
if attempt > 0 {
- time.Sleep(c.retryBackoff(attempt))
+ if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil {
+ return err
+ }
}
if node == nil {
var err error
- _, node, err = c.cmdSlotAndNode(cmd)
+ node, err = c.cmdNode(cmdInfo, slot)
if err != nil {
- cmd.setErr(err)
- break
+ return err
}
}
- var err error
if ask {
pipe := node.Client.Pipeline()
- _ = pipe.Process(NewCmd("ASKING"))
+ _ = pipe.Process(NewCmd("asking"))
_ = pipe.Process(cmd)
- _, err = pipe.Exec()
+ _, lastErr = pipe.ExecContext(ctx)
_ = pipe.Close()
ask = false
} else {
- err = node.Client.Process(cmd)
+ lastErr = node.Client.ProcessContext(ctx, cmd)
}
// If there is no error - we are done.
- if err == nil {
- break
+ if lastErr == nil {
+ return nil
}
-
- // If slave is loading - read from master.
- if c.opt.ReadOnly && internal.IsLoadingError(err) {
- node.MarkAsLoading()
+ if lastErr != Nil {
+ c.state.LazyReload()
+ }
+ if lastErr == pool.ErrClosed || isReadOnlyError(lastErr) {
+ node = nil
continue
}
- if internal.IsRetryableError(err, true) {
- c.state.LazyReload()
-
- // First retry the same node.
- if attempt == 0 {
- continue
- }
-
- // Second try random node.
- node, err = c.nodes.Random()
- if err != nil {
- break
- }
+ // If slave is loading - pick another node.
+ if c.opt.ReadOnly && isLoadingError(lastErr) {
+ node.MarkAsFailing()
+ node = nil
continue
}
var moved bool
var addr string
- moved, ask, addr = internal.IsMovedError(err)
+ moved, ask, addr = isMovedError(lastErr)
if moved || ask {
- c.state.LazyReload()
-
- node, err = c.nodes.GetOrCreate(addr)
+ var err error
+ node, err = c.nodes.Get(addr)
if err != nil {
- break
+ return err
}
continue
}
- if err == pool.ErrClosed {
+ if isRetryableError(lastErr, cmd.readTimeout() == nil) {
+ // First retry the same node.
+ if attempt == 0 {
+ continue
+ }
+
+ // Second try another node.
+ node.MarkAsFailing()
node = nil
continue
}
- break
+ return lastErr
}
-
- return cmd.Err()
+ return lastErr
}
// ForEachMaster concurrently calls the fn on each master node in the cluster.
@@ -1035,6 +840,7 @@ func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
var wg sync.WaitGroup
errCh := make(chan error, 1)
+
for _, master := range state.Masters {
wg.Add(1)
go func(node *clusterNode) {
@@ -1048,6 +854,7 @@ func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
}
}(master)
}
+
wg.Wait()
select {
@@ -1068,6 +875,7 @@ func (c *ClusterClient) ForEachSlave(fn func(client *Client) error) error {
var wg sync.WaitGroup
errCh := make(chan error, 1)
+
for _, slave := range state.Slaves {
wg.Add(1)
go func(node *clusterNode) {
@@ -1081,6 +889,7 @@ func (c *ClusterClient) ForEachSlave(fn func(client *Client) error) error {
}
}(slave)
}
+
wg.Wait()
select {
@@ -1101,6 +910,7 @@ func (c *ClusterClient) ForEachNode(fn func(client *Client) error) error {
var wg sync.WaitGroup
errCh := make(chan error, 1)
+
worker := func(node *clusterNode) {
defer wg.Done()
err := fn(node.Client)
@@ -1122,6 +932,7 @@ func (c *ClusterClient) ForEachNode(fn func(client *Client) error) error {
}
wg.Wait()
+
select {
case err := <-errCh:
return err
@@ -1180,7 +991,7 @@ func (c *ClusterClient) loadState() (*clusterState, error) {
var firstErr error
for _, addr := range addrs {
- node, err := c.nodes.GetOrCreate(addr)
+ node, err := c.nodes.Get(addr)
if err != nil {
if firstErr == nil {
firstErr = err
@@ -1216,7 +1027,7 @@ func (c *ClusterClient) reaper(idleCheckFrequency time.Duration) {
for _, node := range nodes {
_, err := node.Client.connPool.(*pool.ConnPool).ReapStaleConns()
if err != nil {
- internal.Logf("ReapStaleConns failed: %s", err)
+ internal.Logger.Printf("ReapStaleConns failed: %s", err)
}
}
}
@@ -1224,9 +1035,10 @@ func (c *ClusterClient) reaper(idleCheckFrequency time.Duration) {
func (c *ClusterClient) Pipeline() Pipeliner {
pipe := Pipeline{
+ ctx: c.ctx,
exec: c.processPipeline,
}
- pipe.statefulCmdable.setProcessor(pipe.Process)
+ pipe.init()
return &pipe
}
@@ -1234,14 +1046,13 @@ func (c *ClusterClient) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
return c.Pipeline().Pipelined(fn)
}
-func (c *ClusterClient) WrapProcessPipeline(
- fn func(oldProcess func([]Cmder) error) func([]Cmder) error,
-) {
- c.processPipeline = fn(c.processPipeline)
+func (c *ClusterClient) processPipeline(ctx context.Context, cmds []Cmder) error {
+ return c.hooks.processPipeline(ctx, cmds, c._processPipeline)
}
-func (c *ClusterClient) defaultProcessPipeline(cmds []Cmder) error {
- cmdsMap, err := c.mapCmdsByNode(cmds)
+func (c *ClusterClient) _processPipeline(ctx context.Context, cmds []Cmder) error {
+ cmdsMap := newCmdsMap()
+ err := c.mapCmdsByNode(cmdsMap, cmds)
if err != nil {
setCmdsErr(cmds, err)
return err
@@ -1249,31 +1060,36 @@ func (c *ClusterClient) defaultProcessPipeline(cmds []Cmder) error {
for attempt := 0; attempt <= c.opt.MaxRedirects; attempt++ {
if attempt > 0 {
- time.Sleep(c.retryBackoff(attempt))
+ if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil {
+ setCmdsErr(cmds, err)
+ return err
+ }
}
- failedCmds := make(map[*clusterNode][]Cmder)
+ failedCmds := newCmdsMap()
+ var wg sync.WaitGroup
- for node, cmds := range cmdsMap {
- cn, err := node.Client.getConn()
- if err != nil {
- if err == pool.ErrClosed {
- c.remapCmds(cmds, failedCmds)
+ for node, cmds := range cmdsMap.m {
+ wg.Add(1)
+ go func(node *clusterNode, cmds []Cmder) {
+ defer wg.Done()
+
+ err := c._processPipelineNode(ctx, node, cmds, failedCmds)
+ if err == nil {
+ return
+ }
+ if attempt < c.opt.MaxRedirects {
+ if err := c.mapCmdsByNode(failedCmds, cmds); err != nil {
+ setCmdsErr(cmds, err)
+ }
} else {
setCmdsErr(cmds, err)
}
- continue
- }
-
- err = c.pipelineProcessCmds(node, cn, cmds, failedCmds)
- if err == nil || internal.IsRedisError(err) {
- node.Client.connPool.Put(cn)
- } else {
- node.Client.connPool.Remove(cn)
- }
+ }(node, cmds)
}
- if len(failedCmds) == 0 {
+ wg.Wait()
+ if len(failedCmds.m) == 0 {
break
}
cmdsMap = failedCmds
@@ -1282,30 +1098,33 @@ func (c *ClusterClient) defaultProcessPipeline(cmds []Cmder) error {
return cmdsFirstErr(cmds)
}
-func (c *ClusterClient) mapCmdsByNode(cmds []Cmder) (map[*clusterNode][]Cmder, error) {
+func (c *ClusterClient) mapCmdsByNode(cmdsMap *cmdsMap, cmds []Cmder) error {
state, err := c.state.Get()
if err != nil {
- setCmdsErr(cmds, err)
- return nil, err
+ return err
}
- cmdsMap := make(map[*clusterNode][]Cmder)
- cmdsAreReadOnly := c.cmdsAreReadOnly(cmds)
- for _, cmd := range cmds {
- var node *clusterNode
- var err error
- if cmdsAreReadOnly {
- _, node, err = c.cmdSlotAndNode(cmd)
- } else {
+ if c.opt.ReadOnly && c.cmdsAreReadOnly(cmds) {
+ for _, cmd := range cmds {
slot := c.cmdSlot(cmd)
- node, err = state.slotMasterNode(slot)
+ node, err := c.slotReadOnlyNode(state, slot)
+ if err != nil {
+ return err
+ }
+ cmdsMap.Add(node, cmd)
}
- if err != nil {
- return nil, err
- }
- cmdsMap[node] = append(cmdsMap[node], cmd)
+ return nil
}
- return cmdsMap, nil
+
+ for _, cmd := range cmds {
+ slot := c.cmdSlot(cmd)
+ node, err := state.slotMasterNode(slot)
+ if err != nil {
+ return err
+ }
+ cmdsMap.Add(node, cmd)
+ }
+ return nil
}
func (c *ClusterClient) cmdsAreReadOnly(cmds []Cmder) bool {
@@ -1318,94 +1137,83 @@ func (c *ClusterClient) cmdsAreReadOnly(cmds []Cmder) bool {
return true
}
-func (c *ClusterClient) remapCmds(cmds []Cmder, failedCmds map[*clusterNode][]Cmder) {
- remappedCmds, err := c.mapCmdsByNode(cmds)
- if err != nil {
- setCmdsErr(cmds, err)
- return
- }
-
- for node, cmds := range remappedCmds {
- failedCmds[node] = cmds
- }
-}
-
-func (c *ClusterClient) pipelineProcessCmds(
- node *clusterNode, cn *pool.Conn, cmds []Cmder, failedCmds map[*clusterNode][]Cmder,
+func (c *ClusterClient) _processPipelineNode(
+ ctx context.Context, node *clusterNode, cmds []Cmder, failedCmds *cmdsMap,
) error {
- err := cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
- return writeCmd(wr, cmds...)
- })
- if err != nil {
- setCmdsErr(cmds, err)
- failedCmds[node] = cmds
- return err
- }
+ return node.Client.hooks.processPipeline(ctx, cmds, func(ctx context.Context, cmds []Cmder) error {
+ return node.Client.withConn(ctx, func(ctx context.Context, cn *pool.Conn) error {
+ err := cn.WithWriter(ctx, c.opt.WriteTimeout, func(wr *proto.Writer) error {
+ return writeCmds(wr, cmds)
+ })
+ if err != nil {
+ return err
+ }
- err = cn.WithReader(c.opt.ReadTimeout, func(rd *proto.Reader) error {
- return c.pipelineReadCmds(rd, cmds, failedCmds)
+ return cn.WithReader(ctx, c.opt.ReadTimeout, func(rd *proto.Reader) error {
+ return c.pipelineReadCmds(node, rd, cmds, failedCmds)
+ })
+ })
})
- return err
}
func (c *ClusterClient) pipelineReadCmds(
- rd *proto.Reader, cmds []Cmder, failedCmds map[*clusterNode][]Cmder,
+ node *clusterNode, rd *proto.Reader, cmds []Cmder, failedCmds *cmdsMap,
) error {
for _, cmd := range cmds {
err := cmd.readReply(rd)
if err == nil {
continue
}
-
if c.checkMovedErr(cmd, err, failedCmds) {
continue
}
- if internal.IsRedisError(err) {
+ if c.opt.ReadOnly && isLoadingError(err) {
+ node.MarkAsFailing()
+ return err
+ }
+ if isRedisError(err) {
continue
}
-
return err
}
return nil
}
func (c *ClusterClient) checkMovedErr(
- cmd Cmder, err error, failedCmds map[*clusterNode][]Cmder,
+ cmd Cmder, err error, failedCmds *cmdsMap,
) bool {
- moved, ask, addr := internal.IsMovedError(err)
+ moved, ask, addr := isMovedError(err)
+ if !moved && !ask {
+ return false
+ }
+
+ node, err := c.nodes.Get(addr)
+ if err != nil {
+ return false
+ }
if moved {
c.state.LazyReload()
-
- node, err := c.nodes.GetOrCreate(addr)
- if err != nil {
- return false
- }
-
- failedCmds[node] = append(failedCmds[node], cmd)
+ failedCmds.Add(node, cmd)
return true
}
if ask {
- node, err := c.nodes.GetOrCreate(addr)
- if err != nil {
- return false
- }
-
- failedCmds[node] = append(failedCmds[node], NewCmd("ASKING"), cmd)
+ failedCmds.Add(node, NewCmd("asking"), cmd)
return true
}
- return false
+ panic("not reached")
}
// TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.
func (c *ClusterClient) TxPipeline() Pipeliner {
pipe := Pipeline{
+ ctx: c.ctx,
exec: c.processTxPipeline,
}
- pipe.statefulCmdable.setProcessor(pipe.Process)
+ pipe.init()
return &pipe
}
@@ -1413,9 +1221,14 @@ func (c *ClusterClient) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
return c.TxPipeline().Pipelined(fn)
}
-func (c *ClusterClient) defaultProcessTxPipeline(cmds []Cmder) error {
+func (c *ClusterClient) processTxPipeline(ctx context.Context, cmds []Cmder) error {
+ return c.hooks.processPipeline(ctx, cmds, c._processTxPipeline)
+}
+
+func (c *ClusterClient) _processTxPipeline(ctx context.Context, cmds []Cmder) error {
state, err := c.state.Get()
if err != nil {
+ setCmdsErr(cmds, err)
return err
}
@@ -1426,38 +1239,43 @@ func (c *ClusterClient) defaultProcessTxPipeline(cmds []Cmder) error {
setCmdsErr(cmds, err)
continue
}
- cmdsMap := map[*clusterNode][]Cmder{node: cmds}
+ cmdsMap := map[*clusterNode][]Cmder{node: cmds}
for attempt := 0; attempt <= c.opt.MaxRedirects; attempt++ {
if attempt > 0 {
- time.Sleep(c.retryBackoff(attempt))
+ if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil {
+ setCmdsErr(cmds, err)
+ return err
+ }
}
- failedCmds := make(map[*clusterNode][]Cmder)
+ failedCmds := newCmdsMap()
+ var wg sync.WaitGroup
for node, cmds := range cmdsMap {
- cn, err := node.Client.getConn()
- if err != nil {
- if err == pool.ErrClosed {
- c.remapCmds(cmds, failedCmds)
+ wg.Add(1)
+ go func(node *clusterNode, cmds []Cmder) {
+ defer wg.Done()
+
+ err := c._processTxPipelineNode(ctx, node, cmds, failedCmds)
+ if err == nil {
+ return
+ }
+ if attempt < c.opt.MaxRedirects {
+ if err := c.mapCmdsByNode(failedCmds, cmds); err != nil {
+ setCmdsErr(cmds, err)
+ }
} else {
setCmdsErr(cmds, err)
}
- continue
- }
-
- err = c.txPipelineProcessCmds(node, cn, cmds, failedCmds)
- if err == nil || internal.IsRedisError(err) {
- node.Client.connPool.Put(cn)
- } else {
- node.Client.connPool.Remove(cn)
- }
+ }(node, cmds)
}
- if len(failedCmds) == 0 {
+ wg.Wait()
+ if len(failedCmds.m) == 0 {
break
}
- cmdsMap = failedCmds
+ cmdsMap = failedCmds.m
}
}
@@ -1473,48 +1291,51 @@ func (c *ClusterClient) mapCmdsBySlot(cmds []Cmder) map[int][]Cmder {
return cmdsMap
}
-func (c *ClusterClient) txPipelineProcessCmds(
- node *clusterNode, cn *pool.Conn, cmds []Cmder, failedCmds map[*clusterNode][]Cmder,
+func (c *ClusterClient) _processTxPipelineNode(
+ ctx context.Context, node *clusterNode, cmds []Cmder, failedCmds *cmdsMap,
) error {
- err := cn.WithWriter(c.opt.WriteTimeout, func(wr *proto.Writer) error {
- return txPipelineWriteMulti(wr, cmds)
- })
- if err != nil {
- setCmdsErr(cmds, err)
- failedCmds[node] = cmds
- return err
- }
+ return node.Client.hooks.processTxPipeline(ctx, cmds, func(ctx context.Context, cmds []Cmder) error {
+ return node.Client.withConn(ctx, func(ctx context.Context, cn *pool.Conn) error {
+ err := cn.WithWriter(ctx, c.opt.WriteTimeout, func(wr *proto.Writer) error {
+ return writeCmds(wr, cmds)
+ })
+ if err != nil {
+ return err
+ }
- err = cn.WithReader(c.opt.ReadTimeout, func(rd *proto.Reader) error {
- err := c.txPipelineReadQueued(rd, cmds, failedCmds)
- if err != nil {
- setCmdsErr(cmds, err)
- return err
- }
- return pipelineReadCmds(rd, cmds)
+ return 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 := c.txPipelineReadQueued(rd, statusCmd, cmds, failedCmds)
+ if err != nil {
+ moved, ask, addr := isMovedError(err)
+ if moved || ask {
+ return c.cmdsMoved(cmds, moved, ask, addr, failedCmds)
+ }
+ return err
+ }
+
+ return pipelineReadCmds(rd, cmds)
+ })
+ })
})
- return err
}
func (c *ClusterClient) txPipelineReadQueued(
- rd *proto.Reader, cmds []Cmder, failedCmds map[*clusterNode][]Cmder,
+ rd *proto.Reader, statusCmd *StatusCmd, cmds []Cmder, failedCmds *cmdsMap,
) error {
// Parse queued replies.
- var statusCmd StatusCmd
if err := statusCmd.readReply(rd); err != nil {
return err
}
for _, cmd := range cmds {
err := statusCmd.readReply(rd)
- if err == nil {
+ if err == nil || c.checkMovedErr(cmd, err, failedCmds) || isRedisError(err) {
continue
}
-
- if c.checkMovedErr(cmd, err, failedCmds) || internal.IsRedisError(err) {
- continue
- }
-
return err
}
@@ -1529,57 +1350,151 @@ func (c *ClusterClient) txPipelineReadQueued(
switch line[0] {
case proto.ErrorReply:
- err := proto.ParseErrorReply(line)
- for _, cmd := range cmds {
- if !c.checkMovedErr(cmd, err, failedCmds) {
- break
- }
- }
- return err
+ return proto.ParseErrorReply(line)
case proto.ArrayReply:
// ok
default:
- err := fmt.Errorf("redis: expected '*', but got line %q", line)
- return err
+ return fmt.Errorf("redis: expected '*', but got line %q", line)
}
return nil
}
-func (c *ClusterClient) pubSub(channels []string) *PubSub {
+func (c *ClusterClient) cmdsMoved(
+ cmds []Cmder, moved, ask bool, addr string, failedCmds *cmdsMap,
+) error {
+ node, err := c.nodes.Get(addr)
+ if err != nil {
+ return err
+ }
+
+ if moved {
+ c.state.LazyReload()
+ for _, cmd := range cmds {
+ failedCmds.Add(node, cmd)
+ }
+ return nil
+ }
+
+ if ask {
+ for _, cmd := range cmds {
+ failedCmds.Add(node, NewCmd("asking"), cmd)
+ }
+ return nil
+ }
+
+ return nil
+}
+
+func (c *ClusterClient) Watch(fn func(*Tx) error, keys ...string) error {
+ return c.WatchContext(c.ctx, fn, keys...)
+}
+
+func (c *ClusterClient) WatchContext(ctx context.Context, fn func(*Tx) error, keys ...string) error {
+ if len(keys) == 0 {
+ return fmt.Errorf("redis: Watch requires at least one key")
+ }
+
+ slot := hashtag.Slot(keys[0])
+ for _, key := range keys[1:] {
+ if hashtag.Slot(key) != slot {
+ err := fmt.Errorf("redis: Watch requires all keys to be in the same slot")
+ return err
+ }
+ }
+
+ node, err := c.slotMasterNode(slot)
+ if err != nil {
+ return err
+ }
+
+ for attempt := 0; attempt <= c.opt.MaxRedirects; attempt++ {
+ if attempt > 0 {
+ if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil {
+ return err
+ }
+ }
+
+ err = node.Client.WatchContext(ctx, fn, keys...)
+ if err == nil {
+ break
+ }
+ if err != Nil {
+ c.state.LazyReload()
+ }
+
+ moved, ask, addr := isMovedError(err)
+ if moved || ask {
+ node, err = c.nodes.Get(addr)
+ if err != nil {
+ return err
+ }
+ continue
+ }
+
+ if err == pool.ErrClosed || isReadOnlyError(err) {
+ node, err = c.slotMasterNode(slot)
+ if err != nil {
+ return err
+ }
+ continue
+ }
+
+ if isRetryableError(err, true) {
+ continue
+ }
+
+ return err
+ }
+
+ return err
+}
+
+func (c *ClusterClient) pubSub() *PubSub {
var node *clusterNode
pubsub := &PubSub{
opt: c.opt.clientOptions(),
newConn: func(channels []string) (*pool.Conn, error) {
- if node == nil {
- var slot int
- if len(channels) > 0 {
- slot = hashtag.Slot(channels[0])
- } else {
- slot = -1
- }
-
- masterNode, err := c.slotMasterNode(slot)
- if err != nil {
- return nil, err
- }
- node = masterNode
+ if node != nil {
+ panic("node != nil")
}
- return node.Client.newConn()
+
+ var err error
+ if len(channels) > 0 {
+ slot := hashtag.Slot(channels[0])
+ node, err = c.slotMasterNode(slot)
+ } else {
+ node, err = c.nodes.Random()
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ cn, err := node.Client.newConn(context.TODO())
+ if err != nil {
+ node = nil
+
+ return nil, err
+ }
+
+ return cn, nil
},
closeConn: func(cn *pool.Conn) error {
- return node.Client.connPool.CloseConn(cn)
+ err := node.Client.connPool.CloseConn(cn)
+ node = nil
+ return err
},
}
pubsub.init()
+
return pubsub
}
// Subscribe subscribes the client to the specified channels.
// Channels can be omitted to create empty subscription.
func (c *ClusterClient) Subscribe(channels ...string) *PubSub {
- pubsub := c.pubSub(channels)
+ pubsub := c.pubSub()
if len(channels) > 0 {
_ = pubsub.Subscribe(channels...)
}
@@ -1589,13 +1504,105 @@ func (c *ClusterClient) Subscribe(channels ...string) *PubSub {
// PSubscribe subscribes the client to the given patterns.
// Patterns can be omitted to create empty subscription.
func (c *ClusterClient) PSubscribe(channels ...string) *PubSub {
- pubsub := c.pubSub(channels)
+ pubsub := c.pubSub()
if len(channels) > 0 {
_ = pubsub.PSubscribe(channels...)
}
return pubsub
}
+func (c *ClusterClient) retryBackoff(attempt int) time.Duration {
+ return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
+}
+
+func (c *ClusterClient) cmdsInfo() (map[string]*CommandInfo, error) {
+ addrs, err := c.nodes.Addrs()
+ if err != nil {
+ return nil, err
+ }
+
+ var firstErr error
+ for _, addr := range addrs {
+ node, err := c.nodes.Get(addr)
+ if err != nil {
+ return nil, err
+ }
+ if node == nil {
+ continue
+ }
+
+ info, err := node.Client.Command().Result()
+ if err == nil {
+ return info, nil
+ }
+ if firstErr == nil {
+ firstErr = err
+ }
+ }
+ return nil, firstErr
+}
+
+func (c *ClusterClient) cmdInfo(name string) *CommandInfo {
+ cmdsInfo, err := c.cmdsInfoCache.Get()
+ if err != nil {
+ return nil
+ }
+
+ info := cmdsInfo[name]
+ if info == nil {
+ internal.Logger.Printf("info for cmd=%s not found", name)
+ }
+ return info
+}
+
+func (c *ClusterClient) cmdSlot(cmd Cmder) int {
+ args := cmd.Args()
+ if args[0] == "cluster" && args[1] == "getkeysinslot" {
+ return args[2].(int)
+ }
+
+ cmdInfo := c.cmdInfo(cmd.Name())
+ return cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo))
+}
+
+func cmdSlot(cmd Cmder, pos int) int {
+ if pos == 0 {
+ return hashtag.RandomSlot()
+ }
+ firstKey := cmd.stringArg(pos)
+ return hashtag.Slot(firstKey)
+}
+
+func (c *ClusterClient) cmdNode(cmdInfo *CommandInfo, slot int) (*clusterNode, error) {
+ state, err := c.state.Get()
+ if err != nil {
+ return nil, err
+ }
+
+ if c.opt.ReadOnly && cmdInfo != nil && cmdInfo.ReadOnly {
+ return c.slotReadOnlyNode(state, slot)
+ }
+ return state.slotMasterNode(slot)
+}
+
+func (c *clusterClient) slotReadOnlyNode(state *clusterState, slot int) (*clusterNode, error) {
+ if c.opt.RouteByLatency {
+ return state.slotClosestNode(slot)
+ }
+ if c.opt.RouteRandomly {
+ return state.slotRandomNode(slot)
+ }
+ return state.slotSlaveNode(slot)
+}
+
+func (c *ClusterClient) slotMasterNode(slot int) (*clusterNode, error) {
+ state, err := c.state.Get()
+ if err != nil {
+ return nil, err
+ }
+ return state.slotMasterNode(slot)
+}
+
func appendUniqueNode(nodes []*clusterNode, node *clusterNode) []*clusterNode {
for _, n := range nodes {
if n == node {
@@ -1632,3 +1639,22 @@ func remove(ss []string, es ...string) []string {
}
return ss
}
+
+//------------------------------------------------------------------------------
+
+type cmdsMap struct {
+ mu sync.Mutex
+ m map[*clusterNode][]Cmder
+}
+
+func newCmdsMap() *cmdsMap {
+ return &cmdsMap{
+ m: make(map[*clusterNode][]Cmder),
+ }
+}
+
+func (m *cmdsMap) Add(node *clusterNode, cmds ...Cmder) {
+ m.mu.Lock()
+ m.m[node] = append(m.m[node], cmds...)
+ m.mu.Unlock()
+}
diff --git a/vendor/github.com/go-redis/redis/cluster_commands.go b/vendor/github.com/go-redis/redis/v7/cluster_commands.go
similarity index 95%
rename from vendor/github.com/go-redis/redis/cluster_commands.go
rename to vendor/github.com/go-redis/redis/v7/cluster_commands.go
index dff62c90..c9b9b9de 100644
--- a/vendor/github.com/go-redis/redis/cluster_commands.go
+++ b/vendor/github.com/go-redis/redis/v7/cluster_commands.go
@@ -14,7 +14,7 @@ func (c *ClusterClient) DBSize() *IntCmd {
return nil
})
if err != nil {
- cmd.setErr(err)
+ cmd.SetErr(err)
return cmd
}
cmd.val = size
diff --git a/vendor/github.com/go-redis/redis/command.go b/vendor/github.com/go-redis/redis/v7/command.go
similarity index 65%
rename from vendor/github.com/go-redis/redis/command.go
rename to vendor/github.com/go-redis/redis/v7/command.go
index ca44d7c8..266e3892 100644
--- a/vendor/github.com/go-redis/redis/command.go
+++ b/vendor/github.com/go-redis/redis/v7/command.go
@@ -7,27 +7,28 @@ import (
"strings"
"time"
- "github.com/go-redis/redis/internal"
- "github.com/go-redis/redis/internal/proto"
+ "github.com/go-redis/redis/v7/internal"
+ "github.com/go-redis/redis/v7/internal/proto"
+ "github.com/go-redis/redis/v7/internal/util"
)
type Cmder interface {
Name() string
Args() []interface{}
+ String() string
stringArg(int) string
- readReply(rd *proto.Reader) error
- setErr(error)
-
readTimeout() *time.Duration
+ readReply(rd *proto.Reader) error
+ SetErr(error)
Err() error
}
func setCmdsErr(cmds []Cmder, e error) {
for _, cmd := range cmds {
if cmd.Err() == nil {
- cmd.setErr(e)
+ cmd.SetErr(e)
}
}
}
@@ -41,18 +42,21 @@ func cmdsFirstErr(cmds []Cmder) error {
return nil
}
-func writeCmd(wr *proto.Writer, cmds ...Cmder) error {
+func writeCmds(wr *proto.Writer, cmds []Cmder) error {
for _, cmd := range cmds {
- err := wr.WriteArgs(cmd.Args())
- if err != nil {
+ if err := writeCmd(wr, cmd); err != nil {
return err
}
}
return nil
}
+func writeCmd(wr *proto.Writer, cmd Cmder) error {
+ return wr.WriteArgs(cmd.Args())
+}
+
func cmdString(cmd Cmder, val interface{}) string {
- var ss []string
+ ss := make([]string, 0, len(cmd.Args()))
for _, arg := range cmd.Args() {
ss = append(ss, fmt.Sprint(arg))
}
@@ -69,7 +73,6 @@ func cmdString(cmd Cmder, val interface{}) string {
}
}
return s
-
}
func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int {
@@ -92,38 +95,40 @@ func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int {
//------------------------------------------------------------------------------
type baseCmd struct {
- _args []interface{}
- err error
+ args []interface{}
+ err error
_readTimeout *time.Duration
}
var _ Cmder = (*Cmd)(nil)
-func (cmd *baseCmd) Err() error {
- return cmd.err
+func (cmd *baseCmd) Name() string {
+ if len(cmd.args) == 0 {
+ return ""
+ }
+ // Cmd name must be lower cased.
+ return internal.ToLower(cmd.stringArg(0))
}
func (cmd *baseCmd) Args() []interface{} {
- return cmd._args
+ return cmd.args
}
func (cmd *baseCmd) stringArg(pos int) string {
- if pos < 0 || pos >= len(cmd._args) {
+ if pos < 0 || pos >= len(cmd.args) {
return ""
}
- s, _ := cmd._args[pos].(string)
+ s, _ := cmd.args[pos].(string)
return s
}
-func (cmd *baseCmd) Name() string {
- if len(cmd._args) > 0 {
- // Cmd name must be lower cased.
- s := internal.ToLower(cmd.stringArg(0))
- cmd._args[0] = s
- return s
- }
- return ""
+func (cmd *baseCmd) SetErr(e error) {
+ cmd.err = e
+}
+
+func (cmd *baseCmd) Err() error {
+ return cmd.err
}
func (cmd *baseCmd) readTimeout() *time.Duration {
@@ -134,10 +139,6 @@ func (cmd *baseCmd) setReadTimeout(d time.Duration) {
cmd._readTimeout = &d
}
-func (cmd *baseCmd) setErr(e error) {
- cmd.err = e
-}
-
//------------------------------------------------------------------------------
type Cmd struct {
@@ -148,10 +149,14 @@ type Cmd struct {
func NewCmd(args ...interface{}) *Cmd {
return &Cmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
+func (cmd *Cmd) String() string {
+ return cmdString(cmd, cmd.val)
+}
+
func (cmd *Cmd) Val() interface{} {
return cmd.val
}
@@ -160,7 +165,7 @@ func (cmd *Cmd) Result() (interface{}, error) {
return cmd.val, cmd.err
}
-func (cmd *Cmd) String() (string, error) {
+func (cmd *Cmd) Text() (string, error) {
if cmd.err != nil {
return "", cmd.err
}
@@ -183,7 +188,7 @@ func (cmd *Cmd) Int() (int, error) {
case string:
return strconv.Atoi(val)
default:
- err := fmt.Errorf("redis: unexpected type=%T for Int64", val)
+ err := fmt.Errorf("redis: unexpected type=%T for Int", val)
return 0, err
}
}
@@ -218,6 +223,25 @@ func (cmd *Cmd) Uint64() (uint64, error) {
}
}
+func (cmd *Cmd) Float32() (float32, error) {
+ if cmd.err != nil {
+ return 0, cmd.err
+ }
+ switch val := cmd.val.(type) {
+ case int64:
+ return float32(val), nil
+ case string:
+ f, err := strconv.ParseFloat(val, 32)
+ if err != nil {
+ return 0, err
+ }
+ return float32(f), nil
+ default:
+ err := fmt.Errorf("redis: unexpected type=%T for Float32", val)
+ return 0, err
+ }
+}
+
func (cmd *Cmd) Float64() (float64, error) {
if cmd.err != nil {
return 0, cmd.err
@@ -255,27 +279,21 @@ func (cmd *Cmd) readReply(rd *proto.Reader) error {
// Implements proto.MultiBulkParse
func sliceParser(rd *proto.Reader, n int64) (interface{}, error) {
- vals := make([]interface{}, 0, n)
- for i := int64(0); i < n; i++ {
+ vals := make([]interface{}, n)
+ for i := 0; i < len(vals); i++ {
v, err := rd.ReadReply(sliceParser)
if err != nil {
if err == Nil {
- vals = append(vals, nil)
+ vals[i] = nil
continue
}
if err, ok := err.(proto.RedisError); ok {
- vals = append(vals, err)
+ vals[i] = err
continue
}
return nil, err
}
-
- switch v := v.(type) {
- case string:
- vals = append(vals, v)
- default:
- vals = append(vals, v)
- }
+ vals[i] = v
}
return vals, nil
}
@@ -292,7 +310,7 @@ var _ Cmder = (*SliceCmd)(nil)
func NewSliceCmd(args ...interface{}) *SliceCmd {
return &SliceCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -330,7 +348,7 @@ var _ Cmder = (*StatusCmd)(nil)
func NewStatusCmd(args ...interface{}) *StatusCmd {
return &StatusCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -363,7 +381,7 @@ var _ Cmder = (*IntCmd)(nil)
func NewIntCmd(args ...interface{}) *IntCmd {
return &IntCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -375,6 +393,10 @@ func (cmd *IntCmd) Result() (int64, error) {
return cmd.val, cmd.err
}
+func (cmd *IntCmd) Uint64() (uint64, error) {
+ return uint64(cmd.val), cmd.err
+}
+
func (cmd *IntCmd) String() string {
return cmdString(cmd, cmd.val)
}
@@ -386,6 +408,49 @@ func (cmd *IntCmd) readReply(rd *proto.Reader) error {
//------------------------------------------------------------------------------
+type IntSliceCmd struct {
+ baseCmd
+
+ val []int64
+}
+
+var _ Cmder = (*IntSliceCmd)(nil)
+
+func NewIntSliceCmd(args ...interface{}) *IntSliceCmd {
+ return &IntSliceCmd{
+ baseCmd: baseCmd{args: args},
+ }
+}
+
+func (cmd *IntSliceCmd) Val() []int64 {
+ return cmd.val
+}
+
+func (cmd *IntSliceCmd) Result() ([]int64, error) {
+ return cmd.val, cmd.err
+}
+
+func (cmd *IntSliceCmd) String() string {
+ return cmdString(cmd, cmd.val)
+}
+
+func (cmd *IntSliceCmd) readReply(rd *proto.Reader) error {
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.val = make([]int64, n)
+ for i := 0; i < len(cmd.val); i++ {
+ num, err := rd.ReadIntReply()
+ if err != nil {
+ return nil, err
+ }
+ cmd.val[i] = num
+ }
+ return nil, nil
+ })
+ return cmd.err
+}
+
+//------------------------------------------------------------------------------
+
type DurationCmd struct {
baseCmd
@@ -397,7 +462,7 @@ var _ Cmder = (*DurationCmd)(nil)
func NewDurationCmd(precision time.Duration, args ...interface{}) *DurationCmd {
return &DurationCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
precision: precision,
}
}
@@ -420,7 +485,14 @@ func (cmd *DurationCmd) readReply(rd *proto.Reader) error {
if cmd.err != nil {
return cmd.err
}
- cmd.val = time.Duration(n) * cmd.precision
+ switch n {
+ // -2 if the key does not exist
+ // -1 if the key exists but has no associated expire
+ case -2, -1:
+ cmd.val = time.Duration(n)
+ default:
+ cmd.val = time.Duration(n) * cmd.precision
+ }
return nil
}
@@ -436,7 +508,7 @@ var _ Cmder = (*TimeCmd)(nil)
func NewTimeCmd(args ...interface{}) *TimeCmd {
return &TimeCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -453,32 +525,25 @@ func (cmd *TimeCmd) String() string {
}
func (cmd *TimeCmd) readReply(rd *proto.Reader) error {
- var v interface{}
- v, cmd.err = rd.ReadArrayReply(timeParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.val = v.(time.Time)
- return nil
-}
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ if n != 2 {
+ return nil, fmt.Errorf("got %d elements, expected 2", n)
+ }
-// Implements proto.MultiBulkParse
-func timeParser(rd *proto.Reader, n int64) (interface{}, error) {
- if n != 2 {
- return nil, fmt.Errorf("got %d elements, expected 2", n)
- }
+ sec, err := rd.ReadInt()
+ if err != nil {
+ return nil, err
+ }
- sec, err := rd.ReadInt()
- if err != nil {
- return nil, err
- }
+ microsec, err := rd.ReadInt()
+ if err != nil {
+ return nil, err
+ }
- microsec, err := rd.ReadInt()
- if err != nil {
- return nil, err
- }
-
- return time.Unix(sec, microsec*1000), nil
+ cmd.val = time.Unix(sec, microsec*1000)
+ return nil, nil
+ })
+ return cmd.err
}
//------------------------------------------------------------------------------
@@ -493,7 +558,7 @@ var _ Cmder = (*BoolCmd)(nil)
func NewBoolCmd(args ...interface{}) *BoolCmd {
return &BoolCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -514,7 +579,6 @@ func (cmd *BoolCmd) readReply(rd *proto.Reader) error {
v, cmd.err = rd.ReadReply(nil)
// `SET key value NX` returns nil when key already exists. But
// `SETNX key value` returns bool (0/1). So convert nil to bool.
- // TODO: is this okay?
if cmd.err == Nil {
cmd.val = false
cmd.err = nil
@@ -548,7 +612,7 @@ var _ Cmder = (*StringCmd)(nil)
func NewStringCmd(args ...interface{}) *StringCmd {
return &StringCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -561,7 +625,7 @@ func (cmd *StringCmd) Result() (string, error) {
}
func (cmd *StringCmd) Bytes() ([]byte, error) {
- return []byte(cmd.val), cmd.err
+ return util.StringToBytes(cmd.val), cmd.err
}
func (cmd *StringCmd) Int() (int, error) {
@@ -585,6 +649,17 @@ func (cmd *StringCmd) Uint64() (uint64, error) {
return strconv.ParseUint(cmd.Val(), 10, 64)
}
+func (cmd *StringCmd) Float32() (float32, error) {
+ if cmd.err != nil {
+ return 0, cmd.err
+ }
+ f, err := strconv.ParseFloat(cmd.Val(), 32)
+ if err != nil {
+ return 0, err
+ }
+ return float32(f), nil
+}
+
func (cmd *StringCmd) Float64() (float64, error) {
if cmd.err != nil {
return 0, cmd.err
@@ -592,6 +667,13 @@ func (cmd *StringCmd) Float64() (float64, error) {
return strconv.ParseFloat(cmd.Val(), 64)
}
+func (cmd *StringCmd) Time() (time.Time, error) {
+ if cmd.err != nil {
+ return time.Time{}, cmd.err
+ }
+ return time.Parse(time.RFC3339, cmd.Val())
+}
+
func (cmd *StringCmd) Scan(val interface{}) error {
if cmd.err != nil {
return cmd.err
@@ -620,7 +702,7 @@ var _ Cmder = (*FloatCmd)(nil)
func NewFloatCmd(args ...interface{}) *FloatCmd {
return &FloatCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -653,7 +735,7 @@ var _ Cmder = (*StringSliceCmd)(nil)
func NewStringSliceCmd(args ...interface{}) *StringSliceCmd {
return &StringSliceCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -674,29 +756,21 @@ func (cmd *StringSliceCmd) ScanSlice(container interface{}) error {
}
func (cmd *StringSliceCmd) readReply(rd *proto.Reader) error {
- var v interface{}
- v, cmd.err = rd.ReadArrayReply(stringSliceParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.val = v.([]string)
- return nil
-}
-
-// Implements proto.MultiBulkParse
-func stringSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
- ss := make([]string, 0, n)
- for i := int64(0); i < n; i++ {
- s, err := rd.ReadString()
- if err == Nil {
- ss = append(ss, "")
- } else if err != nil {
- return nil, err
- } else {
- ss = append(ss, s)
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.val = make([]string, n)
+ for i := 0; i < len(cmd.val); i++ {
+ switch s, err := rd.ReadString(); {
+ case err == Nil:
+ cmd.val[i] = ""
+ case err != nil:
+ return nil, err
+ default:
+ cmd.val[i] = s
+ }
}
- }
- return ss, nil
+ return nil, nil
+ })
+ return cmd.err
}
//------------------------------------------------------------------------------
@@ -711,7 +785,7 @@ var _ Cmder = (*BoolSliceCmd)(nil)
func NewBoolSliceCmd(args ...interface{}) *BoolSliceCmd {
return &BoolSliceCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -728,26 +802,18 @@ func (cmd *BoolSliceCmd) String() string {
}
func (cmd *BoolSliceCmd) readReply(rd *proto.Reader) error {
- var v interface{}
- v, cmd.err = rd.ReadArrayReply(boolSliceParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.val = v.([]bool)
- return nil
-}
-
-// Implements proto.MultiBulkParse
-func boolSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
- bools := make([]bool, 0, n)
- for i := int64(0); i < n; i++ {
- n, err := rd.ReadIntReply()
- if err != nil {
- return nil, err
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.val = make([]bool, n)
+ for i := 0; i < len(cmd.val); i++ {
+ n, err := rd.ReadIntReply()
+ if err != nil {
+ return nil, err
+ }
+ cmd.val[i] = n == 1
}
- bools = append(bools, n == 1)
- }
- return bools, nil
+ return nil, nil
+ })
+ return cmd.err
}
//------------------------------------------------------------------------------
@@ -762,7 +828,7 @@ var _ Cmder = (*StringStringMapCmd)(nil)
func NewStringStringMapCmd(args ...interface{}) *StringStringMapCmd {
return &StringStringMapCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -779,32 +845,24 @@ func (cmd *StringStringMapCmd) String() string {
}
func (cmd *StringStringMapCmd) readReply(rd *proto.Reader) error {
- var v interface{}
- v, cmd.err = rd.ReadArrayReply(stringStringMapParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.val = v.(map[string]string)
- return nil
-}
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.val = make(map[string]string, n/2)
+ for i := int64(0); i < n; i += 2 {
+ key, err := rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
-// Implements proto.MultiBulkParse
-func stringStringMapParser(rd *proto.Reader, n int64) (interface{}, error) {
- m := make(map[string]string, n/2)
- for i := int64(0); i < n; i += 2 {
- key, err := rd.ReadString()
- if err != nil {
- return nil, err
+ value, err := rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
+
+ cmd.val[key] = value
}
-
- value, err := rd.ReadString()
- if err != nil {
- return nil, err
- }
-
- m[key] = value
- }
- return m, nil
+ return nil, nil
+ })
+ return cmd.err
}
//------------------------------------------------------------------------------
@@ -819,7 +877,7 @@ var _ Cmder = (*StringIntMapCmd)(nil)
func NewStringIntMapCmd(args ...interface{}) *StringIntMapCmd {
return &StringIntMapCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -836,32 +894,24 @@ func (cmd *StringIntMapCmd) String() string {
}
func (cmd *StringIntMapCmd) readReply(rd *proto.Reader) error {
- var v interface{}
- v, cmd.err = rd.ReadArrayReply(stringIntMapParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.val = v.(map[string]int64)
- return nil
-}
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.val = make(map[string]int64, n/2)
+ for i := int64(0); i < n; i += 2 {
+ key, err := rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
-// Implements proto.MultiBulkParse
-func stringIntMapParser(rd *proto.Reader, n int64) (interface{}, error) {
- m := make(map[string]int64, n/2)
- for i := int64(0); i < n; i += 2 {
- key, err := rd.ReadString()
- if err != nil {
- return nil, err
+ n, err := rd.ReadIntReply()
+ if err != nil {
+ return nil, err
+ }
+
+ cmd.val[key] = n
}
-
- n, err := rd.ReadIntReply()
- if err != nil {
- return nil, err
- }
-
- m[key] = n
- }
- return m, nil
+ return nil, nil
+ })
+ return cmd.err
}
//------------------------------------------------------------------------------
@@ -876,7 +926,7 @@ var _ Cmder = (*StringStructMapCmd)(nil)
func NewStringStructMapCmd(args ...interface{}) *StringStructMapCmd {
return &StringStructMapCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -893,27 +943,18 @@ func (cmd *StringStructMapCmd) String() string {
}
func (cmd *StringStructMapCmd) readReply(rd *proto.Reader) error {
- var v interface{}
- v, cmd.err = rd.ReadArrayReply(stringStructMapParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.val = v.(map[string]struct{})
- return nil
-}
-
-// Implements proto.MultiBulkParse
-func stringStructMapParser(rd *proto.Reader, n int64) (interface{}, error) {
- m := make(map[string]struct{}, n)
- for i := int64(0); i < n; i++ {
- key, err := rd.ReadString()
- if err != nil {
- return nil, err
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.val = make(map[string]struct{}, n)
+ for i := int64(0); i < n; i++ {
+ key, err := rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
+ cmd.val[key] = struct{}{}
}
-
- m[key] = struct{}{}
- }
- return m, nil
+ return nil, nil
+ })
+ return cmd.err
}
//------------------------------------------------------------------------------
@@ -933,7 +974,7 @@ var _ Cmder = (*XMessageSliceCmd)(nil)
func NewXMessageSliceCmd(args ...interface{}) *XMessageSliceCmd {
return &XMessageSliceCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -961,23 +1002,30 @@ func (cmd *XMessageSliceCmd) readReply(rd *proto.Reader) error {
// Implements proto.MultiBulkParse
func xMessageSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
- msgs := make([]XMessage, 0, n)
- for i := int64(0); i < n; i++ {
+ msgs := make([]XMessage, n)
+ for i := 0; i < len(msgs); i++ {
+ i := i
_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
id, err := rd.ReadString()
if err != nil {
return nil, err
}
+ var values map[string]interface{}
+
v, err := rd.ReadArrayReply(stringInterfaceMapParser)
if err != nil {
- return nil, err
+ if err != proto.Nil {
+ return nil, err
+ }
+ } else {
+ values = v.(map[string]interface{})
}
- msgs = append(msgs, XMessage{
+ msgs[i] = XMessage{
ID: id,
- Values: v.(map[string]interface{}),
- })
+ Values: values,
+ }
return nil, nil
})
if err != nil {
@@ -1023,7 +1071,7 @@ var _ Cmder = (*XStreamSliceCmd)(nil)
func NewXStreamSliceCmd(args ...interface{}) *XStreamSliceCmd {
return &XStreamSliceCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -1040,45 +1088,38 @@ func (cmd *XStreamSliceCmd) String() string {
}
func (cmd *XStreamSliceCmd) readReply(rd *proto.Reader) error {
- var v interface{}
- v, cmd.err = rd.ReadArrayReply(xStreamSliceParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.val = v.([]XStream)
- return nil
-}
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.val = make([]XStream, n)
+ for i := 0; i < len(cmd.val); i++ {
+ i := i
+ _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ if n != 2 {
+ return nil, fmt.Errorf("got %d, wanted 2", n)
+ }
-// Implements proto.MultiBulkParse
-func xStreamSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
- ret := make([]XStream, 0, n)
- for i := int64(0); i < n; i++ {
- _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
- if n != 2 {
- return nil, fmt.Errorf("got %d, wanted 2", n)
- }
+ stream, err := rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
- stream, err := rd.ReadString()
- if err != nil {
- return nil, err
- }
+ v, err := rd.ReadArrayReply(xMessageSliceParser)
+ if err != nil {
+ return nil, err
+ }
- v, err := rd.ReadArrayReply(xMessageSliceParser)
- if err != nil {
- return nil, err
- }
-
- ret = append(ret, XStream{
- Stream: stream,
- Messages: v.([]XMessage),
+ cmd.val[i] = XStream{
+ Stream: stream,
+ Messages: v.([]XMessage),
+ }
+ return nil, nil
})
- return nil, nil
- })
- if err != nil {
- return nil, err
+ if err != nil {
+ return nil, err
+ }
}
- }
- return ret, nil
+ return nil, nil
+ })
+ return cmd.err
}
//------------------------------------------------------------------------------
@@ -1099,7 +1140,7 @@ var _ Cmder = (*XPendingCmd)(nil)
func NewXPendingCmd(args ...interface{}) *XPendingCmd {
return &XPendingCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -1116,81 +1157,74 @@ func (cmd *XPendingCmd) String() string {
}
func (cmd *XPendingCmd) readReply(rd *proto.Reader) error {
- var info interface{}
- info, cmd.err = rd.ReadArrayReply(xPendingParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.val = info.(*XPending)
- return nil
-}
-
-func xPendingParser(rd *proto.Reader, n int64) (interface{}, error) {
- if n != 4 {
- return nil, fmt.Errorf("got %d, wanted 4", n)
- }
-
- count, err := rd.ReadIntReply()
- if err != nil {
- return nil, err
- }
-
- lower, err := rd.ReadString()
- if err != nil && err != Nil {
- return nil, err
- }
-
- higher, err := rd.ReadString()
- if err != nil && err != Nil {
- return nil, err
- }
-
- pending := &XPending{
- Count: count,
- Lower: lower,
- Higher: higher,
- }
- _, err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
- for i := int64(0); i < n; i++ {
- _, err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
- if n != 2 {
- return nil, fmt.Errorf("got %d, wanted 2", n)
- }
-
- consumerName, err := rd.ReadString()
- if err != nil {
- return nil, err
- }
-
- consumerPending, err := rd.ReadInt()
- if err != nil {
- return nil, err
- }
-
- if pending.Consumers == nil {
- pending.Consumers = make(map[string]int64)
- }
- pending.Consumers[consumerName] = consumerPending
-
- return nil, nil
- })
- if err != nil {
- return nil, err
- }
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ if n != 4 {
+ return nil, fmt.Errorf("got %d, wanted 4", n)
}
+
+ count, err := rd.ReadIntReply()
+ if err != nil {
+ return nil, err
+ }
+
+ lower, err := rd.ReadString()
+ if err != nil && err != Nil {
+ return nil, err
+ }
+
+ higher, err := rd.ReadString()
+ if err != nil && err != Nil {
+ return nil, err
+ }
+
+ cmd.val = &XPending{
+ Count: count,
+ Lower: lower,
+ Higher: higher,
+ }
+ _, err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ for i := int64(0); i < n; i++ {
+ _, err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ if n != 2 {
+ return nil, fmt.Errorf("got %d, wanted 2", n)
+ }
+
+ consumerName, err := rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
+
+ consumerPending, err := rd.ReadInt()
+ if err != nil {
+ return nil, err
+ }
+
+ if cmd.val.Consumers == nil {
+ cmd.val.Consumers = make(map[string]int64)
+ }
+ cmd.val.Consumers[consumerName] = consumerPending
+
+ return nil, nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ }
+ return nil, nil
+ })
+ if err != nil && err != Nil {
+ return nil, err
+ }
+
return nil, nil
})
- if err != nil && err != Nil {
- return nil, err
- }
-
- return pending, nil
+ return cmd.err
}
//------------------------------------------------------------------------------
type XPendingExt struct {
- Id string
+ ID string
Consumer string
Idle time.Duration
RetryCount int64
@@ -1205,7 +1239,7 @@ var _ Cmder = (*XPendingExtCmd)(nil)
func NewXPendingExtCmd(args ...interface{}) *XPendingExtCmd {
return &XPendingExtCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -1222,62 +1256,143 @@ func (cmd *XPendingExtCmd) String() string {
}
func (cmd *XPendingExtCmd) readReply(rd *proto.Reader) error {
- var info interface{}
- info, cmd.err = rd.ReadArrayReply(xPendingExtSliceParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.val = info.([]XPendingExt)
- return nil
-}
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.val = make([]XPendingExt, 0, n)
+ for i := int64(0); i < n; i++ {
+ _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ if n != 4 {
+ return nil, fmt.Errorf("got %d, wanted 4", n)
+ }
-func xPendingExtSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
- ret := make([]XPendingExt, 0, n)
- for i := int64(0); i < n; i++ {
- _, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
- if n != 4 {
- return nil, fmt.Errorf("got %d, wanted 4", n)
- }
+ id, err := rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
- id, err := rd.ReadString()
+ consumer, err := rd.ReadString()
+ if err != nil && err != Nil {
+ return nil, err
+ }
+
+ idle, err := rd.ReadIntReply()
+ if err != nil && err != Nil {
+ return nil, err
+ }
+
+ retryCount, err := rd.ReadIntReply()
+ if err != nil && err != Nil {
+ return nil, err
+ }
+
+ cmd.val = append(cmd.val, XPendingExt{
+ ID: id,
+ Consumer: consumer,
+ Idle: time.Duration(idle) * time.Millisecond,
+ RetryCount: retryCount,
+ })
+ return nil, nil
+ })
if err != nil {
return nil, err
}
+ }
+ return nil, nil
+ })
+ return cmd.err
+}
- consumer, err := rd.ReadString()
- if err != nil && err != Nil {
- return nil, err
+//------------------------------------------------------------------------------
+
+type XInfoGroupsCmd struct {
+ baseCmd
+ val []XInfoGroups
+}
+
+type XInfoGroups struct {
+ Name string
+ Consumers int64
+ Pending int64
+ LastDeliveredID string
+}
+
+var _ Cmder = (*XInfoGroupsCmd)(nil)
+
+func NewXInfoGroupsCmd(stream string) *XInfoGroupsCmd {
+ return &XInfoGroupsCmd{
+ baseCmd: baseCmd{args: []interface{}{"xinfo", "groups", stream}},
+ }
+}
+
+func (cmd *XInfoGroupsCmd) Val() []XInfoGroups {
+ return cmd.val
+}
+
+func (cmd *XInfoGroupsCmd) Result() ([]XInfoGroups, error) {
+ return cmd.val, cmd.err
+}
+
+func (cmd *XInfoGroupsCmd) String() string {
+ return cmdString(cmd, cmd.val)
+}
+
+func (cmd *XInfoGroupsCmd) readReply(rd *proto.Reader) error {
+ _, cmd.err = rd.ReadArrayReply(
+ func(rd *proto.Reader, n int64) (interface{}, error) {
+ for i := int64(0); i < n; i++ {
+ v, err := rd.ReadReply(xGroupInfoParser)
+ if err != nil {
+ return nil, err
+ }
+ cmd.val = append(cmd.val, v.(XInfoGroups))
}
-
- idle, err := rd.ReadIntReply()
- if err != nil && err != Nil {
- return nil, err
- }
-
- retryCount, err := rd.ReadIntReply()
- if err != nil && err != Nil {
- return nil, err
- }
-
- ret = append(ret, XPendingExt{
- Id: id,
- Consumer: consumer,
- Idle: time.Duration(idle) * time.Millisecond,
- RetryCount: retryCount,
- })
return nil, nil
})
+ return nil
+}
+
+func xGroupInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
+ if n != 8 {
+ return nil, fmt.Errorf("redis: got %d elements in XINFO GROUPS reply,"+
+ "wanted 8", n)
+ }
+ var (
+ err error
+ grp XInfoGroups
+ key string
+ val string
+ )
+
+ for i := 0; i < 4; i++ {
+ key, err = rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
+ val, err = rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
+ switch key {
+ case "name":
+ grp.Name = val
+ case "consumers":
+ grp.Consumers, err = strconv.ParseInt(val, 0, 64)
+ case "pending":
+ grp.Pending, err = strconv.ParseInt(val, 0, 64)
+ case "last-delivered-id":
+ grp.LastDeliveredID = val
+ default:
+ return nil, fmt.Errorf("redis: unexpected content %s "+
+ "in XINFO GROUPS reply", key)
+ }
if err != nil {
return nil, err
}
}
- return ret, nil
+ return grp, err
}
//------------------------------------------------------------------------------
-//------------------------------------------------------------------------------
-
type ZSliceCmd struct {
baseCmd
@@ -1288,7 +1403,7 @@ var _ Cmder = (*ZSliceCmd)(nil)
func NewZSliceCmd(args ...interface{}) *ZSliceCmd {
return &ZSliceCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -1305,34 +1420,84 @@ func (cmd *ZSliceCmd) String() string {
}
func (cmd *ZSliceCmd) readReply(rd *proto.Reader) error {
- var v interface{}
- v, cmd.err = rd.ReadArrayReply(zSliceParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.val = v.([]Z)
- return nil
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.val = make([]Z, n/2)
+ for i := 0; i < len(cmd.val); i++ {
+ member, err := rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
+
+ score, err := rd.ReadFloatReply()
+ if err != nil {
+ return nil, err
+ }
+
+ cmd.val[i] = Z{
+ Member: member,
+ Score: score,
+ }
+ }
+ return nil, nil
+ })
+ return cmd.err
}
-// Implements proto.MultiBulkParse
-func zSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
- zz := make([]Z, n/2)
- for i := int64(0); i < n; i += 2 {
+//------------------------------------------------------------------------------
+
+type ZWithKeyCmd struct {
+ baseCmd
+
+ val *ZWithKey
+}
+
+var _ Cmder = (*ZWithKeyCmd)(nil)
+
+func NewZWithKeyCmd(args ...interface{}) *ZWithKeyCmd {
+ return &ZWithKeyCmd{
+ baseCmd: baseCmd{args: args},
+ }
+}
+
+func (cmd *ZWithKeyCmd) Val() *ZWithKey {
+ return cmd.val
+}
+
+func (cmd *ZWithKeyCmd) Result() (*ZWithKey, error) {
+ return cmd.Val(), cmd.Err()
+}
+
+func (cmd *ZWithKeyCmd) String() string {
+ return cmdString(cmd, cmd.val)
+}
+
+func (cmd *ZWithKeyCmd) readReply(rd *proto.Reader) error {
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ if n != 3 {
+ return nil, fmt.Errorf("got %d elements, expected 3", n)
+ }
+
+ cmd.val = &ZWithKey{}
var err error
- z := &zz[i/2]
-
- z.Member, err = rd.ReadString()
+ cmd.val.Key, err = rd.ReadString()
if err != nil {
return nil, err
}
- z.Score, err = rd.ReadFloatReply()
+ cmd.val.Member, err = rd.ReadString()
if err != nil {
return nil, err
}
- }
- return zz, nil
+
+ cmd.val.Score, err = rd.ReadFloatReply()
+ if err != nil {
+ return nil, err
+ }
+
+ return nil, nil
+ })
+ return cmd.err
}
//------------------------------------------------------------------------------
@@ -1350,7 +1515,7 @@ var _ Cmder = (*ScanCmd)(nil)
func NewScanCmd(process func(cmd Cmder) error, args ...interface{}) *ScanCmd {
return &ScanCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
process: process,
}
}
@@ -1382,7 +1547,7 @@ func (cmd *ScanCmd) Iterator() *ScanIterator {
//------------------------------------------------------------------------------
type ClusterNode struct {
- Id string
+ ID string
Addr string
}
@@ -1402,7 +1567,7 @@ var _ Cmder = (*ClusterSlotsCmd)(nil)
func NewClusterSlotsCmd(args ...interface{}) *ClusterSlotsCmd {
return &ClusterSlotsCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -1419,77 +1584,69 @@ func (cmd *ClusterSlotsCmd) String() string {
}
func (cmd *ClusterSlotsCmd) readReply(rd *proto.Reader) error {
- var v interface{}
- v, cmd.err = rd.ReadArrayReply(clusterSlotsParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.val = v.([]ClusterSlot)
- return nil
-}
-
-// Implements proto.MultiBulkParse
-func clusterSlotsParser(rd *proto.Reader, n int64) (interface{}, error) {
- slots := make([]ClusterSlot, n)
- for i := 0; i < len(slots); i++ {
- n, err := rd.ReadArrayLen()
- if err != nil {
- return nil, err
- }
- if n < 2 {
- err := fmt.Errorf("redis: got %d elements in cluster info, expected at least 2", n)
- return nil, err
- }
-
- start, err := rd.ReadIntReply()
- if err != nil {
- return nil, err
- }
-
- end, err := rd.ReadIntReply()
- if err != nil {
- return nil, err
- }
-
- nodes := make([]ClusterNode, n-2)
- for j := 0; j < len(nodes); j++ {
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.val = make([]ClusterSlot, n)
+ for i := 0; i < len(cmd.val); i++ {
n, err := rd.ReadArrayLen()
if err != nil {
return nil, err
}
- if n != 2 && n != 3 {
- err := fmt.Errorf("got %d elements in cluster info address, expected 2 or 3", n)
+ if n < 2 {
+ err := fmt.Errorf("redis: got %d elements in cluster info, expected at least 2", n)
return nil, err
}
- ip, err := rd.ReadString()
+ start, err := rd.ReadIntReply()
if err != nil {
return nil, err
}
- port, err := rd.ReadString()
+ end, err := rd.ReadIntReply()
if err != nil {
return nil, err
}
- nodes[j].Addr = net.JoinHostPort(ip, port)
-
- if n == 3 {
- id, err := rd.ReadString()
+ nodes := make([]ClusterNode, n-2)
+ for j := 0; j < len(nodes); j++ {
+ n, err := rd.ReadArrayLen()
if err != nil {
return nil, err
}
- nodes[j].Id = id
+ if n != 2 && n != 3 {
+ err := fmt.Errorf("got %d elements in cluster info address, expected 2 or 3", n)
+ return nil, err
+ }
+
+ ip, err := rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
+
+ port, err := rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
+
+ nodes[j].Addr = net.JoinHostPort(ip, port)
+
+ if n == 3 {
+ id, err := rd.ReadString()
+ if err != nil {
+ return nil, err
+ }
+ nodes[j].ID = id
+ }
+ }
+
+ cmd.val[i] = ClusterSlot{
+ Start: int(start),
+ End: int(end),
+ Nodes: nodes,
}
}
-
- slots[i] = ClusterSlot{
- Start: int(start),
- End: int(end),
- Nodes: nodes,
- }
- }
- return slots, nil
+ return nil, nil
+ })
+ return cmd.err
}
//------------------------------------------------------------------------------
@@ -1526,6 +1683,13 @@ type GeoLocationCmd struct {
var _ Cmder = (*GeoLocationCmd)(nil)
func NewGeoLocationCmd(q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd {
+ return &GeoLocationCmd{
+ baseCmd: baseCmd{args: geoLocationArgs(q, args...)},
+ q: q,
+ }
+}
+
+func geoLocationArgs(q *GeoRadiusQuery, args ...interface{}) []interface{} {
args = append(args, q.Radius)
if q.Unit != "" {
args = append(args, q.Unit)
@@ -1555,10 +1719,7 @@ func NewGeoLocationCmd(q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd {
args = append(args, "storedist")
args = append(args, q.StoreDist)
}
- return &GeoLocationCmd{
- baseCmd: baseCmd{_args: args},
- q: q,
- }
+ return args
}
func (cmd *GeoLocationCmd) Val() []GeoLocation {
@@ -1583,6 +1744,30 @@ func (cmd *GeoLocationCmd) readReply(rd *proto.Reader) error {
return nil
}
+func newGeoLocationSliceParser(q *GeoRadiusQuery) proto.MultiBulkParse {
+ return func(rd *proto.Reader, n int64) (interface{}, error) {
+ locs := make([]GeoLocation, 0, n)
+ for i := int64(0); i < n; i++ {
+ v, err := rd.ReadReply(newGeoLocationParser(q))
+ if err != nil {
+ return nil, err
+ }
+ switch vv := v.(type) {
+ case string:
+ locs = append(locs, GeoLocation{
+ Name: vv,
+ })
+ case *GeoLocation:
+ //TODO: avoid copying
+ locs = append(locs, *vv)
+ default:
+ return nil, fmt.Errorf("got %T, expected string or *GeoLocation", v)
+ }
+ }
+ return locs, nil
+ }
+}
+
func newGeoLocationParser(q *GeoRadiusQuery) proto.MultiBulkParse {
return func(rd *proto.Reader, n int64) (interface{}, error) {
var loc GeoLocation
@@ -1627,29 +1812,6 @@ func newGeoLocationParser(q *GeoRadiusQuery) proto.MultiBulkParse {
}
}
-func newGeoLocationSliceParser(q *GeoRadiusQuery) proto.MultiBulkParse {
- return func(rd *proto.Reader, n int64) (interface{}, error) {
- locs := make([]GeoLocation, 0, n)
- for i := int64(0); i < n; i++ {
- v, err := rd.ReadReply(newGeoLocationParser(q))
- if err != nil {
- return nil, err
- }
- switch vv := v.(type) {
- case string:
- locs = append(locs, GeoLocation{
- Name: vv,
- })
- case *GeoLocation:
- locs = append(locs, *vv)
- default:
- return nil, fmt.Errorf("got %T, expected string or *GeoLocation", v)
- }
- }
- return locs, nil
- }
-}
-
//------------------------------------------------------------------------------
type GeoPos struct {
@@ -1659,19 +1821,19 @@ type GeoPos struct {
type GeoPosCmd struct {
baseCmd
- positions []*GeoPos
+ val []*GeoPos
}
var _ Cmder = (*GeoPosCmd)(nil)
func NewGeoPosCmd(args ...interface{}) *GeoPosCmd {
return &GeoPosCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
func (cmd *GeoPosCmd) Val() []*GeoPos {
- return cmd.positions
+ return cmd.val
}
func (cmd *GeoPosCmd) Result() ([]*GeoPos, error) {
@@ -1679,55 +1841,42 @@ func (cmd *GeoPosCmd) Result() ([]*GeoPos, error) {
}
func (cmd *GeoPosCmd) String() string {
- return cmdString(cmd, cmd.positions)
+ return cmdString(cmd, cmd.val)
}
func (cmd *GeoPosCmd) readReply(rd *proto.Reader) error {
- var v interface{}
- v, cmd.err = rd.ReadArrayReply(geoPosSliceParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.positions = v.([]*GeoPos)
- return nil
-}
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.val = make([]*GeoPos, n)
+ for i := 0; i < len(cmd.val); i++ {
+ i := i
+ _, err := rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ longitude, err := rd.ReadFloatReply()
+ if err != nil {
+ return nil, err
+ }
-func geoPosSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
- positions := make([]*GeoPos, 0, n)
- for i := int64(0); i < n; i++ {
- v, err := rd.ReadReply(geoPosParser)
- if err != nil {
- if err == Nil {
- positions = append(positions, nil)
- continue
+ latitude, err := rd.ReadFloatReply()
+ if err != nil {
+ return nil, err
+ }
+
+ cmd.val[i] = &GeoPos{
+ Longitude: longitude,
+ Latitude: latitude,
+ }
+ return nil, nil
+ })
+ if err != nil {
+ if err == Nil {
+ cmd.val[i] = nil
+ continue
+ }
+ return nil, err
}
- return nil, err
}
- switch v := v.(type) {
- case *GeoPos:
- positions = append(positions, v)
- default:
- return nil, fmt.Errorf("got %T, expected *GeoPos", v)
- }
- }
- return positions, nil
-}
-
-func geoPosParser(rd *proto.Reader, n int64) (interface{}, error) {
- var pos GeoPos
- var err error
-
- pos.Longitude, err = rd.ReadFloatReply()
- if err != nil {
- return nil, err
- }
-
- pos.Latitude, err = rd.ReadFloatReply()
- if err != nil {
- return nil, err
- }
-
- return &pos, nil
+ return nil, nil
+ })
+ return cmd.err
}
//------------------------------------------------------------------------------
@@ -1752,7 +1901,7 @@ var _ Cmder = (*CommandsInfoCmd)(nil)
func NewCommandsInfoCmd(args ...interface{}) *CommandsInfoCmd {
return &CommandsInfoCmd{
- baseCmd: baseCmd{_args: args},
+ baseCmd: baseCmd{args: args},
}
}
@@ -1769,38 +1918,29 @@ func (cmd *CommandsInfoCmd) String() string {
}
func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error {
- var v interface{}
- v, cmd.err = rd.ReadArrayReply(commandInfoSliceParser)
- if cmd.err != nil {
- return cmd.err
- }
- cmd.val = v.(map[string]*CommandInfo)
- return nil
-}
-
-// Implements proto.MultiBulkParse
-func commandInfoSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
- m := make(map[string]*CommandInfo, n)
- for i := int64(0); i < n; i++ {
- v, err := rd.ReadReply(commandInfoParser)
- if err != nil {
- return nil, err
+ _, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.val = make(map[string]*CommandInfo, n)
+ for i := int64(0); i < n; i++ {
+ v, err := rd.ReadReply(commandInfoParser)
+ if err != nil {
+ return nil, err
+ }
+ vv := v.(*CommandInfo)
+ cmd.val[vv.Name] = vv
}
- vv := v.(*CommandInfo)
- m[vv.Name] = vv
-
- }
- return m, nil
+ return nil, nil
+ })
+ return cmd.err
}
func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
- var cmd CommandInfo
- var err error
-
if n != 6 {
return nil, fmt.Errorf("redis: got %d elements in COMMAND reply, wanted 6", n)
}
+ var cmd CommandInfo
+ var err error
+
cmd.Name, err = rd.ReadString()
if err != nil {
return nil, err
@@ -1812,11 +1952,23 @@ func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
}
cmd.Arity = int8(arity)
- flags, err := rd.ReadReply(stringSliceParser)
+ _, err = rd.ReadReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+ cmd.Flags = make([]string, n)
+ for i := 0; i < len(cmd.Flags); i++ {
+ switch s, err := rd.ReadString(); {
+ case err == Nil:
+ cmd.Flags[i] = ""
+ case err != nil:
+ return nil, err
+ default:
+ cmd.Flags[i] = s
+ }
+ }
+ return nil, nil
+ })
if err != nil {
return nil, err
}
- cmd.Flags = flags.([]string)
firstKeyPos, err := rd.ReadIntReply()
if err != nil {
@@ -1867,6 +2019,15 @@ func (c *cmdsInfoCache) Get() (map[string]*CommandInfo, error) {
if err != nil {
return err
}
+
+ // Extensions have cmd names in upper case. Convert them to lower case.
+ for k, v := range cmds {
+ lower := internal.ToLower(k)
+ if lower != k {
+ cmds[lower] = v
+ }
+ }
+
c.cmds = cmds
return nil
})
diff --git a/vendor/github.com/go-redis/redis/commands.go b/vendor/github.com/go-redis/redis/v7/commands.go
similarity index 60%
rename from vendor/github.com/go-redis/redis/commands.go
rename to vendor/github.com/go-redis/redis/v7/commands.go
index b259e3a8..d4447c4d 100644
--- a/vendor/github.com/go-redis/redis/commands.go
+++ b/vendor/github.com/go-redis/redis/v7/commands.go
@@ -5,23 +5,16 @@ import (
"io"
"time"
- "github.com/go-redis/redis/internal"
+ "github.com/go-redis/redis/v7/internal"
)
-func readTimeout(timeout time.Duration) time.Duration {
- if timeout == 0 {
- return 0
- }
- return timeout + 10*time.Second
-}
-
func usePrecise(dur time.Duration) bool {
return dur < time.Second || dur%time.Second != 0
}
func formatMs(dur time.Duration) int64 {
if dur > 0 && dur < time.Millisecond {
- internal.Logf(
+ internal.Logger.Printf(
"specified duration is %s, but minimal supported value is %s",
dur, time.Millisecond,
)
@@ -31,7 +24,7 @@ func formatMs(dur time.Duration) int64 {
func formatSec(dur time.Duration) int64 {
if dur > 0 && dur < time.Second {
- internal.Logf(
+ internal.Logger.Printf(
"specified duration is %s, but minimal supported value is %s",
dur, time.Second,
)
@@ -41,17 +34,21 @@ func formatSec(dur time.Duration) int64 {
func appendArgs(dst, src []interface{}) []interface{} {
if len(src) == 1 {
- if ss, ok := src[0].([]string); ok {
- for _, s := range ss {
+ switch v := src[0].(type) {
+ case []string:
+ for _, s := range v {
dst = append(dst, s)
}
return dst
+ case map[string]interface{}:
+ for k, v := range v {
+ dst = append(dst, k, v)
+ }
+ return dst
}
}
- for _, v := range src {
- dst = append(dst, v)
- }
+ dst = append(dst, src...)
return dst
}
@@ -74,8 +71,8 @@ type Cmdable interface {
Expire(key string, expiration time.Duration) *BoolCmd
ExpireAt(key string, tm time.Time) *BoolCmd
Keys(pattern string) *StringSliceCmd
- Migrate(host, port, key string, db int64, timeout time.Duration) *StatusCmd
- Move(key string, db int64) *BoolCmd
+ Migrate(host, port, key string, db int, timeout time.Duration) *StatusCmd
+ Move(key string, db int) *BoolCmd
ObjectRefCount(key string) *IntCmd
ObjectEncoding(key string) *StringCmd
ObjectIdleTime(key string) *DurationCmd
@@ -105,6 +102,7 @@ type Cmdable interface {
BitOpXor(destKey string, keys ...string) *IntCmd
BitOpNot(destKey string, key string) *IntCmd
BitPos(key string, bit int64, pos ...int64) *IntCmd
+ BitField(key string, args ...interface{}) *IntSliceCmd
Decr(key string) *IntCmd
DecrBy(key string, decrement int64) *IntCmd
Get(key string) *StringCmd
@@ -115,8 +113,8 @@ type Cmdable interface {
IncrBy(key string, value int64) *IntCmd
IncrByFloat(key string, value float64) *FloatCmd
MGet(keys ...string) *SliceCmd
- MSet(pairs ...interface{}) *StatusCmd
- MSetNX(pairs ...interface{}) *BoolCmd
+ MSet(values ...interface{}) *StatusCmd
+ MSetNX(values ...interface{}) *BoolCmd
Set(key string, value interface{}, expiration time.Duration) *StatusCmd
SetBit(key string, offset int64, value int) *IntCmd
SetNX(key string, value interface{}, expiration time.Duration) *BoolCmd
@@ -132,8 +130,8 @@ type Cmdable interface {
HKeys(key string) *StringSliceCmd
HLen(key string) *IntCmd
HMGet(key string, fields ...string) *SliceCmd
- HMSet(key string, fields map[string]interface{}) *StatusCmd
- HSet(key, field string, value interface{}) *BoolCmd
+ HSet(key string, values ...interface{}) *IntCmd
+ HMSet(key string, values ...interface{}) *BoolCmd
HSetNX(key, field string, value interface{}) *BoolCmd
HVals(key string) *StringSliceCmd
BLPop(timeout time.Duration, keys ...string) *StringSliceCmd
@@ -146,7 +144,7 @@ type Cmdable interface {
LLen(key string) *IntCmd
LPop(key string) *StringCmd
LPush(key string, values ...interface{}) *IntCmd
- LPushX(key string, value interface{}) *IntCmd
+ LPushX(key string, values ...interface{}) *IntCmd
LRange(key string, start, stop int64) *StringSliceCmd
LRem(key string, count int64, value interface{}) *IntCmd
LSet(key string, index int64, value interface{}) *StatusCmd
@@ -154,7 +152,7 @@ type Cmdable interface {
RPop(key string) *StringCmd
RPopLPush(source, destination string) *StringCmd
RPush(key string, values ...interface{}) *IntCmd
- RPushX(key string, value interface{}) *IntCmd
+ RPushX(key string, values ...interface{}) *IntCmd
SAdd(key string, members ...interface{}) *IntCmd
SCard(key string) *IntCmd
SDiff(keys ...string) *StringSliceCmd
@@ -173,6 +171,7 @@ type Cmdable interface {
SUnion(keys ...string) *StringSliceCmd
SUnionStore(destination string, keys ...string) *IntCmd
XAdd(a *XAddArgs) *StringCmd
+ XDel(stream string, ids ...string) *IntCmd
XLen(stream string) *IntCmd
XRange(stream, start, stop string) *XMessageSliceCmd
XRangeN(stream, start, stop string, count int64) *XMessageSliceCmd
@@ -181,6 +180,7 @@ type Cmdable interface {
XRead(a *XReadArgs) *XStreamSliceCmd
XReadStreams(streams ...string) *XStreamSliceCmd
XGroupCreate(stream, group, start string) *StatusCmd
+ XGroupCreateMkStream(stream, group, start string) *StatusCmd
XGroupSetID(stream, group, start string) *StatusCmd
XGroupDestroy(stream, group string) *IntCmd
XGroupDelConsumer(stream, group, consumer string) *IntCmd
@@ -192,25 +192,30 @@ type Cmdable interface {
XClaimJustID(a *XClaimArgs) *StringSliceCmd
XTrim(key string, maxLen int64) *IntCmd
XTrimApprox(key string, maxLen int64) *IntCmd
- ZAdd(key string, members ...Z) *IntCmd
- ZAddNX(key string, members ...Z) *IntCmd
- ZAddXX(key string, members ...Z) *IntCmd
- ZAddCh(key string, members ...Z) *IntCmd
- ZAddNXCh(key string, members ...Z) *IntCmd
- ZAddXXCh(key string, members ...Z) *IntCmd
- ZIncr(key string, member Z) *FloatCmd
- ZIncrNX(key string, member Z) *FloatCmd
- ZIncrXX(key string, member Z) *FloatCmd
+ XInfoGroups(key string) *XInfoGroupsCmd
+ BZPopMax(timeout time.Duration, keys ...string) *ZWithKeyCmd
+ BZPopMin(timeout time.Duration, keys ...string) *ZWithKeyCmd
+ ZAdd(key string, members ...*Z) *IntCmd
+ ZAddNX(key string, members ...*Z) *IntCmd
+ ZAddXX(key string, members ...*Z) *IntCmd
+ ZAddCh(key string, members ...*Z) *IntCmd
+ ZAddNXCh(key string, members ...*Z) *IntCmd
+ ZAddXXCh(key string, members ...*Z) *IntCmd
+ ZIncr(key string, member *Z) *FloatCmd
+ ZIncrNX(key string, member *Z) *FloatCmd
+ ZIncrXX(key string, member *Z) *FloatCmd
ZCard(key string) *IntCmd
ZCount(key, min, max string) *IntCmd
ZLexCount(key, min, max string) *IntCmd
ZIncrBy(key string, increment float64, member string) *FloatCmd
- ZInterStore(destination string, store ZStore, keys ...string) *IntCmd
+ ZInterStore(destination string, store *ZStore) *IntCmd
+ ZPopMax(key string, count ...int64) *ZSliceCmd
+ ZPopMin(key string, count ...int64) *ZSliceCmd
ZRange(key string, start, stop int64) *StringSliceCmd
ZRangeWithScores(key string, start, stop int64) *ZSliceCmd
- ZRangeByScore(key string, opt ZRangeBy) *StringSliceCmd
- ZRangeByLex(key string, opt ZRangeBy) *StringSliceCmd
- ZRangeByScoreWithScores(key string, opt ZRangeBy) *ZSliceCmd
+ ZRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd
+ ZRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd
+ ZRangeByScoreWithScores(key string, opt *ZRangeBy) *ZSliceCmd
ZRank(key, member string) *IntCmd
ZRem(key string, members ...interface{}) *IntCmd
ZRemRangeByRank(key string, start, stop int64) *IntCmd
@@ -218,12 +223,12 @@ type Cmdable interface {
ZRemRangeByLex(key, min, max string) *IntCmd
ZRevRange(key string, start, stop int64) *StringSliceCmd
ZRevRangeWithScores(key string, start, stop int64) *ZSliceCmd
- ZRevRangeByScore(key string, opt ZRangeBy) *StringSliceCmd
- ZRevRangeByLex(key string, opt ZRangeBy) *StringSliceCmd
- ZRevRangeByScoreWithScores(key string, opt ZRangeBy) *ZSliceCmd
+ ZRevRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd
+ ZRevRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd
+ ZRevRangeByScoreWithScores(key string, opt *ZRangeBy) *ZSliceCmd
ZRevRank(key, member string) *IntCmd
ZScore(key, member string) *FloatCmd
- ZUnionStore(dest string, store ZStore, keys ...string) *IntCmd
+ ZUnionStore(dest string, store *ZStore) *IntCmd
PFAdd(key string, els ...interface{}) *IntCmd
PFCount(keys ...string) *IntCmd
PFMerge(dest string, keys ...string) *StatusCmd
@@ -233,6 +238,7 @@ type Cmdable interface {
ClientKillByFilter(keys ...string) *IntCmd
ClientList() *StringCmd
ClientPause(dur time.Duration) *BoolCmd
+ ClientID() *IntCmd
ConfigGet(parameter string) *SliceCmd
ConfigResetStat() *StatusCmd
ConfigSet(parameter, value string) *StatusCmd
@@ -270,6 +276,7 @@ type Cmdable interface {
ClusterResetHard() *StatusCmd
ClusterInfo() *StringCmd
ClusterKeySlot(key string) *IntCmd
+ ClusterGetKeysInSlot(slot int, count int) *StringSliceCmd
ClusterCountFailureReports(nodeID string) *IntCmd
ClusterCountKeysInSlot(slot int) *IntCmd
ClusterDelSlots(slots ...int) *StatusCmd
@@ -282,9 +289,9 @@ type Cmdable interface {
GeoAdd(key string, geoLocation ...*GeoLocation) *IntCmd
GeoPos(key string, members ...string) *GeoPosCmd
GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd
- GeoRadiusRO(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd
+ GeoRadiusStore(key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd
GeoRadiusByMember(key, member string, query *GeoRadiusQuery) *GeoLocationCmd
- GeoRadiusByMemberRO(key, member string, query *GeoRadiusQuery) *GeoLocationCmd
+ GeoRadiusByMemberStore(key, member string, query *GeoRadiusQuery) *IntCmd
GeoDist(key string, member1, member2, unit string) *FloatCmd
GeoHash(key string, members ...string) *StringSliceCmd
ReadOnly() *StatusCmd
@@ -305,132 +312,118 @@ var _ Cmdable = (*Tx)(nil)
var _ Cmdable = (*Ring)(nil)
var _ Cmdable = (*ClusterClient)(nil)
-type cmdable struct {
- process func(cmd Cmder) error
-}
+type cmdable func(cmd Cmder) error
-func (c *cmdable) setProcessor(fn func(Cmder) error) {
- c.process = fn
-}
-
-type statefulCmdable struct {
- cmdable
- process func(cmd Cmder) error
-}
-
-func (c *statefulCmdable) setProcessor(fn func(Cmder) error) {
- c.process = fn
- c.cmdable.setProcessor(fn)
-}
+type statefulCmdable func(cmd Cmder) error
//------------------------------------------------------------------------------
-func (c *statefulCmdable) Auth(password string) *StatusCmd {
+func (c statefulCmdable) Auth(password string) *StatusCmd {
cmd := NewStatusCmd("auth", password)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Echo(message interface{}) *StringCmd {
+func (c cmdable) Echo(message interface{}) *StringCmd {
cmd := NewStringCmd("echo", message)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Ping() *StatusCmd {
+func (c cmdable) Ping() *StatusCmd {
cmd := NewStatusCmd("ping")
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Wait(numSlaves int, timeout time.Duration) *IntCmd {
+func (c cmdable) Wait(numSlaves int, timeout time.Duration) *IntCmd {
cmd := NewIntCmd("wait", numSlaves, int(timeout/time.Millisecond))
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Quit() *StatusCmd {
+func (c cmdable) Quit() *StatusCmd {
panic("not implemented")
}
-func (c *statefulCmdable) Select(index int) *StatusCmd {
+func (c statefulCmdable) Select(index int) *StatusCmd {
cmd := NewStatusCmd("select", index)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *statefulCmdable) SwapDB(index1, index2 int) *StatusCmd {
+func (c statefulCmdable) SwapDB(index1, index2 int) *StatusCmd {
cmd := NewStatusCmd("swapdb", index1, index2)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
//------------------------------------------------------------------------------
-func (c *cmdable) Command() *CommandsInfoCmd {
+func (c cmdable) Command() *CommandsInfoCmd {
cmd := NewCommandsInfoCmd("command")
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Del(keys ...string) *IntCmd {
+func (c cmdable) Del(keys ...string) *IntCmd {
args := make([]interface{}, 1+len(keys))
args[0] = "del"
for i, key := range keys {
args[1+i] = key
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Unlink(keys ...string) *IntCmd {
+func (c cmdable) Unlink(keys ...string) *IntCmd {
args := make([]interface{}, 1+len(keys))
args[0] = "unlink"
for i, key := range keys {
args[1+i] = key
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Dump(key string) *StringCmd {
+func (c cmdable) Dump(key string) *StringCmd {
cmd := NewStringCmd("dump", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Exists(keys ...string) *IntCmd {
+func (c cmdable) Exists(keys ...string) *IntCmd {
args := make([]interface{}, 1+len(keys))
args[0] = "exists"
for i, key := range keys {
args[1+i] = key
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Expire(key string, expiration time.Duration) *BoolCmd {
+func (c cmdable) Expire(key string, expiration time.Duration) *BoolCmd {
cmd := NewBoolCmd("expire", key, formatSec(expiration))
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ExpireAt(key string, tm time.Time) *BoolCmd {
+func (c cmdable) ExpireAt(key string, tm time.Time) *BoolCmd {
cmd := NewBoolCmd("expireat", key, tm.Unix())
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Keys(pattern string) *StringSliceCmd {
+func (c cmdable) Keys(pattern string) *StringSliceCmd {
cmd := NewStringSliceCmd("keys", pattern)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Migrate(host, port, key string, db int64, timeout time.Duration) *StatusCmd {
+func (c cmdable) Migrate(host, port, key string, db int, timeout time.Duration) *StatusCmd {
cmd := NewStatusCmd(
"migrate",
host,
@@ -440,92 +433,92 @@ func (c *cmdable) Migrate(host, port, key string, db int64, timeout time.Duratio
formatMs(timeout),
)
cmd.setReadTimeout(timeout)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Move(key string, db int64) *BoolCmd {
+func (c cmdable) Move(key string, db int) *BoolCmd {
cmd := NewBoolCmd("move", key, db)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ObjectRefCount(key string) *IntCmd {
+func (c cmdable) ObjectRefCount(key string) *IntCmd {
cmd := NewIntCmd("object", "refcount", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ObjectEncoding(key string) *StringCmd {
+func (c cmdable) ObjectEncoding(key string) *StringCmd {
cmd := NewStringCmd("object", "encoding", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ObjectIdleTime(key string) *DurationCmd {
+func (c cmdable) ObjectIdleTime(key string) *DurationCmd {
cmd := NewDurationCmd(time.Second, "object", "idletime", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Persist(key string) *BoolCmd {
+func (c cmdable) Persist(key string) *BoolCmd {
cmd := NewBoolCmd("persist", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) PExpire(key string, expiration time.Duration) *BoolCmd {
+func (c cmdable) PExpire(key string, expiration time.Duration) *BoolCmd {
cmd := NewBoolCmd("pexpire", key, formatMs(expiration))
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) PExpireAt(key string, tm time.Time) *BoolCmd {
+func (c cmdable) PExpireAt(key string, tm time.Time) *BoolCmd {
cmd := NewBoolCmd(
"pexpireat",
key,
tm.UnixNano()/int64(time.Millisecond),
)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) PTTL(key string) *DurationCmd {
+func (c cmdable) PTTL(key string) *DurationCmd {
cmd := NewDurationCmd(time.Millisecond, "pttl", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) RandomKey() *StringCmd {
+func (c cmdable) RandomKey() *StringCmd {
cmd := NewStringCmd("randomkey")
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Rename(key, newkey string) *StatusCmd {
+func (c cmdable) Rename(key, newkey string) *StatusCmd {
cmd := NewStatusCmd("rename", key, newkey)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) RenameNX(key, newkey string) *BoolCmd {
+func (c cmdable) RenameNX(key, newkey string) *BoolCmd {
cmd := NewBoolCmd("renamenx", key, newkey)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Restore(key string, ttl time.Duration, value string) *StatusCmd {
+func (c cmdable) Restore(key string, ttl time.Duration, value string) *StatusCmd {
cmd := NewStatusCmd(
"restore",
key,
formatMs(ttl),
value,
)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) RestoreReplace(key string, ttl time.Duration, value string) *StatusCmd {
+func (c cmdable) RestoreReplace(key string, ttl time.Duration, value string) *StatusCmd {
cmd := NewStatusCmd(
"restore",
key,
@@ -533,7 +526,7 @@ func (c *cmdable) RestoreReplace(key string, ttl time.Duration, value string) *S
value,
"replace",
)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
@@ -565,52 +558,52 @@ func (sort *Sort) args(key string) []interface{} {
return args
}
-func (c *cmdable) Sort(key string, sort *Sort) *StringSliceCmd {
+func (c cmdable) Sort(key string, sort *Sort) *StringSliceCmd {
cmd := NewStringSliceCmd(sort.args(key)...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SortStore(key, store string, sort *Sort) *IntCmd {
+func (c cmdable) SortStore(key, store string, sort *Sort) *IntCmd {
args := sort.args(key)
if store != "" {
args = append(args, "store", store)
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SortInterfaces(key string, sort *Sort) *SliceCmd {
+func (c cmdable) SortInterfaces(key string, sort *Sort) *SliceCmd {
cmd := NewSliceCmd(sort.args(key)...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Touch(keys ...string) *IntCmd {
+func (c cmdable) Touch(keys ...string) *IntCmd {
args := make([]interface{}, len(keys)+1)
args[0] = "touch"
for i, key := range keys {
args[i+1] = key
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) TTL(key string) *DurationCmd {
+func (c cmdable) TTL(key string) *DurationCmd {
cmd := NewDurationCmd(time.Second, "ttl", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Type(key string) *StatusCmd {
+func (c cmdable) Type(key string) *StatusCmd {
cmd := NewStatusCmd("type", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Scan(cursor uint64, match string, count int64) *ScanCmd {
+func (c cmdable) Scan(cursor uint64, match string, count int64) *ScanCmd {
args := []interface{}{"scan", cursor}
if match != "" {
args = append(args, "match", match)
@@ -618,12 +611,12 @@ func (c *cmdable) Scan(cursor uint64, match string, count int64) *ScanCmd {
if count > 0 {
args = append(args, "count", count)
}
- cmd := NewScanCmd(c.process, args...)
- c.process(cmd)
+ cmd := NewScanCmd(c, args...)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SScan(key string, cursor uint64, match string, count int64) *ScanCmd {
+func (c cmdable) SScan(key string, cursor uint64, match string, count int64) *ScanCmd {
args := []interface{}{"sscan", key, cursor}
if match != "" {
args = append(args, "match", match)
@@ -631,12 +624,12 @@ func (c *cmdable) SScan(key string, cursor uint64, match string, count int64) *S
if count > 0 {
args = append(args, "count", count)
}
- cmd := NewScanCmd(c.process, args...)
- c.process(cmd)
+ cmd := NewScanCmd(c, args...)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) HScan(key string, cursor uint64, match string, count int64) *ScanCmd {
+func (c cmdable) HScan(key string, cursor uint64, match string, count int64) *ScanCmd {
args := []interface{}{"hscan", key, cursor}
if match != "" {
args = append(args, "match", match)
@@ -644,12 +637,12 @@ func (c *cmdable) HScan(key string, cursor uint64, match string, count int64) *S
if count > 0 {
args = append(args, "count", count)
}
- cmd := NewScanCmd(c.process, args...)
- c.process(cmd)
+ cmd := NewScanCmd(c, args...)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZScan(key string, cursor uint64, match string, count int64) *ScanCmd {
+func (c cmdable) ZScan(key string, cursor uint64, match string, count int64) *ScanCmd {
args := []interface{}{"zscan", key, cursor}
if match != "" {
args = append(args, "match", match)
@@ -657,16 +650,16 @@ func (c *cmdable) ZScan(key string, cursor uint64, match string, count int64) *S
if count > 0 {
args = append(args, "count", count)
}
- cmd := NewScanCmd(c.process, args...)
- c.process(cmd)
+ cmd := NewScanCmd(c, args...)
+ _ = c(cmd)
return cmd
}
//------------------------------------------------------------------------------
-func (c *cmdable) Append(key, value string) *IntCmd {
+func (c cmdable) Append(key, value string) *IntCmd {
cmd := NewIntCmd("append", key, value)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
@@ -674,7 +667,7 @@ type BitCount struct {
Start, End int64
}
-func (c *cmdable) BitCount(key string, bitCount *BitCount) *IntCmd {
+func (c cmdable) BitCount(key string, bitCount *BitCount) *IntCmd {
args := []interface{}{"bitcount", key}
if bitCount != nil {
args = append(
@@ -684,11 +677,11 @@ func (c *cmdable) BitCount(key string, bitCount *BitCount) *IntCmd {
)
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) bitOp(op, destKey string, keys ...string) *IntCmd {
+func (c cmdable) bitOp(op, destKey string, keys ...string) *IntCmd {
args := make([]interface{}, 3+len(keys))
args[0] = "bitop"
args[1] = op
@@ -697,27 +690,27 @@ func (c *cmdable) bitOp(op, destKey string, keys ...string) *IntCmd {
args[3+i] = key
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) BitOpAnd(destKey string, keys ...string) *IntCmd {
+func (c cmdable) BitOpAnd(destKey string, keys ...string) *IntCmd {
return c.bitOp("and", destKey, keys...)
}
-func (c *cmdable) BitOpOr(destKey string, keys ...string) *IntCmd {
+func (c cmdable) BitOpOr(destKey string, keys ...string) *IntCmd {
return c.bitOp("or", destKey, keys...)
}
-func (c *cmdable) BitOpXor(destKey string, keys ...string) *IntCmd {
+func (c cmdable) BitOpXor(destKey string, keys ...string) *IntCmd {
return c.bitOp("xor", destKey, keys...)
}
-func (c *cmdable) BitOpNot(destKey string, key string) *IntCmd {
+func (c cmdable) BitOpNot(destKey string, key string) *IntCmd {
return c.bitOp("not", destKey, key)
}
-func (c *cmdable) BitPos(key string, bit int64, pos ...int64) *IntCmd {
+func (c cmdable) BitPos(key string, bit int64, pos ...int64) *IntCmd {
args := make([]interface{}, 3+len(pos))
args[0] = "bitpos"
args[1] = key
@@ -733,91 +726,109 @@ func (c *cmdable) BitPos(key string, bit int64, pos ...int64) *IntCmd {
panic("too many arguments")
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Decr(key string) *IntCmd {
+func (c cmdable) BitField(key string, args ...interface{}) *IntSliceCmd {
+ a := make([]interface{}, 0, 2+len(args))
+ a = append(a, "bitfield")
+ a = append(a, key)
+ a = append(a, args...)
+ cmd := NewIntSliceCmd(a...)
+ _ = c(cmd)
+ return cmd
+}
+
+func (c cmdable) Decr(key string) *IntCmd {
cmd := NewIntCmd("decr", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) DecrBy(key string, decrement int64) *IntCmd {
+func (c cmdable) DecrBy(key string, decrement int64) *IntCmd {
cmd := NewIntCmd("decrby", key, decrement)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
// Redis `GET key` command. It returns redis.Nil error when key does not exist.
-func (c *cmdable) Get(key string) *StringCmd {
+func (c cmdable) Get(key string) *StringCmd {
cmd := NewStringCmd("get", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) GetBit(key string, offset int64) *IntCmd {
+func (c cmdable) GetBit(key string, offset int64) *IntCmd {
cmd := NewIntCmd("getbit", key, offset)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) GetRange(key string, start, end int64) *StringCmd {
+func (c cmdable) GetRange(key string, start, end int64) *StringCmd {
cmd := NewStringCmd("getrange", key, start, end)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) GetSet(key string, value interface{}) *StringCmd {
+func (c cmdable) GetSet(key string, value interface{}) *StringCmd {
cmd := NewStringCmd("getset", key, value)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) Incr(key string) *IntCmd {
+func (c cmdable) Incr(key string) *IntCmd {
cmd := NewIntCmd("incr", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) IncrBy(key string, value int64) *IntCmd {
+func (c cmdable) IncrBy(key string, value int64) *IntCmd {
cmd := NewIntCmd("incrby", key, value)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) IncrByFloat(key string, value float64) *FloatCmd {
+func (c cmdable) IncrByFloat(key string, value float64) *FloatCmd {
cmd := NewFloatCmd("incrbyfloat", key, value)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) MGet(keys ...string) *SliceCmd {
+func (c cmdable) MGet(keys ...string) *SliceCmd {
args := make([]interface{}, 1+len(keys))
args[0] = "mget"
for i, key := range keys {
args[1+i] = key
}
cmd := NewSliceCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) MSet(pairs ...interface{}) *StatusCmd {
- args := make([]interface{}, 1, 1+len(pairs))
+// MSet is like Set but accepts multiple values:
+// - MSet("key1", "value1", "key2", "value2")
+// - MSet([]string{"key1", "value1", "key2", "value2"})
+// - MSet(map[string]interface{}{"key1": "value1", "key2": "value2"})
+func (c cmdable) MSet(values ...interface{}) *StatusCmd {
+ args := make([]interface{}, 1, 1+len(values))
args[0] = "mset"
- args = appendArgs(args, pairs)
+ args = appendArgs(args, values)
cmd := NewStatusCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) MSetNX(pairs ...interface{}) *BoolCmd {
- args := make([]interface{}, 1, 1+len(pairs))
+// MSetNX is like SetNX but accepts multiple values:
+// - MSetNX("key1", "value1", "key2", "value2")
+// - MSetNX([]string{"key1", "value1", "key2", "value2"})
+// - MSetNX(map[string]interface{}{"key1": "value1", "key2": "value2"})
+func (c cmdable) MSetNX(values ...interface{}) *BoolCmd {
+ args := make([]interface{}, 1, 1+len(values))
args[0] = "msetnx"
- args = appendArgs(args, pairs)
+ args = appendArgs(args, values)
cmd := NewBoolCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
@@ -825,8 +836,8 @@ func (c *cmdable) MSetNX(pairs ...interface{}) *BoolCmd {
//
// Use expiration for `SETEX`-like behavior.
// Zero expiration means the key has no expiration time.
-func (c *cmdable) Set(key string, value interface{}, expiration time.Duration) *StatusCmd {
- args := make([]interface{}, 3, 4)
+func (c cmdable) Set(key string, value interface{}, expiration time.Duration) *StatusCmd {
+ args := make([]interface{}, 3, 5)
args[0] = "set"
args[1] = key
args[2] = value
@@ -838,25 +849,25 @@ func (c *cmdable) Set(key string, value interface{}, expiration time.Duration) *
}
}
cmd := NewStatusCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SetBit(key string, offset int64, value int) *IntCmd {
+func (c cmdable) SetBit(key string, offset int64, value int) *IntCmd {
cmd := NewIntCmd(
"setbit",
key,
offset,
value,
)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
// Redis `SET key value [expiration] NX` command.
//
// Zero expiration means the key has no expiration time.
-func (c *cmdable) SetNX(key string, value interface{}, expiration time.Duration) *BoolCmd {
+func (c cmdable) SetNX(key string, value interface{}, expiration time.Duration) *BoolCmd {
var cmd *BoolCmd
if expiration == 0 {
// Use old `SETNX` to support old Redis versions.
@@ -868,14 +879,14 @@ func (c *cmdable) SetNX(key string, value interface{}, expiration time.Duration)
cmd = NewBoolCmd("set", key, value, "ex", formatSec(expiration), "nx")
}
}
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
// Redis `SET key value [expiration] XX` command.
//
// Zero expiration means the key has no expiration time.
-func (c *cmdable) SetXX(key string, value interface{}, expiration time.Duration) *BoolCmd {
+func (c cmdable) SetXX(key string, value interface{}, expiration time.Duration) *BoolCmd {
var cmd *BoolCmd
if expiration == 0 {
cmd = NewBoolCmd("set", key, value, "xx")
@@ -886,25 +897,25 @@ func (c *cmdable) SetXX(key string, value interface{}, expiration time.Duration)
cmd = NewBoolCmd("set", key, value, "ex", formatSec(expiration), "xx")
}
}
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SetRange(key string, offset int64, value string) *IntCmd {
+func (c cmdable) SetRange(key string, offset int64, value string) *IntCmd {
cmd := NewIntCmd("setrange", key, offset, value)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) StrLen(key string) *IntCmd {
+func (c cmdable) StrLen(key string) *IntCmd {
cmd := NewIntCmd("strlen", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
//------------------------------------------------------------------------------
-func (c *cmdable) HDel(key string, fields ...string) *IntCmd {
+func (c cmdable) HDel(key string, fields ...string) *IntCmd {
args := make([]interface{}, 2+len(fields))
args[0] = "hdel"
args[1] = key
@@ -912,53 +923,55 @@ func (c *cmdable) HDel(key string, fields ...string) *IntCmd {
args[2+i] = field
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) HExists(key, field string) *BoolCmd {
+func (c cmdable) HExists(key, field string) *BoolCmd {
cmd := NewBoolCmd("hexists", key, field)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) HGet(key, field string) *StringCmd {
+func (c cmdable) HGet(key, field string) *StringCmd {
cmd := NewStringCmd("hget", key, field)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) HGetAll(key string) *StringStringMapCmd {
+func (c cmdable) HGetAll(key string) *StringStringMapCmd {
cmd := NewStringStringMapCmd("hgetall", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) HIncrBy(key, field string, incr int64) *IntCmd {
+func (c cmdable) HIncrBy(key, field string, incr int64) *IntCmd {
cmd := NewIntCmd("hincrby", key, field, incr)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) HIncrByFloat(key, field string, incr float64) *FloatCmd {
+func (c cmdable) HIncrByFloat(key, field string, incr float64) *FloatCmd {
cmd := NewFloatCmd("hincrbyfloat", key, field, incr)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) HKeys(key string) *StringSliceCmd {
+func (c cmdable) HKeys(key string) *StringSliceCmd {
cmd := NewStringSliceCmd("hkeys", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) HLen(key string) *IntCmd {
+func (c cmdable) HLen(key string) *IntCmd {
cmd := NewIntCmd("hlen", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) HMGet(key string, fields ...string) *SliceCmd {
+// HMGet returns the values for the specified fields in the hash stored at key.
+// It returns an interface{} to distinguish between empty string and nil value.
+func (c cmdable) HMGet(key string, fields ...string) *SliceCmd {
args := make([]interface{}, 2+len(fields))
args[0] = "hmget"
args[1] = key
@@ -966,46 +979,52 @@ func (c *cmdable) HMGet(key string, fields ...string) *SliceCmd {
args[2+i] = field
}
cmd := NewSliceCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) HMSet(key string, fields map[string]interface{}) *StatusCmd {
- args := make([]interface{}, 2+len(fields)*2)
+// HSet accepts values in following formats:
+// - HMSet("myhash", "key1", "value1", "key2", "value2")
+// - HMSet("myhash", []string{"key1", "value1", "key2", "value2"})
+// - HMSet("myhash", map[string]interface{}{"key1": "value1", "key2": "value2"})
+//
+// Note that it requires Redis v4 for multiple field/value pairs support.
+func (c cmdable) HSet(key string, values ...interface{}) *IntCmd {
+ args := make([]interface{}, 2, 2+len(values))
+ args[0] = "hset"
+ args[1] = key
+ args = appendArgs(args, values)
+ cmd := NewIntCmd(args...)
+ _ = c(cmd)
+ return cmd
+}
+
+// HMSet is a deprecated version of HSet left for compatibility with Redis 3.
+func (c cmdable) HMSet(key string, values ...interface{}) *BoolCmd {
+ args := make([]interface{}, 2, 2+len(values))
args[0] = "hmset"
args[1] = key
- i := 2
- for k, v := range fields {
- args[i] = k
- args[i+1] = v
- i += 2
- }
- cmd := NewStatusCmd(args...)
- c.process(cmd)
+ args = appendArgs(args, values)
+ cmd := NewBoolCmd(args...)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) HSet(key, field string, value interface{}) *BoolCmd {
- cmd := NewBoolCmd("hset", key, field, value)
- c.process(cmd)
- return cmd
-}
-
-func (c *cmdable) HSetNX(key, field string, value interface{}) *BoolCmd {
+func (c cmdable) HSetNX(key, field string, value interface{}) *BoolCmd {
cmd := NewBoolCmd("hsetnx", key, field, value)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) HVals(key string) *StringSliceCmd {
+func (c cmdable) HVals(key string) *StringSliceCmd {
cmd := NewStringSliceCmd("hvals", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
//------------------------------------------------------------------------------
-func (c *cmdable) BLPop(timeout time.Duration, keys ...string) *StringSliceCmd {
+func (c cmdable) BLPop(timeout time.Duration, keys ...string) *StringSliceCmd {
args := make([]interface{}, 1+len(keys)+1)
args[0] = "blpop"
for i, key := range keys {
@@ -1014,11 +1033,11 @@ func (c *cmdable) BLPop(timeout time.Duration, keys ...string) *StringSliceCmd {
args[len(args)-1] = formatSec(timeout)
cmd := NewStringSliceCmd(args...)
cmd.setReadTimeout(timeout)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) BRPop(timeout time.Duration, keys ...string) *StringSliceCmd {
+func (c cmdable) BRPop(timeout time.Duration, keys ...string) *StringSliceCmd {
args := make([]interface{}, 1+len(keys)+1)
args[0] = "brpop"
for i, key := range keys {
@@ -1027,11 +1046,11 @@ func (c *cmdable) BRPop(timeout time.Duration, keys ...string) *StringSliceCmd {
args[len(keys)+1] = formatSec(timeout)
cmd := NewStringSliceCmd(args...)
cmd.setReadTimeout(timeout)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) BRPopLPush(source, destination string, timeout time.Duration) *StringCmd {
+func (c cmdable) BRPopLPush(source, destination string, timeout time.Duration) *StringCmd {
cmd := NewStringCmd(
"brpoplpush",
source,
@@ -1039,154 +1058,162 @@ func (c *cmdable) BRPopLPush(source, destination string, timeout time.Duration)
formatSec(timeout),
)
cmd.setReadTimeout(timeout)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) LIndex(key string, index int64) *StringCmd {
+func (c cmdable) LIndex(key string, index int64) *StringCmd {
cmd := NewStringCmd("lindex", key, index)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) LInsert(key, op string, pivot, value interface{}) *IntCmd {
+func (c cmdable) LInsert(key, op string, pivot, value interface{}) *IntCmd {
cmd := NewIntCmd("linsert", key, op, pivot, value)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) LInsertBefore(key string, pivot, value interface{}) *IntCmd {
+func (c cmdable) LInsertBefore(key string, pivot, value interface{}) *IntCmd {
cmd := NewIntCmd("linsert", key, "before", pivot, value)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) LInsertAfter(key string, pivot, value interface{}) *IntCmd {
+func (c cmdable) LInsertAfter(key string, pivot, value interface{}) *IntCmd {
cmd := NewIntCmd("linsert", key, "after", pivot, value)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) LLen(key string) *IntCmd {
+func (c cmdable) LLen(key string) *IntCmd {
cmd := NewIntCmd("llen", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) LPop(key string) *StringCmd {
+func (c cmdable) LPop(key string) *StringCmd {
cmd := NewStringCmd("lpop", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) LPush(key string, values ...interface{}) *IntCmd {
+func (c cmdable) LPush(key string, values ...interface{}) *IntCmd {
args := make([]interface{}, 2, 2+len(values))
args[0] = "lpush"
args[1] = key
args = appendArgs(args, values)
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) LPushX(key string, value interface{}) *IntCmd {
- cmd := NewIntCmd("lpushx", key, value)
- c.process(cmd)
+func (c cmdable) LPushX(key string, values ...interface{}) *IntCmd {
+ args := make([]interface{}, 2, 2+len(values))
+ args[0] = "lpushx"
+ args[1] = key
+ args = appendArgs(args, values)
+ cmd := NewIntCmd(args...)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) LRange(key string, start, stop int64) *StringSliceCmd {
+func (c cmdable) LRange(key string, start, stop int64) *StringSliceCmd {
cmd := NewStringSliceCmd(
"lrange",
key,
start,
stop,
)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) LRem(key string, count int64, value interface{}) *IntCmd {
+func (c cmdable) LRem(key string, count int64, value interface{}) *IntCmd {
cmd := NewIntCmd("lrem", key, count, value)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) LSet(key string, index int64, value interface{}) *StatusCmd {
+func (c cmdable) LSet(key string, index int64, value interface{}) *StatusCmd {
cmd := NewStatusCmd("lset", key, index, value)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) LTrim(key string, start, stop int64) *StatusCmd {
+func (c cmdable) LTrim(key string, start, stop int64) *StatusCmd {
cmd := NewStatusCmd(
"ltrim",
key,
start,
stop,
)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) RPop(key string) *StringCmd {
+func (c cmdable) RPop(key string) *StringCmd {
cmd := NewStringCmd("rpop", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) RPopLPush(source, destination string) *StringCmd {
+func (c cmdable) RPopLPush(source, destination string) *StringCmd {
cmd := NewStringCmd("rpoplpush", source, destination)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) RPush(key string, values ...interface{}) *IntCmd {
+func (c cmdable) RPush(key string, values ...interface{}) *IntCmd {
args := make([]interface{}, 2, 2+len(values))
args[0] = "rpush"
args[1] = key
args = appendArgs(args, values)
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) RPushX(key string, value interface{}) *IntCmd {
- cmd := NewIntCmd("rpushx", key, value)
- c.process(cmd)
+func (c cmdable) RPushX(key string, values ...interface{}) *IntCmd {
+ args := make([]interface{}, 2, 2+len(values))
+ args[0] = "rpushx"
+ args[1] = key
+ args = appendArgs(args, values)
+ cmd := NewIntCmd(args...)
+ _ = c(cmd)
return cmd
}
//------------------------------------------------------------------------------
-func (c *cmdable) SAdd(key string, members ...interface{}) *IntCmd {
+func (c cmdable) SAdd(key string, members ...interface{}) *IntCmd {
args := make([]interface{}, 2, 2+len(members))
args[0] = "sadd"
args[1] = key
args = appendArgs(args, members)
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SCard(key string) *IntCmd {
+func (c cmdable) SCard(key string) *IntCmd {
cmd := NewIntCmd("scard", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SDiff(keys ...string) *StringSliceCmd {
+func (c cmdable) SDiff(keys ...string) *StringSliceCmd {
args := make([]interface{}, 1+len(keys))
args[0] = "sdiff"
for i, key := range keys {
args[1+i] = key
}
cmd := NewStringSliceCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SDiffStore(destination string, keys ...string) *IntCmd {
+func (c cmdable) SDiffStore(destination string, keys ...string) *IntCmd {
args := make([]interface{}, 2+len(keys))
args[0] = "sdiffstore"
args[1] = destination
@@ -1194,22 +1221,22 @@ func (c *cmdable) SDiffStore(destination string, keys ...string) *IntCmd {
args[2+i] = key
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SInter(keys ...string) *StringSliceCmd {
+func (c cmdable) SInter(keys ...string) *StringSliceCmd {
args := make([]interface{}, 1+len(keys))
args[0] = "sinter"
for i, key := range keys {
args[1+i] = key
}
cmd := NewStringSliceCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SInterStore(destination string, keys ...string) *IntCmd {
+func (c cmdable) SInterStore(destination string, keys ...string) *IntCmd {
args := make([]interface{}, 2+len(keys))
args[0] = "sinterstore"
args[1] = destination
@@ -1217,86 +1244,86 @@ func (c *cmdable) SInterStore(destination string, keys ...string) *IntCmd {
args[2+i] = key
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SIsMember(key string, member interface{}) *BoolCmd {
+func (c cmdable) SIsMember(key string, member interface{}) *BoolCmd {
cmd := NewBoolCmd("sismember", key, member)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
// Redis `SMEMBERS key` command output as a slice
-func (c *cmdable) SMembers(key string) *StringSliceCmd {
+func (c cmdable) SMembers(key string) *StringSliceCmd {
cmd := NewStringSliceCmd("smembers", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
// Redis `SMEMBERS key` command output as a map
-func (c *cmdable) SMembersMap(key string) *StringStructMapCmd {
+func (c cmdable) SMembersMap(key string) *StringStructMapCmd {
cmd := NewStringStructMapCmd("smembers", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SMove(source, destination string, member interface{}) *BoolCmd {
+func (c cmdable) SMove(source, destination string, member interface{}) *BoolCmd {
cmd := NewBoolCmd("smove", source, destination, member)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
// Redis `SPOP key` command.
-func (c *cmdable) SPop(key string) *StringCmd {
+func (c cmdable) SPop(key string) *StringCmd {
cmd := NewStringCmd("spop", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
// Redis `SPOP key count` command.
-func (c *cmdable) SPopN(key string, count int64) *StringSliceCmd {
+func (c cmdable) SPopN(key string, count int64) *StringSliceCmd {
cmd := NewStringSliceCmd("spop", key, count)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
// Redis `SRANDMEMBER key` command.
-func (c *cmdable) SRandMember(key string) *StringCmd {
+func (c cmdable) SRandMember(key string) *StringCmd {
cmd := NewStringCmd("srandmember", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
// Redis `SRANDMEMBER key count` command.
-func (c *cmdable) SRandMemberN(key string, count int64) *StringSliceCmd {
+func (c cmdable) SRandMemberN(key string, count int64) *StringSliceCmd {
cmd := NewStringSliceCmd("srandmember", key, count)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SRem(key string, members ...interface{}) *IntCmd {
+func (c cmdable) SRem(key string, members ...interface{}) *IntCmd {
args := make([]interface{}, 2, 2+len(members))
args[0] = "srem"
args[1] = key
args = appendArgs(args, members)
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SUnion(keys ...string) *StringSliceCmd {
+func (c cmdable) SUnion(keys ...string) *StringSliceCmd {
args := make([]interface{}, 1+len(keys))
args[0] = "sunion"
for i, key := range keys {
args[1+i] = key
}
cmd := NewStringSliceCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) SUnionStore(destination string, keys ...string) *IntCmd {
+func (c cmdable) SUnionStore(destination string, keys ...string) *IntCmd {
args := make([]interface{}, 2+len(keys))
args[0] = "sunionstore"
args[1] = destination
@@ -1304,7 +1331,7 @@ func (c *cmdable) SUnionStore(destination string, keys ...string) *IntCmd {
args[2+i] = key
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
@@ -1318,7 +1345,7 @@ type XAddArgs struct {
Values map[string]interface{}
}
-func (c *cmdable) XAdd(a *XAddArgs) *StringCmd {
+func (c cmdable) XAdd(a *XAddArgs) *StringCmd {
args := make([]interface{}, 0, 6+len(a.Values)*2)
args = append(args, "xadd")
args = append(args, a.Stream)
@@ -1338,47 +1365,57 @@ func (c *cmdable) XAdd(a *XAddArgs) *StringCmd {
}
cmd := NewStringCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XLen(stream string) *IntCmd {
+func (c cmdable) XDel(stream string, ids ...string) *IntCmd {
+ args := []interface{}{"xdel", stream}
+ for _, id := range ids {
+ args = append(args, id)
+ }
+ cmd := NewIntCmd(args...)
+ _ = c(cmd)
+ return cmd
+}
+
+func (c cmdable) XLen(stream string) *IntCmd {
cmd := NewIntCmd("xlen", stream)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XRange(stream, start, stop string) *XMessageSliceCmd {
+func (c cmdable) XRange(stream, start, stop string) *XMessageSliceCmd {
cmd := NewXMessageSliceCmd("xrange", stream, start, stop)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XRangeN(stream, start, stop string, count int64) *XMessageSliceCmd {
+func (c cmdable) XRangeN(stream, start, stop string, count int64) *XMessageSliceCmd {
cmd := NewXMessageSliceCmd("xrange", stream, start, stop, "count", count)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XRevRange(stream, start, stop string) *XMessageSliceCmd {
+func (c cmdable) XRevRange(stream, start, stop string) *XMessageSliceCmd {
cmd := NewXMessageSliceCmd("xrevrange", stream, start, stop)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XRevRangeN(stream, start, stop string, count int64) *XMessageSliceCmd {
+func (c cmdable) XRevRangeN(stream, start, stop string, count int64) *XMessageSliceCmd {
cmd := NewXMessageSliceCmd("xrevrange", stream, start, stop, "count", count)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
type XReadArgs struct {
- Streams []string
+ Streams []string // list of streams and ids, e.g. stream1 stream2 id1 id2
Count int64
Block time.Duration
}
-func (c *cmdable) XRead(a *XReadArgs) *XStreamSliceCmd {
+func (c cmdable) XRead(a *XReadArgs) *XStreamSliceCmd {
args := make([]interface{}, 0, 5+len(a.Streams))
args = append(args, "xread")
if a.Count > 0 {
@@ -1389,56 +1426,67 @@ func (c *cmdable) XRead(a *XReadArgs) *XStreamSliceCmd {
args = append(args, "block")
args = append(args, int64(a.Block/time.Millisecond))
}
+
args = append(args, "streams")
for _, s := range a.Streams {
args = append(args, s)
}
cmd := NewXStreamSliceCmd(args...)
- c.process(cmd)
+ if a.Block >= 0 {
+ cmd.setReadTimeout(a.Block)
+ }
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XReadStreams(streams ...string) *XStreamSliceCmd {
+func (c cmdable) XReadStreams(streams ...string) *XStreamSliceCmd {
return c.XRead(&XReadArgs{
Streams: streams,
Block: -1,
})
}
-func (c *cmdable) XGroupCreate(stream, group, start string) *StatusCmd {
+func (c cmdable) XGroupCreate(stream, group, start string) *StatusCmd {
cmd := NewStatusCmd("xgroup", "create", stream, group, start)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XGroupSetID(stream, group, start string) *StatusCmd {
+func (c cmdable) XGroupCreateMkStream(stream, group, start string) *StatusCmd {
+ cmd := NewStatusCmd("xgroup", "create", stream, group, start, "mkstream")
+ _ = c(cmd)
+ return cmd
+}
+
+func (c cmdable) XGroupSetID(stream, group, start string) *StatusCmd {
cmd := NewStatusCmd("xgroup", "setid", stream, group, start)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XGroupDestroy(stream, group string) *IntCmd {
+func (c cmdable) XGroupDestroy(stream, group string) *IntCmd {
cmd := NewIntCmd("xgroup", "destroy", stream, group)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XGroupDelConsumer(stream, group, consumer string) *IntCmd {
+func (c cmdable) XGroupDelConsumer(stream, group, consumer string) *IntCmd {
cmd := NewIntCmd("xgroup", "delconsumer", stream, group, consumer)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
type XReadGroupArgs struct {
Group string
Consumer string
- Streams []string
+ Streams []string // list of streams and ids, e.g. stream1 stream2 id1 id2
Count int64
Block time.Duration
+ NoAck bool
}
-func (c *cmdable) XReadGroup(a *XReadGroupArgs) *XStreamSliceCmd {
+func (c cmdable) XReadGroup(a *XReadGroupArgs) *XStreamSliceCmd {
args := make([]interface{}, 0, 8+len(a.Streams))
args = append(args, "xreadgroup", "group", a.Group, a.Consumer)
if a.Count > 0 {
@@ -1447,29 +1495,35 @@ func (c *cmdable) XReadGroup(a *XReadGroupArgs) *XStreamSliceCmd {
if a.Block >= 0 {
args = append(args, "block", int64(a.Block/time.Millisecond))
}
+ if a.NoAck {
+ args = append(args, "noack")
+ }
args = append(args, "streams")
for _, s := range a.Streams {
args = append(args, s)
}
cmd := NewXStreamSliceCmd(args...)
- c.process(cmd)
+ if a.Block >= 0 {
+ cmd.setReadTimeout(a.Block)
+ }
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XAck(stream, group string, ids ...string) *IntCmd {
+func (c cmdable) XAck(stream, group string, ids ...string) *IntCmd {
args := []interface{}{"xack", stream, group}
for _, id := range ids {
args = append(args, id)
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XPending(stream, group string) *XPendingCmd {
+func (c cmdable) XPending(stream, group string) *XPendingCmd {
cmd := NewXPendingCmd("xpending", stream, group)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
@@ -1482,14 +1536,14 @@ type XPendingExtArgs struct {
Consumer string
}
-func (c *cmdable) XPendingExt(a *XPendingExtArgs) *XPendingExtCmd {
+func (c cmdable) XPendingExt(a *XPendingExtArgs) *XPendingExtCmd {
args := make([]interface{}, 0, 7)
args = append(args, "xpending", a.Stream, a.Group, a.Start, a.End, a.Count)
if a.Consumer != "" {
args = append(args, a.Consumer)
}
cmd := NewXPendingExtCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
@@ -1501,18 +1555,18 @@ type XClaimArgs struct {
Messages []string
}
-func (c *cmdable) XClaim(a *XClaimArgs) *XMessageSliceCmd {
+func (c cmdable) XClaim(a *XClaimArgs) *XMessageSliceCmd {
args := xClaimArgs(a)
cmd := NewXMessageSliceCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XClaimJustID(a *XClaimArgs) *StringSliceCmd {
+func (c cmdable) XClaimJustID(a *XClaimArgs) *StringSliceCmd {
args := xClaimArgs(a)
args = append(args, "justid")
cmd := NewStringSliceCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
@@ -1529,15 +1583,21 @@ func xClaimArgs(a *XClaimArgs) []interface{} {
return args
}
-func (c *cmdable) XTrim(key string, maxLen int64) *IntCmd {
+func (c cmdable) XTrim(key string, maxLen int64) *IntCmd {
cmd := NewIntCmd("xtrim", key, "maxlen", maxLen)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) XTrimApprox(key string, maxLen int64) *IntCmd {
+func (c cmdable) XTrimApprox(key string, maxLen int64) *IntCmd {
cmd := NewIntCmd("xtrim", key, "maxlen", "~", maxLen)
- c.process(cmd)
+ _ = c(cmd)
+ return cmd
+}
+
+func (c cmdable) XInfoGroups(key string) *XInfoGroupsCmd {
+ cmd := NewXInfoGroupsCmd(key)
+ _ = c(cmd)
return cmd
}
@@ -1549,25 +1609,60 @@ type Z struct {
Member interface{}
}
+// ZWithKey represents sorted set member including the name of the key where it was popped.
+type ZWithKey struct {
+ Z
+ Key string
+}
+
// ZStore is used as an arg to ZInterStore and ZUnionStore.
type ZStore struct {
+ Keys []string
Weights []float64
// Can be SUM, MIN or MAX.
Aggregate string
}
-func (c *cmdable) zAdd(a []interface{}, n int, members ...Z) *IntCmd {
+// Redis `BZPOPMAX key [key ...] timeout` command.
+func (c cmdable) BZPopMax(timeout time.Duration, keys ...string) *ZWithKeyCmd {
+ args := make([]interface{}, 1+len(keys)+1)
+ args[0] = "bzpopmax"
+ for i, key := range keys {
+ args[1+i] = key
+ }
+ args[len(args)-1] = formatSec(timeout)
+ cmd := NewZWithKeyCmd(args...)
+ cmd.setReadTimeout(timeout)
+ _ = c(cmd)
+ return cmd
+}
+
+// Redis `BZPOPMIN key [key ...] timeout` command.
+func (c cmdable) BZPopMin(timeout time.Duration, keys ...string) *ZWithKeyCmd {
+ args := make([]interface{}, 1+len(keys)+1)
+ args[0] = "bzpopmin"
+ for i, key := range keys {
+ args[1+i] = key
+ }
+ args[len(args)-1] = formatSec(timeout)
+ cmd := NewZWithKeyCmd(args...)
+ cmd.setReadTimeout(timeout)
+ _ = c(cmd)
+ return cmd
+}
+
+func (c cmdable) zAdd(a []interface{}, n int, members ...*Z) *IntCmd {
for i, m := range members {
a[n+2*i] = m.Score
a[n+2*i+1] = m.Member
}
cmd := NewIntCmd(a...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
// Redis `ZADD key score member [score member ...]` command.
-func (c *cmdable) ZAdd(key string, members ...Z) *IntCmd {
+func (c cmdable) ZAdd(key string, members ...*Z) *IntCmd {
const n = 2
a := make([]interface{}, n+2*len(members))
a[0], a[1] = "zadd", key
@@ -1575,7 +1670,7 @@ func (c *cmdable) ZAdd(key string, members ...Z) *IntCmd {
}
// Redis `ZADD key NX score member [score member ...]` command.
-func (c *cmdable) ZAddNX(key string, members ...Z) *IntCmd {
+func (c cmdable) ZAddNX(key string, members ...*Z) *IntCmd {
const n = 3
a := make([]interface{}, n+2*len(members))
a[0], a[1], a[2] = "zadd", key, "nx"
@@ -1583,7 +1678,7 @@ func (c *cmdable) ZAddNX(key string, members ...Z) *IntCmd {
}
// Redis `ZADD key XX score member [score member ...]` command.
-func (c *cmdable) ZAddXX(key string, members ...Z) *IntCmd {
+func (c cmdable) ZAddXX(key string, members ...*Z) *IntCmd {
const n = 3
a := make([]interface{}, n+2*len(members))
a[0], a[1], a[2] = "zadd", key, "xx"
@@ -1591,7 +1686,7 @@ func (c *cmdable) ZAddXX(key string, members ...Z) *IntCmd {
}
// Redis `ZADD key CH score member [score member ...]` command.
-func (c *cmdable) ZAddCh(key string, members ...Z) *IntCmd {
+func (c cmdable) ZAddCh(key string, members ...*Z) *IntCmd {
const n = 3
a := make([]interface{}, n+2*len(members))
a[0], a[1], a[2] = "zadd", key, "ch"
@@ -1599,7 +1694,7 @@ func (c *cmdable) ZAddCh(key string, members ...Z) *IntCmd {
}
// Redis `ZADD key NX CH score member [score member ...]` command.
-func (c *cmdable) ZAddNXCh(key string, members ...Z) *IntCmd {
+func (c cmdable) ZAddNXCh(key string, members ...*Z) *IntCmd {
const n = 4
a := make([]interface{}, n+2*len(members))
a[0], a[1], a[2], a[3] = "zadd", key, "nx", "ch"
@@ -1607,25 +1702,25 @@ func (c *cmdable) ZAddNXCh(key string, members ...Z) *IntCmd {
}
// Redis `ZADD key XX CH score member [score member ...]` command.
-func (c *cmdable) ZAddXXCh(key string, members ...Z) *IntCmd {
+func (c cmdable) ZAddXXCh(key string, members ...*Z) *IntCmd {
const n = 4
a := make([]interface{}, n+2*len(members))
a[0], a[1], a[2], a[3] = "zadd", key, "xx", "ch"
return c.zAdd(a, n, members...)
}
-func (c *cmdable) zIncr(a []interface{}, n int, members ...Z) *FloatCmd {
+func (c cmdable) zIncr(a []interface{}, n int, members ...*Z) *FloatCmd {
for i, m := range members {
a[n+2*i] = m.Score
a[n+2*i+1] = m.Member
}
cmd := NewFloatCmd(a...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
// Redis `ZADD key INCR score member` command.
-func (c *cmdable) ZIncr(key string, member Z) *FloatCmd {
+func (c cmdable) ZIncr(key string, member *Z) *FloatCmd {
const n = 3
a := make([]interface{}, n+2)
a[0], a[1], a[2] = "zadd", key, "incr"
@@ -1633,7 +1728,7 @@ func (c *cmdable) ZIncr(key string, member Z) *FloatCmd {
}
// Redis `ZADD key NX INCR score member` command.
-func (c *cmdable) ZIncrNX(key string, member Z) *FloatCmd {
+func (c cmdable) ZIncrNX(key string, member *Z) *FloatCmd {
const n = 4
a := make([]interface{}, n+2)
a[0], a[1], a[2], a[3] = "zadd", key, "incr", "nx"
@@ -1641,43 +1736,43 @@ func (c *cmdable) ZIncrNX(key string, member Z) *FloatCmd {
}
// Redis `ZADD key XX INCR score member` command.
-func (c *cmdable) ZIncrXX(key string, member Z) *FloatCmd {
+func (c cmdable) ZIncrXX(key string, member *Z) *FloatCmd {
const n = 4
a := make([]interface{}, n+2)
a[0], a[1], a[2], a[3] = "zadd", key, "incr", "xx"
return c.zIncr(a, n, member)
}
-func (c *cmdable) ZCard(key string) *IntCmd {
+func (c cmdable) ZCard(key string) *IntCmd {
cmd := NewIntCmd("zcard", key)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZCount(key, min, max string) *IntCmd {
+func (c cmdable) ZCount(key, min, max string) *IntCmd {
cmd := NewIntCmd("zcount", key, min, max)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZLexCount(key, min, max string) *IntCmd {
+func (c cmdable) ZLexCount(key, min, max string) *IntCmd {
cmd := NewIntCmd("zlexcount", key, min, max)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZIncrBy(key string, increment float64, member string) *FloatCmd {
+func (c cmdable) ZIncrBy(key string, increment float64, member string) *FloatCmd {
cmd := NewFloatCmd("zincrby", key, increment, member)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZInterStore(destination string, store ZStore, keys ...string) *IntCmd {
- args := make([]interface{}, 3+len(keys))
+func (c cmdable) ZInterStore(destination string, store *ZStore) *IntCmd {
+ args := make([]interface{}, 3+len(store.Keys))
args[0] = "zinterstore"
args[1] = destination
- args[2] = len(keys)
- for i, key := range keys {
+ args[2] = len(store.Keys)
+ for i, key := range store.Keys {
args[3+i] = key
}
if len(store.Weights) > 0 {
@@ -1690,11 +1785,51 @@ func (c *cmdable) ZInterStore(destination string, store ZStore, keys ...string)
args = append(args, "aggregate", store.Aggregate)
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) zRange(key string, start, stop int64, withScores bool) *StringSliceCmd {
+func (c cmdable) ZPopMax(key string, count ...int64) *ZSliceCmd {
+ args := []interface{}{
+ "zpopmax",
+ key,
+ }
+
+ switch len(count) {
+ case 0:
+ break
+ case 1:
+ args = append(args, count[0])
+ default:
+ panic("too many arguments")
+ }
+
+ cmd := NewZSliceCmd(args...)
+ _ = c(cmd)
+ return cmd
+}
+
+func (c cmdable) ZPopMin(key string, count ...int64) *ZSliceCmd {
+ args := []interface{}{
+ "zpopmin",
+ key,
+ }
+
+ switch len(count) {
+ case 0:
+ break
+ case 1:
+ args = append(args, count[0])
+ default:
+ panic("too many arguments")
+ }
+
+ cmd := NewZSliceCmd(args...)
+ _ = c(cmd)
+ return cmd
+}
+
+func (c cmdable) zRange(key string, start, stop int64, withScores bool) *StringSliceCmd {
args := []interface{}{
"zrange",
key,
@@ -1705,17 +1840,17 @@ func (c *cmdable) zRange(key string, start, stop int64, withScores bool) *String
args = append(args, "withscores")
}
cmd := NewStringSliceCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZRange(key string, start, stop int64) *StringSliceCmd {
+func (c cmdable) ZRange(key string, start, stop int64) *StringSliceCmd {
return c.zRange(key, start, stop, false)
}
-func (c *cmdable) ZRangeWithScores(key string, start, stop int64) *ZSliceCmd {
+func (c cmdable) ZRangeWithScores(key string, start, stop int64) *ZSliceCmd {
cmd := NewZSliceCmd("zrange", key, start, stop, "withscores")
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
@@ -1724,7 +1859,7 @@ type ZRangeBy struct {
Offset, Count int64
}
-func (c *cmdable) zRangeBy(zcmd, key string, opt ZRangeBy, withScores bool) *StringSliceCmd {
+func (c cmdable) zRangeBy(zcmd, key string, opt *ZRangeBy, withScores bool) *StringSliceCmd {
args := []interface{}{zcmd, key, opt.Min, opt.Max}
if withScores {
args = append(args, "withscores")
@@ -1738,19 +1873,19 @@ func (c *cmdable) zRangeBy(zcmd, key string, opt ZRangeBy, withScores bool) *Str
)
}
cmd := NewStringSliceCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZRangeByScore(key string, opt ZRangeBy) *StringSliceCmd {
+func (c cmdable) ZRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd {
return c.zRangeBy("zrangebyscore", key, opt, false)
}
-func (c *cmdable) ZRangeByLex(key string, opt ZRangeBy) *StringSliceCmd {
+func (c cmdable) ZRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd {
return c.zRangeBy("zrangebylex", key, opt, false)
}
-func (c *cmdable) ZRangeByScoreWithScores(key string, opt ZRangeBy) *ZSliceCmd {
+func (c cmdable) ZRangeByScoreWithScores(key string, opt *ZRangeBy) *ZSliceCmd {
args := []interface{}{"zrangebyscore", key, opt.Min, opt.Max, "withscores"}
if opt.Offset != 0 || opt.Count != 0 {
args = append(
@@ -1761,62 +1896,62 @@ func (c *cmdable) ZRangeByScoreWithScores(key string, opt ZRangeBy) *ZSliceCmd {
)
}
cmd := NewZSliceCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZRank(key, member string) *IntCmd {
+func (c cmdable) ZRank(key, member string) *IntCmd {
cmd := NewIntCmd("zrank", key, member)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZRem(key string, members ...interface{}) *IntCmd {
+func (c cmdable) ZRem(key string, members ...interface{}) *IntCmd {
args := make([]interface{}, 2, 2+len(members))
args[0] = "zrem"
args[1] = key
args = appendArgs(args, members)
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZRemRangeByRank(key string, start, stop int64) *IntCmd {
+func (c cmdable) ZRemRangeByRank(key string, start, stop int64) *IntCmd {
cmd := NewIntCmd(
"zremrangebyrank",
key,
start,
stop,
)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZRemRangeByScore(key, min, max string) *IntCmd {
+func (c cmdable) ZRemRangeByScore(key, min, max string) *IntCmd {
cmd := NewIntCmd("zremrangebyscore", key, min, max)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZRemRangeByLex(key, min, max string) *IntCmd {
+func (c cmdable) ZRemRangeByLex(key, min, max string) *IntCmd {
cmd := NewIntCmd("zremrangebylex", key, min, max)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZRevRange(key string, start, stop int64) *StringSliceCmd {
+func (c cmdable) ZRevRange(key string, start, stop int64) *StringSliceCmd {
cmd := NewStringSliceCmd("zrevrange", key, start, stop)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZRevRangeWithScores(key string, start, stop int64) *ZSliceCmd {
+func (c cmdable) ZRevRangeWithScores(key string, start, stop int64) *ZSliceCmd {
cmd := NewZSliceCmd("zrevrange", key, start, stop, "withscores")
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) zRevRangeBy(zcmd, key string, opt ZRangeBy) *StringSliceCmd {
+func (c cmdable) zRevRangeBy(zcmd, key string, opt *ZRangeBy) *StringSliceCmd {
args := []interface{}{zcmd, key, opt.Max, opt.Min}
if opt.Offset != 0 || opt.Count != 0 {
args = append(
@@ -1827,19 +1962,19 @@ func (c *cmdable) zRevRangeBy(zcmd, key string, opt ZRangeBy) *StringSliceCmd {
)
}
cmd := NewStringSliceCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZRevRangeByScore(key string, opt ZRangeBy) *StringSliceCmd {
+func (c cmdable) ZRevRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd {
return c.zRevRangeBy("zrevrangebyscore", key, opt)
}
-func (c *cmdable) ZRevRangeByLex(key string, opt ZRangeBy) *StringSliceCmd {
+func (c cmdable) ZRevRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd {
return c.zRevRangeBy("zrevrangebylex", key, opt)
}
-func (c *cmdable) ZRevRangeByScoreWithScores(key string, opt ZRangeBy) *ZSliceCmd {
+func (c cmdable) ZRevRangeByScoreWithScores(key string, opt *ZRangeBy) *ZSliceCmd {
args := []interface{}{"zrevrangebyscore", key, opt.Max, opt.Min, "withscores"}
if opt.Offset != 0 || opt.Count != 0 {
args = append(
@@ -1850,28 +1985,28 @@ func (c *cmdable) ZRevRangeByScoreWithScores(key string, opt ZRangeBy) *ZSliceCm
)
}
cmd := NewZSliceCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZRevRank(key, member string) *IntCmd {
+func (c cmdable) ZRevRank(key, member string) *IntCmd {
cmd := NewIntCmd("zrevrank", key, member)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZScore(key, member string) *FloatCmd {
+func (c cmdable) ZScore(key, member string) *FloatCmd {
cmd := NewFloatCmd("zscore", key, member)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ZUnionStore(dest string, store ZStore, keys ...string) *IntCmd {
- args := make([]interface{}, 3+len(keys))
+func (c cmdable) ZUnionStore(dest string, store *ZStore) *IntCmd {
+ args := make([]interface{}, 3+len(store.Keys))
args[0] = "zunionstore"
args[1] = dest
- args[2] = len(keys)
- for i, key := range keys {
+ args[2] = len(store.Keys)
+ for i, key := range store.Keys {
args[3+i] = key
}
if len(store.Weights) > 0 {
@@ -1884,34 +2019,34 @@ func (c *cmdable) ZUnionStore(dest string, store ZStore, keys ...string) *IntCmd
args = append(args, "aggregate", store.Aggregate)
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
//------------------------------------------------------------------------------
-func (c *cmdable) PFAdd(key string, els ...interface{}) *IntCmd {
+func (c cmdable) PFAdd(key string, els ...interface{}) *IntCmd {
args := make([]interface{}, 2, 2+len(els))
args[0] = "pfadd"
args[1] = key
args = appendArgs(args, els)
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) PFCount(keys ...string) *IntCmd {
+func (c cmdable) PFCount(keys ...string) *IntCmd {
args := make([]interface{}, 1+len(keys))
args[0] = "pfcount"
for i, key := range keys {
args[1+i] = key
}
cmd := NewIntCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) PFMerge(dest string, keys ...string) *StatusCmd {
+func (c cmdable) PFMerge(dest string, keys ...string) *StatusCmd {
args := make([]interface{}, 2+len(keys))
args[0] = "pfmerge"
args[1] = dest
@@ -1919,33 +2054,33 @@ func (c *cmdable) PFMerge(dest string, keys ...string) *StatusCmd {
args[2+i] = key
}
cmd := NewStatusCmd(args...)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
//------------------------------------------------------------------------------
-func (c *cmdable) BgRewriteAOF() *StatusCmd {
+func (c cmdable) BgRewriteAOF() *StatusCmd {
cmd := NewStatusCmd("bgrewriteaof")
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) BgSave() *StatusCmd {
+func (c cmdable) BgSave() *StatusCmd {
cmd := NewStatusCmd("bgsave")
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
-func (c *cmdable) ClientKill(ipPort string) *StatusCmd {
+func (c cmdable) ClientKill(ipPort string) *StatusCmd {
cmd := NewStatusCmd("client", "kill", ipPort)
- c.process(cmd)
+ _ = c(cmd)
return cmd
}
// ClientKillByFilter is new style synx, while the ClientKill is old
// CLIENT KILL