diff --git a/go.mod b/go.mod
index 3a1e16c0d3..ee67cd8fc1 100644
--- a/go.mod
+++ b/go.mod
@@ -35,7 +35,7 @@ require (
 	github.com/go-git/go-billy/v5 v5.0.0
 	github.com/go-git/go-git/v5 v5.2.0
 	github.com/go-ldap/ldap/v3 v3.2.4
-	github.com/go-redis/redis/v7 v7.4.0
+	github.com/go-redis/redis/v8 v8.5.0
 	github.com/go-sql-driver/mysql v1.5.0
 	github.com/go-swagger/go-swagger v0.26.0
 	github.com/go-testfixtures/testfixtures/v3 v3.4.1
diff --git a/go.sum b/go.sum
index 5702bab19b..7887d9e014 100644
--- a/go.sum
+++ b/go.sum
@@ -241,6 +241,7 @@ github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D
 github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
 github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
@@ -281,7 +282,6 @@ github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4
 github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
 github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
 github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
@@ -291,13 +291,11 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
 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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 github.com/gliderlabs/ssh v0.3.1 h1:L6VrMUGZaMlNIMN8Hj+CHh4U9yodJE3FAt/rgvfaKvE=
 github.com/gliderlabs/ssh v0.3.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4=
 github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
 github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a h1:FQqoVvjbiUioBBFUL5up+h+GdCa/AnJsL/1bIs/veSI=
 github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
@@ -338,7 +336,6 @@ github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpR
 github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
 github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
 github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
-github.com/go-openapi/analysis v0.19.10 h1:5BHISBAXOc/aJK25irLZnx2D3s6WyYaY9D4gmuz9fdE=
 github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ=
 github.com/go-openapi/analysis v0.19.16 h1:Ub9e++M8sDwtHD+S587TYi+6ANBG1NRYGZDihqk0SaY=
 github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk=
@@ -346,7 +343,6 @@ github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQH
 github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
 github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
-github.com/go-openapi/errors v0.19.6 h1:xZMThgv5SQ7SMbWtKFkCf9bBdvR2iEyw9k3zGZONuys=
 github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
 github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
 github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
@@ -357,7 +353,6 @@ github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12f
 github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
-github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
 github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
 github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
 github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -372,7 +367,6 @@ github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf
 github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
 github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI=
-github.com/go-openapi/loads v0.19.5 h1:jZVYWawIQiA1NBnHla28ktg6hrcfTHsCE+3QLVRBIls=
 github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY=
 github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc=
 github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc=
@@ -391,7 +385,6 @@ github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsd
 github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
 github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
 github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
-github.com/go-openapi/spec v0.19.8 h1:qAdZLh1r6QF/hI/gTq+TJTvsQUodZsM7KLqkAJdiJNg=
 github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
 github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
 github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
@@ -403,7 +396,6 @@ github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+Z
 github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
 github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
 github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
-github.com/go-openapi/strfmt v0.19.5 h1:0utjKrw+BAh8s57XE9Xz8DUBsVvPmRUB6styvl9wWIM=
 github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
 github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
 github.com/go-openapi/strfmt v0.20.0 h1:l2omNtmNbMc39IGptl9BuXBEKcZfS8zjrTsPKTiJiDM=
@@ -413,7 +405,6 @@ github.com/go-openapi/swag v0.18.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/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
-github.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE=
 github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
 github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
 github.com/go-openapi/swag v0.19.13 h1:233UVgMy1DlmCYYfOiFpta6e2urloh+sEs5id6lyzog=
@@ -421,7 +412,6 @@ github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/
 github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
 github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
 github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
-github.com/go-openapi/validate v0.19.10 h1:tG3SZ5DC5KF4cyt7nqLVcQXGj5A7mpaYkAcNPlDK+Yk=
 github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8=
 github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4=
 github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI=
@@ -429,9 +419,10 @@ github.com/go-openapi/validate v0.20.1 h1:QGQ5CvK74E28t3DkegGweKR+auemUi5IdpMc4x
 github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0=
 github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4=
 github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
-github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
-github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
+github.com/go-redis/redis/v8 v8.4.0 h1:J5NCReIgh3QgUJu398hUncxDExN4gMOHI11NVbVicGQ=
 github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M=
+github.com/go-redis/redis/v8 v8.5.0 h1:L3r1Q3I5WOUdXZGCP6g44EruKh0u3n6co5Hl5xWkdGA=
+github.com/go-redis/redis/v8 v8.5.0/go.mod h1:YmEcgBDttjnkbMzDAhDtQxY9yVA7jMN6PCR5HeMvqFE=
 github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
@@ -476,8 +467,6 @@ github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx
 github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 h1:gBeyun7mySAKWg7Fb0GOcv0upX9bdaZScs8QcRo8mEY=
 github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
@@ -678,7 +667,6 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
 github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
-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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -757,7 +745,6 @@ github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbat
 github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0=
 github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
 github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
@@ -812,7 +799,6 @@ github.com/mholt/archiver/v3 v3.5.0/go.mod h1:qqTTPUK/HZPFgFQ/TJ3BzvTpF/dPtFVJXd
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
 github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo=
 github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
-github.com/minio/md5-simd v1.1.0 h1:QPfiOqlZH+Cj9teu0t9b1nTBfPbyTl16Of5MeuShdK4=
 github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
 github.com/minio/md5-simd v1.1.1 h1:9ojcLbuZ4gXbB2sX53MKn8JUZ0sB/2wfwsEcRw+I08U=
 github.com/minio/md5-simd v1.1.1/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
@@ -830,7 +816,6 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
 github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
 github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@@ -883,17 +868,19 @@ github.com/olivere/elastic/v7 v7.0.22/go.mod h1:VDexNy9NjmtAkrjNoI7tImv7FR4tf5zU
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
 github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4=
+github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
 github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
+github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ=
+github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
 github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
 github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
@@ -970,7 +957,6 @@ github.com/quasoft/websspi v1.0.0 h1:5nDgdM5xSur9s+B5w2xQ5kxf5nUGqgFgU4W0aDLZ8Mw
 github.com/quasoft/websspi v1.0.0/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -1003,7 +989,6 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
 github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 h1:pXY9qYc/MP5zdvqWEUH6SjNiu7VhSjuVFTFiTcphaLU=
 github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
 github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
-github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d h1:qQWKKOvHN7Q9c6GdmUteCef2F9ubxMpxY1IKwpIKz68=
 github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY=
 github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
 github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
@@ -1118,7 +1103,6 @@ github.com/yuin/goldmark v1.1.22/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.1 h1:eVwehsLsZlCJCwXyGLgg+Q4iFWE/eTIMG0e8waCmm/I=
 github.com/yuin/goldmark v1.3.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
@@ -1152,7 +1136,10 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
+go.opentelemetry.io/otel v0.14.0 h1:YFBEfjCk9MTjaytCNSUkp9Q8lF7QJezA06T71FbQxLQ=
 go.opentelemetry.io/otel v0.14.0/go.mod h1:vH5xEuwy7Rts0GNtsCW3HYQoZDY+OmBJ6t1bFGGlxgw=
+go.opentelemetry.io/otel v0.16.0 h1:uIWEbdeb4vpKPGITLsRVUS44L5oDbDUCZxn8lkxhmgw=
+go.opentelemetry.io/otel v0.16.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@@ -1278,7 +1265,6 @@ golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwY
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
 golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
 golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -1288,9 +1274,7 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc=
 golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58 h1:Mj83v+wSRNEar42a/MQgxk9X42TdEmrOl9i+y8WbxLo=
 golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210126194326-f9ce19ea3013 h1:55H5j7lotzuFCEOKDsMch+fRNUQ9DgtyHOUP31FNqKc=
 golang.org/x/oauth2 v0.0.0-20210126194326-f9ce19ea3013/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
@@ -1369,11 +1353,10 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
+golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -1382,7 +1365,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -1454,10 +1436,9 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
 golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
 golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
 golang.org/x/tools v0.0.0-20200929161345-d7fc70abf50f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
-golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9 h1:sEvmEcJVKBNUvgCUClbUQeHOAa9U0I2Ce1BooMvVCY4=
 golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b h1:Lq5JUTFhiybGVf28jB6QRpqd13/JPOaCnET17PVzYJE=
 golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1594,7 +1575,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
diff --git a/modules/cache/cache_redis.go b/modules/cache/cache_redis.go
index 3cb0292e21..148725ae66 100644
--- a/modules/cache/cache_redis.go
+++ b/modules/cache/cache_redis.go
@@ -8,10 +8,11 @@ import (
 	"fmt"
 	"time"
 
+	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/nosql"
 
 	"gitea.com/go-chi/cache"
-	"github.com/go-redis/redis/v7"
+	"github.com/go-redis/redis/v8"
 	"github.com/unknwon/com"
 )
 
@@ -28,7 +29,7 @@ type RedisCacher struct {
 func (c *RedisCacher) Put(key string, val interface{}, expire int64) error {
 	key = c.prefix + key
 	if expire == 0 {
-		if err := c.c.Set(key, com.ToStr(val), 0).Err(); err != nil {
+		if err := c.c.Set(graceful.GetManager().HammerContext(), key, com.ToStr(val), 0).Err(); err != nil {
 			return err
 		}
 	} else {
@@ -36,7 +37,7 @@ func (c *RedisCacher) Put(key string, val interface{}, expire int64) error {
 		if err != nil {
 			return err
 		}
-		if err = c.c.Set(key, com.ToStr(val), dur).Err(); err != nil {
+		if err = c.c.Set(graceful.GetManager().HammerContext(), key, com.ToStr(val), dur).Err(); err != nil {
 			return err
 		}
 	}
@@ -44,12 +45,12 @@ func (c *RedisCacher) Put(key string, val interface{}, expire int64) error {
 	if c.occupyMode {
 		return nil
 	}
-	return c.c.HSet(c.hsetName, key, "0").Err()
+	return c.c.HSet(graceful.GetManager().HammerContext(), c.hsetName, key, "0").Err()
 }
 
 // Get gets cached value by given key.
 func (c *RedisCacher) Get(key string) interface{} {
-	val, err := c.c.Get(c.prefix + key).Result()
+	val, err := c.c.Get(graceful.GetManager().HammerContext(), c.prefix+key).Result()
 	if err != nil {
 		return nil
 	}
@@ -59,14 +60,14 @@ func (c *RedisCacher) Get(key string) interface{} {
 // Delete deletes cached value by given key.
 func (c *RedisCacher) Delete(key string) error {
 	key = c.prefix + key
-	if err := c.c.Del(key).Err(); err != nil {
+	if err := c.c.Del(graceful.GetManager().HammerContext(), key).Err(); err != nil {
 		return err
 	}
 
 	if c.occupyMode {
 		return nil
 	}
-	return c.c.HDel(c.hsetName, key).Err()
+	return c.c.HDel(graceful.GetManager().HammerContext(), c.hsetName, key).Err()
 }
 
 // Incr increases cached int-type value by given key as a counter.
@@ -74,7 +75,7 @@ func (c *RedisCacher) Incr(key string) error {
 	if !c.IsExist(key) {
 		return fmt.Errorf("key '%s' not exist", key)
 	}
-	return c.c.Incr(c.prefix + key).Err()
+	return c.c.Incr(graceful.GetManager().HammerContext(), c.prefix+key).Err()
 }
 
 // Decr decreases cached int-type value by given key as a counter.
@@ -82,17 +83,17 @@ func (c *RedisCacher) Decr(key string) error {
 	if !c.IsExist(key) {
 		return fmt.Errorf("key '%s' not exist", key)
 	}
-	return c.c.Decr(c.prefix + key).Err()
+	return c.c.Decr(graceful.GetManager().HammerContext(), c.prefix+key).Err()
 }
 
 // IsExist returns true if cached value exists.
 func (c *RedisCacher) IsExist(key string) bool {
-	if c.c.Exists(c.prefix+key).Val() == 1 {
+	if c.c.Exists(graceful.GetManager().HammerContext(), c.prefix+key).Val() == 1 {
 		return true
 	}
 
 	if !c.occupyMode {
-		c.c.HDel(c.hsetName, c.prefix+key)
+		c.c.HDel(graceful.GetManager().HammerContext(), c.hsetName, c.prefix+key)
 	}
 	return false
 }
@@ -100,17 +101,17 @@ func (c *RedisCacher) IsExist(key string) bool {
 // Flush deletes all cached data.
 func (c *RedisCacher) Flush() error {
 	if c.occupyMode {
-		return c.c.FlushDB().Err()
+		return c.c.FlushDB(graceful.GetManager().HammerContext()).Err()
 	}
 
-	keys, err := c.c.HKeys(c.hsetName).Result()
+	keys, err := c.c.HKeys(graceful.GetManager().HammerContext(), c.hsetName).Result()
 	if err != nil {
 		return err
 	}
-	if err = c.c.Del(keys...).Err(); err != nil {
+	if err = c.c.Del(graceful.GetManager().HammerContext(), keys...).Err(); err != nil {
 		return err
 	}
-	return c.c.Del(c.hsetName).Err()
+	return c.c.Del(graceful.GetManager().HammerContext(), c.hsetName).Err()
 }
 
 // StartAndGC starts GC routine based on config string settings.
@@ -132,7 +133,7 @@ func (c *RedisCacher) StartAndGC(opts cache.Options) error {
 		}
 	}
 
-	return c.c.Ping().Err()
+	return c.c.Ping(graceful.GetManager().HammerContext()).Err()
 }
 
 func init() {
diff --git a/modules/nosql/manager.go b/modules/nosql/manager.go
index ad61d6d18c..a89b5bb633 100644
--- a/modules/nosql/manager.go
+++ b/modules/nosql/manager.go
@@ -9,7 +9,7 @@ import (
 	"sync"
 	"time"
 
-	"github.com/go-redis/redis/v7"
+	"github.com/go-redis/redis/v8"
 	"github.com/syndtr/goleveldb/leveldb"
 )
 
diff --git a/modules/nosql/manager_redis.go b/modules/nosql/manager_redis.go
index 7792a90112..d754a0e07d 100644
--- a/modules/nosql/manager_redis.go
+++ b/modules/nosql/manager_redis.go
@@ -10,7 +10,7 @@ import (
 	"strconv"
 	"strings"
 
-	"github.com/go-redis/redis/v7"
+	"github.com/go-redis/redis/v8"
 )
 
 var replacer = strings.NewReplacer("_", "", "-", "")
diff --git a/modules/queue/queue_bytefifo.go b/modules/queue/queue_bytefifo.go
index cad258bda8..4bb7bbf2b1 100644
--- a/modules/queue/queue_bytefifo.go
+++ b/modules/queue/queue_bytefifo.go
@@ -163,6 +163,11 @@ func (q *ByteFIFOQueue) Shutdown() {
 	log.Debug("%s: %s Shutdown", q.typ, q.name)
 }
 
+// IsShutdown returns a channel which is closed when this Queue is shutdown
+func (q *ByteFIFOQueue) IsShutdown() <-chan struct{} {
+	return q.closed
+}
+
 // Terminate this queue and close the queue
 func (q *ByteFIFOQueue) Terminate() {
 	log.Trace("%s: %s Terminating", q.typ, q.name)
@@ -185,6 +190,11 @@ func (q *ByteFIFOQueue) Terminate() {
 	log.Debug("%s: %s Terminated", q.typ, q.name)
 }
 
+// IsTerminated returns a channel which is closed when this Queue is terminated
+func (q *ByteFIFOQueue) IsTerminated() <-chan struct{} {
+	return q.terminated
+}
+
 var _ (UniqueQueue) = &ByteFIFOUniqueQueue{}
 
 // ByteFIFOUniqueQueue represents a UniqueQueue formed from a UniqueByteFifo
diff --git a/modules/queue/queue_redis.go b/modules/queue/queue_redis.go
index 04e7b5d252..2b1d36f0ad 100644
--- a/modules/queue/queue_redis.go
+++ b/modules/queue/queue_redis.go
@@ -5,10 +5,14 @@
 package queue
 
 import (
+	"context"
+	"fmt"
+
+	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/nosql"
 
-	"github.com/go-redis/redis/v7"
+	"github.com/go-redis/redis/v8"
 )
 
 // RedisQueueType is the type for redis queue
@@ -43,6 +47,8 @@ func NewRedisQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue, error)
 		return nil, err
 	}
 
+	byteFIFO.ctx = graceful.NewChannelContext(byteFIFOQueue.IsTerminated(), fmt.Errorf("queue has been terminated"))
+
 	queue := &RedisQueue{
 		ByteFIFOQueue: byteFIFOQueue,
 	}
@@ -53,13 +59,13 @@ func NewRedisQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue, error)
 }
 
 type redisClient interface {
-	RPush(key string, args ...interface{}) *redis.IntCmd
-	LPop(key string) *redis.StringCmd
-	LLen(key string) *redis.IntCmd
-	SAdd(key string, members ...interface{}) *redis.IntCmd
-	SRem(key string, members ...interface{}) *redis.IntCmd
-	SIsMember(key string, member interface{}) *redis.BoolCmd
-	Ping() *redis.StatusCmd
+	RPush(ctx context.Context, key string, args ...interface{}) *redis.IntCmd
+	LPop(ctx context.Context, key string) *redis.StringCmd
+	LLen(ctx context.Context, key string) *redis.IntCmd
+	SAdd(ctx context.Context, key string, members ...interface{}) *redis.IntCmd
+	SRem(ctx context.Context, key string, members ...interface{}) *redis.IntCmd
+	SIsMember(ctx context.Context, key string, member interface{}) *redis.BoolCmd
+	Ping(ctx context.Context) *redis.StatusCmd
 	Close() error
 }
 
@@ -67,6 +73,7 @@ var _ (ByteFIFO) = &RedisByteFIFO{}
 
 // RedisByteFIFO represents a ByteFIFO formed from a redisClient
 type RedisByteFIFO struct {
+	ctx       context.Context
 	client    redisClient
 	queueName string
 }
@@ -82,8 +89,9 @@ func NewRedisByteFIFO(config RedisByteFIFOConfiguration) (*RedisByteFIFO, error)
 	fifo := &RedisByteFIFO{
 		queueName: config.QueueName,
 	}
+	fifo.ctx = graceful.GetManager().TerminateContext()
 	fifo.client = nosql.GetManager().GetRedisClient(config.ConnectionString)
-	if err := fifo.client.Ping().Err(); err != nil {
+	if err := fifo.client.Ping(graceful.GetManager().ShutdownContext()).Err(); err != nil {
 		return nil, err
 	}
 	return fifo, nil
@@ -96,12 +104,12 @@ func (fifo *RedisByteFIFO) PushFunc(data []byte, fn func() error) error {
 			return err
 		}
 	}
-	return fifo.client.RPush(fifo.queueName, data).Err()
+	return fifo.client.RPush(fifo.ctx, fifo.queueName, data).Err()
 }
 
 // Pop pops data from the start of the fifo
 func (fifo *RedisByteFIFO) Pop() ([]byte, error) {
-	data, err := fifo.client.LPop(fifo.queueName).Bytes()
+	data, err := fifo.client.LPop(fifo.ctx, fifo.queueName).Bytes()
 	if err == nil || err == redis.Nil {
 		return data, nil
 	}
@@ -115,7 +123,7 @@ func (fifo *RedisByteFIFO) Close() error {
 
 // Len returns the length of the fifo
 func (fifo *RedisByteFIFO) Len() int64 {
-	val, err := fifo.client.LLen(fifo.queueName).Result()
+	val, err := fifo.client.LLen(fifo.ctx, fifo.queueName).Result()
 	if err != nil {
 		log.Error("Error whilst getting length of redis queue %s: Error: %v", fifo.queueName, err)
 		return -1
diff --git a/modules/queue/unique_queue_redis.go b/modules/queue/unique_queue_redis.go
index 67efc66bc9..96fcad1a83 100644
--- a/modules/queue/unique_queue_redis.go
+++ b/modules/queue/unique_queue_redis.go
@@ -4,7 +4,12 @@
 
 package queue
 
-import "github.com/go-redis/redis/v7"
+import (
+	"fmt"
+
+	"code.gitea.io/gitea/modules/graceful"
+	"github.com/go-redis/redis/v8"
+)
 
 // RedisUniqueQueueType is the type for redis queue
 const RedisUniqueQueueType Type = "unique-redis"
@@ -46,6 +51,8 @@ func NewRedisUniqueQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue,
 		return nil, err
 	}
 
+	byteFIFO.ctx = graceful.NewChannelContext(byteFIFOQueue.IsTerminated(), fmt.Errorf("queue has been terminated"))
+
 	queue := &RedisUniqueQueue{
 		ByteFIFOUniqueQueue: byteFIFOQueue,
 	}
@@ -86,7 +93,7 @@ func NewRedisUniqueByteFIFO(config RedisUniqueByteFIFOConfiguration) (*RedisUniq
 
 // PushFunc pushes data to the end of the fifo and calls the callback if it is added
 func (fifo *RedisUniqueByteFIFO) PushFunc(data []byte, fn func() error) error {
-	added, err := fifo.client.SAdd(fifo.setName, data).Result()
+	added, err := fifo.client.SAdd(fifo.ctx, fifo.setName, data).Result()
 	if err != nil {
 		return err
 	}
@@ -98,12 +105,12 @@ func (fifo *RedisUniqueByteFIFO) PushFunc(data []byte, fn func() error) error {
 			return err
 		}
 	}
-	return fifo.client.RPush(fifo.queueName, data).Err()
+	return fifo.client.RPush(fifo.ctx, fifo.queueName, data).Err()
 }
 
 // Pop pops data from the start of the fifo
 func (fifo *RedisUniqueByteFIFO) Pop() ([]byte, error) {
-	data, err := fifo.client.LPop(fifo.queueName).Bytes()
+	data, err := fifo.client.LPop(fifo.ctx, fifo.queueName).Bytes()
 	if err != nil && err != redis.Nil {
 		return data, err
 	}
@@ -112,13 +119,13 @@ func (fifo *RedisUniqueByteFIFO) Pop() ([]byte, error) {
 		return data, nil
 	}
 
-	err = fifo.client.SRem(fifo.setName, data).Err()
+	err = fifo.client.SRem(fifo.ctx, fifo.setName, data).Err()
 	return data, err
 }
 
 // Has returns whether the fifo contains this data
 func (fifo *RedisUniqueByteFIFO) Has(data []byte) (bool, error) {
-	return fifo.client.SIsMember(fifo.setName, data).Result()
+	return fifo.client.SIsMember(fifo.ctx, fifo.setName, data).Result()
 }
 
 func init() {
diff --git a/modules/session/redis.go b/modules/session/redis.go
index 55e7a85168..334418bd7e 100644
--- a/modules/session/redis.go
+++ b/modules/session/redis.go
@@ -21,10 +21,11 @@ import (
 	"sync"
 	"time"
 
+	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/nosql"
 
 	"gitea.com/go-chi/session"
-	"github.com/go-redis/redis/v7"
+	"github.com/go-redis/redis/v8"
 )
 
 // RedisStore represents a redis session store implementation.
@@ -90,7 +91,7 @@ func (s *RedisStore) Release() error {
 		return err
 	}
 
-	return s.c.Set(s.prefix+s.sid, string(data), s.duration).Err()
+	return s.c.Set(graceful.GetManager().HammerContext(), s.prefix+s.sid, string(data), s.duration).Err()
 }
 
 // Flush deletes all session data.
@@ -127,20 +128,20 @@ func (p *RedisProvider) Init(maxlifetime int64, configs string) (err error) {
 	}
 
 	p.c = nosql.GetManager().GetRedisClient(uri.String())
-	return p.c.Ping().Err()
+	return p.c.Ping(graceful.GetManager().ShutdownContext()).Err()
 }
 
 // Read returns raw session store by session ID.
 func (p *RedisProvider) Read(sid string) (session.RawStore, error) {
 	psid := p.prefix + sid
 	if !p.Exist(sid) {
-		if err := p.c.Set(psid, "", p.duration).Err(); err != nil {
+		if err := p.c.Set(graceful.GetManager().HammerContext(), psid, "", p.duration).Err(); err != nil {
 			return nil, err
 		}
 	}
 
 	var kv map[interface{}]interface{}
-	kvs, err := p.c.Get(psid).Result()
+	kvs, err := p.c.Get(graceful.GetManager().HammerContext(), psid).Result()
 	if err != nil {
 		return nil, err
 	}
@@ -158,13 +159,13 @@ func (p *RedisProvider) Read(sid string) (session.RawStore, error) {
 
 // Exist returns true if session with given ID exists.
 func (p *RedisProvider) Exist(sid string) bool {
-	v, err := p.c.Exists(p.prefix + sid).Result()
+	v, err := p.c.Exists(graceful.GetManager().HammerContext(), p.prefix+sid).Result()
 	return err == nil && v == 1
 }
 
 // Destroy deletes a session by session ID.
 func (p *RedisProvider) Destroy(sid string) error {
-	return p.c.Del(p.prefix + sid).Err()
+	return p.c.Del(graceful.GetManager().HammerContext(), p.prefix+sid).Err()
 }
 
 // Regenerate regenerates a session store from old session ID to new one.
@@ -176,17 +177,17 @@ func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err
 		return nil, fmt.Errorf("new sid '%s' already exists", sid)
 	} else if !p.Exist(oldsid) {
 		// Make a fake old session.
-		if err = p.c.Set(poldsid, "", p.duration).Err(); err != nil {
+		if err = p.c.Set(graceful.GetManager().HammerContext(), poldsid, "", p.duration).Err(); err != nil {
 			return nil, err
 		}
 	}
 
-	if err = p.c.Rename(poldsid, psid).Err(); err != nil {
+	if err = p.c.Rename(graceful.GetManager().HammerContext(), poldsid, psid).Err(); err != nil {
 		return nil, err
 	}
 
 	var kv map[interface{}]interface{}
-	kvs, err := p.c.Get(psid).Result()
+	kvs, err := p.c.Get(graceful.GetManager().HammerContext(), psid).Result()
 	if err != nil {
 		return nil, err
 	}
@@ -205,7 +206,11 @@ func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err
 
 // Count counts and returns number of sessions.
 func (p *RedisProvider) Count() int {
-	return int(p.c.DBSize().Val())
+	size, err := p.c.DBSize(graceful.GetManager().HammerContext()).Result()
+	if err != nil {
+		return 0
+	}
+	return int(size)
 }
 
 // GC calls GC to clean expired sessions.
diff --git a/vendor/github.com/dgryski/go-rendezvous/LICENSE b/vendor/github.com/dgryski/go-rendezvous/LICENSE
new file mode 100644
index 0000000000..22080f736a
--- /dev/null
+++ b/vendor/github.com/dgryski/go-rendezvous/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017-2020 Damian Gryski <damian@gryski.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/dgryski/go-rendezvous/rdv.go b/vendor/github.com/dgryski/go-rendezvous/rdv.go
new file mode 100644
index 0000000000..7a6f8203c6
--- /dev/null
+++ b/vendor/github.com/dgryski/go-rendezvous/rdv.go
@@ -0,0 +1,79 @@
+package rendezvous
+
+type Rendezvous struct {
+	nodes map[string]int
+	nstr  []string
+	nhash []uint64
+	hash  Hasher
+}
+
+type Hasher func(s string) uint64
+
+func New(nodes []string, hash Hasher) *Rendezvous {
+	r := &Rendezvous{
+		nodes: make(map[string]int, len(nodes)),
+		nstr:  make([]string, len(nodes)),
+		nhash: make([]uint64, len(nodes)),
+		hash:  hash,
+	}
+
+	for i, n := range nodes {
+		r.nodes[n] = i
+		r.nstr[i] = n
+		r.nhash[i] = hash(n)
+	}
+
+	return r
+}
+
+func (r *Rendezvous) Lookup(k string) string {
+	// short-circuit if we're empty
+	if len(r.nodes) == 0 {
+		return ""
+	}
+
+	khash := r.hash(k)
+
+	var midx int
+	var mhash = xorshiftMult64(khash ^ r.nhash[0])
+
+	for i, nhash := range r.nhash[1:] {
+		if h := xorshiftMult64(khash ^ nhash); h > mhash {
+			midx = i + 1
+			mhash = h
+		}
+	}
+
+	return r.nstr[midx]
+}
+
+func (r *Rendezvous) Add(node string) {
+	r.nodes[node] = len(r.nstr)
+	r.nstr = append(r.nstr, node)
+	r.nhash = append(r.nhash, r.hash(node))
+}
+
+func (r *Rendezvous) Remove(node string) {
+	// find index of node to remove
+	nidx := r.nodes[node]
+
+	// remove from the slices
+	l := len(r.nstr)
+	r.nstr[nidx] = r.nstr[l]
+	r.nstr = r.nstr[:l]
+
+	r.nhash[nidx] = r.nhash[l]
+	r.nhash = r.nhash[:l]
+
+	// update the map
+	delete(r.nodes, node)
+	moved := r.nstr[nidx]
+	r.nodes[moved] = nidx
+}
+
+func xorshiftMult64(x uint64) uint64 {
+	x ^= x >> 12 // a
+	x ^= x << 25 // b
+	x ^= x >> 27 // c
+	return x * 2685821657736338717
+}
diff --git a/vendor/github.com/go-redis/redis/v7/.travis.yml b/vendor/github.com/go-redis/redis/v7/.travis.yml
deleted file mode 100644
index 3f93932bc8..0000000000
--- a/vendor/github.com/go-redis/redis/v7/.travis.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-dist: xenial
-language: go
-
-services:
-  - redis-server
-
-go:
-  - 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
deleted file mode 100644
index bd4eccff24..0000000000
--- a/vendor/github.com/go-redis/redis/v7/CHANGELOG.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Changelog
-
-## v7.2
-
-- Existing `HMSet` is renamed to `HSet` and old deprecated `HMSet` is restored for Redis 3 users.
-
-## v7.1
-
-- Existing `Cmd.String` is renamed to `Cmd.Text`. New `Cmd.String` implements `fmt.Stringer` interface.
-
-## 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/v7/README.md b/vendor/github.com/go-redis/redis/v7/README.md
deleted file mode 100644
index 0fbb506ead..0000000000
--- a/vendor/github.com/go-redis/redis/v7/README.md
+++ /dev/null
@@ -1,128 +0,0 @@
-# Redis client for Golang
-
-[![Build Status](https://travis-ci.org/go-redis/redis.png?branch=master)](https://travis-ci.org/go-redis/redis)
-[![GoDoc](https://godoc.org/github.com/go-redis/redis?status.svg)](https://godoc.org/github.com/go-redis/redis)
-[![Airbrake](https://img.shields.io/badge/kudos-airbrake.io-orange.svg)](https://airbrake.io)
-
-Supports:
-
-- Redis 3 commands except QUIT, MONITOR, SLOWLOG and SYNC.
-- Automatic connection pooling with [circuit breaker](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) support.
-- [Pub/Sub](https://godoc.org/github.com/go-redis/redis#PubSub).
-- [Transactions](https://godoc.org/github.com/go-redis/redis#example-Client-TxPipeline).
-- [Pipeline](https://godoc.org/github.com/go-redis/redis#example-Client-Pipeline) and [TxPipeline](https://godoc.org/github.com/go-redis/redis#example-Client-TxPipeline).
-- [Scripting](https://godoc.org/github.com/go-redis/redis#Script).
-- [Timeouts](https://godoc.org/github.com/go-redis/redis#Options).
-- [Redis Sentinel](https://godoc.org/github.com/go-redis/redis#NewFailoverClient).
-- [Redis Cluster](https://godoc.org/github.com/go-redis/redis#NewClusterClient).
-- [Cluster of Redis Servers](https://godoc.org/github.com/go-redis/redis#example-NewClusterClient--ManualSetup) without using cluster mode and Redis Sentinel.
-- [Ring](https://godoc.org/github.com/go-redis/redis#NewRing).
-- [Instrumentation](https://godoc.org/github.com/go-redis/redis#ex-package--Instrumentation).
-- [Cache friendly](https://github.com/go-redis/cache).
-- [Rate limiting](https://github.com/go-redis/redis_rate).
-- [Distributed Locks](https://github.com/bsm/redislock).
-
-API docs: https://godoc.org/github.com/go-redis/redis.
-Examples: https://godoc.org/github.com/go-redis/redis#pkg-examples.
-
-## Installation
-
-go-redis requires a Go version with [Modules](https://github.com/golang/go/wiki/Modules) support and uses import versioning. So please make sure to initialize a Go module before installing go-redis:
-
-``` shell
-go mod init github.com/my/repo
-go get github.com/go-redis/redis/v7
-```
-
-Import:
-
-``` go
-import "github.com/go-redis/redis/v7"
-```
-
-## Quickstart
-
-``` go
-func ExampleNewClient() {
-	client := redis.NewClient(&redis.Options{
-		Addr:     "localhost:6379",
-		Password: "", // no password set
-		DB:       0,  // use default DB
-	})
-
-	pong, err := client.Ping().Result()
-	fmt.Println(pong, err)
-	// Output: PONG <nil>
-}
-
-func ExampleClient() {
-	client := redis.NewClient(&redis.Options{
-		Addr:     "localhost:6379",
-		Password: "", // no password set
-		DB:       0,  // use default DB
-	})
-	err := client.Set("key", "value", 0).Err()
-	if err != nil {
-		panic(err)
-	}
-
-	val, err := client.Get("key").Result()
-	if err != nil {
-		panic(err)
-	}
-	fmt.Println("key", val)
-
-	val2, err := client.Get("key2").Result()
-	if err == redis.Nil {
-		fmt.Println("key2 does not exist")
-	} else if err != nil {
-		panic(err)
-	} else {
-		fmt.Println("key2", val2)
-	}
-	// Output: key value
-	// key2 does not exist
-}
-```
-
-## Howto
-
-Please go through [examples](https://godoc.org/github.com/go-redis/redis#pkg-examples) to get an idea how to use this package.
-
-## Look and feel
-
-Some corner cases:
-
-``` go
-// SET key value EX 10 NX
-set, err := client.SetNX("key", "value", 10*time.Second).Result()
-
-// SORT list LIMIT 0 2 ASC
-vals, err := client.Sort("list", &redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result()
-
-// ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2
-vals, err := client.ZRangeByScoreWithScores("zset", &redis.ZRangeBy{
-	Min: "-inf",
-	Max: "+inf",
-	Offset: 0,
-	Count: 2,
-}).Result()
-
-// ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM
-vals, err := client.ZInterStore("out", &redis.ZStore{
-	Keys: []string{"zset1", "zset2"},
-	Weights: []int64{2, 3}
-}).Result()
-
-// EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello"
-vals, err := client.Eval("return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result()
-
-// custom command
-res, err := client.Do("set", "key", "value").Result()
-```
-
-## See also
-
-- [Golang PostgreSQL ORM](https://github.com/go-pg/pg)
-- [Golang msgpack](https://github.com/vmihailenco/msgpack)
-- [Golang message task queue](https://github.com/vmihailenco/taskq)
diff --git a/vendor/github.com/go-redis/redis/v7/cluster_commands.go b/vendor/github.com/go-redis/redis/v7/cluster_commands.go
deleted file mode 100644
index c9b9b9de24..0000000000
--- a/vendor/github.com/go-redis/redis/v7/cluster_commands.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package redis
-
-import "sync/atomic"
-
-func (c *ClusterClient) DBSize() *IntCmd {
-	cmd := NewIntCmd("dbsize")
-	var size int64
-	err := c.ForEachMaster(func(master *Client) error {
-		n, err := master.DBSize().Result()
-		if err != nil {
-			return err
-		}
-		atomic.AddInt64(&size, n)
-		return nil
-	})
-	if err != nil {
-		cmd.SetErr(err)
-		return cmd
-	}
-	cmd.val = size
-	return cmd
-}
diff --git a/vendor/github.com/go-redis/redis/v7/commands.go b/vendor/github.com/go-redis/redis/v7/commands.go
deleted file mode 100644
index da5ceda13e..0000000000
--- a/vendor/github.com/go-redis/redis/v7/commands.go
+++ /dev/null
@@ -1,2643 +0,0 @@
-package redis
-
-import (
-	"errors"
-	"io"
-	"time"
-
-	"github.com/go-redis/redis/v7/internal"
-)
-
-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.Logger.Printf(
-			"specified duration is %s, but minimal supported value is %s",
-			dur, time.Millisecond,
-		)
-	}
-	return int64(dur / time.Millisecond)
-}
-
-func formatSec(dur time.Duration) int64 {
-	if dur > 0 && dur < time.Second {
-		internal.Logger.Printf(
-			"specified duration is %s, but minimal supported value is %s",
-			dur, time.Second,
-		)
-	}
-	return int64(dur / time.Second)
-}
-
-func appendArgs(dst, src []interface{}) []interface{} {
-	if len(src) == 1 {
-		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
-		}
-	}
-
-	dst = append(dst, src...)
-	return dst
-}
-
-type Cmdable interface {
-	Pipeline() Pipeliner
-	Pipelined(fn func(Pipeliner) error) ([]Cmder, error)
-
-	TxPipelined(fn func(Pipeliner) error) ([]Cmder, error)
-	TxPipeline() Pipeliner
-
-	Command() *CommandsInfoCmd
-	ClientGetName() *StringCmd
-	Echo(message interface{}) *StringCmd
-	Ping() *StatusCmd
-	Quit() *StatusCmd
-	Del(keys ...string) *IntCmd
-	Unlink(keys ...string) *IntCmd
-	Dump(key string) *StringCmd
-	Exists(keys ...string) *IntCmd
-	Expire(key string, expiration time.Duration) *BoolCmd
-	ExpireAt(key string, tm time.Time) *BoolCmd
-	Keys(pattern string) *StringSliceCmd
-	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
-	Persist(key string) *BoolCmd
-	PExpire(key string, expiration time.Duration) *BoolCmd
-	PExpireAt(key string, tm time.Time) *BoolCmd
-	PTTL(key string) *DurationCmd
-	RandomKey() *StringCmd
-	Rename(key, newkey string) *StatusCmd
-	RenameNX(key, newkey string) *BoolCmd
-	Restore(key string, ttl time.Duration, value string) *StatusCmd
-	RestoreReplace(key string, ttl time.Duration, value string) *StatusCmd
-	Sort(key string, sort *Sort) *StringSliceCmd
-	SortStore(key, store string, sort *Sort) *IntCmd
-	SortInterfaces(key string, sort *Sort) *SliceCmd
-	Touch(keys ...string) *IntCmd
-	TTL(key string) *DurationCmd
-	Type(key string) *StatusCmd
-	Scan(cursor uint64, match string, count int64) *ScanCmd
-	SScan(key string, cursor uint64, match string, count int64) *ScanCmd
-	HScan(key string, cursor uint64, match string, count int64) *ScanCmd
-	ZScan(key string, cursor uint64, match string, count int64) *ScanCmd
-	Append(key, value string) *IntCmd
-	BitCount(key string, bitCount *BitCount) *IntCmd
-	BitOpAnd(destKey string, keys ...string) *IntCmd
-	BitOpOr(destKey string, keys ...string) *IntCmd
-	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
-	GetBit(key string, offset int64) *IntCmd
-	GetRange(key string, start, end int64) *StringCmd
-	GetSet(key string, value interface{}) *StringCmd
-	Incr(key string) *IntCmd
-	IncrBy(key string, value int64) *IntCmd
-	IncrByFloat(key string, value float64) *FloatCmd
-	MGet(keys ...string) *SliceCmd
-	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
-	SetXX(key string, value interface{}, expiration time.Duration) *BoolCmd
-	SetRange(key string, offset int64, value string) *IntCmd
-	StrLen(key string) *IntCmd
-	HDel(key string, fields ...string) *IntCmd
-	HExists(key, field string) *BoolCmd
-	HGet(key, field string) *StringCmd
-	HGetAll(key string) *StringStringMapCmd
-	HIncrBy(key, field string, incr int64) *IntCmd
-	HIncrByFloat(key, field string, incr float64) *FloatCmd
-	HKeys(key string) *StringSliceCmd
-	HLen(key string) *IntCmd
-	HMGet(key string, fields ...string) *SliceCmd
-	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
-	BRPop(timeout time.Duration, keys ...string) *StringSliceCmd
-	BRPopLPush(source, destination string, timeout time.Duration) *StringCmd
-	LIndex(key string, index int64) *StringCmd
-	LInsert(key, op string, pivot, value interface{}) *IntCmd
-	LInsertBefore(key string, pivot, value interface{}) *IntCmd
-	LInsertAfter(key string, pivot, value interface{}) *IntCmd
-	LLen(key string) *IntCmd
-	LPop(key string) *StringCmd
-	LPush(key string, values ...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
-	LTrim(key string, start, stop int64) *StatusCmd
-	RPop(key string) *StringCmd
-	RPopLPush(source, destination string) *StringCmd
-	RPush(key string, values ...interface{}) *IntCmd
-	RPushX(key string, values ...interface{}) *IntCmd
-	SAdd(key string, members ...interface{}) *IntCmd
-	SCard(key string) *IntCmd
-	SDiff(keys ...string) *StringSliceCmd
-	SDiffStore(destination string, keys ...string) *IntCmd
-	SInter(keys ...string) *StringSliceCmd
-	SInterStore(destination string, keys ...string) *IntCmd
-	SIsMember(key string, member interface{}) *BoolCmd
-	SMembers(key string) *StringSliceCmd
-	SMembersMap(key string) *StringStructMapCmd
-	SMove(source, destination string, member interface{}) *BoolCmd
-	SPop(key string) *StringCmd
-	SPopN(key string, count int64) *StringSliceCmd
-	SRandMember(key string) *StringCmd
-	SRandMemberN(key string, count int64) *StringSliceCmd
-	SRem(key string, members ...interface{}) *IntCmd
-	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
-	XRevRange(stream string, start, stop string) *XMessageSliceCmd
-	XRevRangeN(stream string, start, stop string, count int64) *XMessageSliceCmd
-	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
-	XReadGroup(a *XReadGroupArgs) *XStreamSliceCmd
-	XAck(stream, group string, ids ...string) *IntCmd
-	XPending(stream, group string) *XPendingCmd
-	XPendingExt(a *XPendingExtArgs) *XPendingExtCmd
-	XClaim(a *XClaimArgs) *XMessageSliceCmd
-	XClaimJustID(a *XClaimArgs) *StringSliceCmd
-	XTrim(key string, maxLen int64) *IntCmd
-	XTrimApprox(key string, maxLen int64) *IntCmd
-	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) *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
-	ZRank(key, member string) *IntCmd
-	ZRem(key string, members ...interface{}) *IntCmd
-	ZRemRangeByRank(key string, start, stop int64) *IntCmd
-	ZRemRangeByScore(key, min, max string) *IntCmd
-	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
-	ZRevRank(key, member string) *IntCmd
-	ZScore(key, member string) *FloatCmd
-	ZUnionStore(dest string, store *ZStore) *IntCmd
-	PFAdd(key string, els ...interface{}) *IntCmd
-	PFCount(keys ...string) *IntCmd
-	PFMerge(dest string, keys ...string) *StatusCmd
-	BgRewriteAOF() *StatusCmd
-	BgSave() *StatusCmd
-	ClientKill(ipPort string) *StatusCmd
-	ClientKillByFilter(keys ...string) *IntCmd
-	ClientList() *StringCmd
-	ClientPause(dur time.Duration) *BoolCmd
-	ClientID() *IntCmd
-	ConfigGet(parameter string) *SliceCmd
-	ConfigResetStat() *StatusCmd
-	ConfigSet(parameter, value string) *StatusCmd
-	ConfigRewrite() *StatusCmd
-	DBSize() *IntCmd
-	FlushAll() *StatusCmd
-	FlushAllAsync() *StatusCmd
-	FlushDB() *StatusCmd
-	FlushDBAsync() *StatusCmd
-	Info(section ...string) *StringCmd
-	LastSave() *IntCmd
-	Save() *StatusCmd
-	Shutdown() *StatusCmd
-	ShutdownSave() *StatusCmd
-	ShutdownNoSave() *StatusCmd
-	SlaveOf(host, port string) *StatusCmd
-	Time() *TimeCmd
-	Eval(script string, keys []string, args ...interface{}) *Cmd
-	EvalSha(sha1 string, keys []string, args ...interface{}) *Cmd
-	ScriptExists(hashes ...string) *BoolSliceCmd
-	ScriptFlush() *StatusCmd
-	ScriptKill() *StatusCmd
-	ScriptLoad(script string) *StringCmd
-	DebugObject(key string) *StringCmd
-	Publish(channel string, message interface{}) *IntCmd
-	PubSubChannels(pattern string) *StringSliceCmd
-	PubSubNumSub(channels ...string) *StringIntMapCmd
-	PubSubNumPat() *IntCmd
-	ClusterSlots() *ClusterSlotsCmd
-	ClusterNodes() *StringCmd
-	ClusterMeet(host, port string) *StatusCmd
-	ClusterForget(nodeID string) *StatusCmd
-	ClusterReplicate(nodeID string) *StatusCmd
-	ClusterResetSoft() *StatusCmd
-	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
-	ClusterDelSlotsRange(min, max int) *StatusCmd
-	ClusterSaveConfig() *StatusCmd
-	ClusterSlaves(nodeID string) *StringSliceCmd
-	ClusterFailover() *StatusCmd
-	ClusterAddSlots(slots ...int) *StatusCmd
-	ClusterAddSlotsRange(min, max int) *StatusCmd
-	GeoAdd(key string, geoLocation ...*GeoLocation) *IntCmd
-	GeoPos(key string, members ...string) *GeoPosCmd
-	GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd
-	GeoRadiusStore(key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd
-	GeoRadiusByMember(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
-	ReadWrite() *StatusCmd
-	MemoryUsage(key string, samples ...int) *IntCmd
-}
-
-type StatefulCmdable interface {
-	Cmdable
-	Auth(password string) *StatusCmd
-	AuthACL(username, password string) *StatusCmd
-	Select(index int) *StatusCmd
-	SwapDB(index1, index2 int) *StatusCmd
-	ClientSetName(name string) *BoolCmd
-}
-
-var _ Cmdable = (*Client)(nil)
-var _ Cmdable = (*Tx)(nil)
-var _ Cmdable = (*Ring)(nil)
-var _ Cmdable = (*ClusterClient)(nil)
-
-type cmdable func(cmd Cmder) error
-
-type statefulCmdable func(cmd Cmder) error
-
-//------------------------------------------------------------------------------
-
-func (c statefulCmdable) Auth(password string) *StatusCmd {
-	cmd := NewStatusCmd("auth", password)
-	_ = c(cmd)
-	return cmd
-}
-
-// Perform an AUTH command, using the given user and pass.
-// Should be used to authenticate the current connection with one of the connections defined in the ACL list
-// when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
-func (c statefulCmdable) AuthACL(username, password string) *StatusCmd {
-	cmd := NewStatusCmd("auth", username, password)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Echo(message interface{}) *StringCmd {
-	cmd := NewStringCmd("echo", message)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Ping() *StatusCmd {
-	cmd := NewStatusCmd("ping")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Wait(numSlaves int, timeout time.Duration) *IntCmd {
-	cmd := NewIntCmd("wait", numSlaves, int(timeout/time.Millisecond))
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Quit() *StatusCmd {
-	panic("not implemented")
-}
-
-func (c statefulCmdable) Select(index int) *StatusCmd {
-	cmd := NewStatusCmd("select", index)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c statefulCmdable) SwapDB(index1, index2 int) *StatusCmd {
-	cmd := NewStatusCmd("swapdb", index1, index2)
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-func (c cmdable) Command() *CommandsInfoCmd {
-	cmd := NewCommandsInfoCmd("command")
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) Dump(key string) *StringCmd {
-	cmd := NewStringCmd("dump", key)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) Expire(key string, expiration time.Duration) *BoolCmd {
-	cmd := NewBoolCmd("expire", key, formatSec(expiration))
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ExpireAt(key string, tm time.Time) *BoolCmd {
-	cmd := NewBoolCmd("expireat", key, tm.Unix())
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Keys(pattern string) *StringSliceCmd {
-	cmd := NewStringSliceCmd("keys", pattern)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Migrate(host, port, key string, db int, timeout time.Duration) *StatusCmd {
-	cmd := NewStatusCmd(
-		"migrate",
-		host,
-		port,
-		key,
-		db,
-		formatMs(timeout),
-	)
-	cmd.setReadTimeout(timeout)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Move(key string, db int) *BoolCmd {
-	cmd := NewBoolCmd("move", key, db)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ObjectRefCount(key string) *IntCmd {
-	cmd := NewIntCmd("object", "refcount", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ObjectEncoding(key string) *StringCmd {
-	cmd := NewStringCmd("object", "encoding", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ObjectIdleTime(key string) *DurationCmd {
-	cmd := NewDurationCmd(time.Second, "object", "idletime", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Persist(key string) *BoolCmd {
-	cmd := NewBoolCmd("persist", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) PExpire(key string, expiration time.Duration) *BoolCmd {
-	cmd := NewBoolCmd("pexpire", key, formatMs(expiration))
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) PExpireAt(key string, tm time.Time) *BoolCmd {
-	cmd := NewBoolCmd(
-		"pexpireat",
-		key,
-		tm.UnixNano()/int64(time.Millisecond),
-	)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) PTTL(key string) *DurationCmd {
-	cmd := NewDurationCmd(time.Millisecond, "pttl", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) RandomKey() *StringCmd {
-	cmd := NewStringCmd("randomkey")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Rename(key, newkey string) *StatusCmd {
-	cmd := NewStatusCmd("rename", key, newkey)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) RenameNX(key, newkey string) *BoolCmd {
-	cmd := NewBoolCmd("renamenx", key, newkey)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Restore(key string, ttl time.Duration, value string) *StatusCmd {
-	cmd := NewStatusCmd(
-		"restore",
-		key,
-		formatMs(ttl),
-		value,
-	)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) RestoreReplace(key string, ttl time.Duration, value string) *StatusCmd {
-	cmd := NewStatusCmd(
-		"restore",
-		key,
-		formatMs(ttl),
-		value,
-		"replace",
-	)
-	_ = c(cmd)
-	return cmd
-}
-
-type Sort struct {
-	By            string
-	Offset, Count int64
-	Get           []string
-	Order         string
-	Alpha         bool
-}
-
-func (sort *Sort) args(key string) []interface{} {
-	args := []interface{}{"sort", key}
-	if sort.By != "" {
-		args = append(args, "by", sort.By)
-	}
-	if sort.Offset != 0 || sort.Count != 0 {
-		args = append(args, "limit", sort.Offset, sort.Count)
-	}
-	for _, get := range sort.Get {
-		args = append(args, "get", get)
-	}
-	if sort.Order != "" {
-		args = append(args, sort.Order)
-	}
-	if sort.Alpha {
-		args = append(args, "alpha")
-	}
-	return args
-}
-
-func (c cmdable) Sort(key string, sort *Sort) *StringSliceCmd {
-	cmd := NewStringSliceCmd(sort.args(key)...)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) SortInterfaces(key string, sort *Sort) *SliceCmd {
-	cmd := NewSliceCmd(sort.args(key)...)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) TTL(key string) *DurationCmd {
-	cmd := NewDurationCmd(time.Second, "ttl", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Type(key string) *StatusCmd {
-	cmd := NewStatusCmd("type", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Scan(cursor uint64, match string, count int64) *ScanCmd {
-	args := []interface{}{"scan", cursor}
-	if match != "" {
-		args = append(args, "match", match)
-	}
-	if count > 0 {
-		args = append(args, "count", count)
-	}
-	cmd := NewScanCmd(c, args...)
-	_ = c(cmd)
-	return cmd
-}
-
-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)
-	}
-	if count > 0 {
-		args = append(args, "count", count)
-	}
-	cmd := NewScanCmd(c, args...)
-	_ = c(cmd)
-	return cmd
-}
-
-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)
-	}
-	if count > 0 {
-		args = append(args, "count", count)
-	}
-	cmd := NewScanCmd(c, args...)
-	_ = c(cmd)
-	return cmd
-}
-
-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)
-	}
-	if count > 0 {
-		args = append(args, "count", count)
-	}
-	cmd := NewScanCmd(c, args...)
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-func (c cmdable) Append(key, value string) *IntCmd {
-	cmd := NewIntCmd("append", key, value)
-	_ = c(cmd)
-	return cmd
-}
-
-type BitCount struct {
-	Start, End int64
-}
-
-func (c cmdable) BitCount(key string, bitCount *BitCount) *IntCmd {
-	args := []interface{}{"bitcount", key}
-	if bitCount != nil {
-		args = append(
-			args,
-			bitCount.Start,
-			bitCount.End,
-		)
-	}
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) bitOp(op, destKey string, keys ...string) *IntCmd {
-	args := make([]interface{}, 3+len(keys))
-	args[0] = "bitop"
-	args[1] = op
-	args[2] = destKey
-	for i, key := range keys {
-		args[3+i] = key
-	}
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) BitOpAnd(destKey string, keys ...string) *IntCmd {
-	return c.bitOp("and", destKey, keys...)
-}
-
-func (c cmdable) BitOpOr(destKey string, keys ...string) *IntCmd {
-	return c.bitOp("or", destKey, keys...)
-}
-
-func (c cmdable) BitOpXor(destKey string, keys ...string) *IntCmd {
-	return c.bitOp("xor", destKey, keys...)
-}
-
-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 {
-	args := make([]interface{}, 3+len(pos))
-	args[0] = "bitpos"
-	args[1] = key
-	args[2] = bit
-	switch len(pos) {
-	case 0:
-	case 1:
-		args[3] = pos[0]
-	case 2:
-		args[3] = pos[0]
-		args[4] = pos[1]
-	default:
-		panic("too many arguments")
-	}
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) DecrBy(key string, decrement int64) *IntCmd {
-	cmd := NewIntCmd("decrby", key, decrement)
-	_ = 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 {
-	cmd := NewStringCmd("get", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) GetBit(key string, offset int64) *IntCmd {
-	cmd := NewIntCmd("getbit", key, offset)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) GetRange(key string, start, end int64) *StringCmd {
-	cmd := NewStringCmd("getrange", key, start, end)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) GetSet(key string, value interface{}) *StringCmd {
-	cmd := NewStringCmd("getset", key, value)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Incr(key string) *IntCmd {
-	cmd := NewIntCmd("incr", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) IncrBy(key string, value int64) *IntCmd {
-	cmd := NewIntCmd("incrby", key, value)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) IncrByFloat(key string, value float64) *FloatCmd {
-	cmd := NewFloatCmd("incrbyfloat", key, value)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-// 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, values)
-	cmd := NewStatusCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-// 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, values)
-	cmd := NewBoolCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-// Redis `SET key value [expiration]` command.
-//
-// 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, 5)
-	args[0] = "set"
-	args[1] = key
-	args[2] = value
-	if expiration > 0 {
-		if usePrecise(expiration) {
-			args = append(args, "px", formatMs(expiration))
-		} else {
-			args = append(args, "ex", formatSec(expiration))
-		}
-	}
-	cmd := NewStatusCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) SetBit(key string, offset int64, value int) *IntCmd {
-	cmd := NewIntCmd(
-		"setbit",
-		key,
-		offset,
-		value,
-	)
-	_ = 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 {
-	var cmd *BoolCmd
-	if expiration == 0 {
-		// Use old `SETNX` to support old Redis versions.
-		cmd = NewBoolCmd("setnx", key, value)
-	} else {
-		if usePrecise(expiration) {
-			cmd = NewBoolCmd("set", key, value, "px", formatMs(expiration), "nx")
-		} else {
-			cmd = NewBoolCmd("set", key, value, "ex", formatSec(expiration), "nx")
-		}
-	}
-	_ = 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 {
-	var cmd *BoolCmd
-	if expiration == 0 {
-		cmd = NewBoolCmd("set", key, value, "xx")
-	} else {
-		if usePrecise(expiration) {
-			cmd = NewBoolCmd("set", key, value, "px", formatMs(expiration), "xx")
-		} else {
-			cmd = NewBoolCmd("set", key, value, "ex", formatSec(expiration), "xx")
-		}
-	}
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) SetRange(key string, offset int64, value string) *IntCmd {
-	cmd := NewIntCmd("setrange", key, offset, value)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) StrLen(key string) *IntCmd {
-	cmd := NewIntCmd("strlen", key)
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-func (c cmdable) HDel(key string, fields ...string) *IntCmd {
-	args := make([]interface{}, 2+len(fields))
-	args[0] = "hdel"
-	args[1] = key
-	for i, field := range fields {
-		args[2+i] = field
-	}
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) HExists(key, field string) *BoolCmd {
-	cmd := NewBoolCmd("hexists", key, field)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) HGet(key, field string) *StringCmd {
-	cmd := NewStringCmd("hget", key, field)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) HGetAll(key string) *StringStringMapCmd {
-	cmd := NewStringStringMapCmd("hgetall", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) HIncrBy(key, field string, incr int64) *IntCmd {
-	cmd := NewIntCmd("hincrby", key, field, incr)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) HIncrByFloat(key, field string, incr float64) *FloatCmd {
-	cmd := NewFloatCmd("hincrbyfloat", key, field, incr)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) HKeys(key string) *StringSliceCmd {
-	cmd := NewStringSliceCmd("hkeys", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) HLen(key string) *IntCmd {
-	cmd := NewIntCmd("hlen", key)
-	_ = c(cmd)
-	return cmd
-}
-
-// 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
-	for i, field := range fields {
-		args[2+i] = field
-	}
-	cmd := NewSliceCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-// 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
-	args = appendArgs(args, values)
-	cmd := NewBoolCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) HSetNX(key, field string, value interface{}) *BoolCmd {
-	cmd := NewBoolCmd("hsetnx", key, field, value)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) HVals(key string) *StringSliceCmd {
-	cmd := NewStringSliceCmd("hvals", key)
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-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 {
-		args[1+i] = key
-	}
-	args[len(args)-1] = formatSec(timeout)
-	cmd := NewStringSliceCmd(args...)
-	cmd.setReadTimeout(timeout)
-	_ = c(cmd)
-	return cmd
-}
-
-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 {
-		args[1+i] = key
-	}
-	args[len(keys)+1] = formatSec(timeout)
-	cmd := NewStringSliceCmd(args...)
-	cmd.setReadTimeout(timeout)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) BRPopLPush(source, destination string, timeout time.Duration) *StringCmd {
-	cmd := NewStringCmd(
-		"brpoplpush",
-		source,
-		destination,
-		formatSec(timeout),
-	)
-	cmd.setReadTimeout(timeout)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) LIndex(key string, index int64) *StringCmd {
-	cmd := NewStringCmd("lindex", key, index)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) LInsert(key, op string, pivot, value interface{}) *IntCmd {
-	cmd := NewIntCmd("linsert", key, op, pivot, value)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) LInsertBefore(key string, pivot, value interface{}) *IntCmd {
-	cmd := NewIntCmd("linsert", key, "before", pivot, value)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) LInsertAfter(key string, pivot, value interface{}) *IntCmd {
-	cmd := NewIntCmd("linsert", key, "after", pivot, value)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) LLen(key string) *IntCmd {
-	cmd := NewIntCmd("llen", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) LPop(key string) *StringCmd {
-	cmd := NewStringCmd("lpop", key)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return 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 {
-	cmd := NewStringSliceCmd(
-		"lrange",
-		key,
-		start,
-		stop,
-	)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) LRem(key string, count int64, value interface{}) *IntCmd {
-	cmd := NewIntCmd("lrem", key, count, value)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) LSet(key string, index int64, value interface{}) *StatusCmd {
-	cmd := NewStatusCmd("lset", key, index, value)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) LTrim(key string, start, stop int64) *StatusCmd {
-	cmd := NewStatusCmd(
-		"ltrim",
-		key,
-		start,
-		stop,
-	)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) RPop(key string) *StringCmd {
-	cmd := NewStringCmd("rpop", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) RPopLPush(source, destination string) *StringCmd {
-	cmd := NewStringCmd("rpoplpush", source, destination)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return 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 {
-	args := make([]interface{}, 2, 2+len(members))
-	args[0] = "sadd"
-	args[1] = key
-	args = appendArgs(args, members)
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) SCard(key string) *IntCmd {
-	cmd := NewIntCmd("scard", key)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) SDiffStore(destination string, keys ...string) *IntCmd {
-	args := make([]interface{}, 2+len(keys))
-	args[0] = "sdiffstore"
-	args[1] = destination
-	for i, key := range keys {
-		args[2+i] = key
-	}
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) SInterStore(destination string, keys ...string) *IntCmd {
-	args := make([]interface{}, 2+len(keys))
-	args[0] = "sinterstore"
-	args[1] = destination
-	for i, key := range keys {
-		args[2+i] = key
-	}
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) SIsMember(key string, member interface{}) *BoolCmd {
-	cmd := NewBoolCmd("sismember", key, member)
-	_ = c(cmd)
-	return cmd
-}
-
-// Redis `SMEMBERS key` command output as a slice
-func (c cmdable) SMembers(key string) *StringSliceCmd {
-	cmd := NewStringSliceCmd("smembers", key)
-	_ = c(cmd)
-	return cmd
-}
-
-// Redis `SMEMBERS key` command output as a map
-func (c cmdable) SMembersMap(key string) *StringStructMapCmd {
-	cmd := NewStringStructMapCmd("smembers", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) SMove(source, destination string, member interface{}) *BoolCmd {
-	cmd := NewBoolCmd("smove", source, destination, member)
-	_ = c(cmd)
-	return cmd
-}
-
-// Redis `SPOP key` command.
-func (c cmdable) SPop(key string) *StringCmd {
-	cmd := NewStringCmd("spop", key)
-	_ = c(cmd)
-	return cmd
-}
-
-// Redis `SPOP key count` command.
-func (c cmdable) SPopN(key string, count int64) *StringSliceCmd {
-	cmd := NewStringSliceCmd("spop", key, count)
-	_ = c(cmd)
-	return cmd
-}
-
-// Redis `SRANDMEMBER key` command.
-func (c cmdable) SRandMember(key string) *StringCmd {
-	cmd := NewStringCmd("srandmember", key)
-	_ = c(cmd)
-	return cmd
-}
-
-// Redis `SRANDMEMBER key count` command.
-func (c cmdable) SRandMemberN(key string, count int64) *StringSliceCmd {
-	cmd := NewStringSliceCmd("srandmember", key, count)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) SUnionStore(destination string, keys ...string) *IntCmd {
-	args := make([]interface{}, 2+len(keys))
-	args[0] = "sunionstore"
-	args[1] = destination
-	for i, key := range keys {
-		args[2+i] = key
-	}
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-type XAddArgs struct {
-	Stream       string
-	MaxLen       int64 // MAXLEN N
-	MaxLenApprox int64 // MAXLEN ~ N
-	ID           string
-	Values       map[string]interface{}
-}
-
-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)
-	if a.MaxLen > 0 {
-		args = append(args, "maxlen", a.MaxLen)
-	} else if a.MaxLenApprox > 0 {
-		args = append(args, "maxlen", "~", a.MaxLenApprox)
-	}
-	if a.ID != "" {
-		args = append(args, a.ID)
-	} else {
-		args = append(args, "*")
-	}
-	for k, v := range a.Values {
-		args = append(args, k)
-		args = append(args, v)
-	}
-
-	cmd := NewStringCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) XRange(stream, start, stop string) *XMessageSliceCmd {
-	cmd := NewXMessageSliceCmd("xrange", stream, start, stop)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) XRangeN(stream, start, stop string, count int64) *XMessageSliceCmd {
-	cmd := NewXMessageSliceCmd("xrange", stream, start, stop, "count", count)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) XRevRange(stream, start, stop string) *XMessageSliceCmd {
-	cmd := NewXMessageSliceCmd("xrevrange", stream, start, stop)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) XRevRangeN(stream, start, stop string, count int64) *XMessageSliceCmd {
-	cmd := NewXMessageSliceCmd("xrevrange", stream, start, stop, "count", count)
-	_ = c(cmd)
-	return cmd
-}
-
-type XReadArgs struct {
-	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 {
-	args := make([]interface{}, 0, 5+len(a.Streams))
-	args = append(args, "xread")
-	if a.Count > 0 {
-		args = append(args, "count")
-		args = append(args, a.Count)
-	}
-	if a.Block >= 0 {
-		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...)
-	if a.Block >= 0 {
-		cmd.setReadTimeout(a.Block)
-	}
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) XReadStreams(streams ...string) *XStreamSliceCmd {
-	return c.XRead(&XReadArgs{
-		Streams: streams,
-		Block:   -1,
-	})
-}
-
-func (c cmdable) XGroupCreate(stream, group, start string) *StatusCmd {
-	cmd := NewStatusCmd("xgroup", "create", stream, group, start)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) XGroupDestroy(stream, group string) *IntCmd {
-	cmd := NewIntCmd("xgroup", "destroy", stream, group)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) XGroupDelConsumer(stream, group, consumer string) *IntCmd {
-	cmd := NewIntCmd("xgroup", "delconsumer", stream, group, consumer)
-	_ = c(cmd)
-	return cmd
-}
-
-type XReadGroupArgs struct {
-	Group    string
-	Consumer 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 {
-	args := make([]interface{}, 0, 8+len(a.Streams))
-	args = append(args, "xreadgroup", "group", a.Group, a.Consumer)
-	if a.Count > 0 {
-		args = append(args, "count", a.Count)
-	}
-	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...)
-	if a.Block >= 0 {
-		cmd.setReadTimeout(a.Block)
-	}
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) XPending(stream, group string) *XPendingCmd {
-	cmd := NewXPendingCmd("xpending", stream, group)
-	_ = c(cmd)
-	return cmd
-}
-
-type XPendingExtArgs struct {
-	Stream   string
-	Group    string
-	Start    string
-	End      string
-	Count    int64
-	Consumer string
-}
-
-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(cmd)
-	return cmd
-}
-
-type XClaimArgs struct {
-	Stream   string
-	Group    string
-	Consumer string
-	MinIdle  time.Duration
-	Messages []string
-}
-
-func (c cmdable) XClaim(a *XClaimArgs) *XMessageSliceCmd {
-	args := xClaimArgs(a)
-	cmd := NewXMessageSliceCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) XClaimJustID(a *XClaimArgs) *StringSliceCmd {
-	args := xClaimArgs(a)
-	args = append(args, "justid")
-	cmd := NewStringSliceCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func xClaimArgs(a *XClaimArgs) []interface{} {
-	args := make([]interface{}, 0, 4+len(a.Messages))
-	args = append(args,
-		"xclaim",
-		a.Stream,
-		a.Group, a.Consumer,
-		int64(a.MinIdle/time.Millisecond))
-	for _, id := range a.Messages {
-		args = append(args, id)
-	}
-	return args
-}
-
-func (c cmdable) XTrim(key string, maxLen int64) *IntCmd {
-	cmd := NewIntCmd("xtrim", key, "maxlen", maxLen)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) XTrimApprox(key string, maxLen int64) *IntCmd {
-	cmd := NewIntCmd("xtrim", key, "maxlen", "~", maxLen)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) XInfoGroups(key string) *XInfoGroupsCmd {
-	cmd := NewXInfoGroupsCmd(key)
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-// Z represents sorted set member.
-type Z struct {
-	Score  float64
-	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
-}
-
-// 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(cmd)
-	return cmd
-}
-
-// Redis `ZADD key score member [score member ...]` command.
-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
-	return c.zAdd(a, n, members...)
-}
-
-// Redis `ZADD key NX score member [score member ...]` command.
-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"
-	return c.zAdd(a, n, members...)
-}
-
-// Redis `ZADD key XX score member [score member ...]` command.
-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"
-	return c.zAdd(a, n, members...)
-}
-
-// Redis `ZADD key CH score member [score member ...]` command.
-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"
-	return c.zAdd(a, n, members...)
-}
-
-// Redis `ZADD key NX CH score member [score member ...]` command.
-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"
-	return c.zAdd(a, n, members...)
-}
-
-// Redis `ZADD key XX CH score member [score member ...]` command.
-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 {
-	for i, m := range members {
-		a[n+2*i] = m.Score
-		a[n+2*i+1] = m.Member
-	}
-	cmd := NewFloatCmd(a...)
-	_ = c(cmd)
-	return cmd
-}
-
-// Redis `ZADD key INCR score member` command.
-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"
-	return c.zIncr(a, n, member)
-}
-
-// Redis `ZADD key NX INCR score member` command.
-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"
-	return c.zIncr(a, n, member)
-}
-
-// Redis `ZADD key XX INCR score member` command.
-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 {
-	cmd := NewIntCmd("zcard", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ZCount(key, min, max string) *IntCmd {
-	cmd := NewIntCmd("zcount", key, min, max)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ZLexCount(key, min, max string) *IntCmd {
-	cmd := NewIntCmd("zlexcount", key, min, max)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ZIncrBy(key string, increment float64, member string) *FloatCmd {
-	cmd := NewFloatCmd("zincrby", key, increment, member)
-	_ = c(cmd)
-	return cmd
-}
-
-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(store.Keys)
-	for i, key := range store.Keys {
-		args[3+i] = key
-	}
-	if len(store.Weights) > 0 {
-		args = append(args, "weights")
-		for _, weight := range store.Weights {
-			args = append(args, weight)
-		}
-	}
-	if store.Aggregate != "" {
-		args = append(args, "aggregate", store.Aggregate)
-	}
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-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,
-		start,
-		stop,
-	}
-	if withScores {
-		args = append(args, "withscores")
-	}
-	cmd := NewStringSliceCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-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 {
-	cmd := NewZSliceCmd("zrange", key, start, stop, "withscores")
-	_ = c(cmd)
-	return cmd
-}
-
-type ZRangeBy struct {
-	Min, Max      string
-	Offset, Count int64
-}
-
-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")
-	}
-	if opt.Offset != 0 || opt.Count != 0 {
-		args = append(
-			args,
-			"limit",
-			opt.Offset,
-			opt.Count,
-		)
-	}
-	cmd := NewStringSliceCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-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 {
-	return c.zRangeBy("zrangebylex", key, opt, false)
-}
-
-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(
-			args,
-			"limit",
-			opt.Offset,
-			opt.Count,
-		)
-	}
-	cmd := NewZSliceCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ZRank(key, member string) *IntCmd {
-	cmd := NewIntCmd("zrank", key, member)
-	_ = c(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) ZRemRangeByRank(key string, start, stop int64) *IntCmd {
-	cmd := NewIntCmd(
-		"zremrangebyrank",
-		key,
-		start,
-		stop,
-	)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ZRemRangeByScore(key, min, max string) *IntCmd {
-	cmd := NewIntCmd("zremrangebyscore", key, min, max)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ZRemRangeByLex(key, min, max string) *IntCmd {
-	cmd := NewIntCmd("zremrangebylex", key, min, max)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ZRevRange(key string, start, stop int64) *StringSliceCmd {
-	cmd := NewStringSliceCmd("zrevrange", key, start, stop)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ZRevRangeWithScores(key string, start, stop int64) *ZSliceCmd {
-	cmd := NewZSliceCmd("zrevrange", key, start, stop, "withscores")
-	_ = c(cmd)
-	return cmd
-}
-
-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(
-			args,
-			"limit",
-			opt.Offset,
-			opt.Count,
-		)
-	}
-	cmd := NewStringSliceCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ZRevRangeByScore(key string, opt *ZRangeBy) *StringSliceCmd {
-	return c.zRevRangeBy("zrevrangebyscore", key, opt)
-}
-
-func (c cmdable) ZRevRangeByLex(key string, opt *ZRangeBy) *StringSliceCmd {
-	return c.zRevRangeBy("zrevrangebylex", key, opt)
-}
-
-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(
-			args,
-			"limit",
-			opt.Offset,
-			opt.Count,
-		)
-	}
-	cmd := NewZSliceCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ZRevRank(key, member string) *IntCmd {
-	cmd := NewIntCmd("zrevrank", key, member)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ZScore(key, member string) *FloatCmd {
-	cmd := NewFloatCmd("zscore", key, member)
-	_ = c(cmd)
-	return cmd
-}
-
-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(store.Keys)
-	for i, key := range store.Keys {
-		args[3+i] = key
-	}
-	if len(store.Weights) > 0 {
-		args = append(args, "weights")
-		for _, weight := range store.Weights {
-			args = append(args, weight)
-		}
-	}
-	if store.Aggregate != "" {
-		args = append(args, "aggregate", store.Aggregate)
-	}
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-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(cmd)
-	return cmd
-}
-
-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(cmd)
-	return cmd
-}
-
-func (c cmdable) PFMerge(dest string, keys ...string) *StatusCmd {
-	args := make([]interface{}, 2+len(keys))
-	args[0] = "pfmerge"
-	args[1] = dest
-	for i, key := range keys {
-		args[2+i] = key
-	}
-	cmd := NewStatusCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-func (c cmdable) BgRewriteAOF() *StatusCmd {
-	cmd := NewStatusCmd("bgrewriteaof")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) BgSave() *StatusCmd {
-	cmd := NewStatusCmd("bgsave")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClientKill(ipPort string) *StatusCmd {
-	cmd := NewStatusCmd("client", "kill", ipPort)
-	_ = c(cmd)
-	return cmd
-}
-
-// ClientKillByFilter is new style synx, while the ClientKill is old
-// CLIENT KILL <option> [value] ... <option> [value]
-func (c cmdable) ClientKillByFilter(keys ...string) *IntCmd {
-	args := make([]interface{}, 2+len(keys))
-	args[0] = "client"
-	args[1] = "kill"
-	for i, key := range keys {
-		args[2+i] = key
-	}
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClientList() *StringCmd {
-	cmd := NewStringCmd("client", "list")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClientPause(dur time.Duration) *BoolCmd {
-	cmd := NewBoolCmd("client", "pause", formatMs(dur))
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClientID() *IntCmd {
-	cmd := NewIntCmd("client", "id")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClientUnblock(id int64) *IntCmd {
-	cmd := NewIntCmd("client", "unblock", id)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClientUnblockWithError(id int64) *IntCmd {
-	cmd := NewIntCmd("client", "unblock", id, "error")
-	_ = c(cmd)
-	return cmd
-}
-
-// ClientSetName assigns a name to the connection.
-func (c statefulCmdable) ClientSetName(name string) *BoolCmd {
-	cmd := NewBoolCmd("client", "setname", name)
-	_ = c(cmd)
-	return cmd
-}
-
-// ClientGetName returns the name of the connection.
-func (c cmdable) ClientGetName() *StringCmd {
-	cmd := NewStringCmd("client", "getname")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ConfigGet(parameter string) *SliceCmd {
-	cmd := NewSliceCmd("config", "get", parameter)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ConfigResetStat() *StatusCmd {
-	cmd := NewStatusCmd("config", "resetstat")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ConfigSet(parameter, value string) *StatusCmd {
-	cmd := NewStatusCmd("config", "set", parameter, value)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ConfigRewrite() *StatusCmd {
-	cmd := NewStatusCmd("config", "rewrite")
-	_ = c(cmd)
-	return cmd
-}
-
-// Deperecated. Use DBSize instead.
-func (c cmdable) DbSize() *IntCmd {
-	return c.DBSize()
-}
-
-func (c cmdable) DBSize() *IntCmd {
-	cmd := NewIntCmd("dbsize")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) FlushAll() *StatusCmd {
-	cmd := NewStatusCmd("flushall")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) FlushAllAsync() *StatusCmd {
-	cmd := NewStatusCmd("flushall", "async")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) FlushDB() *StatusCmd {
-	cmd := NewStatusCmd("flushdb")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) FlushDBAsync() *StatusCmd {
-	cmd := NewStatusCmd("flushdb", "async")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Info(section ...string) *StringCmd {
-	args := []interface{}{"info"}
-	if len(section) > 0 {
-		args = append(args, section[0])
-	}
-	cmd := NewStringCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) LastSave() *IntCmd {
-	cmd := NewIntCmd("lastsave")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) Save() *StatusCmd {
-	cmd := NewStatusCmd("save")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) shutdown(modifier string) *StatusCmd {
-	var args []interface{}
-	if modifier == "" {
-		args = []interface{}{"shutdown"}
-	} else {
-		args = []interface{}{"shutdown", modifier}
-	}
-	cmd := NewStatusCmd(args...)
-	_ = c(cmd)
-	if err := cmd.Err(); err != nil {
-		if err == io.EOF {
-			// Server quit as expected.
-			cmd.err = nil
-		}
-	} else {
-		// Server did not quit. String reply contains the reason.
-		cmd.err = errors.New(cmd.val)
-		cmd.val = ""
-	}
-	return cmd
-}
-
-func (c cmdable) Shutdown() *StatusCmd {
-	return c.shutdown("")
-}
-
-func (c cmdable) ShutdownSave() *StatusCmd {
-	return c.shutdown("save")
-}
-
-func (c cmdable) ShutdownNoSave() *StatusCmd {
-	return c.shutdown("nosave")
-}
-
-func (c cmdable) SlaveOf(host, port string) *StatusCmd {
-	cmd := NewStatusCmd("slaveof", host, port)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) SlowLog() {
-	panic("not implemented")
-}
-
-func (c cmdable) Sync() {
-	panic("not implemented")
-}
-
-func (c cmdable) Time() *TimeCmd {
-	cmd := NewTimeCmd("time")
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-func (c cmdable) Eval(script string, keys []string, args ...interface{}) *Cmd {
-	cmdArgs := make([]interface{}, 3+len(keys), 3+len(keys)+len(args))
-	cmdArgs[0] = "eval"
-	cmdArgs[1] = script
-	cmdArgs[2] = len(keys)
-	for i, key := range keys {
-		cmdArgs[3+i] = key
-	}
-	cmdArgs = appendArgs(cmdArgs, args)
-	cmd := NewCmd(cmdArgs...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) EvalSha(sha1 string, keys []string, args ...interface{}) *Cmd {
-	cmdArgs := make([]interface{}, 3+len(keys), 3+len(keys)+len(args))
-	cmdArgs[0] = "evalsha"
-	cmdArgs[1] = sha1
-	cmdArgs[2] = len(keys)
-	for i, key := range keys {
-		cmdArgs[3+i] = key
-	}
-	cmdArgs = appendArgs(cmdArgs, args)
-	cmd := NewCmd(cmdArgs...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ScriptExists(hashes ...string) *BoolSliceCmd {
-	args := make([]interface{}, 2+len(hashes))
-	args[0] = "script"
-	args[1] = "exists"
-	for i, hash := range hashes {
-		args[2+i] = hash
-	}
-	cmd := NewBoolSliceCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ScriptFlush() *StatusCmd {
-	cmd := NewStatusCmd("script", "flush")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ScriptKill() *StatusCmd {
-	cmd := NewStatusCmd("script", "kill")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ScriptLoad(script string) *StringCmd {
-	cmd := NewStringCmd("script", "load", script)
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-func (c cmdable) DebugObject(key string) *StringCmd {
-	cmd := NewStringCmd("debug", "object", key)
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-// Publish posts the message to the channel.
-func (c cmdable) Publish(channel string, message interface{}) *IntCmd {
-	cmd := NewIntCmd("publish", channel, message)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) PubSubChannels(pattern string) *StringSliceCmd {
-	args := []interface{}{"pubsub", "channels"}
-	if pattern != "*" {
-		args = append(args, pattern)
-	}
-	cmd := NewStringSliceCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) PubSubNumSub(channels ...string) *StringIntMapCmd {
-	args := make([]interface{}, 2+len(channels))
-	args[0] = "pubsub"
-	args[1] = "numsub"
-	for i, channel := range channels {
-		args[2+i] = channel
-	}
-	cmd := NewStringIntMapCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) PubSubNumPat() *IntCmd {
-	cmd := NewIntCmd("pubsub", "numpat")
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-func (c cmdable) ClusterSlots() *ClusterSlotsCmd {
-	cmd := NewClusterSlotsCmd("cluster", "slots")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterNodes() *StringCmd {
-	cmd := NewStringCmd("cluster", "nodes")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterMeet(host, port string) *StatusCmd {
-	cmd := NewStatusCmd("cluster", "meet", host, port)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterForget(nodeID string) *StatusCmd {
-	cmd := NewStatusCmd("cluster", "forget", nodeID)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterReplicate(nodeID string) *StatusCmd {
-	cmd := NewStatusCmd("cluster", "replicate", nodeID)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterResetSoft() *StatusCmd {
-	cmd := NewStatusCmd("cluster", "reset", "soft")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterResetHard() *StatusCmd {
-	cmd := NewStatusCmd("cluster", "reset", "hard")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterInfo() *StringCmd {
-	cmd := NewStringCmd("cluster", "info")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterKeySlot(key string) *IntCmd {
-	cmd := NewIntCmd("cluster", "keyslot", key)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterGetKeysInSlot(slot int, count int) *StringSliceCmd {
-	cmd := NewStringSliceCmd("cluster", "getkeysinslot", slot, count)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterCountFailureReports(nodeID string) *IntCmd {
-	cmd := NewIntCmd("cluster", "count-failure-reports", nodeID)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterCountKeysInSlot(slot int) *IntCmd {
-	cmd := NewIntCmd("cluster", "countkeysinslot", slot)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterDelSlots(slots ...int) *StatusCmd {
-	args := make([]interface{}, 2+len(slots))
-	args[0] = "cluster"
-	args[1] = "delslots"
-	for i, slot := range slots {
-		args[2+i] = slot
-	}
-	cmd := NewStatusCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterDelSlotsRange(min, max int) *StatusCmd {
-	size := max - min + 1
-	slots := make([]int, size)
-	for i := 0; i < size; i++ {
-		slots[i] = min + i
-	}
-	return c.ClusterDelSlots(slots...)
-}
-
-func (c cmdable) ClusterSaveConfig() *StatusCmd {
-	cmd := NewStatusCmd("cluster", "saveconfig")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterSlaves(nodeID string) *StringSliceCmd {
-	cmd := NewStringSliceCmd("cluster", "slaves", nodeID)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ReadOnly() *StatusCmd {
-	cmd := NewStatusCmd("readonly")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ReadWrite() *StatusCmd {
-	cmd := NewStatusCmd("readwrite")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterFailover() *StatusCmd {
-	cmd := NewStatusCmd("cluster", "failover")
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterAddSlots(slots ...int) *StatusCmd {
-	args := make([]interface{}, 2+len(slots))
-	args[0] = "cluster"
-	args[1] = "addslots"
-	for i, num := range slots {
-		args[2+i] = num
-	}
-	cmd := NewStatusCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) ClusterAddSlotsRange(min, max int) *StatusCmd {
-	size := max - min + 1
-	slots := make([]int, size)
-	for i := 0; i < size; i++ {
-		slots[i] = min + i
-	}
-	return c.ClusterAddSlots(slots...)
-}
-
-//------------------------------------------------------------------------------
-
-func (c cmdable) GeoAdd(key string, geoLocation ...*GeoLocation) *IntCmd {
-	args := make([]interface{}, 2+3*len(geoLocation))
-	args[0] = "geoadd"
-	args[1] = key
-	for i, eachLoc := range geoLocation {
-		args[2+3*i] = eachLoc.Longitude
-		args[2+3*i+1] = eachLoc.Latitude
-		args[2+3*i+2] = eachLoc.Name
-	}
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-// GeoRadius is a read-only GEORADIUS_RO command.
-func (c cmdable) GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd {
-	cmd := NewGeoLocationCmd(query, "georadius_ro", key, longitude, latitude)
-	if query.Store != "" || query.StoreDist != "" {
-		cmd.SetErr(errors.New("GeoRadius does not support Store or StoreDist"))
-		return cmd
-	}
-	_ = c(cmd)
-	return cmd
-}
-
-// GeoRadiusStore is a writing GEORADIUS command.
-func (c cmdable) GeoRadiusStore(key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd {
-	args := geoLocationArgs(query, "georadius", key, longitude, latitude)
-	cmd := NewIntCmd(args...)
-	if query.Store == "" && query.StoreDist == "" {
-		cmd.SetErr(errors.New("GeoRadiusStore requires Store or StoreDist"))
-		return cmd
-	}
-	_ = c(cmd)
-	return cmd
-}
-
-// GeoRadius is a read-only GEORADIUSBYMEMBER_RO command.
-func (c cmdable) GeoRadiusByMember(key, member string, query *GeoRadiusQuery) *GeoLocationCmd {
-	cmd := NewGeoLocationCmd(query, "georadiusbymember_ro", key, member)
-	if query.Store != "" || query.StoreDist != "" {
-		cmd.SetErr(errors.New("GeoRadiusByMember does not support Store or StoreDist"))
-		return cmd
-	}
-	_ = c(cmd)
-	return cmd
-}
-
-// GeoRadiusByMemberStore is a writing GEORADIUSBYMEMBER command.
-func (c cmdable) GeoRadiusByMemberStore(key, member string, query *GeoRadiusQuery) *IntCmd {
-	args := geoLocationArgs(query, "georadiusbymember", key, member)
-	cmd := NewIntCmd(args...)
-	if query.Store == "" && query.StoreDist == "" {
-		cmd.SetErr(errors.New("GeoRadiusByMemberStore requires Store or StoreDist"))
-		return cmd
-	}
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) GeoDist(key string, member1, member2, unit string) *FloatCmd {
-	if unit == "" {
-		unit = "km"
-	}
-	cmd := NewFloatCmd("geodist", key, member1, member2, unit)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) GeoHash(key string, members ...string) *StringSliceCmd {
-	args := make([]interface{}, 2+len(members))
-	args[0] = "geohash"
-	args[1] = key
-	for i, member := range members {
-		args[2+i] = member
-	}
-	cmd := NewStringSliceCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-func (c cmdable) GeoPos(key string, members ...string) *GeoPosCmd {
-	args := make([]interface{}, 2+len(members))
-	args[0] = "geopos"
-	args[1] = key
-	for i, member := range members {
-		args[2+i] = member
-	}
-	cmd := NewGeoPosCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
-
-//------------------------------------------------------------------------------
-
-func (c cmdable) MemoryUsage(key string, samples ...int) *IntCmd {
-	args := []interface{}{"memory", "usage", key}
-	if len(samples) > 0 {
-		if len(samples) != 1 {
-			panic("MemoryUsage expects single sample count")
-		}
-		args = append(args, "SAMPLES", samples[0])
-	}
-	cmd := NewIntCmd(args...)
-	_ = c(cmd)
-	return cmd
-}
diff --git a/vendor/github.com/go-redis/redis/v7/go.mod b/vendor/github.com/go-redis/redis/v7/go.mod
deleted file mode 100644
index e3a4dec54a..0000000000
--- a/vendor/github.com/go-redis/redis/v7/go.mod
+++ /dev/null
@@ -1,15 +0,0 @@
-module github.com/go-redis/redis/v7
-
-require (
-	github.com/golang/protobuf v1.3.2 // indirect
-	github.com/kr/pretty v0.1.0 // indirect
-	github.com/onsi/ginkgo v1.10.1
-	github.com/onsi/gomega v1.7.0
-	golang.org/x/net v0.0.0-20190923162816-aa69164e4478 // indirect
-	golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
-	golang.org/x/text v0.3.2 // indirect
-	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
-	gopkg.in/yaml.v2 v2.2.4 // indirect
-)
-
-go 1.11
diff --git a/vendor/github.com/go-redis/redis/v7/go.sum b/vendor/github.com/go-redis/redis/v7/go.sum
deleted file mode 100644
index 6a04dbb63a..0000000000
--- a/vendor/github.com/go-redis/redis/v7/go.sum
+++ /dev/null
@@ -1,47 +0,0 @@
-github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
-github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
-github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
-golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
-golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/vendor/github.com/go-redis/redis/v7/internal/consistenthash/consistenthash.go b/vendor/github.com/go-redis/redis/v7/internal/consistenthash/consistenthash.go
deleted file mode 100644
index a9c56f0762..0000000000
--- a/vendor/github.com/go-redis/redis/v7/internal/consistenthash/consistenthash.go
+++ /dev/null
@@ -1,81 +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 consistenthash provides an implementation of a ring hash.
-package consistenthash
-
-import (
-	"hash/crc32"
-	"sort"
-	"strconv"
-)
-
-type Hash func(data []byte) uint32
-
-type Map struct {
-	hash     Hash
-	replicas int
-	keys     []int // Sorted
-	hashMap  map[int]string
-}
-
-func New(replicas int, fn Hash) *Map {
-	m := &Map{
-		replicas: replicas,
-		hash:     fn,
-		hashMap:  make(map[int]string),
-	}
-	if m.hash == nil {
-		m.hash = crc32.ChecksumIEEE
-	}
-	return m
-}
-
-// Returns true if there are no items available.
-func (m *Map) IsEmpty() bool {
-	return len(m.keys) == 0
-}
-
-// Adds some keys to the hash.
-func (m *Map) Add(keys ...string) {
-	for _, key := range keys {
-		for i := 0; i < m.replicas; i++ {
-			hash := int(m.hash([]byte(strconv.Itoa(i) + key)))
-			m.keys = append(m.keys, hash)
-			m.hashMap[hash] = key
-		}
-	}
-	sort.Ints(m.keys)
-}
-
-// Gets the closest item in the hash to the provided key.
-func (m *Map) Get(key string) string {
-	if m.IsEmpty() {
-		return ""
-	}
-
-	hash := int(m.hash([]byte(key)))
-
-	// Binary search for appropriate replica.
-	idx := sort.Search(len(m.keys), func(i int) bool { return m.keys[i] >= hash })
-
-	// Means we have cycled back to the first replica.
-	if idx == len(m.keys) {
-		idx = 0
-	}
-
-	return m.hashMap[m.keys[idx]]
-}
diff --git a/vendor/github.com/go-redis/redis/v7/internal/internal.go b/vendor/github.com/go-redis/redis/v7/internal/internal.go
deleted file mode 100644
index ad3fc3c9ff..0000000000
--- a/vendor/github.com/go-redis/redis/v7/internal/internal.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package internal
-
-import (
-	"math/rand"
-	"time"
-)
-
-// Retry backoff with jitter sleep to prevent overloaded conditions during intervals
-// https://www.awsarchitectureblog.com/2015/03/backoff.html
-func RetryBackoff(retry int, minBackoff, maxBackoff time.Duration) time.Duration {
-	if retry < 0 {
-		retry = 0
-	}
-
-	backoff := minBackoff << uint(retry)
-	if backoff > maxBackoff || backoff < minBackoff {
-		backoff = maxBackoff
-	}
-
-	if backoff == 0 {
-		return 0
-	}
-	return time.Duration(rand.Int63n(int64(backoff)))
-}
diff --git a/vendor/github.com/go-redis/redis/v7/internal/log.go b/vendor/github.com/go-redis/redis/v7/internal/log.go
deleted file mode 100644
index 405a2728d6..0000000000
--- a/vendor/github.com/go-redis/redis/v7/internal/log.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package internal
-
-import (
-	"log"
-	"os"
-)
-
-var Logger = log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile)
diff --git a/vendor/github.com/go-redis/redis/v7/internal/pool/pool_sticky.go b/vendor/github.com/go-redis/redis/v7/internal/pool/pool_sticky.go
deleted file mode 100644
index d4a355a44f..0000000000
--- a/vendor/github.com/go-redis/redis/v7/internal/pool/pool_sticky.go
+++ /dev/null
@@ -1,112 +0,0 @@
-package pool
-
-import (
-	"context"
-	"sync"
-)
-
-type StickyConnPool struct {
-	pool     *ConnPool
-	reusable bool
-
-	cn     *Conn
-	closed bool
-	mu     sync.Mutex
-}
-
-var _ Pooler = (*StickyConnPool)(nil)
-
-func NewStickyConnPool(pool *ConnPool, reusable bool) *StickyConnPool {
-	return &StickyConnPool{
-		pool:     pool,
-		reusable: reusable,
-	}
-}
-
-func (p *StickyConnPool) NewConn(context.Context) (*Conn, error) {
-	panic("not implemented")
-}
-
-func (p *StickyConnPool) CloseConn(*Conn) error {
-	panic("not implemented")
-}
-
-func (p *StickyConnPool) Get(ctx context.Context) (*Conn, error) {
-	p.mu.Lock()
-	defer p.mu.Unlock()
-
-	if p.closed {
-		return nil, ErrClosed
-	}
-	if p.cn != nil {
-		return p.cn, nil
-	}
-
-	cn, err := p.pool.Get(ctx)
-	if err != nil {
-		return nil, err
-	}
-
-	p.cn = cn
-	return cn, nil
-}
-
-func (p *StickyConnPool) putUpstream() {
-	p.pool.Put(p.cn)
-	p.cn = nil
-}
-
-func (p *StickyConnPool) Put(cn *Conn) {}
-
-func (p *StickyConnPool) removeUpstream(reason error) {
-	p.pool.Remove(p.cn, reason)
-	p.cn = nil
-}
-
-func (p *StickyConnPool) Remove(cn *Conn, reason error) {
-	p.removeUpstream(reason)
-}
-
-func (p *StickyConnPool) Len() int {
-	p.mu.Lock()
-	defer p.mu.Unlock()
-
-	if p.cn == nil {
-		return 0
-	}
-	return 1
-}
-
-func (p *StickyConnPool) IdleLen() int {
-	p.mu.Lock()
-	defer p.mu.Unlock()
-
-	if p.cn == nil {
-		return 1
-	}
-	return 0
-}
-
-func (p *StickyConnPool) Stats() *Stats {
-	return nil
-}
-
-func (p *StickyConnPool) Close() error {
-	p.mu.Lock()
-	defer p.mu.Unlock()
-
-	if p.closed {
-		return ErrClosed
-	}
-	p.closed = true
-
-	if p.cn != nil {
-		if p.reusable {
-			p.putUpstream()
-		} else {
-			p.removeUpstream(ErrClosed)
-		}
-	}
-
-	return nil
-}
diff --git a/vendor/github.com/go-redis/redis/v7/internal/util.go b/vendor/github.com/go-redis/redis/v7/internal/util.go
deleted file mode 100644
index 844f34bad8..0000000000
--- a/vendor/github.com/go-redis/redis/v7/internal/util.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package internal
-
-import (
-	"context"
-	"time"
-
-	"github.com/go-redis/redis/v7/internal/util"
-)
-
-func Sleep(ctx context.Context, dur time.Duration) error {
-	t := time.NewTimer(dur)
-	defer t.Stop()
-
-	select {
-	case <-t.C:
-		return nil
-	case <-ctx.Done():
-		return ctx.Err()
-	}
-}
-
-func ToLower(s string) string {
-	if isLower(s) {
-		return s
-	}
-
-	b := make([]byte, len(s))
-	for i := range b {
-		c := s[i]
-		if c >= 'A' && c <= 'Z' {
-			c += 'a' - 'A'
-		}
-		b[i] = c
-	}
-	return util.BytesToString(b)
-}
-
-func isLower(s string) bool {
-	for i := 0; i < len(s); i++ {
-		c := s[i]
-		if c >= 'A' && c <= 'Z' {
-			return false
-		}
-	}
-	return true
-}
-
-func Unwrap(err error) error {
-	u, ok := err.(interface {
-		Unwrap() error
-	})
-	if !ok {
-		return nil
-	}
-	return u.Unwrap()
-}
diff --git a/vendor/github.com/go-redis/redis/v7/script.go b/vendor/github.com/go-redis/redis/v7/script.go
deleted file mode 100644
index 88b7d0a2ea..0000000000
--- a/vendor/github.com/go-redis/redis/v7/script.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package redis
-
-import (
-	"crypto/sha1"
-	"encoding/hex"
-	"io"
-	"strings"
-)
-
-type scripter interface {
-	Eval(script string, keys []string, args ...interface{}) *Cmd
-	EvalSha(sha1 string, keys []string, args ...interface{}) *Cmd
-	ScriptExists(hashes ...string) *BoolSliceCmd
-	ScriptLoad(script string) *StringCmd
-}
-
-var _ scripter = (*Client)(nil)
-var _ scripter = (*Ring)(nil)
-var _ scripter = (*ClusterClient)(nil)
-
-type Script struct {
-	src, hash string
-}
-
-func NewScript(src string) *Script {
-	h := sha1.New()
-	_, _ = io.WriteString(h, src)
-	return &Script{
-		src:  src,
-		hash: hex.EncodeToString(h.Sum(nil)),
-	}
-}
-
-func (s *Script) Hash() string {
-	return s.hash
-}
-
-func (s *Script) Load(c scripter) *StringCmd {
-	return c.ScriptLoad(s.src)
-}
-
-func (s *Script) Exists(c scripter) *BoolSliceCmd {
-	return c.ScriptExists(s.hash)
-}
-
-func (s *Script) Eval(c scripter, keys []string, args ...interface{}) *Cmd {
-	return c.Eval(s.src, keys, args...)
-}
-
-func (s *Script) EvalSha(c scripter, keys []string, args ...interface{}) *Cmd {
-	return c.EvalSha(s.hash, keys, args...)
-}
-
-// Run optimistically uses EVALSHA to run the script. If script does not exist
-// it is retried using EVAL.
-func (s *Script) Run(c scripter, keys []string, args ...interface{}) *Cmd {
-	r := s.EvalSha(c, keys, args...)
-	if err := r.Err(); err != nil && strings.HasPrefix(err.Error(), "NOSCRIPT ") {
-		return s.Eval(c, keys, args...)
-	}
-	return r
-}
diff --git a/vendor/github.com/go-redis/redis/v7/sentinel.go b/vendor/github.com/go-redis/redis/v7/sentinel.go
deleted file mode 100644
index 8aa40ef799..0000000000
--- a/vendor/github.com/go-redis/redis/v7/sentinel.go
+++ /dev/null
@@ -1,509 +0,0 @@
-package redis
-
-import (
-	"context"
-	"crypto/tls"
-	"errors"
-	"net"
-	"strings"
-	"sync"
-	"time"
-
-	"github.com/go-redis/redis/v7/internal"
-	"github.com/go-redis/redis/v7/internal/pool"
-)
-
-//------------------------------------------------------------------------------
-
-// FailoverOptions are used to configure a failover client and should
-// be passed to NewFailoverClient.
-type FailoverOptions struct {
-	// The master name.
-	MasterName string
-	// A seed list of host:port addresses of sentinel nodes.
-	SentinelAddrs    []string
-	SentinelUsername string
-	SentinelPassword string
-
-	// Following options are copied from Options struct.
-
-	Dialer    func(ctx context.Context, network, addr string) (net.Conn, error)
-	OnConnect func(*Conn) error
-
-	Username string
-	Password string
-	DB       int
-
-	MaxRetries      int
-	MinRetryBackoff time.Duration
-	MaxRetryBackoff time.Duration
-
-	DialTimeout  time.Duration
-	ReadTimeout  time.Duration
-	WriteTimeout time.Duration
-
-	PoolSize           int
-	MinIdleConns       int
-	MaxConnAge         time.Duration
-	PoolTimeout        time.Duration
-	IdleTimeout        time.Duration
-	IdleCheckFrequency time.Duration
-
-	TLSConfig *tls.Config
-}
-
-func (opt *FailoverOptions) options() *Options {
-	return &Options{
-		Addr:      "FailoverClient",
-		Dialer:    opt.Dialer,
-		OnConnect: opt.OnConnect,
-
-		DB:       opt.DB,
-		Username: opt.Username,
-		Password: opt.Password,
-
-		MaxRetries:      opt.MaxRetries,
-		MinRetryBackoff: opt.MinRetryBackoff,
-		MaxRetryBackoff: opt.MaxRetryBackoff,
-
-		DialTimeout:  opt.DialTimeout,
-		ReadTimeout:  opt.ReadTimeout,
-		WriteTimeout: opt.WriteTimeout,
-
-		PoolSize:           opt.PoolSize,
-		PoolTimeout:        opt.PoolTimeout,
-		IdleTimeout:        opt.IdleTimeout,
-		IdleCheckFrequency: opt.IdleCheckFrequency,
-		MinIdleConns:       opt.MinIdleConns,
-		MaxConnAge:         opt.MaxConnAge,
-
-		TLSConfig: opt.TLSConfig,
-	}
-}
-
-// NewFailoverClient returns a Redis client that uses Redis Sentinel
-// for automatic failover. It's safe for concurrent use by multiple
-// goroutines.
-func NewFailoverClient(failoverOpt *FailoverOptions) *Client {
-	opt := failoverOpt.options()
-	opt.init()
-
-	failover := &sentinelFailover{
-		masterName:    failoverOpt.MasterName,
-		sentinelAddrs: failoverOpt.SentinelAddrs,
-		username:      failoverOpt.SentinelUsername,
-		password:      failoverOpt.SentinelPassword,
-
-		opt: opt,
-	}
-
-	c := Client{
-		baseClient: newBaseClient(opt, failover.Pool()),
-		ctx:        context.Background(),
-	}
-	c.cmdable = c.Process
-	c.onClose = failover.Close
-
-	return &c
-}
-
-//------------------------------------------------------------------------------
-
-type SentinelClient struct {
-	*baseClient
-	ctx context.Context
-}
-
-func NewSentinelClient(opt *Options) *SentinelClient {
-	opt.init()
-	c := &SentinelClient{
-		baseClient: &baseClient{
-			opt:      opt,
-			connPool: newConnPool(opt),
-		},
-		ctx: context.Background(),
-	}
-	return c
-}
-
-func (c *SentinelClient) Context() context.Context {
-	return c.ctx
-}
-
-func (c *SentinelClient) WithContext(ctx context.Context) *SentinelClient {
-	if ctx == nil {
-		panic("nil context")
-	}
-	clone := *c
-	clone.ctx = ctx
-	return &clone
-}
-
-func (c *SentinelClient) Process(cmd Cmder) error {
-	return c.ProcessContext(c.ctx, cmd)
-}
-
-func (c *SentinelClient) ProcessContext(ctx context.Context, cmd Cmder) error {
-	return c.baseClient.process(ctx, cmd)
-}
-
-func (c *SentinelClient) pubSub() *PubSub {
-	pubsub := &PubSub{
-		opt: c.opt,
-
-		newConn: func(channels []string) (*pool.Conn, error) {
-			return c.newConn(context.TODO())
-		},
-		closeConn: c.connPool.CloseConn,
-	}
-	pubsub.init()
-	return pubsub
-}
-
-// Ping is used to test if a connection is still alive, or to
-// measure latency.
-func (c *SentinelClient) Ping() *StringCmd {
-	cmd := NewStringCmd("ping")
-	_ = c.Process(cmd)
-	return cmd
-}
-
-// Subscribe subscribes the client to the specified channels.
-// Channels can be omitted to create empty subscription.
-func (c *SentinelClient) Subscribe(channels ...string) *PubSub {
-	pubsub := c.pubSub()
-	if len(channels) > 0 {
-		_ = pubsub.Subscribe(channels...)
-	}
-	return pubsub
-}
-
-// PSubscribe subscribes the client to the given patterns.
-// Patterns can be omitted to create empty subscription.
-func (c *SentinelClient) PSubscribe(channels ...string) *PubSub {
-	pubsub := c.pubSub()
-	if len(channels) > 0 {
-		_ = pubsub.PSubscribe(channels...)
-	}
-	return pubsub
-}
-
-func (c *SentinelClient) GetMasterAddrByName(name string) *StringSliceCmd {
-	cmd := NewStringSliceCmd("sentinel", "get-master-addr-by-name", name)
-	_ = c.Process(cmd)
-	return cmd
-}
-
-func (c *SentinelClient) Sentinels(name string) *SliceCmd {
-	cmd := NewSliceCmd("sentinel", "sentinels", name)
-	_ = c.Process(cmd)
-	return cmd
-}
-
-// Failover forces a failover as if the master was not reachable, and without
-// asking for agreement to other Sentinels.
-func (c *SentinelClient) Failover(name string) *StatusCmd {
-	cmd := NewStatusCmd("sentinel", "failover", name)
-	_ = c.Process(cmd)
-	return cmd
-}
-
-// Reset resets all the masters with matching name. The pattern argument is a
-// glob-style pattern. The reset process clears any previous state in a master
-// (including a failover in progress), and removes every slave and sentinel
-// already discovered and associated with the master.
-func (c *SentinelClient) Reset(pattern string) *IntCmd {
-	cmd := NewIntCmd("sentinel", "reset", pattern)
-	_ = c.Process(cmd)
-	return cmd
-}
-
-// FlushConfig forces Sentinel to rewrite its configuration on disk, including
-// the current Sentinel state.
-func (c *SentinelClient) FlushConfig() *StatusCmd {
-	cmd := NewStatusCmd("sentinel", "flushconfig")
-	_ = c.Process(cmd)
-	return cmd
-}
-
-// Master shows the state and info of the specified master.
-func (c *SentinelClient) Master(name string) *StringStringMapCmd {
-	cmd := NewStringStringMapCmd("sentinel", "master", name)
-	_ = c.Process(cmd)
-	return cmd
-}
-
-// Masters shows a list of monitored masters and their state.
-func (c *SentinelClient) Masters() *SliceCmd {
-	cmd := NewSliceCmd("sentinel", "masters")
-	_ = c.Process(cmd)
-	return cmd
-}
-
-// Slaves shows a list of slaves for the specified master and their state.
-func (c *SentinelClient) Slaves(name string) *SliceCmd {
-	cmd := NewSliceCmd("sentinel", "slaves", name)
-	_ = c.Process(cmd)
-	return cmd
-}
-
-// CkQuorum checks if the current Sentinel configuration is able to reach the
-// quorum needed to failover a master, and the majority needed to authorize the
-// failover. This command should be used in monitoring systems to check if a
-// Sentinel deployment is ok.
-func (c *SentinelClient) CkQuorum(name string) *StringCmd {
-	cmd := NewStringCmd("sentinel", "ckquorum", name)
-	_ = c.Process(cmd)
-	return cmd
-}
-
-// Monitor tells the Sentinel to start monitoring a new master with the specified
-// name, ip, port, and quorum.
-func (c *SentinelClient) Monitor(name, ip, port, quorum string) *StringCmd {
-	cmd := NewStringCmd("sentinel", "monitor", name, ip, port, quorum)
-	_ = c.Process(cmd)
-	return cmd
-}
-
-// Set is used in order to change configuration parameters of a specific master.
-func (c *SentinelClient) Set(name, option, value string) *StringCmd {
-	cmd := NewStringCmd("sentinel", "set", name, option, value)
-	_ = c.Process(cmd)
-	return cmd
-}
-
-// Remove is used in order to remove the specified master: the master will no
-// longer be monitored, and will totally be removed from the internal state of
-// the Sentinel.
-func (c *SentinelClient) Remove(name string) *StringCmd {
-	cmd := NewStringCmd("sentinel", "remove", name)
-	_ = c.Process(cmd)
-	return cmd
-}
-
-type sentinelFailover struct {
-	sentinelAddrs []string
-
-	opt      *Options
-	username string
-	password string
-
-	pool     *pool.ConnPool
-	poolOnce sync.Once
-
-	mu          sync.RWMutex
-	masterName  string
-	_masterAddr string
-	sentinel    *SentinelClient
-	pubsub      *PubSub
-}
-
-func (c *sentinelFailover) Close() error {
-	c.mu.Lock()
-	defer c.mu.Unlock()
-	if c.sentinel != nil {
-		return c.closeSentinel()
-	}
-	return nil
-}
-
-func (c *sentinelFailover) closeSentinel() error {
-	firstErr := c.pubsub.Close()
-	c.pubsub = nil
-
-	err := c.sentinel.Close()
-	if err != nil && firstErr == nil {
-		firstErr = err
-	}
-	c.sentinel = nil
-
-	return firstErr
-}
-
-func (c *sentinelFailover) Pool() *pool.ConnPool {
-	c.poolOnce.Do(func() {
-		opt := *c.opt
-		opt.Dialer = c.dial
-		c.pool = newConnPool(&opt)
-	})
-	return c.pool
-}
-
-func (c *sentinelFailover) dial(ctx context.Context, network, _ string) (net.Conn, error) {
-	addr, err := c.MasterAddr()
-	if err != nil {
-		return nil, err
-	}
-	if c.opt.Dialer != nil {
-		return c.opt.Dialer(ctx, network, addr)
-	}
-	return net.DialTimeout("tcp", addr, c.opt.DialTimeout)
-}
-
-func (c *sentinelFailover) MasterAddr() (string, error) {
-	addr, err := c.masterAddr()
-	if err != nil {
-		return "", err
-	}
-	c.switchMaster(addr)
-	return addr, nil
-}
-
-func (c *sentinelFailover) masterAddr() (string, error) {
-	c.mu.RLock()
-	sentinel := c.sentinel
-	c.mu.RUnlock()
-
-	if sentinel != nil {
-		addr := c.getMasterAddr(sentinel)
-		if addr != "" {
-			return addr, nil
-		}
-	}
-
-	c.mu.Lock()
-	defer c.mu.Unlock()
-
-	if c.sentinel != nil {
-		addr := c.getMasterAddr(c.sentinel)
-		if addr != "" {
-			return addr, nil
-		}
-		_ = c.closeSentinel()
-	}
-
-	for i, sentinelAddr := range c.sentinelAddrs {
-		sentinel := NewSentinelClient(&Options{
-			Addr:   sentinelAddr,
-			Dialer: c.opt.Dialer,
-
-			Username: c.username,
-			Password: c.password,
-
-			MaxRetries: c.opt.MaxRetries,
-
-			DialTimeout:  c.opt.DialTimeout,
-			ReadTimeout:  c.opt.ReadTimeout,
-			WriteTimeout: c.opt.WriteTimeout,
-
-			PoolSize:           c.opt.PoolSize,
-			PoolTimeout:        c.opt.PoolTimeout,
-			IdleTimeout:        c.opt.IdleTimeout,
-			IdleCheckFrequency: c.opt.IdleCheckFrequency,
-
-			TLSConfig: c.opt.TLSConfig,
-		})
-
-		masterAddr, err := sentinel.GetMasterAddrByName(c.masterName).Result()
-		if err != nil {
-			internal.Logger.Printf("sentinel: GetMasterAddrByName master=%q failed: %s",
-				c.masterName, err)
-			_ = sentinel.Close()
-			continue
-		}
-
-		// Push working sentinel to the top.
-		c.sentinelAddrs[0], c.sentinelAddrs[i] = c.sentinelAddrs[i], c.sentinelAddrs[0]
-		c.setSentinel(sentinel)
-
-		addr := net.JoinHostPort(masterAddr[0], masterAddr[1])
-		return addr, nil
-	}
-
-	return "", errors.New("redis: all sentinels are unreachable")
-}
-
-func (c *sentinelFailover) getMasterAddr(sentinel *SentinelClient) string {
-	addr, err := sentinel.GetMasterAddrByName(c.masterName).Result()
-	if err != nil {
-		internal.Logger.Printf("sentinel: GetMasterAddrByName name=%q failed: %s",
-			c.masterName, err)
-		return ""
-	}
-	return net.JoinHostPort(addr[0], addr[1])
-}
-
-func (c *sentinelFailover) switchMaster(addr string) {
-	c.mu.RLock()
-	masterAddr := c._masterAddr
-	c.mu.RUnlock()
-	if masterAddr == addr {
-		return
-	}
-
-	c.mu.Lock()
-	defer c.mu.Unlock()
-
-	if c._masterAddr == addr {
-		return
-	}
-
-	internal.Logger.Printf("sentinel: new master=%q addr=%q",
-		c.masterName, addr)
-	_ = c.Pool().Filter(func(cn *pool.Conn) bool {
-		return cn.RemoteAddr().String() != addr
-	})
-	c._masterAddr = addr
-}
-
-func (c *sentinelFailover) setSentinel(sentinel *SentinelClient) {
-	if c.sentinel != nil {
-		panic("not reached")
-	}
-	c.sentinel = sentinel
-	c.discoverSentinels()
-
-	c.pubsub = sentinel.Subscribe("+switch-master")
-	go c.listen(c.pubsub)
-}
-
-func (c *sentinelFailover) discoverSentinels() {
-	sentinels, err := c.sentinel.Sentinels(c.masterName).Result()
-	if err != nil {
-		internal.Logger.Printf("sentinel: Sentinels master=%q failed: %s", c.masterName, err)
-		return
-	}
-	for _, sentinel := range sentinels {
-		vals := sentinel.([]interface{})
-		for i := 0; i < len(vals); i += 2 {
-			key := vals[i].(string)
-			if key == "name" {
-				sentinelAddr := vals[i+1].(string)
-				if !contains(c.sentinelAddrs, sentinelAddr) {
-					internal.Logger.Printf("sentinel: discovered new sentinel=%q for master=%q",
-						sentinelAddr, c.masterName)
-					c.sentinelAddrs = append(c.sentinelAddrs, sentinelAddr)
-				}
-			}
-		}
-	}
-}
-
-func (c *sentinelFailover) listen(pubsub *PubSub) {
-	ch := pubsub.Channel()
-	for {
-		msg, ok := <-ch
-		if !ok {
-			break
-		}
-
-		if msg.Channel == "+switch-master" {
-			parts := strings.Split(msg.Payload, " ")
-			if parts[0] != c.masterName {
-				internal.Logger.Printf("sentinel: ignore addr for master=%q", parts[0])
-				continue
-			}
-			addr := net.JoinHostPort(parts[3], parts[4])
-			c.switchMaster(addr)
-		}
-	}
-}
-
-func contains(slice []string, str string) bool {
-	for _, s := range slice {
-		if s == str {
-			return true
-		}
-	}
-	return false
-}
diff --git a/vendor/github.com/go-redis/redis/v7/.gitignore b/vendor/github.com/go-redis/redis/v8/.gitignore
similarity index 72%
rename from vendor/github.com/go-redis/redis/v7/.gitignore
rename to vendor/github.com/go-redis/redis/v8/.gitignore
index ebfe903bcd..b975a7b4c3 100644
--- a/vendor/github.com/go-redis/redis/v7/.gitignore
+++ b/vendor/github.com/go-redis/redis/v8/.gitignore
@@ -1,2 +1,3 @@
 *.rdb
 testdata/*/
+.idea/
diff --git a/vendor/github.com/go-redis/redis/v7/.golangci.yml b/vendor/github.com/go-redis/redis/v8/.golangci.yml
similarity index 58%
rename from vendor/github.com/go-redis/redis/v7/.golangci.yml
rename to vendor/github.com/go-redis/redis/v8/.golangci.yml
index 912dab1ef3..b88b2b9b57 100644
--- a/vendor/github.com/go-redis/redis/v7/.golangci.yml
+++ b/vendor/github.com/go-redis/redis/v8/.golangci.yml
@@ -7,9 +7,18 @@ linters:
   disable:
     - funlen
     - gochecknoglobals
+    - gochecknoinits
     - gocognit
     - goconst
     - godox
     - gosec
     - maligned
     - wsl
+    - gomnd
+    - goerr113
+    - exhaustive
+    - nestif
+    - nlreturn
+    - exhaustivestruct
+    - wrapcheck
+    - errorlint
diff --git a/vendor/github.com/go-redis/redis/v8/.prettierrc b/vendor/github.com/go-redis/redis/v8/.prettierrc
new file mode 100644
index 0000000000..8b7f044ad1
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/.prettierrc
@@ -0,0 +1,4 @@
+semi: false
+singleQuote: true
+proseWrap: always
+printWidth: 100
diff --git a/vendor/github.com/go-redis/redis/v8/.travis.yml b/vendor/github.com/go-redis/redis/v8/.travis.yml
new file mode 100644
index 0000000000..a221d84dba
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/.travis.yml
@@ -0,0 +1,20 @@
+dist: xenial
+language: go
+
+services:
+  - redis-server
+
+go:
+  - 1.14.x
+  - 1.15.x
+  - tip
+
+matrix:
+  allow_failures:
+    - go: tip
+
+go_import_path: github.com/go-redis/redis
+
+before_install:
+  - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s --
+    -b $(go env GOPATH)/bin v1.32.2
diff --git a/vendor/github.com/go-redis/redis/v8/CHANGELOG.md b/vendor/github.com/go-redis/redis/v8/CHANGELOG.md
new file mode 100644
index 0000000000..8392d54859
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Changelog
+
+> :heart: [**Uptrace.dev** - distributed traces, logs, and errors in one place](https://uptrace.dev)
+
+See https://redis.uptrace.dev/changelog/
diff --git a/vendor/github.com/go-redis/redis/v7/LICENSE b/vendor/github.com/go-redis/redis/v8/LICENSE
similarity index 100%
rename from vendor/github.com/go-redis/redis/v7/LICENSE
rename to vendor/github.com/go-redis/redis/v8/LICENSE
diff --git a/vendor/github.com/go-redis/redis/v7/Makefile b/vendor/github.com/go-redis/redis/v8/Makefile
similarity index 77%
rename from vendor/github.com/go-redis/redis/v7/Makefile
rename to vendor/github.com/go-redis/redis/v8/Makefile
index 86609c6e07..d5d5d1900f 100644
--- a/vendor/github.com/go-redis/redis/v7/Makefile
+++ b/vendor/github.com/go-redis/redis/v8/Makefile
@@ -3,6 +3,7 @@ all: testdeps
 	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
@@ -18,3 +19,9 @@ testdata/redis:
 
 testdata/redis/src/redis-server: testdata/redis
 	cd $< && make all
+
+tag:
+	git tag $(VERSION)
+	git tag extra/rediscmd/$(VERSION)
+	git tag extra/redisotel/$(VERSION)
+	git tag extra/rediscensus/$(VERSION)
diff --git a/vendor/github.com/go-redis/redis/v8/README.md b/vendor/github.com/go-redis/redis/v8/README.md
new file mode 100644
index 0000000000..e086653665
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/README.md
@@ -0,0 +1,159 @@
+# Redis client for Golang
+
+[![Build Status](https://travis-ci.org/go-redis/redis.png?branch=master)](https://travis-ci.org/go-redis/redis)
+[![PkgGoDev](https://pkg.go.dev/badge/github.com/go-redis/redis/v8)](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc)
+[![Documentation](https://img.shields.io/badge/redis-documentation-informational)](https://redis.uptrace.dev/)
+[![Chat](https://discordapp.com/api/guilds/752070105847955518/widget.png)](https://discord.gg/rWtp5Aj)
+
+> :heart: [**Uptrace.dev** - distributed traces, logs, and errors in one place](https://uptrace.dev)
+
+- Join [Discord](https://discord.gg/rWtp5Aj) to ask questions.
+- [Documentation](https://redis.uptrace.dev)
+- [Reference](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc)
+- [Examples](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#pkg-examples)
+- [RealWorld example app](https://github.com/uptrace/go-treemux-realworld-example-app)
+
+## Ecosystem
+
+- [Redis Mock](https://github.com/go-redis/redismock).
+- [Distributed Locks](https://github.com/bsm/redislock).
+- [Redis Cache](https://github.com/go-redis/cache).
+- [Rate limiting](https://github.com/go-redis/redis_rate).
+
+## Features
+
+- Redis 3 commands except QUIT, MONITOR, and SYNC.
+- Automatic connection pooling with
+  [circuit breaker](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) support.
+- [Pub/Sub](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#PubSub).
+- [Transactions](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#example-Client-TxPipeline).
+- [Pipeline](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#example-Client-Pipeline) and
+  [TxPipeline](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#example-Client-TxPipeline).
+- [Scripting](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#Script).
+- [Timeouts](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#Options).
+- [Redis Sentinel](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#NewFailoverClient).
+- [Redis Cluster](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#NewClusterClient).
+- [Cluster of Redis Servers](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#example-NewClusterClient--ManualSetup)
+  without using cluster mode and Redis Sentinel.
+- [Ring](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#NewRing).
+- [Instrumentation](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#ex-package--Instrumentation).
+
+## Installation
+
+go-redis supports 2 last Go versions and requires a Go version with
+[modules](https://github.com/golang/go/wiki/Modules) support. So make sure to initialize a Go
+module:
+
+```shell
+go mod init github.com/my/repo
+```
+
+And then install go-redis/v8 (note _v8_ in the import; omitting it is a popular mistake):
+
+```shell
+go get github.com/go-redis/redis/v8
+```
+
+## Quickstart
+
+```go
+import (
+    "context"
+    "github.com/go-redis/redis/v8"
+)
+
+var ctx = context.Background()
+
+func ExampleClient() {
+    rdb := redis.NewClient(&redis.Options{
+        Addr:     "localhost:6379",
+        Password: "", // no password set
+        DB:       0,  // use default DB
+    })
+
+    err := rdb.Set(ctx, "key", "value", 0).Err()
+    if err != nil {
+        panic(err)
+    }
+
+    val, err := rdb.Get(ctx, "key").Result()
+    if err != nil {
+        panic(err)
+    }
+    fmt.Println("key", val)
+
+    val2, err := rdb.Get(ctx, "key2").Result()
+    if err == redis.Nil {
+        fmt.Println("key2 does not exist")
+    } else if err != nil {
+        panic(err)
+    } else {
+        fmt.Println("key2", val2)
+    }
+    // Output: key value
+    // key2 does not exist
+}
+```
+
+## Look and feel
+
+Some corner cases:
+
+```go
+// SET key value EX 10 NX
+set, err := rdb.SetNX(ctx, "key", "value", 10*time.Second).Result()
+
+// SET key value keepttl NX
+set, err := rdb.SetNX(ctx, "key", "value", redis.KeepTTL).Result()
+
+// SORT list LIMIT 0 2 ASC
+vals, err := rdb.Sort(ctx, "list", &redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result()
+
+// ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2
+vals, err := rdb.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{
+    Min: "-inf",
+    Max: "+inf",
+    Offset: 0,
+    Count: 2,
+}).Result()
+
+// ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM
+vals, err := rdb.ZInterStore(ctx, "out", &redis.ZStore{
+    Keys: []string{"zset1", "zset2"},
+    Weights: []int64{2, 3}
+}).Result()
+
+// EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello"
+vals, err := rdb.Eval(ctx, "return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result()
+
+// custom command
+res, err := rdb.Do(ctx, "set", "key", "value").Result()
+```
+## Run the test
+go-redis will start a redis-server and run the test cases. 
+
+The paths of redis-server bin file and redis config file are definded in `main_test.go`:
+```
+var (
+	redisServerBin, _  = filepath.Abs(filepath.Join("testdata", "redis", "src", "redis-server"))
+	redisServerConf, _ = filepath.Abs(filepath.Join("testdata", "redis", "redis.conf"))
+)
+```
+
+For local testing, you can change the variables to refer to your local files, or create a soft link to the corresponding folder for redis-server and copy the config file to `testdata/redis/`:
+```
+ln -s /usr/bin/redis-server ./go-redis/testdata/redis/src
+cp ./go-redis/testdata/redis.conf ./go-redis/testdata/redis/
+```
+
+Lastly, run:
+```
+go test
+```
+
+## See also
+
+- [Fast and flexible HTTP router](https://github.com/vmihailenco/treemux)
+- [Golang PostgreSQL ORM](https://github.com/go-pg/pg)
+- [Golang msgpack](https://github.com/vmihailenco/msgpack)
+- [Golang message task queue](https://github.com/vmihailenco/taskq)
diff --git a/vendor/github.com/go-redis/redis/v7/cluster.go b/vendor/github.com/go-redis/redis/v8/cluster.go
similarity index 75%
rename from vendor/github.com/go-redis/redis/v7/cluster.go
rename to vendor/github.com/go-redis/redis/v8/cluster.go
index 1907de6c41..2ce475c414 100644
--- a/vendor/github.com/go-redis/redis/v7/cluster.go
+++ b/vendor/github.com/go-redis/redis/v8/cluster.go
@@ -5,7 +5,6 @@ import (
 	"crypto/tls"
 	"fmt"
 	"math"
-	"math/rand"
 	"net"
 	"runtime"
 	"sort"
@@ -13,10 +12,11 @@ import (
 	"sync/atomic"
 	"time"
 
-	"github.com/go-redis/redis/v7/internal"
-	"github.com/go-redis/redis/v7/internal/hashtag"
-	"github.com/go-redis/redis/v7/internal/pool"
-	"github.com/go-redis/redis/v7/internal/proto"
+	"github.com/go-redis/redis/v8/internal"
+	"github.com/go-redis/redis/v8/internal/hashtag"
+	"github.com/go-redis/redis/v8/internal/pool"
+	"github.com/go-redis/redis/v8/internal/proto"
+	"github.com/go-redis/redis/v8/internal/rand"
 )
 
 var errClusterNoNodes = fmt.Errorf("redis: cluster has no nodes")
@@ -27,9 +27,12 @@ type ClusterOptions struct {
 	// A seed list of host:port addresses of cluster nodes.
 	Addrs []string
 
+	// NewClient creates a cluster node client with provided name and options.
+	NewClient func(opt *Options) *Client
+
 	// The maximum number of retries before giving up. Command is retried
 	// on network errors and MOVED/ASK redirects.
-	// Default is 8 retries.
+	// Default is 3 retries.
 	MaxRedirects int
 
 	// Enables read-only commands on slave nodes.
@@ -46,16 +49,13 @@ type ClusterOptions struct {
 	// and load-balance read/write operations between master and slaves.
 	// It can use service like ZooKeeper to maintain configuration information
 	// and Cluster.ReloadState to manually trigger state reloading.
-	ClusterSlots func() ([]ClusterSlot, error)
-
-	// Optional hook that is called when a new node is created.
-	OnNewNode func(*Client)
+	ClusterSlots func(context.Context) ([]ClusterSlot, error)
 
 	// Following options are copied from Options struct.
 
 	Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
 
-	OnConnect func(*Conn) error
+	OnConnect func(ctx context.Context, cn *Conn) error
 
 	Username string
 	Password string
@@ -68,9 +68,6 @@ type ClusterOptions struct {
 	ReadTimeout  time.Duration
 	WriteTimeout time.Duration
 
-	// NewClient creates a cluster node client with provided name and options.
-	NewClient func(opt *Options) *Client
-
 	// PoolSize applies per cluster node and not for the whole cluster.
 	PoolSize           int
 	MinIdleConns       int
@@ -86,10 +83,10 @@ func (opt *ClusterOptions) init() {
 	if opt.MaxRedirects == -1 {
 		opt.MaxRedirects = 0
 	} else if opt.MaxRedirects == 0 {
-		opt.MaxRedirects = 8
+		opt.MaxRedirects = 3
 	}
 
-	if (opt.RouteByLatency || opt.RouteRandomly) && opt.ClusterSlots == nil {
+	if opt.RouteByLatency || opt.RouteRandomly {
 		opt.ReadOnly = true
 	}
 
@@ -110,6 +107,9 @@ func (opt *ClusterOptions) init() {
 		opt.WriteTimeout = opt.ReadTimeout
 	}
 
+	if opt.MaxRetries == 0 {
+		opt.MaxRetries = -1
+	}
 	switch opt.MinRetryBackoff {
 	case -1:
 		opt.MinRetryBackoff = 0
@@ -135,12 +135,12 @@ func (opt *ClusterOptions) clientOptions() *Options {
 		Dialer:    opt.Dialer,
 		OnConnect: opt.OnConnect,
 
+		Username: opt.Username,
+		Password: opt.Password,
+
 		MaxRetries:      opt.MaxRetries,
 		MinRetryBackoff: opt.MinRetryBackoff,
 		MaxRetryBackoff: opt.MaxRetryBackoff,
-		Username:        opt.Username,
-		Password:        opt.Password,
-		readOnly:        opt.ReadOnly,
 
 		DialTimeout:  opt.DialTimeout,
 		ReadTimeout:  opt.ReadTimeout,
@@ -154,6 +154,12 @@ func (opt *ClusterOptions) clientOptions() *Options {
 		IdleCheckFrequency: disableIdleCheck,
 
 		TLSConfig: opt.TLSConfig,
+		// If ClusterSlots is populated, then we probably have an artificial
+		// cluster whose nodes are not in clustering mode (otherwise there isn't
+		// much use for ClusterSlots config).  This means we cannot execute the
+		// READONLY command against that node -- setting readOnly to false in such
+		// situations in the options below will prevent that from happening.
+		readOnly: opt.ReadOnly && opt.ClusterSlots == nil,
 	}
 }
 
@@ -179,10 +185,6 @@ func newClusterNode(clOpt *ClusterOptions, addr string) *clusterNode {
 		go node.updateLatency()
 	}
 
-	if clOpt.OnNewNode != nil {
-		clOpt.OnNewNode(node.Client)
-	}
-
 	return &node
 }
 
@@ -195,16 +197,19 @@ func (n *clusterNode) Close() error {
 }
 
 func (n *clusterNode) updateLatency() {
-	const probes = 10
+	const numProbe = 10
+	var dur uint64
+
+	for i := 0; i < numProbe; i++ {
+		time.Sleep(time.Duration(10+rand.Intn(10)) * time.Millisecond)
 
-	var latency uint32
-	for i := 0; i < probes; i++ {
 		start := time.Now()
-		n.Client.Ping()
-		probe := uint32(time.Since(start) / time.Microsecond)
-		latency = (latency + probe) / 2
+		n.Client.Ping(context.TODO())
+		dur += uint64(time.Since(start) / time.Microsecond)
 	}
-	atomic.StoreUint32(&n.latency, latency)
+
+	latency := float64(dur) / float64(numProbe)
+	atomic.StoreUint32(&n.latency, uint32(latency+0.5))
 }
 
 func (n *clusterNode) Latency() time.Duration {
@@ -248,11 +253,11 @@ func (n *clusterNode) SetGeneration(gen uint32) {
 type clusterNodes struct {
 	opt *ClusterOptions
 
-	mu           sync.RWMutex
-	allAddrs     []string
-	allNodes     map[string]*clusterNode
-	clusterAddrs []string
-	closed       bool
+	mu          sync.RWMutex
+	addrs       []string
+	nodes       map[string]*clusterNode
+	activeAddrs []string
+	closed      bool
 
 	_generation uint32 // atomic
 }
@@ -261,8 +266,8 @@ func newClusterNodes(opt *ClusterOptions) *clusterNodes {
 	return &clusterNodes{
 		opt: opt,
 
-		allAddrs: opt.Addrs,
-		allNodes: make(map[string]*clusterNode),
+		addrs: opt.Addrs,
+		nodes: make(map[string]*clusterNode),
 	}
 }
 
@@ -276,14 +281,14 @@ func (c *clusterNodes) Close() error {
 	c.closed = true
 
 	var firstErr error
-	for _, node := range c.allNodes {
+	for _, node := range c.nodes {
 		if err := node.Client.Close(); err != nil && firstErr == nil {
 			firstErr = err
 		}
 	}
 
-	c.allNodes = nil
-	c.clusterAddrs = nil
+	c.nodes = nil
+	c.activeAddrs = nil
 
 	return firstErr
 }
@@ -293,10 +298,10 @@ func (c *clusterNodes) Addrs() ([]string, error) {
 	c.mu.RLock()
 	closed := c.closed
 	if !closed {
-		if len(c.clusterAddrs) > 0 {
-			addrs = c.clusterAddrs
+		if len(c.activeAddrs) > 0 {
+			addrs = c.activeAddrs
 		} else {
-			addrs = c.allAddrs
+			addrs = c.addrs
 		}
 	}
 	c.mu.RUnlock()
@@ -318,16 +323,23 @@ func (c *clusterNodes) NextGeneration() uint32 {
 func (c *clusterNodes) GC(generation uint32) {
 	//nolint:prealloc
 	var collected []*clusterNode
+
 	c.mu.Lock()
-	for addr, node := range c.allNodes {
+
+	c.activeAddrs = c.activeAddrs[:0]
+	for addr, node := range c.nodes {
 		if node.Generation() >= generation {
+			c.activeAddrs = append(c.activeAddrs, addr)
+			if c.opt.RouteByLatency {
+				go node.updateLatency()
+			}
 			continue
 		}
 
-		c.clusterAddrs = remove(c.clusterAddrs, addr)
-		delete(c.allNodes, addr)
+		delete(c.nodes, addr)
 		collected = append(collected, node)
 	}
+
 	c.mu.Unlock()
 
 	for _, node := range collected {
@@ -351,18 +363,17 @@ func (c *clusterNodes) Get(addr string) (*clusterNode, error) {
 		return nil, pool.ErrClosed
 	}
 
-	node, ok := c.allNodes[addr]
+	node, ok := c.nodes[addr]
 	if ok {
-		return node, err
+		return node, nil
 	}
 
 	node = newClusterNode(c.opt, addr)
 
-	c.allAddrs = appendIfNotExists(c.allAddrs, addr)
-	c.clusterAddrs = append(c.clusterAddrs, addr)
-	c.allNodes[addr] = node
+	c.addrs = appendIfNotExists(c.addrs, addr)
+	c.nodes[addr] = node
 
-	return node, err
+	return node, nil
 }
 
 func (c *clusterNodes) get(addr string) (*clusterNode, error) {
@@ -372,7 +383,7 @@ func (c *clusterNodes) get(addr string) (*clusterNode, error) {
 	if c.closed {
 		err = pool.ErrClosed
 	} else {
-		node = c.allNodes[addr]
+		node = c.nodes[addr]
 	}
 	c.mu.RUnlock()
 	return node, err
@@ -386,8 +397,8 @@ func (c *clusterNodes) All() ([]*clusterNode, error) {
 		return nil, pool.ErrClosed
 	}
 
-	cp := make([]*clusterNode, 0, len(c.allNodes))
-	for _, node := range c.allNodes {
+	cp := make([]*clusterNode, 0, len(c.nodes))
+	for _, node := range c.nodes {
 		cp = append(cp, node)
 	}
 	return cp, nil
@@ -552,8 +563,6 @@ func (c *clusterState) slotSlaveNode(slot int) (*clusterNode, error) {
 }
 
 func (c *clusterState) slotClosestNode(slot int) (*clusterNode, error) {
-	const threshold = time.Millisecond
-
 	nodes := c.slotNodes(slot)
 	if len(nodes) == 0 {
 		return c.nodes.Random()
@@ -564,11 +573,16 @@ func (c *clusterState) slotClosestNode(slot int) (*clusterNode, error) {
 		if n.Failing() {
 			continue
 		}
-		if node == nil || node.Latency()-n.Latency() > threshold {
+		if node == nil || n.Latency() < node.Latency() {
 			node = n
 		}
 	}
-	return node, nil
+	if node != nil {
+		return node, nil
+	}
+
+	// If all nodes are failing - return random node
+	return c.nodes.Random()
 }
 
 func (c *clusterState) slotRandomNode(slot int) (*clusterNode, error) {
@@ -597,20 +611,20 @@ func (c *clusterState) slotNodes(slot int) []*clusterNode {
 //------------------------------------------------------------------------------
 
 type clusterStateHolder struct {
-	load func() (*clusterState, error)
+	load func(ctx context.Context) (*clusterState, error)
 
 	state     atomic.Value
 	reloading uint32 // atomic
 }
 
-func newClusterStateHolder(fn func() (*clusterState, error)) *clusterStateHolder {
+func newClusterStateHolder(fn func(ctx context.Context) (*clusterState, error)) *clusterStateHolder {
 	return &clusterStateHolder{
 		load: fn,
 	}
 }
 
-func (c *clusterStateHolder) Reload() (*clusterState, error) {
-	state, err := c.load()
+func (c *clusterStateHolder) Reload(ctx context.Context) (*clusterState, error) {
+	state, err := c.load(ctx)
 	if err != nil {
 		return nil, err
 	}
@@ -618,39 +632,39 @@ func (c *clusterStateHolder) Reload() (*clusterState, error) {
 	return state, nil
 }
 
-func (c *clusterStateHolder) LazyReload() {
+func (c *clusterStateHolder) LazyReload(ctx context.Context) {
 	if !atomic.CompareAndSwapUint32(&c.reloading, 0, 1) {
 		return
 	}
 	go func() {
 		defer atomic.StoreUint32(&c.reloading, 0)
 
-		_, err := c.Reload()
+		_, err := c.Reload(ctx)
 		if err != nil {
 			return
 		}
-		time.Sleep(100 * time.Millisecond)
+		time.Sleep(200 * time.Millisecond)
 	}()
 }
 
-func (c *clusterStateHolder) Get() (*clusterState, error) {
+func (c *clusterStateHolder) Get(ctx context.Context) (*clusterState, error) {
 	v := c.state.Load()
 	if v != nil {
 		state := v.(*clusterState)
-		if time.Since(state.createdAt) > time.Minute {
-			c.LazyReload()
+		if time.Since(state.createdAt) > 10*time.Second {
+			c.LazyReload(ctx)
 		}
 		return state, nil
 	}
-	return c.Reload()
+	return c.Reload(ctx)
 }
 
-func (c *clusterStateHolder) ReloadOrGet() (*clusterState, error) {
-	state, err := c.Reload()
+func (c *clusterStateHolder) ReloadOrGet(ctx context.Context) (*clusterState, error) {
+	state, err := c.Reload(ctx)
 	if err == nil {
 		return state, nil
 	}
-	return c.Get()
+	return c.Get(ctx)
 }
 
 //------------------------------------------------------------------------------
@@ -717,9 +731,8 @@ func (c *ClusterClient) Options() *ClusterOptions {
 
 // 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
+func (c *ClusterClient) ReloadState(ctx context.Context) {
+	c.state.LazyReload(ctx)
 }
 
 // Close closes the cluster client, releasing any open resources.
@@ -731,34 +744,17 @@ 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.ProcessContext(ctx, cmd)
+func (c *ClusterClient) Do(ctx context.Context, args ...interface{}) *Cmd {
+	cmd := NewCmd(ctx, args...)
+	_ = c.Process(ctx, cmd)
 	return cmd
 }
 
-func (c *ClusterClient) Process(cmd Cmder) error {
-	return c.ProcessContext(c.ctx, cmd)
-}
-
-func (c *ClusterClient) ProcessContext(ctx context.Context, cmd Cmder) error {
+func (c *ClusterClient) Process(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)
 
@@ -774,7 +770,7 @@ func (c *ClusterClient) _process(ctx context.Context, cmd Cmder) error {
 
 		if node == nil {
 			var err error
-			node, err = c.cmdNode(cmdInfo, slot)
+			node, err = c.cmdNode(ctx, cmdInfo, slot)
 			if err != nil {
 				return err
 			}
@@ -782,23 +778,23 @@ func (c *ClusterClient) _process(ctx context.Context, cmd Cmder) error {
 
 		if ask {
 			pipe := node.Client.Pipeline()
-			_ = pipe.Process(NewCmd("asking"))
-			_ = pipe.Process(cmd)
-			_, lastErr = pipe.ExecContext(ctx)
+			_ = pipe.Process(ctx, NewCmd(ctx, "asking"))
+			_ = pipe.Process(ctx, cmd)
+			_, lastErr = pipe.Exec(ctx)
 			_ = pipe.Close()
 			ask = false
 		} else {
-			lastErr = node.Client.ProcessContext(ctx, cmd)
+			lastErr = node.Client.Process(ctx, cmd)
 		}
 
 		// If there is no error - we are done.
 		if lastErr == nil {
 			return nil
 		}
-		if lastErr != Nil {
-			c.state.LazyReload()
-		}
-		if lastErr == pool.ErrClosed || isReadOnlyError(lastErr) {
+		if isReadOnly := isReadOnlyError(lastErr); isReadOnly || lastErr == pool.ErrClosed {
+			if isReadOnly {
+				c.state.LazyReload(ctx)
+			}
 			node = nil
 			continue
 		}
@@ -822,7 +818,7 @@ func (c *ClusterClient) _process(ctx context.Context, cmd Cmder) error {
 			continue
 		}
 
-		if isRetryableError(lastErr, cmd.readTimeout() == nil) {
+		if shouldRetry(lastErr, cmd.readTimeout() == nil) {
 			// First retry the same node.
 			if attempt == 0 {
 				continue
@@ -841,8 +837,11 @@ func (c *ClusterClient) _process(ctx context.Context, cmd Cmder) error {
 
 // ForEachMaster concurrently calls the fn on each master node in the cluster.
 // It returns the first error if any.
-func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
-	state, err := c.state.ReloadOrGet()
+func (c *ClusterClient) ForEachMaster(
+	ctx context.Context,
+	fn func(ctx context.Context, client *Client) error,
+) error {
+	state, err := c.state.ReloadOrGet(ctx)
 	if err != nil {
 		return err
 	}
@@ -854,7 +853,7 @@ func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
 		wg.Add(1)
 		go func(node *clusterNode) {
 			defer wg.Done()
-			err := fn(node.Client)
+			err := fn(ctx, node.Client)
 			if err != nil {
 				select {
 				case errCh <- err:
@@ -876,8 +875,11 @@ func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
 
 // ForEachSlave concurrently calls the fn on each slave node in the cluster.
 // It returns the first error if any.
-func (c *ClusterClient) ForEachSlave(fn func(client *Client) error) error {
-	state, err := c.state.ReloadOrGet()
+func (c *ClusterClient) ForEachSlave(
+	ctx context.Context,
+	fn func(ctx context.Context, client *Client) error,
+) error {
+	state, err := c.state.ReloadOrGet(ctx)
 	if err != nil {
 		return err
 	}
@@ -889,7 +891,7 @@ func (c *ClusterClient) ForEachSlave(fn func(client *Client) error) error {
 		wg.Add(1)
 		go func(node *clusterNode) {
 			defer wg.Done()
-			err := fn(node.Client)
+			err := fn(ctx, node.Client)
 			if err != nil {
 				select {
 				case errCh <- err:
@@ -909,10 +911,13 @@ func (c *ClusterClient) ForEachSlave(fn func(client *Client) error) error {
 	}
 }
 
-// ForEachNode concurrently calls the fn on each known node in the cluster.
+// ForEachShard concurrently calls the fn on each known node in the cluster.
 // It returns the first error if any.
-func (c *ClusterClient) ForEachNode(fn func(client *Client) error) error {
-	state, err := c.state.ReloadOrGet()
+func (c *ClusterClient) ForEachShard(
+	ctx context.Context,
+	fn func(ctx context.Context, client *Client) error,
+) error {
+	state, err := c.state.ReloadOrGet(ctx)
 	if err != nil {
 		return err
 	}
@@ -922,7 +927,7 @@ func (c *ClusterClient) ForEachNode(fn func(client *Client) error) error {
 
 	worker := func(node *clusterNode) {
 		defer wg.Done()
-		err := fn(node.Client)
+		err := fn(ctx, node.Client)
 		if err != nil {
 			select {
 			case errCh <- err:
@@ -954,7 +959,7 @@ func (c *ClusterClient) ForEachNode(fn func(client *Client) error) error {
 func (c *ClusterClient) PoolStats() *PoolStats {
 	var acc PoolStats
 
-	state, _ := c.state.Get()
+	state, _ := c.state.Get(context.TODO())
 	if state == nil {
 		return &acc
 	}
@@ -984,9 +989,9 @@ func (c *ClusterClient) PoolStats() *PoolStats {
 	return &acc
 }
 
-func (c *ClusterClient) loadState() (*clusterState, error) {
+func (c *ClusterClient) loadState(ctx context.Context) (*clusterState, error) {
 	if c.opt.ClusterSlots != nil {
-		slots, err := c.opt.ClusterSlots()
+		slots, err := c.opt.ClusterSlots(ctx)
 		if err != nil {
 			return nil, err
 		}
@@ -999,7 +1004,10 @@ func (c *ClusterClient) loadState() (*clusterState, error) {
 	}
 
 	var firstErr error
-	for _, addr := range addrs {
+
+	for _, idx := range rand.Perm(len(addrs)) {
+		addr := addrs[idx]
+
 		node, err := c.nodes.Get(addr)
 		if err != nil {
 			if firstErr == nil {
@@ -1008,7 +1016,7 @@ func (c *ClusterClient) loadState() (*clusterState, error) {
 			continue
 		}
 
-		slots, err := node.Client.ClusterSlots().Result()
+		slots, err := node.Client.ClusterSlots(ctx).Result()
 		if err != nil {
 			if firstErr == nil {
 				firstErr = err
@@ -1019,6 +1027,16 @@ func (c *ClusterClient) loadState() (*clusterState, error) {
 		return newClusterState(c.nodes, slots, node.Client.opt.Addr)
 	}
 
+	/*
+	 * No node is connectable. It's possible that all nodes' IP has changed.
+	 * Clear activeAddrs to let client be able to re-connect using the initial
+	 * setting of the addresses (e.g. [redis-cluster-0:6379, redis-cluster-1:6379]),
+	 * which might have chance to resolve domain name and get updated IP address.
+	 */
+	c.nodes.mu.Lock()
+	c.nodes.activeAddrs = nil
+	c.nodes.mu.Unlock()
+
 	return nil, firstErr
 }
 
@@ -1036,7 +1054,7 @@ func (c *ClusterClient) reaper(idleCheckFrequency time.Duration) {
 		for _, node := range nodes {
 			_, err := node.Client.connPool.(*pool.ConnPool).ReapStaleConns()
 			if err != nil {
-				internal.Logger.Printf("ReapStaleConns failed: %s", err)
+				internal.Logger.Printf(c.Context(), "ReapStaleConns failed: %s", err)
 			}
 		}
 	}
@@ -1051,8 +1069,8 @@ func (c *ClusterClient) Pipeline() Pipeliner {
 	return &pipe
 }
 
-func (c *ClusterClient) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
-	return c.Pipeline().Pipelined(fn)
+func (c *ClusterClient) Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) {
+	return c.Pipeline().Pipelined(ctx, fn)
 }
 
 func (c *ClusterClient) processPipeline(ctx context.Context, cmds []Cmder) error {
@@ -1061,7 +1079,7 @@ func (c *ClusterClient) processPipeline(ctx context.Context, cmds []Cmder) error
 
 func (c *ClusterClient) _processPipeline(ctx context.Context, cmds []Cmder) error {
 	cmdsMap := newCmdsMap()
-	err := c.mapCmdsByNode(cmdsMap, cmds)
+	err := c.mapCmdsByNode(ctx, cmdsMap, cmds)
 	if err != nil {
 		setCmdsErr(cmds, err)
 		return err
@@ -1088,7 +1106,7 @@ func (c *ClusterClient) _processPipeline(ctx context.Context, cmds []Cmder) erro
 					return
 				}
 				if attempt < c.opt.MaxRedirects {
-					if err := c.mapCmdsByNode(failedCmds, cmds); err != nil {
+					if err := c.mapCmdsByNode(ctx, failedCmds, cmds); err != nil {
 						setCmdsErr(cmds, err)
 					}
 				} else {
@@ -1107,8 +1125,8 @@ func (c *ClusterClient) _processPipeline(ctx context.Context, cmds []Cmder) erro
 	return cmdsFirstErr(cmds)
 }
 
-func (c *ClusterClient) mapCmdsByNode(cmdsMap *cmdsMap, cmds []Cmder) error {
-	state, err := c.state.Get()
+func (c *ClusterClient) mapCmdsByNode(ctx context.Context, cmdsMap *cmdsMap, cmds []Cmder) error {
+	state, err := c.state.Get(ctx)
 	if err != nil {
 		return err
 	}
@@ -1159,21 +1177,28 @@ func (c *ClusterClient) _processPipelineNode(
 			}
 
 			return cn.WithReader(ctx, c.opt.ReadTimeout, func(rd *proto.Reader) error {
-				return c.pipelineReadCmds(node, rd, cmds, failedCmds)
+				return c.pipelineReadCmds(ctx, node, rd, cmds, failedCmds)
 			})
 		})
 	})
 }
 
 func (c *ClusterClient) pipelineReadCmds(
-	node *clusterNode, rd *proto.Reader, cmds []Cmder, failedCmds *cmdsMap,
+	ctx context.Context,
+	node *clusterNode,
+	rd *proto.Reader,
+	cmds []Cmder,
+	failedCmds *cmdsMap,
 ) error {
 	for _, cmd := range cmds {
 		err := cmd.readReply(rd)
+		cmd.SetErr(err)
+
 		if err == nil {
 			continue
 		}
-		if c.checkMovedErr(cmd, err, failedCmds) {
+
+		if c.checkMovedErr(ctx, cmd, err, failedCmds) {
 			continue
 		}
 
@@ -1190,7 +1215,7 @@ func (c *ClusterClient) pipelineReadCmds(
 }
 
 func (c *ClusterClient) checkMovedErr(
-	cmd Cmder, err error, failedCmds *cmdsMap,
+	ctx context.Context, cmd Cmder, err error, failedCmds *cmdsMap,
 ) bool {
 	moved, ask, addr := isMovedError(err)
 	if !moved && !ask {
@@ -1203,13 +1228,13 @@ func (c *ClusterClient) checkMovedErr(
 	}
 
 	if moved {
-		c.state.LazyReload()
+		c.state.LazyReload(ctx)
 		failedCmds.Add(node, cmd)
 		return true
 	}
 
 	if ask {
-		failedCmds.Add(node, NewCmd("asking"), cmd)
+		failedCmds.Add(node, NewCmd(ctx, "asking"), cmd)
 		return true
 	}
 
@@ -1226,16 +1251,19 @@ func (c *ClusterClient) TxPipeline() Pipeliner {
 	return &pipe
 }
 
-func (c *ClusterClient) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
-	return c.TxPipeline().Pipelined(fn)
+func (c *ClusterClient) TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) {
+	return c.TxPipeline().Pipelined(ctx, fn)
 }
 
 func (c *ClusterClient) processTxPipeline(ctx context.Context, cmds []Cmder) error {
-	return c.hooks.processPipeline(ctx, cmds, c._processTxPipeline)
+	return c.hooks.processTxPipeline(ctx, cmds, c._processTxPipeline)
 }
 
 func (c *ClusterClient) _processTxPipeline(ctx context.Context, cmds []Cmder) error {
-	state, err := c.state.Get()
+	// Trim multi .. exec.
+	cmds = cmds[1 : len(cmds)-1]
+
+	state, err := c.state.Get(ctx)
 	if err != nil {
 		setCmdsErr(cmds, err)
 		return err
@@ -1270,8 +1298,9 @@ func (c *ClusterClient) _processTxPipeline(ctx context.Context, cmds []Cmder) er
 					if err == nil {
 						return
 					}
+
 					if attempt < c.opt.MaxRedirects {
-						if err := c.mapCmdsByNode(failedCmds, cmds); err != nil {
+						if err := c.mapCmdsByNode(ctx, failedCmds, cmds); err != nil {
 							setCmdsErr(cmds, err)
 						}
 					} else {
@@ -1317,11 +1346,11 @@ func (c *ClusterClient) _processTxPipelineNode(
 				// Trim multi and exec.
 				cmds = cmds[1 : len(cmds)-1]
 
-				err := c.txPipelineReadQueued(rd, statusCmd, cmds, failedCmds)
+				err := c.txPipelineReadQueued(ctx, rd, statusCmd, cmds, failedCmds)
 				if err != nil {
 					moved, ask, addr := isMovedError(err)
 					if moved || ask {
-						return c.cmdsMoved(cmds, moved, ask, addr, failedCmds)
+						return c.cmdsMoved(ctx, cmds, moved, ask, addr, failedCmds)
 					}
 					return err
 				}
@@ -1333,7 +1362,11 @@ func (c *ClusterClient) _processTxPipelineNode(
 }
 
 func (c *ClusterClient) txPipelineReadQueued(
-	rd *proto.Reader, statusCmd *StatusCmd, cmds []Cmder, failedCmds *cmdsMap,
+	ctx context.Context,
+	rd *proto.Reader,
+	statusCmd *StatusCmd,
+	cmds []Cmder,
+	failedCmds *cmdsMap,
 ) error {
 	// Parse queued replies.
 	if err := statusCmd.readReply(rd); err != nil {
@@ -1342,7 +1375,7 @@ func (c *ClusterClient) txPipelineReadQueued(
 
 	for _, cmd := range cmds {
 		err := statusCmd.readReply(rd)
-		if err == nil || c.checkMovedErr(cmd, err, failedCmds) || isRedisError(err) {
+		if err == nil || c.checkMovedErr(ctx, cmd, err, failedCmds) || isRedisError(err) {
 			continue
 		}
 		return err
@@ -1370,7 +1403,10 @@ func (c *ClusterClient) txPipelineReadQueued(
 }
 
 func (c *ClusterClient) cmdsMoved(
-	cmds []Cmder, moved, ask bool, addr string, failedCmds *cmdsMap,
+	ctx context.Context, cmds []Cmder,
+	moved, ask bool,
+	addr string,
+	failedCmds *cmdsMap,
 ) error {
 	node, err := c.nodes.Get(addr)
 	if err != nil {
@@ -1378,7 +1414,7 @@ func (c *ClusterClient) cmdsMoved(
 	}
 
 	if moved {
-		c.state.LazyReload()
+		c.state.LazyReload(ctx)
 		for _, cmd := range cmds {
 			failedCmds.Add(node, cmd)
 		}
@@ -1387,7 +1423,7 @@ func (c *ClusterClient) cmdsMoved(
 
 	if ask {
 		for _, cmd := range cmds {
-			failedCmds.Add(node, NewCmd("asking"), cmd)
+			failedCmds.Add(node, NewCmd(ctx, "asking"), cmd)
 		}
 		return nil
 	}
@@ -1395,11 +1431,7 @@ func (c *ClusterClient) cmdsMoved(
 	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 {
+func (c *ClusterClient) Watch(ctx context.Context, fn func(*Tx) error, keys ...string) error {
 	if len(keys) == 0 {
 		return fmt.Errorf("redis: Watch requires at least one key")
 	}
@@ -1412,7 +1444,7 @@ func (c *ClusterClient) WatchContext(ctx context.Context, fn func(*Tx) error, ke
 		}
 	}
 
-	node, err := c.slotMasterNode(slot)
+	node, err := c.slotMasterNode(ctx, slot)
 	if err != nil {
 		return err
 	}
@@ -1424,13 +1456,10 @@ func (c *ClusterClient) WatchContext(ctx context.Context, fn func(*Tx) error, ke
 			}
 		}
 
-		err = node.Client.WatchContext(ctx, fn, keys...)
+		err = node.Client.Watch(ctx, fn, keys...)
 		if err == nil {
 			break
 		}
-		if err != Nil {
-			c.state.LazyReload()
-		}
 
 		moved, ask, addr := isMovedError(err)
 		if moved || ask {
@@ -1441,15 +1470,18 @@ func (c *ClusterClient) WatchContext(ctx context.Context, fn func(*Tx) error, ke
 			continue
 		}
 
-		if err == pool.ErrClosed || isReadOnlyError(err) {
-			node, err = c.slotMasterNode(slot)
+		if isReadOnly := isReadOnlyError(err); isReadOnly || err == pool.ErrClosed {
+			if isReadOnly {
+				c.state.LazyReload(ctx)
+			}
+			node, err = c.slotMasterNode(ctx, slot)
 			if err != nil {
 				return err
 			}
 			continue
 		}
 
-		if isRetryableError(err, true) {
+		if shouldRetry(err, true) {
 			continue
 		}
 
@@ -1464,7 +1496,7 @@ func (c *ClusterClient) pubSub() *PubSub {
 	pubsub := &PubSub{
 		opt: c.opt.clientOptions(),
 
-		newConn: func(channels []string) (*pool.Conn, error) {
+		newConn: func(ctx context.Context, channels []string) (*pool.Conn, error) {
 			if node != nil {
 				panic("node != nil")
 			}
@@ -1472,7 +1504,7 @@ func (c *ClusterClient) pubSub() *PubSub {
 			var err error
 			if len(channels) > 0 {
 				slot := hashtag.Slot(channels[0])
-				node, err = c.slotMasterNode(slot)
+				node, err = c.slotMasterNode(ctx, slot)
 			} else {
 				node, err = c.nodes.Random()
 			}
@@ -1502,20 +1534,20 @@ func (c *ClusterClient) pubSub() *PubSub {
 
 // Subscribe subscribes the client to the specified channels.
 // Channels can be omitted to create empty subscription.
-func (c *ClusterClient) Subscribe(channels ...string) *PubSub {
+func (c *ClusterClient) Subscribe(ctx context.Context, channels ...string) *PubSub {
 	pubsub := c.pubSub()
 	if len(channels) > 0 {
-		_ = pubsub.Subscribe(channels...)
+		_ = pubsub.Subscribe(ctx, channels...)
 	}
 	return pubsub
 }
 
 // PSubscribe subscribes the client to the given patterns.
 // Patterns can be omitted to create empty subscription.
-func (c *ClusterClient) PSubscribe(channels ...string) *PubSub {
+func (c *ClusterClient) PSubscribe(ctx context.Context, channels ...string) *PubSub {
 	pubsub := c.pubSub()
 	if len(channels) > 0 {
-		_ = pubsub.PSubscribe(channels...)
+		_ = pubsub.PSubscribe(ctx, channels...)
 	}
 	return pubsub
 }
@@ -1524,23 +1556,34 @@ 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) {
+func (c *ClusterClient) cmdsInfo(ctx context.Context) (map[string]*CommandInfo, error) {
+	// Try 3 random nodes.
+	const nodeLimit = 3
+
 	addrs, err := c.nodes.Addrs()
 	if err != nil {
 		return nil, err
 	}
 
 	var firstErr error
-	for _, addr := range addrs {
+
+	perm := rand.Perm(len(addrs))
+	if len(perm) > nodeLimit {
+		perm = perm[:nodeLimit]
+	}
+
+	for _, idx := range perm {
+		addr := addrs[idx]
+
 		node, err := c.nodes.Get(addr)
 		if err != nil {
-			return nil, err
-		}
-		if node == nil {
+			if firstErr == nil {
+				firstErr = err
+			}
 			continue
 		}
 
-		info, err := node.Client.Command().Result()
+		info, err := node.Client.Command(ctx).Result()
 		if err == nil {
 			return info, nil
 		}
@@ -1548,18 +1591,22 @@ func (c *ClusterClient) cmdsInfo() (map[string]*CommandInfo, error) {
 			firstErr = err
 		}
 	}
+
+	if firstErr == nil {
+		panic("not reached")
+	}
 	return nil, firstErr
 }
 
 func (c *ClusterClient) cmdInfo(name string) *CommandInfo {
-	cmdsInfo, err := c.cmdsInfoCache.Get()
+	cmdsInfo, err := c.cmdsInfoCache.Get(c.ctx)
 	if err != nil {
 		return nil
 	}
 
 	info := cmdsInfo[name]
 	if info == nil {
-		internal.Logger.Printf("info for cmd=%s not found", name)
+		internal.Logger.Printf(c.Context(), "info for cmd=%s not found", name)
 	}
 	return info
 }
@@ -1582,8 +1629,12 @@ func cmdSlot(cmd Cmder, pos int) int {
 	return hashtag.Slot(firstKey)
 }
 
-func (c *ClusterClient) cmdNode(cmdInfo *CommandInfo, slot int) (*clusterNode, error) {
-	state, err := c.state.Get()
+func (c *ClusterClient) cmdNode(
+	ctx context.Context,
+	cmdInfo *CommandInfo,
+	slot int,
+) (*clusterNode, error) {
+	state, err := c.state.Get(ctx)
 	if err != nil {
 		return nil, err
 	}
@@ -1604,14 +1655,43 @@ func (c *clusterClient) slotReadOnlyNode(state *clusterState, slot int) (*cluste
 	return state.slotSlaveNode(slot)
 }
 
-func (c *ClusterClient) slotMasterNode(slot int) (*clusterNode, error) {
-	state, err := c.state.Get()
+func (c *ClusterClient) slotMasterNode(ctx context.Context, slot int) (*clusterNode, error) {
+	state, err := c.state.Get(ctx)
 	if err != nil {
 		return nil, err
 	}
 	return state.slotMasterNode(slot)
 }
 
+// SlaveForKey gets a client for a replica node to run any command on it.
+// This is especially useful if we want to run a particular lua script which has
+// only read only commands on the replica.
+// This is because other redis commands generally have a flag that points that
+// they are read only and automatically run on the replica nodes
+// if ClusterOptions.ReadOnly flag is set to true.
+func (c *ClusterClient) SlaveForKey(ctx context.Context, key string) (*Client, error) {
+	state, err := c.state.Get(ctx)
+	if err != nil {
+		return nil, err
+	}
+	slot := hashtag.Slot(key)
+	node, err := c.slotReadOnlyNode(state, slot)
+	if err != nil {
+		return nil, err
+	}
+	return node.Client, err
+}
+
+// MasterForKey return a client to the master node for a particular key.
+func (c *ClusterClient) MasterForKey(ctx context.Context, key string) (*Client, error) {
+	slot := hashtag.Slot(key)
+	node, err := c.slotMasterNode(ctx, slot)
+	if err != nil {
+		return nil, err
+	}
+	return node.Client, err
+}
+
 func appendUniqueNode(nodes []*clusterNode, node *clusterNode) []*clusterNode {
 	for _, n := range nodes {
 		if n == node {
@@ -1634,21 +1714,6 @@ loop:
 	return ss
 }
 
-func remove(ss []string, es ...string) []string {
-	if len(es) == 0 {
-		return ss[:0]
-	}
-	for _, e := range es {
-		for i, s := range ss {
-			if s == e {
-				ss = append(ss[:i], ss[i+1:]...)
-				break
-			}
-		}
-	}
-	return ss
-}
-
 //------------------------------------------------------------------------------
 
 type cmdsMap struct {
diff --git a/vendor/github.com/go-redis/redis/v8/cluster_commands.go b/vendor/github.com/go-redis/redis/v8/cluster_commands.go
new file mode 100644
index 0000000000..1f0bae067a
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/cluster_commands.go
@@ -0,0 +1,25 @@
+package redis
+
+import (
+	"context"
+	"sync/atomic"
+)
+
+func (c *ClusterClient) DBSize(ctx context.Context) *IntCmd {
+	cmd := NewIntCmd(ctx, "dbsize")
+	var size int64
+	err := c.ForEachMaster(ctx, func(ctx context.Context, master *Client) error {
+		n, err := master.DBSize(ctx).Result()
+		if err != nil {
+			return err
+		}
+		atomic.AddInt64(&size, n)
+		return nil
+	})
+	if err != nil {
+		cmd.SetErr(err)
+		return cmd
+	}
+	cmd.val = size
+	return cmd
+}
diff --git a/vendor/github.com/go-redis/redis/v7/command.go b/vendor/github.com/go-redis/redis/v8/command.go
similarity index 69%
rename from vendor/github.com/go-redis/redis/v7/command.go
rename to vendor/github.com/go-redis/redis/v8/command.go
index dd7fe4a91e..2932035e72 100644
--- a/vendor/github.com/go-redis/redis/v7/command.go
+++ b/vendor/github.com/go-redis/redis/v8/command.go
@@ -1,22 +1,26 @@
 package redis
 
 import (
+	"context"
 	"fmt"
 	"net"
 	"strconv"
-	"strings"
 	"time"
 
-	"github.com/go-redis/redis/v7/internal"
-	"github.com/go-redis/redis/v7/internal/proto"
-	"github.com/go-redis/redis/v7/internal/util"
+	"github.com/go-redis/redis/v8/internal"
+	"github.com/go-redis/redis/v8/internal/hscan"
+	"github.com/go-redis/redis/v8/internal/proto"
+	"github.com/go-redis/redis/v8/internal/util"
 )
 
 type Cmder interface {
 	Name() string
+	FullName() string
 	Args() []interface{}
 	String() string
 	stringArg(int) string
+	firstKeyPos() int8
+	setFirstKeyPos(int8)
 
 	readTimeout() *time.Duration
 	readReply(rd *proto.Reader) error
@@ -55,27 +59,11 @@ func writeCmd(wr *proto.Writer, cmd Cmder) error {
 	return wr.WriteArgs(cmd.Args())
 }
 
-func cmdString(cmd Cmder, val interface{}) string {
-	ss := make([]string, 0, len(cmd.Args()))
-	for _, arg := range cmd.Args() {
-		ss = append(ss, fmt.Sprint(arg))
-	}
-	s := strings.Join(ss, " ")
-	if err := cmd.Err(); err != nil {
-		return s + ": " + err.Error()
-	}
-	if val != nil {
-		switch vv := val.(type) {
-		case []byte:
-			return s + ": " + string(vv)
-		default:
-			return s + ": " + fmt.Sprint(val)
-		}
-	}
-	return s
-}
-
 func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int {
+	if pos := cmd.firstKeyPos(); pos != 0 {
+		return int(pos)
+	}
+
 	switch cmd.Name() {
 	case "eval", "evalsha":
 		if cmd.stringArg(2) != "0" {
@@ -85,18 +73,47 @@ func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int {
 		return 0
 	case "publish":
 		return 1
+	case "memory":
+		// https://github.com/redis/redis/issues/7493
+		if cmd.stringArg(1) == "usage" {
+			return 2
+		}
 	}
-	if info == nil {
-		return 0
+
+	if info != nil {
+		return int(info.FirstKeyPos)
 	}
-	return int(info.FirstKeyPos)
+	return 0
+}
+
+func cmdString(cmd Cmder, val interface{}) string {
+	b := make([]byte, 0, 64)
+
+	for i, arg := range cmd.Args() {
+		if i > 0 {
+			b = append(b, ' ')
+		}
+		b = internal.AppendArg(b, arg)
+	}
+
+	if err := cmd.Err(); err != nil {
+		b = append(b, ": "...)
+		b = append(b, err.Error()...)
+	} else if val != nil {
+		b = append(b, ": "...)
+		b = internal.AppendArg(b, val)
+	}
+
+	return internal.String(b)
 }
 
 //------------------------------------------------------------------------------
 
 type baseCmd struct {
-	args []interface{}
-	err  error
+	ctx    context.Context
+	args   []interface{}
+	err    error
+	keyPos int8
 
 	_readTimeout *time.Duration
 }
@@ -111,6 +128,21 @@ func (cmd *baseCmd) Name() string {
 	return internal.ToLower(cmd.stringArg(0))
 }
 
+func (cmd *baseCmd) FullName() string {
+	switch name := cmd.Name(); name {
+	case "cluster", "command":
+		if len(cmd.args) == 1 {
+			return name
+		}
+		if s2, ok := cmd.args[1].(string); ok {
+			return name + " " + s2
+		}
+		return name
+	default:
+		return name
+	}
+}
+
 func (cmd *baseCmd) Args() []interface{} {
 	return cmd.args
 }
@@ -123,6 +155,14 @@ func (cmd *baseCmd) stringArg(pos int) string {
 	return s
 }
 
+func (cmd *baseCmd) firstKeyPos() int8 {
+	return cmd.keyPos
+}
+
+func (cmd *baseCmd) setFirstKeyPos(keyPos int8) {
+	cmd.keyPos = keyPos
+}
+
 func (cmd *baseCmd) SetErr(e error) {
 	cmd.err = e
 }
@@ -147,9 +187,12 @@ type Cmd struct {
 	val interface{}
 }
 
-func NewCmd(args ...interface{}) *Cmd {
+func NewCmd(ctx context.Context, args ...interface{}) *Cmd {
 	return &Cmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -272,12 +315,12 @@ func (cmd *Cmd) Bool() (bool, error) {
 	}
 }
 
-func (cmd *Cmd) readReply(rd *proto.Reader) error {
-	cmd.val, cmd.err = rd.ReadReply(sliceParser)
-	return cmd.err
+func (cmd *Cmd) readReply(rd *proto.Reader) (err error) {
+	cmd.val, err = rd.ReadReply(sliceParser)
+	return err
 }
 
-// Implements proto.MultiBulkParse
+// sliceParser implements proto.MultiBulkParse.
 func sliceParser(rd *proto.Reader, n int64) (interface{}, error) {
 	vals := make([]interface{}, n)
 	for i := 0; i < len(vals); i++ {
@@ -308,9 +351,12 @@ type SliceCmd struct {
 
 var _ Cmder = (*SliceCmd)(nil)
 
-func NewSliceCmd(args ...interface{}) *SliceCmd {
+func NewSliceCmd(ctx context.Context, args ...interface{}) *SliceCmd {
 	return &SliceCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -326,12 +372,31 @@ func (cmd *SliceCmd) String() string {
 	return cmdString(cmd, cmd.val)
 }
 
-func (cmd *SliceCmd) readReply(rd *proto.Reader) error {
-	var v interface{}
-	v, cmd.err = rd.ReadArrayReply(sliceParser)
+// Scan scans the results from the map into a destination struct. The map keys
+// are matched in the Redis struct fields by the `redis:"field"` tag.
+func (cmd *SliceCmd) Scan(dst interface{}) error {
 	if cmd.err != nil {
 		return cmd.err
 	}
+
+	// Pass the list of keys and values.
+	// Skip the first two args for: HMGET key
+	var args []interface{}
+	if cmd.args[0] == "hmget" {
+		args = cmd.args[2:]
+	} else {
+		// Otherwise, it's: MGET field field ...
+		args = cmd.args[1:]
+	}
+
+	return hscan.Scan(dst, args, cmd.val)
+}
+
+func (cmd *SliceCmd) readReply(rd *proto.Reader) error {
+	v, err := rd.ReadArrayReply(sliceParser)
+	if err != nil {
+		return err
+	}
 	cmd.val = v.([]interface{})
 	return nil
 }
@@ -346,9 +411,12 @@ type StatusCmd struct {
 
 var _ Cmder = (*StatusCmd)(nil)
 
-func NewStatusCmd(args ...interface{}) *StatusCmd {
+func NewStatusCmd(ctx context.Context, args ...interface{}) *StatusCmd {
 	return &StatusCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -364,9 +432,9 @@ func (cmd *StatusCmd) String() string {
 	return cmdString(cmd, cmd.val)
 }
 
-func (cmd *StatusCmd) readReply(rd *proto.Reader) error {
-	cmd.val, cmd.err = rd.ReadString()
-	return cmd.err
+func (cmd *StatusCmd) readReply(rd *proto.Reader) (err error) {
+	cmd.val, err = rd.ReadString()
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -379,9 +447,12 @@ type IntCmd struct {
 
 var _ Cmder = (*IntCmd)(nil)
 
-func NewIntCmd(args ...interface{}) *IntCmd {
+func NewIntCmd(ctx context.Context, args ...interface{}) *IntCmd {
 	return &IntCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -401,9 +472,9 @@ func (cmd *IntCmd) String() string {
 	return cmdString(cmd, cmd.val)
 }
 
-func (cmd *IntCmd) readReply(rd *proto.Reader) error {
-	cmd.val, cmd.err = rd.ReadIntReply()
-	return cmd.err
+func (cmd *IntCmd) readReply(rd *proto.Reader) (err error) {
+	cmd.val, err = rd.ReadIntReply()
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -416,9 +487,12 @@ type IntSliceCmd struct {
 
 var _ Cmder = (*IntSliceCmd)(nil)
 
-func NewIntSliceCmd(args ...interface{}) *IntSliceCmd {
+func NewIntSliceCmd(ctx context.Context, args ...interface{}) *IntSliceCmd {
 	return &IntSliceCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -435,7 +509,7 @@ func (cmd *IntSliceCmd) String() string {
 }
 
 func (cmd *IntSliceCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, 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()
@@ -446,7 +520,7 @@ func (cmd *IntSliceCmd) readReply(rd *proto.Reader) error {
 		}
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -460,9 +534,12 @@ type DurationCmd struct {
 
 var _ Cmder = (*DurationCmd)(nil)
 
-func NewDurationCmd(precision time.Duration, args ...interface{}) *DurationCmd {
+func NewDurationCmd(ctx context.Context, precision time.Duration, args ...interface{}) *DurationCmd {
 	return &DurationCmd{
-		baseCmd:   baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 		precision: precision,
 	}
 }
@@ -480,10 +557,9 @@ func (cmd *DurationCmd) String() string {
 }
 
 func (cmd *DurationCmd) readReply(rd *proto.Reader) error {
-	var n int64
-	n, cmd.err = rd.ReadIntReply()
-	if cmd.err != nil {
-		return cmd.err
+	n, err := rd.ReadIntReply()
+	if err != nil {
+		return err
 	}
 	switch n {
 	// -2 if the key does not exist
@@ -506,9 +582,12 @@ type TimeCmd struct {
 
 var _ Cmder = (*TimeCmd)(nil)
 
-func NewTimeCmd(args ...interface{}) *TimeCmd {
+func NewTimeCmd(ctx context.Context, args ...interface{}) *TimeCmd {
 	return &TimeCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -525,7 +604,7 @@ func (cmd *TimeCmd) String() string {
 }
 
 func (cmd *TimeCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
 		if n != 2 {
 			return nil, fmt.Errorf("got %d elements, expected 2", n)
 		}
@@ -543,7 +622,7 @@ func (cmd *TimeCmd) readReply(rd *proto.Reader) error {
 		cmd.val = time.Unix(sec, microsec*1000)
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -556,9 +635,12 @@ type BoolCmd struct {
 
 var _ Cmder = (*BoolCmd)(nil)
 
-func NewBoolCmd(args ...interface{}) *BoolCmd {
+func NewBoolCmd(ctx context.Context, args ...interface{}) *BoolCmd {
 	return &BoolCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -575,17 +657,15 @@ func (cmd *BoolCmd) String() string {
 }
 
 func (cmd *BoolCmd) readReply(rd *proto.Reader) error {
-	var v interface{}
-	v, cmd.err = rd.ReadReply(nil)
+	v, 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.
-	if cmd.err == Nil {
+	if err == Nil {
 		cmd.val = false
-		cmd.err = nil
 		return nil
 	}
-	if cmd.err != nil {
-		return cmd.err
+	if err != nil {
+		return err
 	}
 	switch v := v.(type) {
 	case int64:
@@ -595,8 +675,7 @@ func (cmd *BoolCmd) readReply(rd *proto.Reader) error {
 		cmd.val = v == "OK"
 		return nil
 	default:
-		cmd.err = fmt.Errorf("got %T, wanted int64 or string", v)
-		return cmd.err
+		return fmt.Errorf("got %T, wanted int64 or string", v)
 	}
 }
 
@@ -610,9 +689,12 @@ type StringCmd struct {
 
 var _ Cmder = (*StringCmd)(nil)
 
-func NewStringCmd(args ...interface{}) *StringCmd {
+func NewStringCmd(ctx context.Context, args ...interface{}) *StringCmd {
 	return &StringCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -685,9 +767,9 @@ func (cmd *StringCmd) String() string {
 	return cmdString(cmd, cmd.val)
 }
 
-func (cmd *StringCmd) readReply(rd *proto.Reader) error {
-	cmd.val, cmd.err = rd.ReadString()
-	return cmd.err
+func (cmd *StringCmd) readReply(rd *proto.Reader) (err error) {
+	cmd.val, err = rd.ReadString()
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -700,9 +782,12 @@ type FloatCmd struct {
 
 var _ Cmder = (*FloatCmd)(nil)
 
-func NewFloatCmd(args ...interface{}) *FloatCmd {
+func NewFloatCmd(ctx context.Context, args ...interface{}) *FloatCmd {
 	return &FloatCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -718,9 +803,9 @@ func (cmd *FloatCmd) String() string {
 	return cmdString(cmd, cmd.val)
 }
 
-func (cmd *FloatCmd) readReply(rd *proto.Reader) error {
-	cmd.val, cmd.err = rd.ReadFloatReply()
-	return cmd.err
+func (cmd *FloatCmd) readReply(rd *proto.Reader) (err error) {
+	cmd.val, err = rd.ReadFloatReply()
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -733,9 +818,12 @@ type StringSliceCmd struct {
 
 var _ Cmder = (*StringSliceCmd)(nil)
 
-func NewStringSliceCmd(args ...interface{}) *StringSliceCmd {
+func NewStringSliceCmd(ctx context.Context, args ...interface{}) *StringSliceCmd {
 	return &StringSliceCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -756,7 +844,7 @@ func (cmd *StringSliceCmd) ScanSlice(container interface{}) error {
 }
 
 func (cmd *StringSliceCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, 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(); {
@@ -770,7 +858,7 @@ func (cmd *StringSliceCmd) readReply(rd *proto.Reader) error {
 		}
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -783,9 +871,12 @@ type BoolSliceCmd struct {
 
 var _ Cmder = (*BoolSliceCmd)(nil)
 
-func NewBoolSliceCmd(args ...interface{}) *BoolSliceCmd {
+func NewBoolSliceCmd(ctx context.Context, args ...interface{}) *BoolSliceCmd {
 	return &BoolSliceCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -802,7 +893,7 @@ func (cmd *BoolSliceCmd) String() string {
 }
 
 func (cmd *BoolSliceCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, 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()
@@ -813,7 +904,7 @@ func (cmd *BoolSliceCmd) readReply(rd *proto.Reader) error {
 		}
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -826,9 +917,12 @@ type StringStringMapCmd struct {
 
 var _ Cmder = (*StringStringMapCmd)(nil)
 
-func NewStringStringMapCmd(args ...interface{}) *StringStringMapCmd {
+func NewStringStringMapCmd(ctx context.Context, args ...interface{}) *StringStringMapCmd {
 	return &StringStringMapCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -844,8 +938,29 @@ func (cmd *StringStringMapCmd) String() string {
 	return cmdString(cmd, cmd.val)
 }
 
+// Scan scans the results from the map into a destination struct. The map keys
+// are matched in the Redis struct fields by the `redis:"field"` tag.
+func (cmd *StringStringMapCmd) Scan(dst interface{}) error {
+	if cmd.err != nil {
+		return cmd.err
+	}
+
+	strct, err := hscan.Struct(dst)
+	if err != nil {
+		return err
+	}
+
+	for k, v := range cmd.val {
+		if err := strct.Scan(k, v); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 func (cmd *StringStringMapCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, 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()
@@ -862,7 +977,7 @@ func (cmd *StringStringMapCmd) readReply(rd *proto.Reader) error {
 		}
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -875,9 +990,12 @@ type StringIntMapCmd struct {
 
 var _ Cmder = (*StringIntMapCmd)(nil)
 
-func NewStringIntMapCmd(args ...interface{}) *StringIntMapCmd {
+func NewStringIntMapCmd(ctx context.Context, args ...interface{}) *StringIntMapCmd {
 	return &StringIntMapCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -894,7 +1012,7 @@ func (cmd *StringIntMapCmd) String() string {
 }
 
 func (cmd *StringIntMapCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, 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()
@@ -911,7 +1029,7 @@ func (cmd *StringIntMapCmd) readReply(rd *proto.Reader) error {
 		}
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -924,9 +1042,12 @@ type StringStructMapCmd struct {
 
 var _ Cmder = (*StringStructMapCmd)(nil)
 
-func NewStringStructMapCmd(args ...interface{}) *StringStructMapCmd {
+func NewStringStructMapCmd(ctx context.Context, args ...interface{}) *StringStructMapCmd {
 	return &StringStructMapCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -943,7 +1064,7 @@ func (cmd *StringStructMapCmd) String() string {
 }
 
 func (cmd *StringStructMapCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, 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()
@@ -954,7 +1075,7 @@ func (cmd *StringStructMapCmd) readReply(rd *proto.Reader) error {
 		}
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -972,9 +1093,12 @@ type XMessageSliceCmd struct {
 
 var _ Cmder = (*XMessageSliceCmd)(nil)
 
-func NewXMessageSliceCmd(args ...interface{}) *XMessageSliceCmd {
+func NewXMessageSliceCmd(ctx context.Context, args ...interface{}) *XMessageSliceCmd {
 	return &XMessageSliceCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -991,43 +1115,21 @@ func (cmd *XMessageSliceCmd) String() string {
 }
 
 func (cmd *XMessageSliceCmd) readReply(rd *proto.Reader) error {
-	var v interface{}
-	v, cmd.err = rd.ReadArrayReply(xMessageSliceParser)
-	if cmd.err != nil {
-		return cmd.err
-	}
-	cmd.val = v.([]XMessage)
-	return nil
+	var err error
+	cmd.val, err = readXMessageSlice(rd)
+	return err
 }
 
-// Implements proto.MultiBulkParse
-func xMessageSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
+func readXMessageSlice(rd *proto.Reader) ([]XMessage, error) {
+	n, err := rd.ReadArrayLen()
+	if err != nil {
+		return nil, err
+	}
+
 	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 {
-				if err != proto.Nil {
-					return nil, err
-				}
-			} else {
-				values = v.(map[string]interface{})
-			}
-
-			msgs[i] = XMessage{
-				ID:     id,
-				Values: values,
-			}
-			return nil, nil
-		})
+	for i := 0; i < n; i++ {
+		var err error
+		msgs[i], err = readXMessage(rd)
 		if err != nil {
 			return nil, err
 		}
@@ -1035,7 +1137,38 @@ func xMessageSliceParser(rd *proto.Reader, n int64) (interface{}, error) {
 	return msgs, nil
 }
 
-// Implements proto.MultiBulkParse
+func readXMessage(rd *proto.Reader) (XMessage, error) {
+	n, err := rd.ReadArrayLen()
+	if err != nil {
+		return XMessage{}, err
+	}
+	if n != 2 {
+		return XMessage{}, fmt.Errorf("got %d, wanted 2", n)
+	}
+
+	id, err := rd.ReadString()
+	if err != nil {
+		return XMessage{}, err
+	}
+
+	var values map[string]interface{}
+
+	v, err := rd.ReadArrayReply(stringInterfaceMapParser)
+	if err != nil {
+		if err != proto.Nil {
+			return XMessage{}, err
+		}
+	} else {
+		values = v.(map[string]interface{})
+	}
+
+	return XMessage{
+		ID:     id,
+		Values: values,
+	}, nil
+}
+
+// stringInterfaceMapParser implements proto.MultiBulkParse.
 func stringInterfaceMapParser(rd *proto.Reader, n int64) (interface{}, error) {
 	m := make(map[string]interface{}, n/2)
 	for i := int64(0); i < n; i += 2 {
@@ -1069,9 +1202,12 @@ type XStreamSliceCmd struct {
 
 var _ Cmder = (*XStreamSliceCmd)(nil)
 
-func NewXStreamSliceCmd(args ...interface{}) *XStreamSliceCmd {
+func NewXStreamSliceCmd(ctx context.Context, args ...interface{}) *XStreamSliceCmd {
 	return &XStreamSliceCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -1088,7 +1224,7 @@ func (cmd *XStreamSliceCmd) String() string {
 }
 
 func (cmd *XStreamSliceCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, 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
@@ -1102,14 +1238,14 @@ func (cmd *XStreamSliceCmd) readReply(rd *proto.Reader) error {
 					return nil, err
 				}
 
-				v, err := rd.ReadArrayReply(xMessageSliceParser)
+				msgs, err := readXMessageSlice(rd)
 				if err != nil {
 					return nil, err
 				}
 
 				cmd.val[i] = XStream{
 					Stream:   stream,
-					Messages: v.([]XMessage),
+					Messages: msgs,
 				}
 				return nil, nil
 			})
@@ -1119,7 +1255,7 @@ func (cmd *XStreamSliceCmd) readReply(rd *proto.Reader) error {
 		}
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -1138,9 +1274,12 @@ type XPendingCmd struct {
 
 var _ Cmder = (*XPendingCmd)(nil)
 
-func NewXPendingCmd(args ...interface{}) *XPendingCmd {
+func NewXPendingCmd(ctx context.Context, args ...interface{}) *XPendingCmd {
 	return &XPendingCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -1157,7 +1296,7 @@ func (cmd *XPendingCmd) String() string {
 }
 
 func (cmd *XPendingCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
 		if n != 4 {
 			return nil, fmt.Errorf("got %d, wanted 4", n)
 		}
@@ -1218,7 +1357,7 @@ func (cmd *XPendingCmd) readReply(rd *proto.Reader) error {
 
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -1237,9 +1376,12 @@ type XPendingExtCmd struct {
 
 var _ Cmder = (*XPendingExtCmd)(nil)
 
-func NewXPendingExtCmd(args ...interface{}) *XPendingExtCmd {
+func NewXPendingExtCmd(ctx context.Context, args ...interface{}) *XPendingExtCmd {
 	return &XPendingExtCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -1256,7 +1398,7 @@ func (cmd *XPendingExtCmd) String() string {
 }
 
 func (cmd *XPendingExtCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, 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) {
@@ -1298,17 +1440,17 @@ func (cmd *XPendingExtCmd) readReply(rd *proto.Reader) error {
 		}
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
 
 type XInfoGroupsCmd struct {
 	baseCmd
-	val []XInfoGroups
+	val []XInfoGroup
 }
 
-type XInfoGroups struct {
+type XInfoGroup struct {
 	Name            string
 	Consumers       int64
 	Pending         int64
@@ -1317,17 +1459,20 @@ type XInfoGroups struct {
 
 var _ Cmder = (*XInfoGroupsCmd)(nil)
 
-func NewXInfoGroupsCmd(stream string) *XInfoGroupsCmd {
+func NewXInfoGroupsCmd(ctx context.Context, stream string) *XInfoGroupsCmd {
 	return &XInfoGroupsCmd{
-		baseCmd: baseCmd{args: []interface{}{"xinfo", "groups", stream}},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: []interface{}{"xinfo", "groups", stream},
+		},
 	}
 }
 
-func (cmd *XInfoGroupsCmd) Val() []XInfoGroups {
+func (cmd *XInfoGroupsCmd) Val() []XInfoGroup {
 	return cmd.val
 }
 
-func (cmd *XInfoGroupsCmd) Result() ([]XInfoGroups, error) {
+func (cmd *XInfoGroupsCmd) Result() ([]XInfoGroup, error) {
 	return cmd.val, cmd.err
 }
 
@@ -1336,59 +1481,152 @@ func (cmd *XInfoGroupsCmd) String() string {
 }
 
 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))
-			}
-			return nil, nil
-		})
+	n, err := rd.ReadArrayLen()
+	if err != nil {
+		return err
+	}
+
+	cmd.val = make([]XInfoGroup, n)
+
+	for i := 0; i < n; i++ {
+		cmd.val[i], err = readXGroupInfo(rd)
+		if err != nil {
+			return err
+		}
+	}
+
 	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)
+func readXGroupInfo(rd *proto.Reader) (XInfoGroup, error) {
+	var group XInfoGroup
+
+	n, err := rd.ReadArrayLen()
+	if err != nil {
+		return group, err
+	}
+	if n != 8 {
+		return group, 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()
+		key, err := rd.ReadString()
 		if err != nil {
-			return nil, err
+			return group, err
 		}
-		val, err = rd.ReadString()
+
+		val, err := rd.ReadString()
+		if err != nil {
+			return group, err
+		}
+
+		switch key {
+		case "name":
+			group.Name = val
+		case "consumers":
+			group.Consumers, err = strconv.ParseInt(val, 0, 64)
+			if err != nil {
+				return group, err
+			}
+		case "pending":
+			group.Pending, err = strconv.ParseInt(val, 0, 64)
+			if err != nil {
+				return group, err
+			}
+		case "last-delivered-id":
+			group.LastDeliveredID = val
+		default:
+			return group, fmt.Errorf("redis: unexpected content %s in XINFO GROUPS reply", key)
+		}
+	}
+
+	return group, nil
+}
+
+//------------------------------------------------------------------------------
+
+type XInfoStreamCmd struct {
+	baseCmd
+	val *XInfoStream
+}
+
+type XInfoStream struct {
+	Length          int64
+	RadixTreeKeys   int64
+	RadixTreeNodes  int64
+	Groups          int64
+	LastGeneratedID string
+	FirstEntry      XMessage
+	LastEntry       XMessage
+}
+
+var _ Cmder = (*XInfoStreamCmd)(nil)
+
+func NewXInfoStreamCmd(ctx context.Context, stream string) *XInfoStreamCmd {
+	return &XInfoStreamCmd{
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: []interface{}{"xinfo", "stream", stream},
+		},
+	}
+}
+
+func (cmd *XInfoStreamCmd) Val() *XInfoStream {
+	return cmd.val
+}
+
+func (cmd *XInfoStreamCmd) Result() (*XInfoStream, error) {
+	return cmd.val, cmd.err
+}
+
+func (cmd *XInfoStreamCmd) String() string {
+	return cmdString(cmd, cmd.val)
+}
+
+func (cmd *XInfoStreamCmd) readReply(rd *proto.Reader) error {
+	v, err := rd.ReadReply(xStreamInfoParser)
+	if err != nil {
+		return err
+	}
+	cmd.val = v.(*XInfoStream)
+	return nil
+}
+
+func xStreamInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
+	if n != 14 {
+		return nil, fmt.Errorf("redis: got %d elements in XINFO STREAM reply,"+
+			"wanted 14", n)
+	}
+	var info XInfoStream
+	for i := 0; i < 7; i++ {
+		key, 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
+		case "length":
+			info.Length, err = rd.ReadIntReply()
+		case "radix-tree-keys":
+			info.RadixTreeKeys, err = rd.ReadIntReply()
+		case "radix-tree-nodes":
+			info.RadixTreeNodes, err = rd.ReadIntReply()
+		case "groups":
+			info.Groups, err = rd.ReadIntReply()
+		case "last-generated-id":
+			info.LastGeneratedID, err = rd.ReadString()
+		case "first-entry":
+			info.FirstEntry, err = readXMessage(rd)
+		case "last-entry":
+			info.LastEntry, err = readXMessage(rd)
 		default:
 			return nil, fmt.Errorf("redis: unexpected content %s "+
-				"in XINFO GROUPS reply", key)
+				"in XINFO STREAM reply", key)
 		}
 		if err != nil {
 			return nil, err
 		}
 	}
-	return grp, err
+	return &info, nil
 }
 
 //------------------------------------------------------------------------------
@@ -1401,9 +1639,12 @@ type ZSliceCmd struct {
 
 var _ Cmder = (*ZSliceCmd)(nil)
 
-func NewZSliceCmd(args ...interface{}) *ZSliceCmd {
+func NewZSliceCmd(ctx context.Context, args ...interface{}) *ZSliceCmd {
 	return &ZSliceCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -1420,7 +1661,7 @@ func (cmd *ZSliceCmd) String() string {
 }
 
 func (cmd *ZSliceCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, 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()
@@ -1440,7 +1681,7 @@ func (cmd *ZSliceCmd) readReply(rd *proto.Reader) error {
 		}
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -1453,9 +1694,12 @@ type ZWithKeyCmd struct {
 
 var _ Cmder = (*ZWithKeyCmd)(nil)
 
-func NewZWithKeyCmd(args ...interface{}) *ZWithKeyCmd {
+func NewZWithKeyCmd(ctx context.Context, args ...interface{}) *ZWithKeyCmd {
 	return &ZWithKeyCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -1472,7 +1716,7 @@ func (cmd *ZWithKeyCmd) String() string {
 }
 
 func (cmd *ZWithKeyCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
 		if n != 3 {
 			return nil, fmt.Errorf("got %d elements, expected 3", n)
 		}
@@ -1497,7 +1741,7 @@ func (cmd *ZWithKeyCmd) readReply(rd *proto.Reader) error {
 
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -1508,14 +1752,17 @@ type ScanCmd struct {
 	page   []string
 	cursor uint64
 
-	process func(cmd Cmder) error
+	process cmdable
 }
 
 var _ Cmder = (*ScanCmd)(nil)
 
-func NewScanCmd(process func(cmd Cmder) error, args ...interface{}) *ScanCmd {
+func NewScanCmd(ctx context.Context, process cmdable, args ...interface{}) *ScanCmd {
 	return &ScanCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 		process: process,
 	}
 }
@@ -1532,9 +1779,9 @@ func (cmd *ScanCmd) String() string {
 	return cmdString(cmd, cmd.page)
 }
 
-func (cmd *ScanCmd) readReply(rd *proto.Reader) error {
-	cmd.page, cmd.cursor, cmd.err = rd.ReadScanReply()
-	return cmd.err
+func (cmd *ScanCmd) readReply(rd *proto.Reader) (err error) {
+	cmd.page, cmd.cursor, err = rd.ReadScanReply()
+	return err
 }
 
 // Iterator creates a new ScanIterator.
@@ -1565,9 +1812,12 @@ type ClusterSlotsCmd struct {
 
 var _ Cmder = (*ClusterSlotsCmd)(nil)
 
-func NewClusterSlotsCmd(args ...interface{}) *ClusterSlotsCmd {
+func NewClusterSlotsCmd(ctx context.Context, args ...interface{}) *ClusterSlotsCmd {
 	return &ClusterSlotsCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -1584,7 +1834,7 @@ func (cmd *ClusterSlotsCmd) String() string {
 }
 
 func (cmd *ClusterSlotsCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, 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()
@@ -1646,7 +1896,7 @@ func (cmd *ClusterSlotsCmd) readReply(rd *proto.Reader) error {
 		}
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -1682,10 +1932,13 @@ type GeoLocationCmd struct {
 
 var _ Cmder = (*GeoLocationCmd)(nil)
 
-func NewGeoLocationCmd(q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd {
+func NewGeoLocationCmd(ctx context.Context, q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd {
 	return &GeoLocationCmd{
-		baseCmd: baseCmd{args: geoLocationArgs(q, args...)},
-		q:       q,
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: geoLocationArgs(q, args...),
+		},
+		q: q,
 	}
 }
 
@@ -1735,10 +1988,9 @@ func (cmd *GeoLocationCmd) String() string {
 }
 
 func (cmd *GeoLocationCmd) readReply(rd *proto.Reader) error {
-	var v interface{}
-	v, cmd.err = rd.ReadArrayReply(newGeoLocationSliceParser(cmd.q))
-	if cmd.err != nil {
-		return cmd.err
+	v, err := rd.ReadArrayReply(newGeoLocationSliceParser(cmd.q))
+	if err != nil {
+		return err
 	}
 	cmd.locations = v.([]GeoLocation)
 	return nil
@@ -1758,7 +2010,7 @@ func newGeoLocationSliceParser(q *GeoRadiusQuery) proto.MultiBulkParse {
 					Name: vv,
 				})
 			case *GeoLocation:
-				//TODO: avoid copying
+				// TODO: avoid copying
 				locs = append(locs, *vv)
 			default:
 				return nil, fmt.Errorf("got %T, expected string or *GeoLocation", v)
@@ -1826,9 +2078,12 @@ type GeoPosCmd struct {
 
 var _ Cmder = (*GeoPosCmd)(nil)
 
-func NewGeoPosCmd(args ...interface{}) *GeoPosCmd {
+func NewGeoPosCmd(ctx context.Context, args ...interface{}) *GeoPosCmd {
 	return &GeoPosCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -1845,7 +2100,7 @@ func (cmd *GeoPosCmd) String() string {
 }
 
 func (cmd *GeoPosCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, 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
@@ -1876,7 +2131,7 @@ func (cmd *GeoPosCmd) readReply(rd *proto.Reader) error {
 		}
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 //------------------------------------------------------------------------------
@@ -1900,9 +2155,12 @@ type CommandsInfoCmd struct {
 
 var _ Cmder = (*CommandsInfoCmd)(nil)
 
-func NewCommandsInfoCmd(args ...interface{}) *CommandsInfoCmd {
+func NewCommandsInfoCmd(ctx context.Context, args ...interface{}) *CommandsInfoCmd {
 	return &CommandsInfoCmd{
-		baseCmd: baseCmd{args: args},
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
 	}
 }
 
@@ -1919,7 +2177,7 @@ func (cmd *CommandsInfoCmd) String() string {
 }
 
 func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error {
-	_, cmd.err = rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+	_, 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)
@@ -1931,7 +2189,7 @@ func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error {
 		}
 		return nil, nil
 	})
-	return cmd.err
+	return err
 }
 
 func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
@@ -2030,21 +2288,21 @@ func commandInfoParser(rd *proto.Reader, n int64) (interface{}, error) {
 //------------------------------------------------------------------------------
 
 type cmdsInfoCache struct {
-	fn func() (map[string]*CommandInfo, error)
+	fn func(ctx context.Context) (map[string]*CommandInfo, error)
 
 	once internal.Once
 	cmds map[string]*CommandInfo
 }
 
-func newCmdsInfoCache(fn func() (map[string]*CommandInfo, error)) *cmdsInfoCache {
+func newCmdsInfoCache(fn func(ctx context.Context) (map[string]*CommandInfo, error)) *cmdsInfoCache {
 	return &cmdsInfoCache{
 		fn: fn,
 	}
 }
 
-func (c *cmdsInfoCache) Get() (map[string]*CommandInfo, error) {
+func (c *cmdsInfoCache) Get(ctx context.Context) (map[string]*CommandInfo, error) {
 	err := c.once.Do(func() error {
-		cmds, err := c.fn()
+		cmds, err := c.fn(ctx)
 		if err != nil {
 			return err
 		}
@@ -2062,3 +2320,119 @@ func (c *cmdsInfoCache) Get() (map[string]*CommandInfo, error) {
 	})
 	return c.cmds, err
 }
+
+//------------------------------------------------------------------------------
+
+type SlowLog struct {
+	ID       int64
+	Time     time.Time
+	Duration time.Duration
+	Args     []string
+	// These are also optional fields emitted only by Redis 4.0 or greater:
+	// https://redis.io/commands/slowlog#output-format
+	ClientAddr string
+	ClientName string
+}
+
+type SlowLogCmd struct {
+	baseCmd
+
+	val []SlowLog
+}
+
+var _ Cmder = (*SlowLogCmd)(nil)
+
+func NewSlowLogCmd(ctx context.Context, args ...interface{}) *SlowLogCmd {
+	return &SlowLogCmd{
+		baseCmd: baseCmd{
+			ctx:  ctx,
+			args: args,
+		},
+	}
+}
+
+func (cmd *SlowLogCmd) Val() []SlowLog {
+	return cmd.val
+}
+
+func (cmd *SlowLogCmd) Result() ([]SlowLog, error) {
+	return cmd.Val(), cmd.Err()
+}
+
+func (cmd *SlowLogCmd) String() string {
+	return cmdString(cmd, cmd.val)
+}
+
+func (cmd *SlowLogCmd) readReply(rd *proto.Reader) error {
+	_, err := rd.ReadArrayReply(func(rd *proto.Reader, n int64) (interface{}, error) {
+		cmd.val = make([]SlowLog, n)
+		for i := 0; i < len(cmd.val); i++ {
+			n, err := rd.ReadArrayLen()
+			if err != nil {
+				return nil, err
+			}
+			if n < 4 {
+				err := fmt.Errorf("redis: got %d elements in slowlog get, expected at least 4", n)
+				return nil, err
+			}
+
+			id, err := rd.ReadIntReply()
+			if err != nil {
+				return nil, err
+			}
+
+			createdAt, err := rd.ReadIntReply()
+			if err != nil {
+				return nil, err
+			}
+			createdAtTime := time.Unix(createdAt, 0)
+
+			costs, err := rd.ReadIntReply()
+			if err != nil {
+				return nil, err
+			}
+			costsDuration := time.Duration(costs) * time.Microsecond
+
+			cmdLen, err := rd.ReadArrayLen()
+			if err != nil {
+				return nil, err
+			}
+			if cmdLen < 1 {
+				err := fmt.Errorf("redis: got %d elements commands reply in slowlog get, expected at least 1", cmdLen)
+				return nil, err
+			}
+
+			cmdString := make([]string, cmdLen)
+			for i := 0; i < cmdLen; i++ {
+				cmdString[i], err = rd.ReadString()
+				if err != nil {
+					return nil, err
+				}
+			}
+
+			var address, name string
+			for i := 4; i < n; i++ {
+				str, err := rd.ReadString()
+				if err != nil {
+					return nil, err
+				}
+				if i == 4 {
+					address = str
+				} else if i == 5 {
+					name = str
+				}
+			}
+
+			cmd.val[i] = SlowLog{
+				ID:         id,
+				Time:       createdAtTime,
+				Duration:   costsDuration,
+				Args:       cmdString,
+				ClientAddr: address,
+				ClientName: name,
+			}
+		}
+		return nil, nil
+	})
+	return err
+}
diff --git a/vendor/github.com/go-redis/redis/v8/commands.go b/vendor/github.com/go-redis/redis/v8/commands.go
new file mode 100644
index 0000000000..422a7c5be2
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/commands.go
@@ -0,0 +1,2790 @@
+package redis
+
+import (
+	"context"
+	"errors"
+	"io"
+	"time"
+
+	"github.com/go-redis/redis/v8/internal"
+)
+
+// KeepTTL is an option for Set command to keep key's existing TTL.
+// For example:
+//
+//    rdb.Set(ctx, key, value, redis.KeepTTL)
+const KeepTTL = -1
+
+func usePrecise(dur time.Duration) bool {
+	return dur < time.Second || dur%time.Second != 0
+}
+
+func formatMs(ctx context.Context, dur time.Duration) int64 {
+	if dur > 0 && dur < time.Millisecond {
+		internal.Logger.Printf(
+			ctx,
+			"specified duration is %s, but minimal supported value is %s - truncating to 1ms",
+			dur, time.Millisecond,
+		)
+		return 1
+	}
+	return int64(dur / time.Millisecond)
+}
+
+func formatSec(ctx context.Context, dur time.Duration) int64 {
+	if dur > 0 && dur < time.Second {
+		internal.Logger.Printf(
+			ctx,
+			"specified duration is %s, but minimal supported value is %s - truncating to 1s",
+			dur, time.Second,
+		)
+		return 1
+	}
+	return int64(dur / time.Second)
+}
+
+func appendArgs(dst, src []interface{}) []interface{} {
+	if len(src) == 1 {
+		return appendArg(dst, src[0])
+	}
+
+	dst = append(dst, src...)
+	return dst
+}
+
+func appendArg(dst []interface{}, arg interface{}) []interface{} {
+	switch arg := arg.(type) {
+	case []string:
+		for _, s := range arg {
+			dst = append(dst, s)
+		}
+		return dst
+	case []interface{}:
+		dst = append(dst, arg...)
+		return dst
+	case map[string]interface{}:
+		for k, v := range arg {
+			dst = append(dst, k, v)
+		}
+		return dst
+	default:
+		return append(dst, arg)
+	}
+}
+
+type Cmdable interface {
+	Pipeline() Pipeliner
+	Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error)
+
+	TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error)
+	TxPipeline() Pipeliner
+
+	Command(ctx context.Context) *CommandsInfoCmd
+	ClientGetName(ctx context.Context) *StringCmd
+	Echo(ctx context.Context, message interface{}) *StringCmd
+	Ping(ctx context.Context) *StatusCmd
+	Quit(ctx context.Context) *StatusCmd
+	Del(ctx context.Context, keys ...string) *IntCmd
+	Unlink(ctx context.Context, keys ...string) *IntCmd
+	Dump(ctx context.Context, key string) *StringCmd
+	Exists(ctx context.Context, keys ...string) *IntCmd
+	Expire(ctx context.Context, key string, expiration time.Duration) *BoolCmd
+	ExpireAt(ctx context.Context, key string, tm time.Time) *BoolCmd
+	Keys(ctx context.Context, pattern string) *StringSliceCmd
+	Migrate(ctx context.Context, host, port, key string, db int, timeout time.Duration) *StatusCmd
+	Move(ctx context.Context, key string, db int) *BoolCmd
+	ObjectRefCount(ctx context.Context, key string) *IntCmd
+	ObjectEncoding(ctx context.Context, key string) *StringCmd
+	ObjectIdleTime(ctx context.Context, key string) *DurationCmd
+	Persist(ctx context.Context, key string) *BoolCmd
+	PExpire(ctx context.Context, key string, expiration time.Duration) *BoolCmd
+	PExpireAt(ctx context.Context, key string, tm time.Time) *BoolCmd
+	PTTL(ctx context.Context, key string) *DurationCmd
+	RandomKey(ctx context.Context) *StringCmd
+	Rename(ctx context.Context, key, newkey string) *StatusCmd
+	RenameNX(ctx context.Context, key, newkey string) *BoolCmd
+	Restore(ctx context.Context, key string, ttl time.Duration, value string) *StatusCmd
+	RestoreReplace(ctx context.Context, key string, ttl time.Duration, value string) *StatusCmd
+	Sort(ctx context.Context, key string, sort *Sort) *StringSliceCmd
+	SortStore(ctx context.Context, key, store string, sort *Sort) *IntCmd
+	SortInterfaces(ctx context.Context, key string, sort *Sort) *SliceCmd
+	Touch(ctx context.Context, keys ...string) *IntCmd
+	TTL(ctx context.Context, key string) *DurationCmd
+	Type(ctx context.Context, key string) *StatusCmd
+	Append(ctx context.Context, key, value string) *IntCmd
+	Decr(ctx context.Context, key string) *IntCmd
+	DecrBy(ctx context.Context, key string, decrement int64) *IntCmd
+	Get(ctx context.Context, key string) *StringCmd
+	GetRange(ctx context.Context, key string, start, end int64) *StringCmd
+	GetSet(ctx context.Context, key string, value interface{}) *StringCmd
+	Incr(ctx context.Context, key string) *IntCmd
+	IncrBy(ctx context.Context, key string, value int64) *IntCmd
+	IncrByFloat(ctx context.Context, key string, value float64) *FloatCmd
+	MGet(ctx context.Context, keys ...string) *SliceCmd
+	MSet(ctx context.Context, values ...interface{}) *StatusCmd
+	MSetNX(ctx context.Context, values ...interface{}) *BoolCmd
+	Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd
+	SetEX(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd
+	SetNX(ctx context.Context, key string, value interface{}, expiration time.Duration) *BoolCmd
+	SetXX(ctx context.Context, key string, value interface{}, expiration time.Duration) *BoolCmd
+	SetRange(ctx context.Context, key string, offset int64, value string) *IntCmd
+	StrLen(ctx context.Context, key string) *IntCmd
+
+	GetBit(ctx context.Context, key string, offset int64) *IntCmd
+	SetBit(ctx context.Context, key string, offset int64, value int) *IntCmd
+	BitCount(ctx context.Context, key string, bitCount *BitCount) *IntCmd
+	BitOpAnd(ctx context.Context, destKey string, keys ...string) *IntCmd
+	BitOpOr(ctx context.Context, destKey string, keys ...string) *IntCmd
+	BitOpXor(ctx context.Context, destKey string, keys ...string) *IntCmd
+	BitOpNot(ctx context.Context, destKey string, key string) *IntCmd
+	BitPos(ctx context.Context, key string, bit int64, pos ...int64) *IntCmd
+	BitField(ctx context.Context, key string, args ...interface{}) *IntSliceCmd
+
+	Scan(ctx context.Context, cursor uint64, match string, count int64) *ScanCmd
+	ScanType(ctx context.Context, cursor uint64, match string, count int64, keyType string) *ScanCmd
+	SScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd
+	HScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd
+	ZScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd
+
+	HDel(ctx context.Context, key string, fields ...string) *IntCmd
+	HExists(ctx context.Context, key, field string) *BoolCmd
+	HGet(ctx context.Context, key, field string) *StringCmd
+	HGetAll(ctx context.Context, key string) *StringStringMapCmd
+	HIncrBy(ctx context.Context, key, field string, incr int64) *IntCmd
+	HIncrByFloat(ctx context.Context, key, field string, incr float64) *FloatCmd
+	HKeys(ctx context.Context, key string) *StringSliceCmd
+	HLen(ctx context.Context, key string) *IntCmd
+	HMGet(ctx context.Context, key string, fields ...string) *SliceCmd
+	HSet(ctx context.Context, key string, values ...interface{}) *IntCmd
+	HMSet(ctx context.Context, key string, values ...interface{}) *BoolCmd
+	HSetNX(ctx context.Context, key, field string, value interface{}) *BoolCmd
+	HVals(ctx context.Context, key string) *StringSliceCmd
+
+	BLPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd
+	BRPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd
+	BRPopLPush(ctx context.Context, source, destination string, timeout time.Duration) *StringCmd
+	LIndex(ctx context.Context, key string, index int64) *StringCmd
+	LInsert(ctx context.Context, key, op string, pivot, value interface{}) *IntCmd
+	LInsertBefore(ctx context.Context, key string, pivot, value interface{}) *IntCmd
+	LInsertAfter(ctx context.Context, key string, pivot, value interface{}) *IntCmd
+	LLen(ctx context.Context, key string) *IntCmd
+	LPop(ctx context.Context, key string) *StringCmd
+	LPos(ctx context.Context, key string, value string, args LPosArgs) *IntCmd
+	LPosCount(ctx context.Context, key string, value string, count int64, args LPosArgs) *IntSliceCmd
+	LPush(ctx context.Context, key string, values ...interface{}) *IntCmd
+	LPushX(ctx context.Context, key string, values ...interface{}) *IntCmd
+	LRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd
+	LRem(ctx context.Context, key string, count int64, value interface{}) *IntCmd
+	LSet(ctx context.Context, key string, index int64, value interface{}) *StatusCmd
+	LTrim(ctx context.Context, key string, start, stop int64) *StatusCmd
+	RPop(ctx context.Context, key string) *StringCmd
+	RPopLPush(ctx context.Context, source, destination string) *StringCmd
+	RPush(ctx context.Context, key string, values ...interface{}) *IntCmd
+	RPushX(ctx context.Context, key string, values ...interface{}) *IntCmd
+
+	SAdd(ctx context.Context, key string, members ...interface{}) *IntCmd
+	SCard(ctx context.Context, key string) *IntCmd
+	SDiff(ctx context.Context, keys ...string) *StringSliceCmd
+	SDiffStore(ctx context.Context, destination string, keys ...string) *IntCmd
+	SInter(ctx context.Context, keys ...string) *StringSliceCmd
+	SInterStore(ctx context.Context, destination string, keys ...string) *IntCmd
+	SIsMember(ctx context.Context, key string, member interface{}) *BoolCmd
+	SMembers(ctx context.Context, key string) *StringSliceCmd
+	SMembersMap(ctx context.Context, key string) *StringStructMapCmd
+	SMove(ctx context.Context, source, destination string, member interface{}) *BoolCmd
+	SPop(ctx context.Context, key string) *StringCmd
+	SPopN(ctx context.Context, key string, count int64) *StringSliceCmd
+	SRandMember(ctx context.Context, key string) *StringCmd
+	SRandMemberN(ctx context.Context, key string, count int64) *StringSliceCmd
+	SRem(ctx context.Context, key string, members ...interface{}) *IntCmd
+	SUnion(ctx context.Context, keys ...string) *StringSliceCmd
+	SUnionStore(ctx context.Context, destination string, keys ...string) *IntCmd
+
+	XAdd(ctx context.Context, a *XAddArgs) *StringCmd
+	XDel(ctx context.Context, stream string, ids ...string) *IntCmd
+	XLen(ctx context.Context, stream string) *IntCmd
+	XRange(ctx context.Context, stream, start, stop string) *XMessageSliceCmd
+	XRangeN(ctx context.Context, stream, start, stop string, count int64) *XMessageSliceCmd
+	XRevRange(ctx context.Context, stream string, start, stop string) *XMessageSliceCmd
+	XRevRangeN(ctx context.Context, stream string, start, stop string, count int64) *XMessageSliceCmd
+	XRead(ctx context.Context, a *XReadArgs) *XStreamSliceCmd
+	XReadStreams(ctx context.Context, streams ...string) *XStreamSliceCmd
+	XGroupCreate(ctx context.Context, stream, group, start string) *StatusCmd
+	XGroupCreateMkStream(ctx context.Context, stream, group, start string) *StatusCmd
+	XGroupSetID(ctx context.Context, stream, group, start string) *StatusCmd
+	XGroupDestroy(ctx context.Context, stream, group string) *IntCmd
+	XGroupDelConsumer(ctx context.Context, stream, group, consumer string) *IntCmd
+	XReadGroup(ctx context.Context, a *XReadGroupArgs) *XStreamSliceCmd
+	XAck(ctx context.Context, stream, group string, ids ...string) *IntCmd
+	XPending(ctx context.Context, stream, group string) *XPendingCmd
+	XPendingExt(ctx context.Context, a *XPendingExtArgs) *XPendingExtCmd
+	XClaim(ctx context.Context, a *XClaimArgs) *XMessageSliceCmd
+	XClaimJustID(ctx context.Context, a *XClaimArgs) *StringSliceCmd
+	XTrim(ctx context.Context, key string, maxLen int64) *IntCmd
+	XTrimApprox(ctx context.Context, key string, maxLen int64) *IntCmd
+	XInfoGroups(ctx context.Context, key string) *XInfoGroupsCmd
+	XInfoStream(ctx context.Context, key string) *XInfoStreamCmd
+
+	BZPopMax(ctx context.Context, timeout time.Duration, keys ...string) *ZWithKeyCmd
+	BZPopMin(ctx context.Context, timeout time.Duration, keys ...string) *ZWithKeyCmd
+	ZAdd(ctx context.Context, key string, members ...*Z) *IntCmd
+	ZAddNX(ctx context.Context, key string, members ...*Z) *IntCmd
+	ZAddXX(ctx context.Context, key string, members ...*Z) *IntCmd
+	ZAddCh(ctx context.Context, key string, members ...*Z) *IntCmd
+	ZAddNXCh(ctx context.Context, key string, members ...*Z) *IntCmd
+	ZAddXXCh(ctx context.Context, key string, members ...*Z) *IntCmd
+	ZIncr(ctx context.Context, key string, member *Z) *FloatCmd
+	ZIncrNX(ctx context.Context, key string, member *Z) *FloatCmd
+	ZIncrXX(ctx context.Context, key string, member *Z) *FloatCmd
+	ZCard(ctx context.Context, key string) *IntCmd
+	ZCount(ctx context.Context, key, min, max string) *IntCmd
+	ZLexCount(ctx context.Context, key, min, max string) *IntCmd
+	ZIncrBy(ctx context.Context, key string, increment float64, member string) *FloatCmd
+	ZInterStore(ctx context.Context, destination string, store *ZStore) *IntCmd
+	ZPopMax(ctx context.Context, key string, count ...int64) *ZSliceCmd
+	ZPopMin(ctx context.Context, key string, count ...int64) *ZSliceCmd
+	ZRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd
+	ZRangeWithScores(ctx context.Context, key string, start, stop int64) *ZSliceCmd
+	ZRangeByScore(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd
+	ZRangeByLex(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd
+	ZRangeByScoreWithScores(ctx context.Context, key string, opt *ZRangeBy) *ZSliceCmd
+	ZRank(ctx context.Context, key, member string) *IntCmd
+	ZRem(ctx context.Context, key string, members ...interface{}) *IntCmd
+	ZRemRangeByRank(ctx context.Context, key string, start, stop int64) *IntCmd
+	ZRemRangeByScore(ctx context.Context, key, min, max string) *IntCmd
+	ZRemRangeByLex(ctx context.Context, key, min, max string) *IntCmd
+	ZRevRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd
+	ZRevRangeWithScores(ctx context.Context, key string, start, stop int64) *ZSliceCmd
+	ZRevRangeByScore(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd
+	ZRevRangeByLex(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd
+	ZRevRangeByScoreWithScores(ctx context.Context, key string, opt *ZRangeBy) *ZSliceCmd
+	ZRevRank(ctx context.Context, key, member string) *IntCmd
+	ZScore(ctx context.Context, key, member string) *FloatCmd
+	ZUnionStore(ctx context.Context, dest string, store *ZStore) *IntCmd
+
+	PFAdd(ctx context.Context, key string, els ...interface{}) *IntCmd
+	PFCount(ctx context.Context, keys ...string) *IntCmd
+	PFMerge(ctx context.Context, dest string, keys ...string) *StatusCmd
+
+	BgRewriteAOF(ctx context.Context) *StatusCmd
+	BgSave(ctx context.Context) *StatusCmd
+	ClientKill(ctx context.Context, ipPort string) *StatusCmd
+	ClientKillByFilter(ctx context.Context, keys ...string) *IntCmd
+	ClientList(ctx context.Context) *StringCmd
+	ClientPause(ctx context.Context, dur time.Duration) *BoolCmd
+	ClientID(ctx context.Context) *IntCmd
+	ConfigGet(ctx context.Context, parameter string) *SliceCmd
+	ConfigResetStat(ctx context.Context) *StatusCmd
+	ConfigSet(ctx context.Context, parameter, value string) *StatusCmd
+	ConfigRewrite(ctx context.Context) *StatusCmd
+	DBSize(ctx context.Context) *IntCmd
+	FlushAll(ctx context.Context) *StatusCmd
+	FlushAllAsync(ctx context.Context) *StatusCmd
+	FlushDB(ctx context.Context) *StatusCmd
+	FlushDBAsync(ctx context.Context) *StatusCmd
+	Info(ctx context.Context, section ...string) *StringCmd
+	LastSave(ctx context.Context) *IntCmd
+	Save(ctx context.Context) *StatusCmd
+	Shutdown(ctx context.Context) *StatusCmd
+	ShutdownSave(ctx context.Context) *StatusCmd
+	ShutdownNoSave(ctx context.Context) *StatusCmd
+	SlaveOf(ctx context.Context, host, port string) *StatusCmd
+	Time(ctx context.Context) *TimeCmd
+	DebugObject(ctx context.Context, key string) *StringCmd
+	ReadOnly(ctx context.Context) *StatusCmd
+	ReadWrite(ctx context.Context) *StatusCmd
+	MemoryUsage(ctx context.Context, key string, samples ...int) *IntCmd
+
+	Eval(ctx context.Context, script string, keys []string, args ...interface{}) *Cmd
+	EvalSha(ctx context.Context, sha1 string, keys []string, args ...interface{}) *Cmd
+	ScriptExists(ctx context.Context, hashes ...string) *BoolSliceCmd
+	ScriptFlush(ctx context.Context) *StatusCmd
+	ScriptKill(ctx context.Context) *StatusCmd
+	ScriptLoad(ctx context.Context, script string) *StringCmd
+
+	Publish(ctx context.Context, channel string, message interface{}) *IntCmd
+	PubSubChannels(ctx context.Context, pattern string) *StringSliceCmd
+	PubSubNumSub(ctx context.Context, channels ...string) *StringIntMapCmd
+	PubSubNumPat(ctx context.Context) *IntCmd
+
+	ClusterSlots(ctx context.Context) *ClusterSlotsCmd
+	ClusterNodes(ctx context.Context) *StringCmd
+	ClusterMeet(ctx context.Context, host, port string) *StatusCmd
+	ClusterForget(ctx context.Context, nodeID string) *StatusCmd
+	ClusterReplicate(ctx context.Context, nodeID string) *StatusCmd
+	ClusterResetSoft(ctx context.Context) *StatusCmd
+	ClusterResetHard(ctx context.Context) *StatusCmd
+	ClusterInfo(ctx context.Context) *StringCmd
+	ClusterKeySlot(ctx context.Context, key string) *IntCmd
+	ClusterGetKeysInSlot(ctx context.Context, slot int, count int) *StringSliceCmd
+	ClusterCountFailureReports(ctx context.Context, nodeID string) *IntCmd
+	ClusterCountKeysInSlot(ctx context.Context, slot int) *IntCmd
+	ClusterDelSlots(ctx context.Context, slots ...int) *StatusCmd
+	ClusterDelSlotsRange(ctx context.Context, min, max int) *StatusCmd
+	ClusterSaveConfig(ctx context.Context) *StatusCmd
+	ClusterSlaves(ctx context.Context, nodeID string) *StringSliceCmd
+	ClusterFailover(ctx context.Context) *StatusCmd
+	ClusterAddSlots(ctx context.Context, slots ...int) *StatusCmd
+	ClusterAddSlotsRange(ctx context.Context, min, max int) *StatusCmd
+
+	GeoAdd(ctx context.Context, key string, geoLocation ...*GeoLocation) *IntCmd
+	GeoPos(ctx context.Context, key string, members ...string) *GeoPosCmd
+	GeoRadius(ctx context.Context, key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd
+	GeoRadiusStore(ctx context.Context, key string, longitude, latitude float64, query *GeoRadiusQuery) *IntCmd
+	GeoRadiusByMember(ctx context.Context, key, member string, query *GeoRadiusQuery) *GeoLocationCmd
+	GeoRadiusByMemberStore(ctx context.Context, key, member string, query *GeoRadiusQuery) *IntCmd
+	GeoDist(ctx context.Context, key string, member1, member2, unit string) *FloatCmd
+	GeoHash(ctx context.Context, key string, members ...string) *StringSliceCmd
+}
+
+type StatefulCmdable interface {
+	Cmdable
+	Auth(ctx context.Context, password string) *StatusCmd
+	AuthACL(ctx context.Context, username, password string) *StatusCmd
+	Select(ctx context.Context, index int) *StatusCmd
+	SwapDB(ctx context.Context, index1, index2 int) *StatusCmd
+	ClientSetName(ctx context.Context, name string) *BoolCmd
+}
+
+var (
+	_ Cmdable = (*Client)(nil)
+	_ Cmdable = (*Tx)(nil)
+	_ Cmdable = (*Ring)(nil)
+	_ Cmdable = (*ClusterClient)(nil)
+)
+
+type cmdable func(ctx context.Context, cmd Cmder) error
+
+type statefulCmdable func(ctx context.Context, cmd Cmder) error
+
+//------------------------------------------------------------------------------
+
+func (c statefulCmdable) Auth(ctx context.Context, password string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "auth", password)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Perform an AUTH command, using the given user and pass.
+// Should be used to authenticate the current connection with one of the connections defined in the ACL list
+// when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
+func (c statefulCmdable) AuthACL(ctx context.Context, username, password string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "auth", username, password)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Wait(ctx context.Context, numSlaves int, timeout time.Duration) *IntCmd {
+	cmd := NewIntCmd(ctx, "wait", numSlaves, int(timeout/time.Millisecond))
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c statefulCmdable) Select(ctx context.Context, index int) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "select", index)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c statefulCmdable) SwapDB(ctx context.Context, index1, index2 int) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "swapdb", index1, index2)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// ClientSetName assigns a name to the connection.
+func (c statefulCmdable) ClientSetName(ctx context.Context, name string) *BoolCmd {
+	cmd := NewBoolCmd(ctx, "client", "setname", name)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+func (c cmdable) Command(ctx context.Context) *CommandsInfoCmd {
+	cmd := NewCommandsInfoCmd(ctx, "command")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// ClientGetName returns the name of the connection.
+func (c cmdable) ClientGetName(ctx context.Context) *StringCmd {
+	cmd := NewStringCmd(ctx, "client", "getname")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Echo(ctx context.Context, message interface{}) *StringCmd {
+	cmd := NewStringCmd(ctx, "echo", message)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Ping(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "ping")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Quit(ctx context.Context) *StatusCmd {
+	panic("not implemented")
+}
+
+func (c cmdable) Del(ctx context.Context, keys ...string) *IntCmd {
+	args := make([]interface{}, 1+len(keys))
+	args[0] = "del"
+	for i, key := range keys {
+		args[1+i] = key
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Unlink(ctx context.Context, keys ...string) *IntCmd {
+	args := make([]interface{}, 1+len(keys))
+	args[0] = "unlink"
+	for i, key := range keys {
+		args[1+i] = key
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Dump(ctx context.Context, key string) *StringCmd {
+	cmd := NewStringCmd(ctx, "dump", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Exists(ctx context.Context, keys ...string) *IntCmd {
+	args := make([]interface{}, 1+len(keys))
+	args[0] = "exists"
+	for i, key := range keys {
+		args[1+i] = key
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Expire(ctx context.Context, key string, expiration time.Duration) *BoolCmd {
+	cmd := NewBoolCmd(ctx, "expire", key, formatSec(ctx, expiration))
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ExpireAt(ctx context.Context, key string, tm time.Time) *BoolCmd {
+	cmd := NewBoolCmd(ctx, "expireat", key, tm.Unix())
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Keys(ctx context.Context, pattern string) *StringSliceCmd {
+	cmd := NewStringSliceCmd(ctx, "keys", pattern)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Migrate(ctx context.Context, host, port, key string, db int, timeout time.Duration) *StatusCmd {
+	cmd := NewStatusCmd(
+		ctx,
+		"migrate",
+		host,
+		port,
+		key,
+		db,
+		formatMs(ctx, timeout),
+	)
+	cmd.setReadTimeout(timeout)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Move(ctx context.Context, key string, db int) *BoolCmd {
+	cmd := NewBoolCmd(ctx, "move", key, db)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ObjectRefCount(ctx context.Context, key string) *IntCmd {
+	cmd := NewIntCmd(ctx, "object", "refcount", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ObjectEncoding(ctx context.Context, key string) *StringCmd {
+	cmd := NewStringCmd(ctx, "object", "encoding", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ObjectIdleTime(ctx context.Context, key string) *DurationCmd {
+	cmd := NewDurationCmd(ctx, time.Second, "object", "idletime", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Persist(ctx context.Context, key string) *BoolCmd {
+	cmd := NewBoolCmd(ctx, "persist", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) PExpire(ctx context.Context, key string, expiration time.Duration) *BoolCmd {
+	cmd := NewBoolCmd(ctx, "pexpire", key, formatMs(ctx, expiration))
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) PExpireAt(ctx context.Context, key string, tm time.Time) *BoolCmd {
+	cmd := NewBoolCmd(
+		ctx,
+		"pexpireat",
+		key,
+		tm.UnixNano()/int64(time.Millisecond),
+	)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) PTTL(ctx context.Context, key string) *DurationCmd {
+	cmd := NewDurationCmd(ctx, time.Millisecond, "pttl", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) RandomKey(ctx context.Context) *StringCmd {
+	cmd := NewStringCmd(ctx, "randomkey")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Rename(ctx context.Context, key, newkey string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "rename", key, newkey)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) RenameNX(ctx context.Context, key, newkey string) *BoolCmd {
+	cmd := NewBoolCmd(ctx, "renamenx", key, newkey)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Restore(ctx context.Context, key string, ttl time.Duration, value string) *StatusCmd {
+	cmd := NewStatusCmd(
+		ctx,
+		"restore",
+		key,
+		formatMs(ctx, ttl),
+		value,
+	)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) RestoreReplace(ctx context.Context, key string, ttl time.Duration, value string) *StatusCmd {
+	cmd := NewStatusCmd(
+		ctx,
+		"restore",
+		key,
+		formatMs(ctx, ttl),
+		value,
+		"replace",
+	)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+type Sort struct {
+	By            string
+	Offset, Count int64
+	Get           []string
+	Order         string
+	Alpha         bool
+}
+
+func (sort *Sort) args(key string) []interface{} {
+	args := []interface{}{"sort", key}
+	if sort.By != "" {
+		args = append(args, "by", sort.By)
+	}
+	if sort.Offset != 0 || sort.Count != 0 {
+		args = append(args, "limit", sort.Offset, sort.Count)
+	}
+	for _, get := range sort.Get {
+		args = append(args, "get", get)
+	}
+	if sort.Order != "" {
+		args = append(args, sort.Order)
+	}
+	if sort.Alpha {
+		args = append(args, "alpha")
+	}
+	return args
+}
+
+func (c cmdable) Sort(ctx context.Context, key string, sort *Sort) *StringSliceCmd {
+	cmd := NewStringSliceCmd(ctx, sort.args(key)...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SortStore(ctx context.Context, key, store string, sort *Sort) *IntCmd {
+	args := sort.args(key)
+	if store != "" {
+		args = append(args, "store", store)
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SortInterfaces(ctx context.Context, key string, sort *Sort) *SliceCmd {
+	cmd := NewSliceCmd(ctx, sort.args(key)...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Touch(ctx context.Context, keys ...string) *IntCmd {
+	args := make([]interface{}, len(keys)+1)
+	args[0] = "touch"
+	for i, key := range keys {
+		args[i+1] = key
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) TTL(ctx context.Context, key string) *DurationCmd {
+	cmd := NewDurationCmd(ctx, time.Second, "ttl", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Type(ctx context.Context, key string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "type", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Append(ctx context.Context, key, value string) *IntCmd {
+	cmd := NewIntCmd(ctx, "append", key, value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Decr(ctx context.Context, key string) *IntCmd {
+	cmd := NewIntCmd(ctx, "decr", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) DecrBy(ctx context.Context, key string, decrement int64) *IntCmd {
+	cmd := NewIntCmd(ctx, "decrby", key, decrement)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `GET key` command. It returns redis.Nil error when key does not exist.
+func (c cmdable) Get(ctx context.Context, key string) *StringCmd {
+	cmd := NewStringCmd(ctx, "get", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) GetRange(ctx context.Context, key string, start, end int64) *StringCmd {
+	cmd := NewStringCmd(ctx, "getrange", key, start, end)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) GetSet(ctx context.Context, key string, value interface{}) *StringCmd {
+	cmd := NewStringCmd(ctx, "getset", key, value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Incr(ctx context.Context, key string) *IntCmd {
+	cmd := NewIntCmd(ctx, "incr", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) IncrBy(ctx context.Context, key string, value int64) *IntCmd {
+	cmd := NewIntCmd(ctx, "incrby", key, value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) IncrByFloat(ctx context.Context, key string, value float64) *FloatCmd {
+	cmd := NewFloatCmd(ctx, "incrbyfloat", key, value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) MGet(ctx context.Context, keys ...string) *SliceCmd {
+	args := make([]interface{}, 1+len(keys))
+	args[0] = "mget"
+	for i, key := range keys {
+		args[1+i] = key
+	}
+	cmd := NewSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// 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(ctx context.Context, values ...interface{}) *StatusCmd {
+	args := make([]interface{}, 1, 1+len(values))
+	args[0] = "mset"
+	args = appendArgs(args, values)
+	cmd := NewStatusCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// 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(ctx context.Context, values ...interface{}) *BoolCmd {
+	args := make([]interface{}, 1, 1+len(values))
+	args[0] = "msetnx"
+	args = appendArgs(args, values)
+	cmd := NewBoolCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `SET key value [expiration]` command.
+// Use expiration for `SETEX`-like behavior.
+//
+// Zero expiration means the key has no expiration time.
+// KeepTTL(-1) expiration is a Redis KEEPTTL option to keep existing TTL.
+func (c cmdable) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd {
+	args := make([]interface{}, 3, 5)
+	args[0] = "set"
+	args[1] = key
+	args[2] = value
+	if expiration > 0 {
+		if usePrecise(expiration) {
+			args = append(args, "px", formatMs(ctx, expiration))
+		} else {
+			args = append(args, "ex", formatSec(ctx, expiration))
+		}
+	} else if expiration == KeepTTL {
+		args = append(args, "keepttl")
+	}
+
+	cmd := NewStatusCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `SETEX key expiration value` command.
+func (c cmdable) SetEX(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "setex", key, formatSec(ctx, expiration), value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `SET key value [expiration] NX` command.
+//
+// Zero expiration means the key has no expiration time.
+// KeepTTL(-1) expiration is a Redis KEEPTTL option to keep existing TTL.
+func (c cmdable) SetNX(ctx context.Context, key string, value interface{}, expiration time.Duration) *BoolCmd {
+	var cmd *BoolCmd
+	switch expiration {
+	case 0:
+		// Use old `SETNX` to support old Redis versions.
+		cmd = NewBoolCmd(ctx, "setnx", key, value)
+	case KeepTTL:
+		cmd = NewBoolCmd(ctx, "set", key, value, "keepttl", "nx")
+	default:
+		if usePrecise(expiration) {
+			cmd = NewBoolCmd(ctx, "set", key, value, "px", formatMs(ctx, expiration), "nx")
+		} else {
+			cmd = NewBoolCmd(ctx, "set", key, value, "ex", formatSec(ctx, expiration), "nx")
+		}
+	}
+
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `SET key value [expiration] XX` command.
+//
+// Zero expiration means the key has no expiration time.
+// KeepTTL(-1) expiration is a Redis KEEPTTL option to keep existing TTL.
+func (c cmdable) SetXX(ctx context.Context, key string, value interface{}, expiration time.Duration) *BoolCmd {
+	var cmd *BoolCmd
+	switch expiration {
+	case 0:
+		cmd = NewBoolCmd(ctx, "set", key, value, "xx")
+	case KeepTTL:
+		cmd = NewBoolCmd(ctx, "set", key, value, "keepttl", "xx")
+	default:
+		if usePrecise(expiration) {
+			cmd = NewBoolCmd(ctx, "set", key, value, "px", formatMs(ctx, expiration), "xx")
+		} else {
+			cmd = NewBoolCmd(ctx, "set", key, value, "ex", formatSec(ctx, expiration), "xx")
+		}
+	}
+
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SetRange(ctx context.Context, key string, offset int64, value string) *IntCmd {
+	cmd := NewIntCmd(ctx, "setrange", key, offset, value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) StrLen(ctx context.Context, key string) *IntCmd {
+	cmd := NewIntCmd(ctx, "strlen", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+func (c cmdable) GetBit(ctx context.Context, key string, offset int64) *IntCmd {
+	cmd := NewIntCmd(ctx, "getbit", key, offset)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SetBit(ctx context.Context, key string, offset int64, value int) *IntCmd {
+	cmd := NewIntCmd(
+		ctx,
+		"setbit",
+		key,
+		offset,
+		value,
+	)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+type BitCount struct {
+	Start, End int64
+}
+
+func (c cmdable) BitCount(ctx context.Context, key string, bitCount *BitCount) *IntCmd {
+	args := []interface{}{"bitcount", key}
+	if bitCount != nil {
+		args = append(
+			args,
+			bitCount.Start,
+			bitCount.End,
+		)
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) bitOp(ctx context.Context, op, destKey string, keys ...string) *IntCmd {
+	args := make([]interface{}, 3+len(keys))
+	args[0] = "bitop"
+	args[1] = op
+	args[2] = destKey
+	for i, key := range keys {
+		args[3+i] = key
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) BitOpAnd(ctx context.Context, destKey string, keys ...string) *IntCmd {
+	return c.bitOp(ctx, "and", destKey, keys...)
+}
+
+func (c cmdable) BitOpOr(ctx context.Context, destKey string, keys ...string) *IntCmd {
+	return c.bitOp(ctx, "or", destKey, keys...)
+}
+
+func (c cmdable) BitOpXor(ctx context.Context, destKey string, keys ...string) *IntCmd {
+	return c.bitOp(ctx, "xor", destKey, keys...)
+}
+
+func (c cmdable) BitOpNot(ctx context.Context, destKey string, key string) *IntCmd {
+	return c.bitOp(ctx, "not", destKey, key)
+}
+
+func (c cmdable) BitPos(ctx context.Context, key string, bit int64, pos ...int64) *IntCmd {
+	args := make([]interface{}, 3+len(pos))
+	args[0] = "bitpos"
+	args[1] = key
+	args[2] = bit
+	switch len(pos) {
+	case 0:
+	case 1:
+		args[3] = pos[0]
+	case 2:
+		args[3] = pos[0]
+		args[4] = pos[1]
+	default:
+		panic("too many arguments")
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) BitField(ctx context.Context, 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(ctx, a...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+func (c cmdable) Scan(ctx context.Context, cursor uint64, match string, count int64) *ScanCmd {
+	args := []interface{}{"scan", cursor}
+	if match != "" {
+		args = append(args, "match", match)
+	}
+	if count > 0 {
+		args = append(args, "count", count)
+	}
+	cmd := NewScanCmd(ctx, c, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ScanType(ctx context.Context, cursor uint64, match string, count int64, keyType string) *ScanCmd {
+	args := []interface{}{"scan", cursor}
+	if match != "" {
+		args = append(args, "match", match)
+	}
+	if count > 0 {
+		args = append(args, "count", count)
+	}
+	if keyType != "" {
+		args = append(args, "type", keyType)
+	}
+	cmd := NewScanCmd(ctx, c, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd {
+	args := []interface{}{"sscan", key, cursor}
+	if match != "" {
+		args = append(args, "match", match)
+	}
+	if count > 0 {
+		args = append(args, "count", count)
+	}
+	cmd := NewScanCmd(ctx, c, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) HScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd {
+	args := []interface{}{"hscan", key, cursor}
+	if match != "" {
+		args = append(args, "match", match)
+	}
+	if count > 0 {
+		args = append(args, "count", count)
+	}
+	cmd := NewScanCmd(ctx, c, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZScan(ctx context.Context, key string, cursor uint64, match string, count int64) *ScanCmd {
+	args := []interface{}{"zscan", key, cursor}
+	if match != "" {
+		args = append(args, "match", match)
+	}
+	if count > 0 {
+		args = append(args, "count", count)
+	}
+	cmd := NewScanCmd(ctx, c, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+func (c cmdable) HDel(ctx context.Context, key string, fields ...string) *IntCmd {
+	args := make([]interface{}, 2+len(fields))
+	args[0] = "hdel"
+	args[1] = key
+	for i, field := range fields {
+		args[2+i] = field
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) HExists(ctx context.Context, key, field string) *BoolCmd {
+	cmd := NewBoolCmd(ctx, "hexists", key, field)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) HGet(ctx context.Context, key, field string) *StringCmd {
+	cmd := NewStringCmd(ctx, "hget", key, field)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) HGetAll(ctx context.Context, key string) *StringStringMapCmd {
+	cmd := NewStringStringMapCmd(ctx, "hgetall", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) HIncrBy(ctx context.Context, key, field string, incr int64) *IntCmd {
+	cmd := NewIntCmd(ctx, "hincrby", key, field, incr)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) HIncrByFloat(ctx context.Context, key, field string, incr float64) *FloatCmd {
+	cmd := NewFloatCmd(ctx, "hincrbyfloat", key, field, incr)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) HKeys(ctx context.Context, key string) *StringSliceCmd {
+	cmd := NewStringSliceCmd(ctx, "hkeys", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) HLen(ctx context.Context, key string) *IntCmd {
+	cmd := NewIntCmd(ctx, "hlen", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// 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(ctx context.Context, key string, fields ...string) *SliceCmd {
+	args := make([]interface{}, 2+len(fields))
+	args[0] = "hmget"
+	args[1] = key
+	for i, field := range fields {
+		args[2+i] = field
+	}
+	cmd := NewSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// HSet accepts values in following formats:
+//   - HSet("myhash", "key1", "value1", "key2", "value2")
+//   - HSet("myhash", []string{"key1", "value1", "key2", "value2"})
+//   - HSet("myhash", map[string]interface{}{"key1": "value1", "key2": "value2"})
+//
+// Note that it requires Redis v4 for multiple field/value pairs support.
+func (c cmdable) HSet(ctx context.Context, key string, values ...interface{}) *IntCmd {
+	args := make([]interface{}, 2, 2+len(values))
+	args[0] = "hset"
+	args[1] = key
+	args = appendArgs(args, values)
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// HMSet is a deprecated version of HSet left for compatibility with Redis 3.
+func (c cmdable) HMSet(ctx context.Context, key string, values ...interface{}) *BoolCmd {
+	args := make([]interface{}, 2, 2+len(values))
+	args[0] = "hmset"
+	args[1] = key
+	args = appendArgs(args, values)
+	cmd := NewBoolCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) HSetNX(ctx context.Context, key, field string, value interface{}) *BoolCmd {
+	cmd := NewBoolCmd(ctx, "hsetnx", key, field, value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) HVals(ctx context.Context, key string) *StringSliceCmd {
+	cmd := NewStringSliceCmd(ctx, "hvals", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+func (c cmdable) BLPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd {
+	args := make([]interface{}, 1+len(keys)+1)
+	args[0] = "blpop"
+	for i, key := range keys {
+		args[1+i] = key
+	}
+	args[len(args)-1] = formatSec(ctx, timeout)
+	cmd := NewStringSliceCmd(ctx, args...)
+	cmd.setReadTimeout(timeout)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) BRPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd {
+	args := make([]interface{}, 1+len(keys)+1)
+	args[0] = "brpop"
+	for i, key := range keys {
+		args[1+i] = key
+	}
+	args[len(keys)+1] = formatSec(ctx, timeout)
+	cmd := NewStringSliceCmd(ctx, args...)
+	cmd.setReadTimeout(timeout)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) BRPopLPush(ctx context.Context, source, destination string, timeout time.Duration) *StringCmd {
+	cmd := NewStringCmd(
+		ctx,
+		"brpoplpush",
+		source,
+		destination,
+		formatSec(ctx, timeout),
+	)
+	cmd.setReadTimeout(timeout)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LIndex(ctx context.Context, key string, index int64) *StringCmd {
+	cmd := NewStringCmd(ctx, "lindex", key, index)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LInsert(ctx context.Context, key, op string, pivot, value interface{}) *IntCmd {
+	cmd := NewIntCmd(ctx, "linsert", key, op, pivot, value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LInsertBefore(ctx context.Context, key string, pivot, value interface{}) *IntCmd {
+	cmd := NewIntCmd(ctx, "linsert", key, "before", pivot, value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LInsertAfter(ctx context.Context, key string, pivot, value interface{}) *IntCmd {
+	cmd := NewIntCmd(ctx, "linsert", key, "after", pivot, value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LLen(ctx context.Context, key string) *IntCmd {
+	cmd := NewIntCmd(ctx, "llen", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LPop(ctx context.Context, key string) *StringCmd {
+	cmd := NewStringCmd(ctx, "lpop", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+type LPosArgs struct {
+	Rank, MaxLen int64
+}
+
+func (c cmdable) LPos(ctx context.Context, key string, value string, a LPosArgs) *IntCmd {
+	args := []interface{}{"lpos", key, value}
+	if a.Rank != 0 {
+		args = append(args, "rank", a.Rank)
+	}
+	if a.MaxLen != 0 {
+		args = append(args, "maxlen", a.MaxLen)
+	}
+
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LPosCount(ctx context.Context, key string, value string, count int64, a LPosArgs) *IntSliceCmd {
+	args := []interface{}{"lpos", key, value, "count", count}
+	if a.Rank != 0 {
+		args = append(args, "rank", a.Rank)
+	}
+	if a.MaxLen != 0 {
+		args = append(args, "maxlen", a.MaxLen)
+	}
+	cmd := NewIntSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LPush(ctx context.Context, key string, values ...interface{}) *IntCmd {
+	args := make([]interface{}, 2, 2+len(values))
+	args[0] = "lpush"
+	args[1] = key
+	args = appendArgs(args, values)
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LPushX(ctx context.Context, key string, values ...interface{}) *IntCmd {
+	args := make([]interface{}, 2, 2+len(values))
+	args[0] = "lpushx"
+	args[1] = key
+	args = appendArgs(args, values)
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd {
+	cmd := NewStringSliceCmd(
+		ctx,
+		"lrange",
+		key,
+		start,
+		stop,
+	)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LRem(ctx context.Context, key string, count int64, value interface{}) *IntCmd {
+	cmd := NewIntCmd(ctx, "lrem", key, count, value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LSet(ctx context.Context, key string, index int64, value interface{}) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "lset", key, index, value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LTrim(ctx context.Context, key string, start, stop int64) *StatusCmd {
+	cmd := NewStatusCmd(
+		ctx,
+		"ltrim",
+		key,
+		start,
+		stop,
+	)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) RPop(ctx context.Context, key string) *StringCmd {
+	cmd := NewStringCmd(ctx, "rpop", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) RPopLPush(ctx context.Context, source, destination string) *StringCmd {
+	cmd := NewStringCmd(ctx, "rpoplpush", source, destination)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) RPush(ctx context.Context, key string, values ...interface{}) *IntCmd {
+	args := make([]interface{}, 2, 2+len(values))
+	args[0] = "rpush"
+	args[1] = key
+	args = appendArgs(args, values)
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) RPushX(ctx context.Context, key string, values ...interface{}) *IntCmd {
+	args := make([]interface{}, 2, 2+len(values))
+	args[0] = "rpushx"
+	args[1] = key
+	args = appendArgs(args, values)
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+func (c cmdable) SAdd(ctx context.Context, key string, members ...interface{}) *IntCmd {
+	args := make([]interface{}, 2, 2+len(members))
+	args[0] = "sadd"
+	args[1] = key
+	args = appendArgs(args, members)
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SCard(ctx context.Context, key string) *IntCmd {
+	cmd := NewIntCmd(ctx, "scard", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SDiff(ctx context.Context, keys ...string) *StringSliceCmd {
+	args := make([]interface{}, 1+len(keys))
+	args[0] = "sdiff"
+	for i, key := range keys {
+		args[1+i] = key
+	}
+	cmd := NewStringSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SDiffStore(ctx context.Context, destination string, keys ...string) *IntCmd {
+	args := make([]interface{}, 2+len(keys))
+	args[0] = "sdiffstore"
+	args[1] = destination
+	for i, key := range keys {
+		args[2+i] = key
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SInter(ctx context.Context, keys ...string) *StringSliceCmd {
+	args := make([]interface{}, 1+len(keys))
+	args[0] = "sinter"
+	for i, key := range keys {
+		args[1+i] = key
+	}
+	cmd := NewStringSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SInterStore(ctx context.Context, destination string, keys ...string) *IntCmd {
+	args := make([]interface{}, 2+len(keys))
+	args[0] = "sinterstore"
+	args[1] = destination
+	for i, key := range keys {
+		args[2+i] = key
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SIsMember(ctx context.Context, key string, member interface{}) *BoolCmd {
+	cmd := NewBoolCmd(ctx, "sismember", key, member)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `SMEMBERS key` command output as a slice.
+func (c cmdable) SMembers(ctx context.Context, key string) *StringSliceCmd {
+	cmd := NewStringSliceCmd(ctx, "smembers", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `SMEMBERS key` command output as a map.
+func (c cmdable) SMembersMap(ctx context.Context, key string) *StringStructMapCmd {
+	cmd := NewStringStructMapCmd(ctx, "smembers", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SMove(ctx context.Context, source, destination string, member interface{}) *BoolCmd {
+	cmd := NewBoolCmd(ctx, "smove", source, destination, member)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `SPOP key` command.
+func (c cmdable) SPop(ctx context.Context, key string) *StringCmd {
+	cmd := NewStringCmd(ctx, "spop", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `SPOP key count` command.
+func (c cmdable) SPopN(ctx context.Context, key string, count int64) *StringSliceCmd {
+	cmd := NewStringSliceCmd(ctx, "spop", key, count)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `SRANDMEMBER key` command.
+func (c cmdable) SRandMember(ctx context.Context, key string) *StringCmd {
+	cmd := NewStringCmd(ctx, "srandmember", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `SRANDMEMBER key count` command.
+func (c cmdable) SRandMemberN(ctx context.Context, key string, count int64) *StringSliceCmd {
+	cmd := NewStringSliceCmd(ctx, "srandmember", key, count)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SRem(ctx context.Context, key string, members ...interface{}) *IntCmd {
+	args := make([]interface{}, 2, 2+len(members))
+	args[0] = "srem"
+	args[1] = key
+	args = appendArgs(args, members)
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SUnion(ctx context.Context, keys ...string) *StringSliceCmd {
+	args := make([]interface{}, 1+len(keys))
+	args[0] = "sunion"
+	for i, key := range keys {
+		args[1+i] = key
+	}
+	cmd := NewStringSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SUnionStore(ctx context.Context, destination string, keys ...string) *IntCmd {
+	args := make([]interface{}, 2+len(keys))
+	args[0] = "sunionstore"
+	args[1] = destination
+	for i, key := range keys {
+		args[2+i] = key
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+// XAddArgs accepts values in the following formats:
+//   - XAddArgs.Values = []interface{}{"key1", "value1", "key2", "value2"}
+//   - XAddArgs.Values = []string("key1", "value1", "key2", "value2")
+//   - XAddArgs.Values = map[string]interface{}{"key1": "value1", "key2": "value2"}
+//
+// Note that map will not preserve the order of key-value pairs.
+type XAddArgs struct {
+	Stream       string
+	MaxLen       int64 // MAXLEN N
+	MaxLenApprox int64 // MAXLEN ~ N
+	ID           string
+	Values       interface{}
+}
+
+func (c cmdable) XAdd(ctx context.Context, a *XAddArgs) *StringCmd {
+	args := make([]interface{}, 0, 8)
+	args = append(args, "xadd")
+	args = append(args, a.Stream)
+	if a.MaxLen > 0 {
+		args = append(args, "maxlen", a.MaxLen)
+	} else if a.MaxLenApprox > 0 {
+		args = append(args, "maxlen", "~", a.MaxLenApprox)
+	}
+	if a.ID != "" {
+		args = append(args, a.ID)
+	} else {
+		args = append(args, "*")
+	}
+	args = appendArg(args, a.Values)
+
+	cmd := NewStringCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XDel(ctx context.Context, stream string, ids ...string) *IntCmd {
+	args := []interface{}{"xdel", stream}
+	for _, id := range ids {
+		args = append(args, id)
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XLen(ctx context.Context, stream string) *IntCmd {
+	cmd := NewIntCmd(ctx, "xlen", stream)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XRange(ctx context.Context, stream, start, stop string) *XMessageSliceCmd {
+	cmd := NewXMessageSliceCmd(ctx, "xrange", stream, start, stop)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XRangeN(ctx context.Context, stream, start, stop string, count int64) *XMessageSliceCmd {
+	cmd := NewXMessageSliceCmd(ctx, "xrange", stream, start, stop, "count", count)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XRevRange(ctx context.Context, stream, start, stop string) *XMessageSliceCmd {
+	cmd := NewXMessageSliceCmd(ctx, "xrevrange", stream, start, stop)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XRevRangeN(ctx context.Context, stream, start, stop string, count int64) *XMessageSliceCmd {
+	cmd := NewXMessageSliceCmd(ctx, "xrevrange", stream, start, stop, "count", count)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+type XReadArgs struct {
+	Streams []string // list of streams and ids, e.g. stream1 stream2 id1 id2
+	Count   int64
+	Block   time.Duration
+}
+
+func (c cmdable) XRead(ctx context.Context, a *XReadArgs) *XStreamSliceCmd {
+	args := make([]interface{}, 0, 5+len(a.Streams))
+	args = append(args, "xread")
+
+	keyPos := int8(1)
+	if a.Count > 0 {
+		args = append(args, "count")
+		args = append(args, a.Count)
+		keyPos += 2
+	}
+	if a.Block >= 0 {
+		args = append(args, "block")
+		args = append(args, int64(a.Block/time.Millisecond))
+		keyPos += 2
+	}
+	args = append(args, "streams")
+	keyPos++
+	for _, s := range a.Streams {
+		args = append(args, s)
+	}
+
+	cmd := NewXStreamSliceCmd(ctx, args...)
+	if a.Block >= 0 {
+		cmd.setReadTimeout(a.Block)
+	}
+	cmd.setFirstKeyPos(keyPos)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XReadStreams(ctx context.Context, streams ...string) *XStreamSliceCmd {
+	return c.XRead(ctx, &XReadArgs{
+		Streams: streams,
+		Block:   -1,
+	})
+}
+
+func (c cmdable) XGroupCreate(ctx context.Context, stream, group, start string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "xgroup", "create", stream, group, start)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XGroupCreateMkStream(ctx context.Context, stream, group, start string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "xgroup", "create", stream, group, start, "mkstream")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XGroupSetID(ctx context.Context, stream, group, start string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "xgroup", "setid", stream, group, start)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XGroupDestroy(ctx context.Context, stream, group string) *IntCmd {
+	cmd := NewIntCmd(ctx, "xgroup", "destroy", stream, group)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XGroupDelConsumer(ctx context.Context, stream, group, consumer string) *IntCmd {
+	cmd := NewIntCmd(ctx, "xgroup", "delconsumer", stream, group, consumer)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+type XReadGroupArgs struct {
+	Group    string
+	Consumer 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(ctx context.Context, a *XReadGroupArgs) *XStreamSliceCmd {
+	args := make([]interface{}, 0, 8+len(a.Streams))
+	args = append(args, "xreadgroup", "group", a.Group, a.Consumer)
+
+	keyPos := int8(1)
+	if a.Count > 0 {
+		args = append(args, "count", a.Count)
+		keyPos += 2
+	}
+	if a.Block >= 0 {
+		args = append(args, "block", int64(a.Block/time.Millisecond))
+		keyPos += 2
+	}
+	if a.NoAck {
+		args = append(args, "noack")
+		keyPos++
+	}
+	args = append(args, "streams")
+	keyPos++
+	for _, s := range a.Streams {
+		args = append(args, s)
+	}
+
+	cmd := NewXStreamSliceCmd(ctx, args...)
+	if a.Block >= 0 {
+		cmd.setReadTimeout(a.Block)
+	}
+	cmd.setFirstKeyPos(keyPos)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XAck(ctx context.Context, stream, group string, ids ...string) *IntCmd {
+	args := []interface{}{"xack", stream, group}
+	for _, id := range ids {
+		args = append(args, id)
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XPending(ctx context.Context, stream, group string) *XPendingCmd {
+	cmd := NewXPendingCmd(ctx, "xpending", stream, group)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+type XPendingExtArgs struct {
+	Stream   string
+	Group    string
+	Start    string
+	End      string
+	Count    int64
+	Consumer string
+}
+
+func (c cmdable) XPendingExt(ctx context.Context, 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(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+type XClaimArgs struct {
+	Stream   string
+	Group    string
+	Consumer string
+	MinIdle  time.Duration
+	Messages []string
+}
+
+func (c cmdable) XClaim(ctx context.Context, a *XClaimArgs) *XMessageSliceCmd {
+	args := xClaimArgs(a)
+	cmd := NewXMessageSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XClaimJustID(ctx context.Context, a *XClaimArgs) *StringSliceCmd {
+	args := xClaimArgs(a)
+	args = append(args, "justid")
+	cmd := NewStringSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func xClaimArgs(a *XClaimArgs) []interface{} {
+	args := make([]interface{}, 0, 4+len(a.Messages))
+	args = append(args,
+		"xclaim",
+		a.Stream,
+		a.Group, a.Consumer,
+		int64(a.MinIdle/time.Millisecond))
+	for _, id := range a.Messages {
+		args = append(args, id)
+	}
+	return args
+}
+
+func (c cmdable) XTrim(ctx context.Context, key string, maxLen int64) *IntCmd {
+	cmd := NewIntCmd(ctx, "xtrim", key, "maxlen", maxLen)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XTrimApprox(ctx context.Context, key string, maxLen int64) *IntCmd {
+	cmd := NewIntCmd(ctx, "xtrim", key, "maxlen", "~", maxLen)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XInfoGroups(ctx context.Context, key string) *XInfoGroupsCmd {
+	cmd := NewXInfoGroupsCmd(ctx, key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) XInfoStream(ctx context.Context, key string) *XInfoStreamCmd {
+	cmd := NewXInfoStreamCmd(ctx, key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+// Z represents sorted set member.
+type Z struct {
+	Score  float64
+	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
+}
+
+// Redis `BZPOPMAX key [key ...] timeout` command.
+func (c cmdable) BZPopMax(ctx context.Context, 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(ctx, timeout)
+	cmd := NewZWithKeyCmd(ctx, args...)
+	cmd.setReadTimeout(timeout)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `BZPOPMIN key [key ...] timeout` command.
+func (c cmdable) BZPopMin(ctx context.Context, 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(ctx, timeout)
+	cmd := NewZWithKeyCmd(ctx, args...)
+	cmd.setReadTimeout(timeout)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) zAdd(ctx context.Context, 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(ctx, a...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `ZADD key score member [score member ...]` command.
+func (c cmdable) ZAdd(ctx context.Context, key string, members ...*Z) *IntCmd {
+	const n = 2
+	a := make([]interface{}, n+2*len(members))
+	a[0], a[1] = "zadd", key
+	return c.zAdd(ctx, a, n, members...)
+}
+
+// Redis `ZADD key NX score member [score member ...]` command.
+func (c cmdable) ZAddNX(ctx context.Context, key string, members ...*Z) *IntCmd {
+	const n = 3
+	a := make([]interface{}, n+2*len(members))
+	a[0], a[1], a[2] = "zadd", key, "nx"
+	return c.zAdd(ctx, a, n, members...)
+}
+
+// Redis `ZADD key XX score member [score member ...]` command.
+func (c cmdable) ZAddXX(ctx context.Context, key string, members ...*Z) *IntCmd {
+	const n = 3
+	a := make([]interface{}, n+2*len(members))
+	a[0], a[1], a[2] = "zadd", key, "xx"
+	return c.zAdd(ctx, a, n, members...)
+}
+
+// Redis `ZADD key CH score member [score member ...]` command.
+func (c cmdable) ZAddCh(ctx context.Context, key string, members ...*Z) *IntCmd {
+	const n = 3
+	a := make([]interface{}, n+2*len(members))
+	a[0], a[1], a[2] = "zadd", key, "ch"
+	return c.zAdd(ctx, a, n, members...)
+}
+
+// Redis `ZADD key NX CH score member [score member ...]` command.
+func (c cmdable) ZAddNXCh(ctx context.Context, 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"
+	return c.zAdd(ctx, a, n, members...)
+}
+
+// Redis `ZADD key XX CH score member [score member ...]` command.
+func (c cmdable) ZAddXXCh(ctx context.Context, 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(ctx, a, n, members...)
+}
+
+func (c cmdable) zIncr(ctx context.Context, 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(ctx, a...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// Redis `ZADD key INCR score member` command.
+func (c cmdable) ZIncr(ctx context.Context, key string, member *Z) *FloatCmd {
+	const n = 3
+	a := make([]interface{}, n+2)
+	a[0], a[1], a[2] = "zadd", key, "incr"
+	return c.zIncr(ctx, a, n, member)
+}
+
+// Redis `ZADD key NX INCR score member` command.
+func (c cmdable) ZIncrNX(ctx context.Context, 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"
+	return c.zIncr(ctx, a, n, member)
+}
+
+// Redis `ZADD key XX INCR score member` command.
+func (c cmdable) ZIncrXX(ctx context.Context, 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(ctx, a, n, member)
+}
+
+func (c cmdable) ZCard(ctx context.Context, key string) *IntCmd {
+	cmd := NewIntCmd(ctx, "zcard", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZCount(ctx context.Context, key, min, max string) *IntCmd {
+	cmd := NewIntCmd(ctx, "zcount", key, min, max)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZLexCount(ctx context.Context, key, min, max string) *IntCmd {
+	cmd := NewIntCmd(ctx, "zlexcount", key, min, max)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZIncrBy(ctx context.Context, key string, increment float64, member string) *FloatCmd {
+	cmd := NewFloatCmd(ctx, "zincrby", key, increment, member)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZInterStore(ctx context.Context, destination string, store *ZStore) *IntCmd {
+	args := make([]interface{}, 3+len(store.Keys))
+	args[0] = "zinterstore"
+	args[1] = destination
+	args[2] = len(store.Keys)
+	for i, key := range store.Keys {
+		args[3+i] = key
+	}
+	if len(store.Weights) > 0 {
+		args = append(args, "weights")
+		for _, weight := range store.Weights {
+			args = append(args, weight)
+		}
+	}
+	if store.Aggregate != "" {
+		args = append(args, "aggregate", store.Aggregate)
+	}
+	cmd := NewIntCmd(ctx, args...)
+	cmd.setFirstKeyPos(3)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZPopMax(ctx context.Context, 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(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZPopMin(ctx context.Context, 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(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) zRange(ctx context.Context, key string, start, stop int64, withScores bool) *StringSliceCmd {
+	args := []interface{}{
+		"zrange",
+		key,
+		start,
+		stop,
+	}
+	if withScores {
+		args = append(args, "withscores")
+	}
+	cmd := NewStringSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd {
+	return c.zRange(ctx, key, start, stop, false)
+}
+
+func (c cmdable) ZRangeWithScores(ctx context.Context, key string, start, stop int64) *ZSliceCmd {
+	cmd := NewZSliceCmd(ctx, "zrange", key, start, stop, "withscores")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+type ZRangeBy struct {
+	Min, Max      string
+	Offset, Count int64
+}
+
+func (c cmdable) zRangeBy(ctx context.Context, zcmd, key string, opt *ZRangeBy, withScores bool) *StringSliceCmd {
+	args := []interface{}{zcmd, key, opt.Min, opt.Max}
+	if withScores {
+		args = append(args, "withscores")
+	}
+	if opt.Offset != 0 || opt.Count != 0 {
+		args = append(
+			args,
+			"limit",
+			opt.Offset,
+			opt.Count,
+		)
+	}
+	cmd := NewStringSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZRangeByScore(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd {
+	return c.zRangeBy(ctx, "zrangebyscore", key, opt, false)
+}
+
+func (c cmdable) ZRangeByLex(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd {
+	return c.zRangeBy(ctx, "zrangebylex", key, opt, false)
+}
+
+func (c cmdable) ZRangeByScoreWithScores(ctx context.Context, key string, opt *ZRangeBy) *ZSliceCmd {
+	args := []interface{}{"zrangebyscore", key, opt.Min, opt.Max, "withscores"}
+	if opt.Offset != 0 || opt.Count != 0 {
+		args = append(
+			args,
+			"limit",
+			opt.Offset,
+			opt.Count,
+		)
+	}
+	cmd := NewZSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZRank(ctx context.Context, key, member string) *IntCmd {
+	cmd := NewIntCmd(ctx, "zrank", key, member)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZRem(ctx context.Context, key string, members ...interface{}) *IntCmd {
+	args := make([]interface{}, 2, 2+len(members))
+	args[0] = "zrem"
+	args[1] = key
+	args = appendArgs(args, members)
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZRemRangeByRank(ctx context.Context, key string, start, stop int64) *IntCmd {
+	cmd := NewIntCmd(
+		ctx,
+		"zremrangebyrank",
+		key,
+		start,
+		stop,
+	)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZRemRangeByScore(ctx context.Context, key, min, max string) *IntCmd {
+	cmd := NewIntCmd(ctx, "zremrangebyscore", key, min, max)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZRemRangeByLex(ctx context.Context, key, min, max string) *IntCmd {
+	cmd := NewIntCmd(ctx, "zremrangebylex", key, min, max)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZRevRange(ctx context.Context, key string, start, stop int64) *StringSliceCmd {
+	cmd := NewStringSliceCmd(ctx, "zrevrange", key, start, stop)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZRevRangeWithScores(ctx context.Context, key string, start, stop int64) *ZSliceCmd {
+	cmd := NewZSliceCmd(ctx, "zrevrange", key, start, stop, "withscores")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) zRevRangeBy(ctx context.Context, zcmd, key string, opt *ZRangeBy) *StringSliceCmd {
+	args := []interface{}{zcmd, key, opt.Max, opt.Min}
+	if opt.Offset != 0 || opt.Count != 0 {
+		args = append(
+			args,
+			"limit",
+			opt.Offset,
+			opt.Count,
+		)
+	}
+	cmd := NewStringSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZRevRangeByScore(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd {
+	return c.zRevRangeBy(ctx, "zrevrangebyscore", key, opt)
+}
+
+func (c cmdable) ZRevRangeByLex(ctx context.Context, key string, opt *ZRangeBy) *StringSliceCmd {
+	return c.zRevRangeBy(ctx, "zrevrangebylex", key, opt)
+}
+
+func (c cmdable) ZRevRangeByScoreWithScores(ctx context.Context, key string, opt *ZRangeBy) *ZSliceCmd {
+	args := []interface{}{"zrevrangebyscore", key, opt.Max, opt.Min, "withscores"}
+	if opt.Offset != 0 || opt.Count != 0 {
+		args = append(
+			args,
+			"limit",
+			opt.Offset,
+			opt.Count,
+		)
+	}
+	cmd := NewZSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZRevRank(ctx context.Context, key, member string) *IntCmd {
+	cmd := NewIntCmd(ctx, "zrevrank", key, member)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZScore(ctx context.Context, key, member string) *FloatCmd {
+	cmd := NewFloatCmd(ctx, "zscore", key, member)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ZUnionStore(ctx context.Context, dest string, store *ZStore) *IntCmd {
+	args := make([]interface{}, 3+len(store.Keys))
+	args[0] = "zunionstore"
+	args[1] = dest
+	args[2] = len(store.Keys)
+	for i, key := range store.Keys {
+		args[3+i] = key
+	}
+	if len(store.Weights) > 0 {
+		args = append(args, "weights")
+		for _, weight := range store.Weights {
+			args = append(args, weight)
+		}
+	}
+	if store.Aggregate != "" {
+		args = append(args, "aggregate", store.Aggregate)
+	}
+
+	cmd := NewIntCmd(ctx, args...)
+	cmd.setFirstKeyPos(3)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+func (c cmdable) PFAdd(ctx context.Context, key string, els ...interface{}) *IntCmd {
+	args := make([]interface{}, 2, 2+len(els))
+	args[0] = "pfadd"
+	args[1] = key
+	args = appendArgs(args, els)
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) PFCount(ctx context.Context, keys ...string) *IntCmd {
+	args := make([]interface{}, 1+len(keys))
+	args[0] = "pfcount"
+	for i, key := range keys {
+		args[1+i] = key
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) PFMerge(ctx context.Context, dest string, keys ...string) *StatusCmd {
+	args := make([]interface{}, 2+len(keys))
+	args[0] = "pfmerge"
+	args[1] = dest
+	for i, key := range keys {
+		args[2+i] = key
+	}
+	cmd := NewStatusCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+func (c cmdable) BgRewriteAOF(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "bgrewriteaof")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) BgSave(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "bgsave")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClientKill(ctx context.Context, ipPort string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "client", "kill", ipPort)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// ClientKillByFilter is new style syntax, while the ClientKill is old
+//
+//   CLIENT KILL <option> [value] ... <option> [value]
+func (c cmdable) ClientKillByFilter(ctx context.Context, keys ...string) *IntCmd {
+	args := make([]interface{}, 2+len(keys))
+	args[0] = "client"
+	args[1] = "kill"
+	for i, key := range keys {
+		args[2+i] = key
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClientList(ctx context.Context) *StringCmd {
+	cmd := NewStringCmd(ctx, "client", "list")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClientPause(ctx context.Context, dur time.Duration) *BoolCmd {
+	cmd := NewBoolCmd(ctx, "client", "pause", formatMs(ctx, dur))
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClientID(ctx context.Context) *IntCmd {
+	cmd := NewIntCmd(ctx, "client", "id")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClientUnblock(ctx context.Context, id int64) *IntCmd {
+	cmd := NewIntCmd(ctx, "client", "unblock", id)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClientUnblockWithError(ctx context.Context, id int64) *IntCmd {
+	cmd := NewIntCmd(ctx, "client", "unblock", id, "error")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ConfigGet(ctx context.Context, parameter string) *SliceCmd {
+	cmd := NewSliceCmd(ctx, "config", "get", parameter)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ConfigResetStat(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "config", "resetstat")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ConfigSet(ctx context.Context, parameter, value string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "config", "set", parameter, value)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ConfigRewrite(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "config", "rewrite")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) DBSize(ctx context.Context) *IntCmd {
+	cmd := NewIntCmd(ctx, "dbsize")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) FlushAll(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "flushall")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) FlushAllAsync(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "flushall", "async")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) FlushDB(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "flushdb")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) FlushDBAsync(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "flushdb", "async")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Info(ctx context.Context, section ...string) *StringCmd {
+	args := []interface{}{"info"}
+	if len(section) > 0 {
+		args = append(args, section[0])
+	}
+	cmd := NewStringCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) LastSave(ctx context.Context) *IntCmd {
+	cmd := NewIntCmd(ctx, "lastsave")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Save(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "save")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) shutdown(ctx context.Context, modifier string) *StatusCmd {
+	var args []interface{}
+	if modifier == "" {
+		args = []interface{}{"shutdown"}
+	} else {
+		args = []interface{}{"shutdown", modifier}
+	}
+	cmd := NewStatusCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	if err := cmd.Err(); err != nil {
+		if err == io.EOF {
+			// Server quit as expected.
+			cmd.err = nil
+		}
+	} else {
+		// Server did not quit. String reply contains the reason.
+		cmd.err = errors.New(cmd.val)
+		cmd.val = ""
+	}
+	return cmd
+}
+
+func (c cmdable) Shutdown(ctx context.Context) *StatusCmd {
+	return c.shutdown(ctx, "")
+}
+
+func (c cmdable) ShutdownSave(ctx context.Context) *StatusCmd {
+	return c.shutdown(ctx, "save")
+}
+
+func (c cmdable) ShutdownNoSave(ctx context.Context) *StatusCmd {
+	return c.shutdown(ctx, "nosave")
+}
+
+func (c cmdable) SlaveOf(ctx context.Context, host, port string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "slaveof", host, port)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) SlowLogGet(ctx context.Context, num int64) *SlowLogCmd {
+	cmd := NewSlowLogCmd(context.Background(), "slowlog", "get", num)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) Sync(ctx context.Context) {
+	panic("not implemented")
+}
+
+func (c cmdable) Time(ctx context.Context) *TimeCmd {
+	cmd := NewTimeCmd(ctx, "time")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) DebugObject(ctx context.Context, key string) *StringCmd {
+	cmd := NewStringCmd(ctx, "debug", "object", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ReadOnly(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "readonly")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ReadWrite(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "readwrite")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) MemoryUsage(ctx context.Context, key string, samples ...int) *IntCmd {
+	args := []interface{}{"memory", "usage", key}
+	if len(samples) > 0 {
+		if len(samples) != 1 {
+			panic("MemoryUsage expects single sample count")
+		}
+		args = append(args, "SAMPLES", samples[0])
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+func (c cmdable) Eval(ctx context.Context, script string, keys []string, args ...interface{}) *Cmd {
+	cmdArgs := make([]interface{}, 3+len(keys), 3+len(keys)+len(args))
+	cmdArgs[0] = "eval"
+	cmdArgs[1] = script
+	cmdArgs[2] = len(keys)
+	for i, key := range keys {
+		cmdArgs[3+i] = key
+	}
+	cmdArgs = appendArgs(cmdArgs, args)
+	cmd := NewCmd(ctx, cmdArgs...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) EvalSha(ctx context.Context, sha1 string, keys []string, args ...interface{}) *Cmd {
+	cmdArgs := make([]interface{}, 3+len(keys), 3+len(keys)+len(args))
+	cmdArgs[0] = "evalsha"
+	cmdArgs[1] = sha1
+	cmdArgs[2] = len(keys)
+	for i, key := range keys {
+		cmdArgs[3+i] = key
+	}
+	cmdArgs = appendArgs(cmdArgs, args)
+	cmd := NewCmd(ctx, cmdArgs...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ScriptExists(ctx context.Context, hashes ...string) *BoolSliceCmd {
+	args := make([]interface{}, 2+len(hashes))
+	args[0] = "script"
+	args[1] = "exists"
+	for i, hash := range hashes {
+		args[2+i] = hash
+	}
+	cmd := NewBoolSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ScriptFlush(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "script", "flush")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ScriptKill(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "script", "kill")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ScriptLoad(ctx context.Context, script string) *StringCmd {
+	cmd := NewStringCmd(ctx, "script", "load", script)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+// Publish posts the message to the channel.
+func (c cmdable) Publish(ctx context.Context, channel string, message interface{}) *IntCmd {
+	cmd := NewIntCmd(ctx, "publish", channel, message)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) PubSubChannels(ctx context.Context, pattern string) *StringSliceCmd {
+	args := []interface{}{"pubsub", "channels"}
+	if pattern != "*" {
+		args = append(args, pattern)
+	}
+	cmd := NewStringSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) PubSubNumSub(ctx context.Context, channels ...string) *StringIntMapCmd {
+	args := make([]interface{}, 2+len(channels))
+	args[0] = "pubsub"
+	args[1] = "numsub"
+	for i, channel := range channels {
+		args[2+i] = channel
+	}
+	cmd := NewStringIntMapCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) PubSubNumPat(ctx context.Context) *IntCmd {
+	cmd := NewIntCmd(ctx, "pubsub", "numpat")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+func (c cmdable) ClusterSlots(ctx context.Context) *ClusterSlotsCmd {
+	cmd := NewClusterSlotsCmd(ctx, "cluster", "slots")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterNodes(ctx context.Context) *StringCmd {
+	cmd := NewStringCmd(ctx, "cluster", "nodes")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterMeet(ctx context.Context, host, port string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "cluster", "meet", host, port)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterForget(ctx context.Context, nodeID string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "cluster", "forget", nodeID)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterReplicate(ctx context.Context, nodeID string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "cluster", "replicate", nodeID)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterResetSoft(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "cluster", "reset", "soft")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterResetHard(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "cluster", "reset", "hard")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterInfo(ctx context.Context) *StringCmd {
+	cmd := NewStringCmd(ctx, "cluster", "info")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterKeySlot(ctx context.Context, key string) *IntCmd {
+	cmd := NewIntCmd(ctx, "cluster", "keyslot", key)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterGetKeysInSlot(ctx context.Context, slot int, count int) *StringSliceCmd {
+	cmd := NewStringSliceCmd(ctx, "cluster", "getkeysinslot", slot, count)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterCountFailureReports(ctx context.Context, nodeID string) *IntCmd {
+	cmd := NewIntCmd(ctx, "cluster", "count-failure-reports", nodeID)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterCountKeysInSlot(ctx context.Context, slot int) *IntCmd {
+	cmd := NewIntCmd(ctx, "cluster", "countkeysinslot", slot)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterDelSlots(ctx context.Context, slots ...int) *StatusCmd {
+	args := make([]interface{}, 2+len(slots))
+	args[0] = "cluster"
+	args[1] = "delslots"
+	for i, slot := range slots {
+		args[2+i] = slot
+	}
+	cmd := NewStatusCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterDelSlotsRange(ctx context.Context, min, max int) *StatusCmd {
+	size := max - min + 1
+	slots := make([]int, size)
+	for i := 0; i < size; i++ {
+		slots[i] = min + i
+	}
+	return c.ClusterDelSlots(ctx, slots...)
+}
+
+func (c cmdable) ClusterSaveConfig(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "cluster", "saveconfig")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterSlaves(ctx context.Context, nodeID string) *StringSliceCmd {
+	cmd := NewStringSliceCmd(ctx, "cluster", "slaves", nodeID)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterFailover(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "cluster", "failover")
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterAddSlots(ctx context.Context, slots ...int) *StatusCmd {
+	args := make([]interface{}, 2+len(slots))
+	args[0] = "cluster"
+	args[1] = "addslots"
+	for i, num := range slots {
+		args[2+i] = num
+	}
+	cmd := NewStatusCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) ClusterAddSlotsRange(ctx context.Context, min, max int) *StatusCmd {
+	size := max - min + 1
+	slots := make([]int, size)
+	for i := 0; i < size; i++ {
+		slots[i] = min + i
+	}
+	return c.ClusterAddSlots(ctx, slots...)
+}
+
+//------------------------------------------------------------------------------
+
+func (c cmdable) GeoAdd(ctx context.Context, key string, geoLocation ...*GeoLocation) *IntCmd {
+	args := make([]interface{}, 2+3*len(geoLocation))
+	args[0] = "geoadd"
+	args[1] = key
+	for i, eachLoc := range geoLocation {
+		args[2+3*i] = eachLoc.Longitude
+		args[2+3*i+1] = eachLoc.Latitude
+		args[2+3*i+2] = eachLoc.Name
+	}
+	cmd := NewIntCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// GeoRadius is a read-only GEORADIUS_RO command.
+func (c cmdable) GeoRadius(
+	ctx context.Context, key string, longitude, latitude float64, query *GeoRadiusQuery,
+) *GeoLocationCmd {
+	cmd := NewGeoLocationCmd(ctx, query, "georadius_ro", key, longitude, latitude)
+	if query.Store != "" || query.StoreDist != "" {
+		cmd.SetErr(errors.New("GeoRadius does not support Store or StoreDist"))
+		return cmd
+	}
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// GeoRadiusStore is a writing GEORADIUS command.
+func (c cmdable) GeoRadiusStore(
+	ctx context.Context, key string, longitude, latitude float64, query *GeoRadiusQuery,
+) *IntCmd {
+	args := geoLocationArgs(query, "georadius", key, longitude, latitude)
+	cmd := NewIntCmd(ctx, args...)
+	if query.Store == "" && query.StoreDist == "" {
+		cmd.SetErr(errors.New("GeoRadiusStore requires Store or StoreDist"))
+		return cmd
+	}
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// GeoRadius is a read-only GEORADIUSBYMEMBER_RO command.
+func (c cmdable) GeoRadiusByMember(
+	ctx context.Context, key, member string, query *GeoRadiusQuery,
+) *GeoLocationCmd {
+	cmd := NewGeoLocationCmd(ctx, query, "georadiusbymember_ro", key, member)
+	if query.Store != "" || query.StoreDist != "" {
+		cmd.SetErr(errors.New("GeoRadiusByMember does not support Store or StoreDist"))
+		return cmd
+	}
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+// GeoRadiusByMemberStore is a writing GEORADIUSBYMEMBER command.
+func (c cmdable) GeoRadiusByMemberStore(
+	ctx context.Context, key, member string, query *GeoRadiusQuery,
+) *IntCmd {
+	args := geoLocationArgs(query, "georadiusbymember", key, member)
+	cmd := NewIntCmd(ctx, args...)
+	if query.Store == "" && query.StoreDist == "" {
+		cmd.SetErr(errors.New("GeoRadiusByMemberStore requires Store or StoreDist"))
+		return cmd
+	}
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) GeoDist(
+	ctx context.Context, key string, member1, member2, unit string,
+) *FloatCmd {
+	if unit == "" {
+		unit = "km"
+	}
+	cmd := NewFloatCmd(ctx, "geodist", key, member1, member2, unit)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) GeoHash(ctx context.Context, key string, members ...string) *StringSliceCmd {
+	args := make([]interface{}, 2+len(members))
+	args[0] = "geohash"
+	args[1] = key
+	for i, member := range members {
+		args[2+i] = member
+	}
+	cmd := NewStringSliceCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
+
+func (c cmdable) GeoPos(ctx context.Context, key string, members ...string) *GeoPosCmd {
+	args := make([]interface{}, 2+len(members))
+	args[0] = "geopos"
+	args[1] = key
+	for i, member := range members {
+		args[2+i] = member
+	}
+	cmd := NewGeoPosCmd(ctx, args...)
+	_ = c(ctx, cmd)
+	return cmd
+}
diff --git a/vendor/github.com/go-redis/redis/v7/doc.go b/vendor/github.com/go-redis/redis/v8/doc.go
similarity index 100%
rename from vendor/github.com/go-redis/redis/v7/doc.go
rename to vendor/github.com/go-redis/redis/v8/doc.go
diff --git a/vendor/github.com/go-redis/redis/v7/error.go b/vendor/github.com/go-redis/redis/v8/error.go
similarity index 76%
rename from vendor/github.com/go-redis/redis/v7/error.go
rename to vendor/github.com/go-redis/redis/v8/error.go
index 0ffaca9f8f..2ed5dc6284 100644
--- a/vendor/github.com/go-redis/redis/v7/error.go
+++ b/vendor/github.com/go-redis/redis/v8/error.go
@@ -6,8 +6,8 @@ import (
 	"net"
 	"strings"
 
-	"github.com/go-redis/redis/v7/internal/pool"
-	"github.com/go-redis/redis/v7/internal/proto"
+	"github.com/go-redis/redis/v8/internal/pool"
+	"github.com/go-redis/redis/v8/internal/proto"
 )
 
 var ErrClosed = pool.ErrClosed
@@ -24,15 +24,16 @@ type Error interface {
 
 var _ Error = proto.RedisError("")
 
-func isRetryableError(err error, retryTimeout bool) bool {
+func shouldRetry(err error, retryTimeout bool) bool {
 	switch err {
+	case io.EOF, io.ErrUnexpectedEOF:
+		return true
 	case nil, context.Canceled, context.DeadlineExceeded:
 		return false
-	case io.EOF:
-		return true
 	}
-	if netErr, ok := err.(net.Error); ok {
-		if netErr.Timeout() {
+
+	if v, ok := err.(timeoutError); ok {
+		if v.Timeout() {
 			return retryTimeout
 		}
 		return true
@@ -51,6 +52,10 @@ func isRetryableError(err error, retryTimeout bool) bool {
 	if strings.HasPrefix(s, "CLUSTERDOWN ") {
 		return true
 	}
+	if strings.HasPrefix(s, "TRYAGAIN ") {
+		return true
+	}
+
 	return false
 }
 
@@ -60,19 +65,25 @@ func isRedisError(err error) bool {
 }
 
 func isBadConn(err error, allowTimeout bool) bool {
-	if err == nil {
+	switch err {
+	case nil:
 		return false
+	case context.Canceled, context.DeadlineExceeded:
+		return true
 	}
+
 	if isRedisError(err) {
 		// Close connections in read only state in case domain addr is used
 		// and domain resolves to a different Redis Server. See #790.
 		return isReadOnlyError(err)
 	}
+
 	if allowTimeout {
 		if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
-			return false
+			return !netErr.Temporary()
 		}
 	}
+
 	return true
 }
 
@@ -106,3 +117,9 @@ func isLoadingError(err error) bool {
 func isReadOnlyError(err error) bool {
 	return strings.HasPrefix(err.Error(), "READONLY ")
 }
+
+//------------------------------------------------------------------------------
+
+type timeoutError interface {
+	Timeout() bool
+}
diff --git a/vendor/github.com/go-redis/redis/v8/go.mod b/vendor/github.com/go-redis/redis/v8/go.mod
new file mode 100644
index 0000000000..4f5952762f
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/go.mod
@@ -0,0 +1,11 @@
+module github.com/go-redis/redis/v8
+
+go 1.13
+
+require (
+	github.com/cespare/xxhash/v2 v2.1.1
+	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f
+	github.com/onsi/ginkgo v1.15.0
+	github.com/onsi/gomega v1.10.5
+	go.opentelemetry.io/otel v0.16.0
+)
diff --git a/vendor/github.com/go-redis/redis/v8/go.sum b/vendor/github.com/go-redis/redis/v8/go.sum
new file mode 100644
index 0000000000..b46929e693
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/go.sum
@@ -0,0 +1,97 @@
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/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/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
+github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U=
+github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ=
+github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+go.opentelemetry.io/otel v0.16.0 h1:uIWEbdeb4vpKPGITLsRVUS44L5oDbDUCZxn8lkxhmgw=
+go.opentelemetry.io/otel v0.16.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+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=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/github.com/go-redis/redis/v8/internal/arg.go b/vendor/github.com/go-redis/redis/v8/internal/arg.go
new file mode 100644
index 0000000000..b97fa0d685
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/internal/arg.go
@@ -0,0 +1,56 @@
+package internal
+
+import (
+	"fmt"
+	"strconv"
+	"time"
+)
+
+func AppendArg(b []byte, v interface{}) []byte {
+	switch v := v.(type) {
+	case nil:
+		return append(b, "<nil>"...)
+	case string:
+		return appendUTF8String(b, Bytes(v))
+	case []byte:
+		return appendUTF8String(b, v)
+	case int:
+		return strconv.AppendInt(b, int64(v), 10)
+	case int8:
+		return strconv.AppendInt(b, int64(v), 10)
+	case int16:
+		return strconv.AppendInt(b, int64(v), 10)
+	case int32:
+		return strconv.AppendInt(b, int64(v), 10)
+	case int64:
+		return strconv.AppendInt(b, v, 10)
+	case uint:
+		return strconv.AppendUint(b, uint64(v), 10)
+	case uint8:
+		return strconv.AppendUint(b, uint64(v), 10)
+	case uint16:
+		return strconv.AppendUint(b, uint64(v), 10)
+	case uint32:
+		return strconv.AppendUint(b, uint64(v), 10)
+	case uint64:
+		return strconv.AppendUint(b, v, 10)
+	case float32:
+		return strconv.AppendFloat(b, float64(v), 'f', -1, 64)
+	case float64:
+		return strconv.AppendFloat(b, v, 'f', -1, 64)
+	case bool:
+		if v {
+			return append(b, "true"...)
+		}
+		return append(b, "false"...)
+	case time.Time:
+		return v.AppendFormat(b, time.RFC3339Nano)
+	default:
+		return append(b, fmt.Sprint(v)...)
+	}
+}
+
+func appendUTF8String(dst []byte, src []byte) []byte {
+	dst = append(dst, src...)
+	return dst
+}
diff --git a/vendor/github.com/go-redis/redis/v7/internal/hashtag/hashtag.go b/vendor/github.com/go-redis/redis/v8/internal/hashtag/hashtag.go
similarity index 98%
rename from vendor/github.com/go-redis/redis/v7/internal/hashtag/hashtag.go
rename to vendor/github.com/go-redis/redis/v8/internal/hashtag/hashtag.go
index 22f5b3981b..2fc74ad1cd 100644
--- a/vendor/github.com/go-redis/redis/v7/internal/hashtag/hashtag.go
+++ b/vendor/github.com/go-redis/redis/v8/internal/hashtag/hashtag.go
@@ -1,8 +1,9 @@
 package hashtag
 
 import (
-	"math/rand"
 	"strings"
+
+	"github.com/go-redis/redis/v8/internal/rand"
 )
 
 const slotNumber = 16384
diff --git a/vendor/github.com/go-redis/redis/v8/internal/hscan/hscan.go b/vendor/github.com/go-redis/redis/v8/internal/hscan/hscan.go
new file mode 100644
index 0000000000..181260b802
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/internal/hscan/hscan.go
@@ -0,0 +1,151 @@
+package hscan
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+	"strconv"
+)
+
+// decoderFunc represents decoding functions for default built-in types.
+type decoderFunc func(reflect.Value, string) error
+
+var (
+	// List of built-in decoders indexed by their numeric constant values (eg: reflect.Bool = 1).
+	decoders = []decoderFunc{
+		reflect.Bool:          decodeBool,
+		reflect.Int:           decodeInt,
+		reflect.Int8:          decodeInt,
+		reflect.Int16:         decodeInt,
+		reflect.Int32:         decodeInt,
+		reflect.Int64:         decodeInt,
+		reflect.Uint:          decodeUint,
+		reflect.Uint8:         decodeUint,
+		reflect.Uint16:        decodeUint,
+		reflect.Uint32:        decodeUint,
+		reflect.Uint64:        decodeUint,
+		reflect.Float32:       decodeFloat,
+		reflect.Float64:       decodeFloat,
+		reflect.Complex64:     decodeUnsupported,
+		reflect.Complex128:    decodeUnsupported,
+		reflect.Array:         decodeUnsupported,
+		reflect.Chan:          decodeUnsupported,
+		reflect.Func:          decodeUnsupported,
+		reflect.Interface:     decodeUnsupported,
+		reflect.Map:           decodeUnsupported,
+		reflect.Ptr:           decodeUnsupported,
+		reflect.Slice:         decodeSlice,
+		reflect.String:        decodeString,
+		reflect.Struct:        decodeUnsupported,
+		reflect.UnsafePointer: decodeUnsupported,
+	}
+
+	// Global map of struct field specs that is populated once for every new
+	// struct type that is scanned. This caches the field types and the corresponding
+	// decoder functions to avoid iterating through struct fields on subsequent scans.
+	globalStructMap = newStructMap()
+)
+
+func Struct(dst interface{}) (StructValue, error) {
+	v := reflect.ValueOf(dst)
+
+	// The dstination to scan into should be a struct pointer.
+	if v.Kind() != reflect.Ptr || v.IsNil() {
+		return StructValue{}, fmt.Errorf("redis.Scan(non-pointer %T)", dst)
+	}
+
+	v = v.Elem()
+	if v.Kind() != reflect.Struct {
+		return StructValue{}, fmt.Errorf("redis.Scan(non-struct %T)", dst)
+	}
+
+	return StructValue{
+		spec:  globalStructMap.get(v.Type()),
+		value: v,
+	}, nil
+}
+
+// Scan scans the results from a key-value Redis map result set to a destination struct.
+// The Redis keys are matched to the struct's field with the `redis` tag.
+func Scan(dst interface{}, keys []interface{}, vals []interface{}) error {
+	if len(keys) != len(vals) {
+		return errors.New("args should have the same number of keys and vals")
+	}
+
+	strct, err := Struct(dst)
+	if err != nil {
+		return err
+	}
+
+	// Iterate through the (key, value) sequence.
+	for i := 0; i < len(vals); i++ {
+		key, ok := keys[i].(string)
+		if !ok {
+			continue
+		}
+
+		val, ok := vals[i].(string)
+		if !ok {
+			continue
+		}
+
+		if err := strct.Scan(key, val); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func decodeBool(f reflect.Value, s string) error {
+	b, err := strconv.ParseBool(s)
+	if err != nil {
+		return err
+	}
+	f.SetBool(b)
+	return nil
+}
+
+func decodeInt(f reflect.Value, s string) error {
+	v, err := strconv.ParseInt(s, 10, 0)
+	if err != nil {
+		return err
+	}
+	f.SetInt(v)
+	return nil
+}
+
+func decodeUint(f reflect.Value, s string) error {
+	v, err := strconv.ParseUint(s, 10, 0)
+	if err != nil {
+		return err
+	}
+	f.SetUint(v)
+	return nil
+}
+
+func decodeFloat(f reflect.Value, s string) error {
+	v, err := strconv.ParseFloat(s, 0)
+	if err != nil {
+		return err
+	}
+	f.SetFloat(v)
+	return nil
+}
+
+func decodeString(f reflect.Value, s string) error {
+	f.SetString(s)
+	return nil
+}
+
+func decodeSlice(f reflect.Value, s string) error {
+	// []byte slice ([]uint8).
+	if f.Type().Elem().Kind() == reflect.Uint8 {
+		f.SetBytes([]byte(s))
+	}
+	return nil
+}
+
+func decodeUnsupported(v reflect.Value, s string) error {
+	return fmt.Errorf("redis.Scan(unsupported %s)", v.Type())
+}
diff --git a/vendor/github.com/go-redis/redis/v8/internal/hscan/structmap.go b/vendor/github.com/go-redis/redis/v8/internal/hscan/structmap.go
new file mode 100644
index 0000000000..37d86ba551
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/internal/hscan/structmap.go
@@ -0,0 +1,87 @@
+package hscan
+
+import (
+	"reflect"
+	"strings"
+	"sync"
+)
+
+// structMap contains the map of struct fields for target structs
+// indexed by the struct type.
+type structMap struct {
+	m sync.Map
+}
+
+func newStructMap() *structMap {
+	return new(structMap)
+}
+
+func (s *structMap) get(t reflect.Type) *structSpec {
+	if v, ok := s.m.Load(t); ok {
+		return v.(*structSpec)
+	}
+
+	spec := newStructSpec(t, "redis")
+	s.m.Store(t, spec)
+	return spec
+}
+
+//------------------------------------------------------------------------------
+
+// structSpec contains the list of all fields in a target struct.
+type structSpec struct {
+	m map[string]*structField
+}
+
+func (s *structSpec) set(tag string, sf *structField) {
+	s.m[tag] = sf
+}
+
+func newStructSpec(t reflect.Type, fieldTag string) *structSpec {
+	out := &structSpec{
+		m: make(map[string]*structField),
+	}
+
+	num := t.NumField()
+	for i := 0; i < num; i++ {
+		f := t.Field(i)
+
+		tag := f.Tag.Get(fieldTag)
+		if tag == "" || tag == "-" {
+			continue
+		}
+
+		tag = strings.Split(tag, ",")[0]
+		if tag == "" {
+			continue
+		}
+
+		// Use the built-in decoder.
+		out.set(tag, &structField{index: i, fn: decoders[f.Type.Kind()]})
+	}
+
+	return out
+}
+
+//------------------------------------------------------------------------------
+
+// structField represents a single field in a target struct.
+type structField struct {
+	index int
+	fn    decoderFunc
+}
+
+//------------------------------------------------------------------------------
+
+type StructValue struct {
+	spec  *structSpec
+	value reflect.Value
+}
+
+func (s StructValue) Scan(key string, value string) error {
+	field, ok := s.spec.m[key]
+	if !ok {
+		return nil
+	}
+	return field.fn(s.value.Field(field.index), value)
+}
diff --git a/vendor/github.com/go-redis/redis/v8/internal/instruments.go b/vendor/github.com/go-redis/redis/v8/internal/instruments.go
new file mode 100644
index 0000000000..2d7dacce7a
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/internal/instruments.go
@@ -0,0 +1,33 @@
+package internal
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel"
+	"go.opentelemetry.io/otel/metric"
+)
+
+var (
+	// WritesCounter is a count of write commands performed.
+	WritesCounter metric.Int64Counter
+	// NewConnectionsCounter is a count of new connections.
+	NewConnectionsCounter metric.Int64Counter
+)
+
+func init() {
+	defer func() {
+		if r := recover(); r != nil {
+			Logger.Printf(context.Background(), "Error creating meter github.com/go-redis/redis for Instruments", r)
+		}
+	}()
+
+	meter := metric.Must(otel.Meter("github.com/go-redis/redis"))
+
+	WritesCounter = meter.NewInt64Counter("redis.writes",
+		metric.WithDescription("the number of writes initiated"),
+	)
+
+	NewConnectionsCounter = meter.NewInt64Counter("redis.new_connections",
+		metric.WithDescription("the number of connections created"),
+	)
+}
diff --git a/vendor/github.com/go-redis/redis/v8/internal/internal.go b/vendor/github.com/go-redis/redis/v8/internal/internal.go
new file mode 100644
index 0000000000..4a59c599be
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/internal/internal.go
@@ -0,0 +1,29 @@
+package internal
+
+import (
+	"time"
+
+	"github.com/go-redis/redis/v8/internal/rand"
+)
+
+func RetryBackoff(retry int, minBackoff, maxBackoff time.Duration) time.Duration {
+	if retry < 0 {
+		panic("not reached")
+	}
+	if minBackoff == 0 {
+		return 0
+	}
+
+	d := minBackoff << uint(retry)
+	if d < minBackoff {
+		return maxBackoff
+	}
+
+	d = minBackoff + time.Duration(rand.Int63n(int64(d)))
+
+	if d > maxBackoff || d < minBackoff {
+		d = maxBackoff
+	}
+
+	return d
+}
diff --git a/vendor/github.com/go-redis/redis/v8/internal/log.go b/vendor/github.com/go-redis/redis/v8/internal/log.go
new file mode 100644
index 0000000000..3810f9e4e7
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/internal/log.go
@@ -0,0 +1,24 @@
+package internal
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"os"
+)
+
+type Logging interface {
+	Printf(ctx context.Context, format string, v ...interface{})
+}
+
+type logger struct {
+	log *log.Logger
+}
+
+func (l *logger) Printf(ctx context.Context, format string, v ...interface{}) {
+	_ = l.log.Output(2, fmt.Sprintf(format, v...))
+}
+
+var Logger Logging = &logger{
+	log: log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile),
+}
diff --git a/vendor/github.com/go-redis/redis/v7/internal/once.go b/vendor/github.com/go-redis/redis/v8/internal/once.go
similarity index 100%
rename from vendor/github.com/go-redis/redis/v7/internal/once.go
rename to vendor/github.com/go-redis/redis/v8/internal/once.go
diff --git a/vendor/github.com/go-redis/redis/v7/internal/pool/conn.go b/vendor/github.com/go-redis/redis/v8/internal/pool/conn.go
similarity index 55%
rename from vendor/github.com/go-redis/redis/v7/internal/pool/conn.go
rename to vendor/github.com/go-redis/redis/v8/internal/pool/conn.go
index e9a2585463..b784530438 100644
--- a/vendor/github.com/go-redis/redis/v7/internal/pool/conn.go
+++ b/vendor/github.com/go-redis/redis/v8/internal/pool/conn.go
@@ -1,26 +1,30 @@
 package pool
 
 import (
+	"bufio"
 	"context"
 	"net"
 	"sync/atomic"
 	"time"
 
-	"github.com/go-redis/redis/v7/internal/proto"
+	"github.com/go-redis/redis/v8/internal"
+	"github.com/go-redis/redis/v8/internal/proto"
+	"go.opentelemetry.io/otel/trace"
 )
 
 var noDeadline = time.Time{}
 
 type Conn struct {
+	usedAt  int64 // atomic
 	netConn net.Conn
 
 	rd *proto.Reader
+	bw *bufio.Writer
 	wr *proto.Writer
 
 	Inited    bool
 	pooled    bool
 	createdAt time.Time
-	usedAt    int64 // atomic
 }
 
 func NewConn(netConn net.Conn) *Conn {
@@ -29,7 +33,8 @@ func NewConn(netConn net.Conn) *Conn {
 		createdAt: time.Now(),
 	}
 	cn.rd = proto.NewReader(netConn)
-	cn.wr = proto.NewWriter(netConn)
+	cn.bw = bufio.NewWriter(netConn)
+	cn.wr = proto.NewWriter(cn.bw)
 	cn.SetUsedAt(time.Now())
 	return cn
 }
@@ -46,7 +51,7 @@ func (cn *Conn) SetUsedAt(tm time.Time) {
 func (cn *Conn) SetNetConn(netConn net.Conn) {
 	cn.netConn = netConn
 	cn.rd.Reset(netConn)
-	cn.wr.Reset(netConn)
+	cn.bw.Reset(netConn)
 }
 
 func (cn *Conn) Write(b []byte) (int, error) {
@@ -54,35 +59,48 @@ func (cn *Conn) Write(b []byte) (int, error) {
 }
 
 func (cn *Conn) RemoteAddr() net.Addr {
-	return cn.netConn.RemoteAddr()
+	if cn.netConn != nil {
+		return cn.netConn.RemoteAddr()
+	}
+	return nil
 }
 
 func (cn *Conn) WithReader(ctx context.Context, timeout time.Duration, fn func(rd *proto.Reader) error) error {
-	err := cn.netConn.SetReadDeadline(cn.deadline(ctx, timeout))
-	if err != nil {
-		return err
-	}
-	return fn(cn.rd)
+	return internal.WithSpan(ctx, "redis.with_reader", func(ctx context.Context, span trace.Span) error {
+		if err := cn.netConn.SetReadDeadline(cn.deadline(ctx, timeout)); err != nil {
+			return internal.RecordError(ctx, span, err)
+		}
+		if err := fn(cn.rd); err != nil {
+			return internal.RecordError(ctx, span, err)
+		}
+		return nil
+	})
 }
 
 func (cn *Conn) WithWriter(
 	ctx context.Context, timeout time.Duration, fn func(wr *proto.Writer) error,
 ) error {
-	err := cn.netConn.SetWriteDeadline(cn.deadline(ctx, timeout))
-	if err != nil {
-		return err
-	}
+	return internal.WithSpan(ctx, "redis.with_writer", func(ctx context.Context, span trace.Span) error {
+		if err := cn.netConn.SetWriteDeadline(cn.deadline(ctx, timeout)); err != nil {
+			return internal.RecordError(ctx, span, err)
+		}
 
-	if cn.wr.Buffered() > 0 {
-		cn.wr.Reset(cn.netConn)
-	}
+		if cn.bw.Buffered() > 0 {
+			cn.bw.Reset(cn.netConn)
+		}
 
-	err = fn(cn.wr)
-	if err != nil {
-		return err
-	}
+		if err := fn(cn.wr); err != nil {
+			return internal.RecordError(ctx, span, err)
+		}
 
-	return cn.wr.Flush()
+		if err := cn.bw.Flush(); err != nil {
+			return internal.RecordError(ctx, span, err)
+		}
+
+		internal.WritesCounter.Add(ctx, 1)
+
+		return nil
+	})
 }
 
 func (cn *Conn) Close() error {
diff --git a/vendor/github.com/go-redis/redis/v7/internal/pool/pool.go b/vendor/github.com/go-redis/redis/v8/internal/pool/pool.go
similarity index 91%
rename from vendor/github.com/go-redis/redis/v7/internal/pool/pool.go
rename to vendor/github.com/go-redis/redis/v8/internal/pool/pool.go
index a8d8276a9f..254a18de50 100644
--- a/vendor/github.com/go-redis/redis/v7/internal/pool/pool.go
+++ b/vendor/github.com/go-redis/redis/v8/internal/pool/pool.go
@@ -8,11 +8,13 @@ import (
 	"sync/atomic"
 	"time"
 
-	"github.com/go-redis/redis/v7/internal"
+	"github.com/go-redis/redis/v8/internal"
 )
 
-var ErrClosed = errors.New("redis: client is closed")
-var ErrPoolTimeout = errors.New("redis: connection pool timeout")
+var (
+	ErrClosed      = errors.New("redis: client is closed")
+	ErrPoolTimeout = errors.New("redis: connection pool timeout")
+)
 
 var timers = sync.Pool{
 	New: func() interface{} {
@@ -38,8 +40,8 @@ type Pooler interface {
 	CloseConn(*Conn) error
 
 	Get(context.Context) (*Conn, error)
-	Put(*Conn)
-	Remove(*Conn, error)
+	Put(context.Context, *Conn)
+	Remove(context.Context, *Conn, error)
 
 	Len() int
 	IdleLen() int
@@ -60,13 +62,16 @@ type Options struct {
 	IdleCheckFrequency time.Duration
 }
 
+type lastDialErrorWrap struct {
+	err error
+}
+
 type ConnPool struct {
 	opt *Options
 
 	dialErrorsNum uint32 // atomic
 
-	lastDialErrorMu sync.RWMutex
-	lastDialError   error
+	lastDialError atomic.Value
 
 	queue chan struct{}
 
@@ -158,6 +163,7 @@ func (p *ConnPool) newConn(ctx context.Context, pooled bool) (*Conn, error) {
 		}
 	}
 	p.connsMu.Unlock()
+
 	return cn, nil
 }
 
@@ -179,6 +185,7 @@ func (p *ConnPool) dialConn(ctx context.Context, pooled bool) (*Conn, error) {
 		return nil, err
 	}
 
+	internal.NewConnectionsCounter.Add(ctx, 1)
 	cn := NewConn(netConn)
 	cn.pooled = pooled
 	return cn, nil
@@ -204,16 +211,15 @@ func (p *ConnPool) tryDial() {
 }
 
 func (p *ConnPool) setLastDialError(err error) {
-	p.lastDialErrorMu.Lock()
-	p.lastDialError = err
-	p.lastDialErrorMu.Unlock()
+	p.lastDialError.Store(&lastDialErrorWrap{err: err})
 }
 
 func (p *ConnPool) getLastDialError() error {
-	p.lastDialErrorMu.RLock()
-	err := p.lastDialError
-	p.lastDialErrorMu.RUnlock()
-	return err
+	err, _ := p.lastDialError.Load().(*lastDialErrorWrap)
+	if err != nil {
+		return err.err
+	}
+	return nil
 }
 
 // Get returns existed connection from the pool or creates a new one.
@@ -313,15 +319,15 @@ func (p *ConnPool) popIdle() *Conn {
 	return cn
 }
 
-func (p *ConnPool) Put(cn *Conn) {
+func (p *ConnPool) Put(ctx context.Context, cn *Conn) {
 	if cn.rd.Buffered() > 0 {
-		internal.Logger.Printf("Conn has unread data")
-		p.Remove(cn, BadConnError{})
+		internal.Logger.Printf(ctx, "Conn has unread data")
+		p.Remove(ctx, cn, BadConnError{})
 		return
 	}
 
 	if !cn.pooled {
-		p.Remove(cn, nil)
+		p.Remove(ctx, cn, nil)
 		return
 	}
 
@@ -332,7 +338,7 @@ func (p *ConnPool) Put(cn *Conn) {
 	p.freeTurn()
 }
 
-func (p *ConnPool) Remove(cn *Conn, reason error) {
+func (p *ConnPool) Remove(ctx context.Context, cn *Conn, reason error) {
 	p.removeConnWithLock(cn)
 	p.freeTurn()
 	_ = p.closeConn(cn)
@@ -403,8 +409,10 @@ func (p *ConnPool) closed() bool {
 }
 
 func (p *ConnPool) Filter(fn func(*Conn) bool) error {
-	var firstErr error
 	p.connsMu.Lock()
+	defer p.connsMu.Unlock()
+
+	var firstErr error
 	for _, cn := range p.conns {
 		if fn(cn) {
 			if err := p.closeConn(cn); err != nil && firstErr == nil {
@@ -412,7 +420,6 @@ func (p *ConnPool) Filter(fn func(*Conn) bool) error {
 			}
 		}
 	}
-	p.connsMu.Unlock()
 	return firstErr
 }
 
@@ -453,7 +460,7 @@ func (p *ConnPool) reaper(frequency time.Duration) {
 			}
 			_, err := p.ReapStaleConns()
 			if err != nil {
-				internal.Logger.Printf("ReapStaleConns failed: %s", err)
+				internal.Logger.Printf(context.Background(), "ReapStaleConns failed: %s", err)
 				continue
 			}
 		case <-p.closedCh:
@@ -470,6 +477,7 @@ func (p *ConnPool) ReapStaleConns() (int, error) {
 		p.connsMu.Lock()
 		cn := p.reapStaleConn()
 		p.connsMu.Unlock()
+
 		p.freeTurn()
 
 		if cn != nil {
diff --git a/vendor/github.com/go-redis/redis/v8/internal/pool/pool_single.go b/vendor/github.com/go-redis/redis/v8/internal/pool/pool_single.go
new file mode 100644
index 0000000000..5a3fde191b
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/internal/pool/pool_single.go
@@ -0,0 +1,58 @@
+package pool
+
+import "context"
+
+type SingleConnPool struct {
+	pool      Pooler
+	cn        *Conn
+	stickyErr error
+}
+
+var _ Pooler = (*SingleConnPool)(nil)
+
+func NewSingleConnPool(pool Pooler, cn *Conn) *SingleConnPool {
+	return &SingleConnPool{
+		pool: pool,
+		cn:   cn,
+	}
+}
+
+func (p *SingleConnPool) NewConn(ctx context.Context) (*Conn, error) {
+	return p.pool.NewConn(ctx)
+}
+
+func (p *SingleConnPool) CloseConn(cn *Conn) error {
+	return p.pool.CloseConn(cn)
+}
+
+func (p *SingleConnPool) Get(ctx context.Context) (*Conn, error) {
+	if p.stickyErr != nil {
+		return nil, p.stickyErr
+	}
+	return p.cn, nil
+}
+
+func (p *SingleConnPool) Put(ctx context.Context, cn *Conn) {}
+
+func (p *SingleConnPool) Remove(ctx context.Context, cn *Conn, reason error) {
+	p.cn = nil
+	p.stickyErr = reason
+}
+
+func (p *SingleConnPool) Close() error {
+	p.cn = nil
+	p.stickyErr = ErrClosed
+	return nil
+}
+
+func (p *SingleConnPool) Len() int {
+	return 0
+}
+
+func (p *SingleConnPool) IdleLen() int {
+	return 0
+}
+
+func (p *SingleConnPool) Stats() *Stats {
+	return &Stats{}
+}
diff --git a/vendor/github.com/go-redis/redis/v7/internal/pool/pool_single.go b/vendor/github.com/go-redis/redis/v8/internal/pool/pool_sticky.go
similarity index 61%
rename from vendor/github.com/go-redis/redis/v7/internal/pool/pool_single.go
rename to vendor/github.com/go-redis/redis/v8/internal/pool/pool_sticky.go
index 04758a00d4..c3e7e7c045 100644
--- a/vendor/github.com/go-redis/redis/v7/internal/pool/pool_single.go
+++ b/vendor/github.com/go-redis/redis/v8/internal/pool/pool_sticky.go
@@ -2,6 +2,7 @@ package pool
 
 import (
 	"context"
+	"errors"
 	"fmt"
 	"sync/atomic"
 )
@@ -30,9 +31,11 @@ func (e BadConnError) Unwrap() error {
 	return e.wrapped
 }
 
-type SingleConnPool struct {
-	pool  Pooler
-	level int32 // atomic
+//------------------------------------------------------------------------------
+
+type StickyConnPool struct {
+	pool   Pooler
+	shared int32 // atomic
 
 	state uint32 // atomic
 	ch    chan *Conn
@@ -40,37 +43,29 @@ type SingleConnPool struct {
 	_badConnError atomic.Value
 }
 
-var _ Pooler = (*SingleConnPool)(nil)
+var _ Pooler = (*StickyConnPool)(nil)
 
-func NewSingleConnPool(pool Pooler) *SingleConnPool {
-	p, ok := pool.(*SingleConnPool)
+func NewStickyConnPool(pool Pooler) *StickyConnPool {
+	p, ok := pool.(*StickyConnPool)
 	if !ok {
-		p = &SingleConnPool{
+		p = &StickyConnPool{
 			pool: pool,
 			ch:   make(chan *Conn, 1),
 		}
 	}
-	atomic.AddInt32(&p.level, 1)
+	atomic.AddInt32(&p.shared, 1)
 	return p
 }
 
-func (p *SingleConnPool) SetConn(cn *Conn) {
-	if atomic.CompareAndSwapUint32(&p.state, stateDefault, stateInited) {
-		p.ch <- cn
-	} else {
-		panic("not reached")
-	}
-}
-
-func (p *SingleConnPool) NewConn(ctx context.Context) (*Conn, error) {
+func (p *StickyConnPool) NewConn(ctx context.Context) (*Conn, error) {
 	return p.pool.NewConn(ctx)
 }
 
-func (p *SingleConnPool) CloseConn(cn *Conn) error {
+func (p *StickyConnPool) CloseConn(cn *Conn) error {
 	return p.pool.CloseConn(cn)
 }
 
-func (p *SingleConnPool) Get(ctx context.Context) (*Conn, error) {
+func (p *StickyConnPool) Get(ctx context.Context) (*Conn, error) {
 	// In worst case this races with Close which is not a very common operation.
 	for i := 0; i < 1000; i++ {
 		switch atomic.LoadUint32(&p.state) {
@@ -82,7 +77,7 @@ func (p *SingleConnPool) Get(ctx context.Context) (*Conn, error) {
 			if atomic.CompareAndSwapUint32(&p.state, stateDefault, stateInited) {
 				return cn, nil
 			}
-			p.pool.Remove(cn, ErrClosed)
+			p.pool.Remove(ctx, cn, ErrClosed)
 		case stateInited:
 			if err := p.badConnError(); err != nil {
 				return nil, err
@@ -98,60 +93,38 @@ func (p *SingleConnPool) Get(ctx context.Context) (*Conn, error) {
 			panic("not reached")
 		}
 	}
-	return nil, fmt.Errorf("redis: SingleConnPool.Get: infinite loop")
+	return nil, fmt.Errorf("redis: StickyConnPool.Get: infinite loop")
 }
 
-func (p *SingleConnPool) Put(cn *Conn) {
+func (p *StickyConnPool) Put(ctx context.Context, cn *Conn) {
 	defer func() {
 		if recover() != nil {
-			p.freeConn(cn)
+			p.freeConn(ctx, cn)
 		}
 	}()
 	p.ch <- cn
 }
 
-func (p *SingleConnPool) freeConn(cn *Conn) {
+func (p *StickyConnPool) freeConn(ctx context.Context, cn *Conn) {
 	if err := p.badConnError(); err != nil {
-		p.pool.Remove(cn, err)
+		p.pool.Remove(ctx, cn, err)
 	} else {
-		p.pool.Put(cn)
+		p.pool.Put(ctx, cn)
 	}
 }
 
-func (p *SingleConnPool) Remove(cn *Conn, reason error) {
+func (p *StickyConnPool) Remove(ctx context.Context, cn *Conn, reason error) {
 	defer func() {
 		if recover() != nil {
-			p.pool.Remove(cn, ErrClosed)
+			p.pool.Remove(ctx, cn, ErrClosed)
 		}
 	}()
 	p._badConnError.Store(BadConnError{wrapped: reason})
 	p.ch <- cn
 }
 
-func (p *SingleConnPool) Len() int {
-	switch atomic.LoadUint32(&p.state) {
-	case stateDefault:
-		return 0
-	case stateInited:
-		return 1
-	case stateClosed:
-		return 0
-	default:
-		panic("not reached")
-	}
-}
-
-func (p *SingleConnPool) IdleLen() int {
-	return len(p.ch)
-}
-
-func (p *SingleConnPool) Stats() *Stats {
-	return &Stats{}
-}
-
-func (p *SingleConnPool) Close() error {
-	level := atomic.AddInt32(&p.level, -1)
-	if level > 0 {
+func (p *StickyConnPool) Close() error {
+	if shared := atomic.AddInt32(&p.shared, -1); shared > 0 {
 		return nil
 	}
 
@@ -164,16 +137,16 @@ func (p *SingleConnPool) Close() error {
 			close(p.ch)
 			cn, ok := <-p.ch
 			if ok {
-				p.freeConn(cn)
+				p.freeConn(context.TODO(), cn)
 			}
 			return nil
 		}
 	}
 
-	return fmt.Errorf("redis: SingleConnPool.Close: infinite loop")
+	return errors.New("redis: StickyConnPool.Close: infinite loop")
 }
 
-func (p *SingleConnPool) Reset() error {
+func (p *StickyConnPool) Reset(ctx context.Context) error {
 	if p.badConnError() == nil {
 		return nil
 	}
@@ -183,21 +156,21 @@ func (p *SingleConnPool) Reset() error {
 		if !ok {
 			return ErrClosed
 		}
-		p.pool.Remove(cn, ErrClosed)
+		p.pool.Remove(ctx, cn, ErrClosed)
 		p._badConnError.Store(BadConnError{wrapped: nil})
 	default:
-		return fmt.Errorf("redis: SingleConnPool does not have a Conn")
+		return errors.New("redis: StickyConnPool does not have a Conn")
 	}
 
 	if !atomic.CompareAndSwapUint32(&p.state, stateInited, stateDefault) {
 		state := atomic.LoadUint32(&p.state)
-		return fmt.Errorf("redis: invalid SingleConnPool state: %d", state)
+		return fmt.Errorf("redis: invalid StickyConnPool state: %d", state)
 	}
 
 	return nil
 }
 
-func (p *SingleConnPool) badConnError() error {
+func (p *StickyConnPool) badConnError() error {
 	if v := p._badConnError.Load(); v != nil {
 		err := v.(BadConnError)
 		if err.wrapped != nil {
@@ -206,3 +179,24 @@ func (p *SingleConnPool) badConnError() error {
 	}
 	return nil
 }
+
+func (p *StickyConnPool) Len() int {
+	switch atomic.LoadUint32(&p.state) {
+	case stateDefault:
+		return 0
+	case stateInited:
+		return 1
+	case stateClosed:
+		return 0
+	default:
+		panic("not reached")
+	}
+}
+
+func (p *StickyConnPool) IdleLen() int {
+	return len(p.ch)
+}
+
+func (p *StickyConnPool) Stats() *Stats {
+	return &Stats{}
+}
diff --git a/vendor/github.com/go-redis/redis/v7/internal/proto/reader.go b/vendor/github.com/go-redis/redis/v8/internal/proto/reader.go
similarity index 92%
rename from vendor/github.com/go-redis/redis/v7/internal/proto/reader.go
rename to vendor/github.com/go-redis/redis/v8/internal/proto/reader.go
index d3f646e98f..0fbc51e9ac 100644
--- a/vendor/github.com/go-redis/redis/v7/internal/proto/reader.go
+++ b/vendor/github.com/go-redis/redis/v8/internal/proto/reader.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"io"
 
-	"github.com/go-redis/redis/v7/internal/util"
+	"github.com/go-redis/redis/v8/internal/util"
 )
 
 const (
@@ -71,13 +71,25 @@ func (r *Reader) ReadLine() ([]byte, error) {
 func (r *Reader) readLine() ([]byte, error) {
 	b, err := r.rd.ReadSlice('\n')
 	if err != nil {
-		return nil, err
+		if err != bufio.ErrBufferFull {
+			return nil, err
+		}
+
+		full := make([]byte, len(b))
+		copy(full, b)
+
+		b, err = r.rd.ReadBytes('\n')
+		if err != nil {
+			return nil, err
+		}
+
+		full = append(full, b...)
+		b = full
 	}
 	if len(b) <= 2 || b[len(b)-1] != '\n' || b[len(b)-2] != '\r' {
 		return nil, fmt.Errorf("redis: invalid reply: %q", b)
 	}
-	b = b[:len(b)-2]
-	return b, nil
+	return b[:len(b)-2], nil
 }
 
 func (r *Reader) ReadReply(m MultiBulkParse) (interface{}, error) {
@@ -181,7 +193,7 @@ func (r *Reader) ReadArrayReply(m MultiBulkParse) (interface{}, error) {
 	}
 }
 
-func (r *Reader) ReadArrayLen() (int64, error) {
+func (r *Reader) ReadArrayLen() (int, error) {
 	line, err := r.ReadLine()
 	if err != nil {
 		return 0, err
@@ -190,7 +202,11 @@ func (r *Reader) ReadArrayLen() (int64, error) {
 	case ErrorReply:
 		return 0, ParseErrorReply(line)
 	case ArrayReply:
-		return parseArrayLen(line)
+		n, err := parseArrayLen(line)
+		if err != nil {
+			return 0, err
+		}
+		return int(n), nil
 	default:
 		return 0, fmt.Errorf("redis: can't parse array reply: %.100q", line)
 	}
@@ -216,7 +232,8 @@ func (r *Reader) ReadScanReply() ([]string, uint64, error) {
 	}
 
 	keys := make([]string, n)
-	for i := int64(0); i < n; i++ {
+
+	for i := 0; i < n; i++ {
 		key, err := r.ReadString()
 		if err != nil {
 			return nil, 0, err
diff --git a/vendor/github.com/go-redis/redis/v7/internal/proto/scan.go b/vendor/github.com/go-redis/redis/v8/internal/proto/scan.go
similarity index 92%
rename from vendor/github.com/go-redis/redis/v7/internal/proto/scan.go
rename to vendor/github.com/go-redis/redis/v8/internal/proto/scan.go
index 90c1e4ae6e..c2c3ed17de 100644
--- a/vendor/github.com/go-redis/redis/v7/internal/proto/scan.go
+++ b/vendor/github.com/go-redis/redis/v8/internal/proto/scan.go
@@ -4,10 +4,13 @@ import (
 	"encoding"
 	"fmt"
 	"reflect"
+	"time"
 
-	"github.com/go-redis/redis/v7/internal/util"
+	"github.com/go-redis/redis/v8/internal/util"
 )
 
+// Scan parses bytes `b` to `v` with appropriate type.
+// nolint: gocyclo
 func Scan(b []byte, v interface{}) error {
 	switch v := v.(type) {
 	case nil:
@@ -99,6 +102,10 @@ func Scan(b []byte, v interface{}) error {
 	case *bool:
 		*v = len(b) == 1 && b[0] == '1'
 		return nil
+	case *time.Time:
+		var err error
+		*v, err = time.Parse(time.RFC3339Nano, util.BytesToString(b))
+		return err
 	case encoding.BinaryUnmarshaler:
 		return v.UnmarshalBinary(b)
 	default:
@@ -124,7 +131,7 @@ func ScanSlice(data []string, slice interface{}) error {
 	for i, s := range data {
 		elem := next()
 		if err := Scan([]byte(s), elem.Addr().Interface()); err != nil {
-			err = fmt.Errorf("redis: ScanSlice index=%d value=%q failed: %s", i, s, err)
+			err = fmt.Errorf("redis: ScanSlice index=%d value=%q failed: %w", i, s, err)
 			return err
 		}
 	}
diff --git a/vendor/github.com/go-redis/redis/v7/internal/proto/writer.go b/vendor/github.com/go-redis/redis/v8/internal/proto/writer.go
similarity index 71%
rename from vendor/github.com/go-redis/redis/v7/internal/proto/writer.go
rename to vendor/github.com/go-redis/redis/v8/internal/proto/writer.go
index d552f1e84d..81b09b8e4f 100644
--- a/vendor/github.com/go-redis/redis/v7/internal/proto/writer.go
+++ b/vendor/github.com/go-redis/redis/v8/internal/proto/writer.go
@@ -1,26 +1,32 @@
 package proto
 
 import (
-	"bufio"
 	"encoding"
 	"fmt"
 	"io"
 	"strconv"
 	"time"
 
-	"github.com/go-redis/redis/v7/internal/util"
+	"github.com/go-redis/redis/v8/internal/util"
 )
 
+type writer interface {
+	io.Writer
+	io.ByteWriter
+	// io.StringWriter
+	WriteString(s string) (n int, err error)
+}
+
 type Writer struct {
-	wr *bufio.Writer
+	writer
 
 	lenBuf []byte
 	numBuf []byte
 }
 
-func NewWriter(wr io.Writer) *Writer {
+func NewWriter(wr writer) *Writer {
 	return &Writer{
-		wr: bufio.NewWriter(wr),
+		writer: wr,
 
 		lenBuf: make([]byte, 64),
 		numBuf: make([]byte, 64),
@@ -28,19 +34,16 @@ func NewWriter(wr io.Writer) *Writer {
 }
 
 func (w *Writer) WriteArgs(args []interface{}) error {
-	err := w.wr.WriteByte(ArrayReply)
-	if err != nil {
+	if err := w.WriteByte(ArrayReply); err != nil {
 		return err
 	}
 
-	err = w.writeLen(len(args))
-	if err != nil {
+	if err := w.writeLen(len(args)); err != nil {
 		return err
 	}
 
 	for _, arg := range args {
-		err := w.writeArg(arg)
-		if err != nil {
+		if err := w.WriteArg(arg); err != nil {
 			return err
 		}
 	}
@@ -51,11 +54,11 @@ func (w *Writer) WriteArgs(args []interface{}) error {
 func (w *Writer) writeLen(n int) error {
 	w.lenBuf = strconv.AppendUint(w.lenBuf[:0], uint64(n), 10)
 	w.lenBuf = append(w.lenBuf, '\r', '\n')
-	_, err := w.wr.Write(w.lenBuf)
+	_, err := w.Write(w.lenBuf)
 	return err
 }
 
-func (w *Writer) writeArg(v interface{}) error {
+func (w *Writer) WriteArg(v interface{}) error {
 	switch v := v.(type) {
 	case nil:
 		return w.string("")
@@ -93,7 +96,8 @@ func (w *Writer) writeArg(v interface{}) error {
 		}
 		return w.int(0)
 	case time.Time:
-		return w.string(v.Format(time.RFC3339Nano))
+		w.numBuf = v.AppendFormat(w.numBuf[:0], time.RFC3339Nano)
+		return w.bytes(w.numBuf)
 	case encoding.BinaryMarshaler:
 		b, err := v.MarshalBinary()
 		if err != nil {
@@ -107,18 +111,15 @@ func (w *Writer) writeArg(v interface{}) error {
 }
 
 func (w *Writer) bytes(b []byte) error {
-	err := w.wr.WriteByte(StringReply)
-	if err != nil {
+	if err := w.WriteByte(StringReply); err != nil {
 		return err
 	}
 
-	err = w.writeLen(len(b))
-	if err != nil {
+	if err := w.writeLen(len(b)); err != nil {
 		return err
 	}
 
-	_, err = w.wr.Write(b)
-	if err != nil {
+	if _, err := w.Write(b); err != nil {
 		return err
 	}
 
@@ -145,21 +146,8 @@ func (w *Writer) float(f float64) error {
 }
 
 func (w *Writer) crlf() error {
-	err := w.wr.WriteByte('\r')
-	if err != nil {
+	if err := w.WriteByte('\r'); err != nil {
 		return err
 	}
-	return w.wr.WriteByte('\n')
-}
-
-func (w *Writer) Buffered() int {
-	return w.wr.Buffered()
-}
-
-func (w *Writer) Reset(wr io.Writer) {
-	w.wr.Reset(wr)
-}
-
-func (w *Writer) Flush() error {
-	return w.wr.Flush()
+	return w.WriteByte('\n')
 }
diff --git a/vendor/github.com/go-redis/redis/v8/internal/rand/rand.go b/vendor/github.com/go-redis/redis/v8/internal/rand/rand.go
new file mode 100644
index 0000000000..40676f3cb6
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/internal/rand/rand.go
@@ -0,0 +1,45 @@
+package rand
+
+import (
+	"math/rand"
+	"sync"
+)
+
+// Int returns a non-negative pseudo-random int.
+func Int() int { return pseudo.Int() }
+
+// Intn returns, as an int, a non-negative pseudo-random number in [0,n).
+// It panics if n <= 0.
+func Intn(n int) int { return pseudo.Intn(n) }
+
+// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n).
+// It panics if n <= 0.
+func Int63n(n int64) int64 { return pseudo.Int63n(n) }
+
+// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n).
+func Perm(n int) []int { return pseudo.Perm(n) }
+
+// Seed uses the provided seed value to initialize the default Source to a
+// deterministic state. If Seed is not called, the generator behaves as if
+// seeded by Seed(1).
+func Seed(n int64) { pseudo.Seed(n) }
+
+var pseudo = rand.New(&source{src: rand.NewSource(1)})
+
+type source struct {
+	src rand.Source
+	mu  sync.Mutex
+}
+
+func (s *source) Int63() int64 {
+	s.mu.Lock()
+	n := s.src.Int63()
+	s.mu.Unlock()
+	return n
+}
+
+func (s *source) Seed(seed int64) {
+	s.mu.Lock()
+	s.src.Seed(seed)
+	s.mu.Unlock()
+}
diff --git a/vendor/github.com/go-redis/redis/v8/internal/safe.go b/vendor/github.com/go-redis/redis/v8/internal/safe.go
new file mode 100644
index 0000000000..862ff0eb3a
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/internal/safe.go
@@ -0,0 +1,11 @@
+// +build appengine
+
+package internal
+
+func String(b []byte) string {
+	return string(b)
+}
+
+func Bytes(s string) []byte {
+	return []byte(s)
+}
diff --git a/vendor/github.com/go-redis/redis/v8/internal/unsafe.go b/vendor/github.com/go-redis/redis/v8/internal/unsafe.go
new file mode 100644
index 0000000000..4bc79701f4
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/internal/unsafe.go
@@ -0,0 +1,20 @@
+// +build !appengine
+
+package internal
+
+import "unsafe"
+
+// String converts byte slice to string.
+func String(b []byte) string {
+	return *(*string)(unsafe.Pointer(&b))
+}
+
+// Bytes converts string to byte slice.
+func Bytes(s string) []byte {
+	return *(*[]byte)(unsafe.Pointer(
+		&struct {
+			string
+			Cap int
+		}{s, len(s)},
+	))
+}
diff --git a/vendor/github.com/go-redis/redis/v8/internal/util.go b/vendor/github.com/go-redis/redis/v8/internal/util.go
new file mode 100644
index 0000000000..4d7921bf32
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/internal/util.go
@@ -0,0 +1,73 @@
+package internal
+
+import (
+	"context"
+	"time"
+
+	"github.com/go-redis/redis/v8/internal/proto"
+	"github.com/go-redis/redis/v8/internal/util"
+	"go.opentelemetry.io/otel"
+	"go.opentelemetry.io/otel/trace"
+)
+
+func Sleep(ctx context.Context, dur time.Duration) error {
+	return WithSpan(ctx, "time.Sleep", func(ctx context.Context, span trace.Span) error {
+		t := time.NewTimer(dur)
+		defer t.Stop()
+
+		select {
+		case <-t.C:
+			return nil
+		case <-ctx.Done():
+			return ctx.Err()
+		}
+	})
+}
+
+func ToLower(s string) string {
+	if isLower(s) {
+		return s
+	}
+
+	b := make([]byte, len(s))
+	for i := range b {
+		c := s[i]
+		if c >= 'A' && c <= 'Z' {
+			c += 'a' - 'A'
+		}
+		b[i] = c
+	}
+	return util.BytesToString(b)
+}
+
+func isLower(s string) bool {
+	for i := 0; i < len(s); i++ {
+		c := s[i]
+		if c >= 'A' && c <= 'Z' {
+			return false
+		}
+	}
+	return true
+}
+
+//------------------------------------------------------------------------------
+
+var tracer = otel.Tracer("github.com/go-redis/redis")
+
+func WithSpan(ctx context.Context, name string, fn func(context.Context, trace.Span) error) error {
+	if span := trace.SpanFromContext(ctx); !span.IsRecording() {
+		return fn(ctx, span)
+	}
+
+	ctx, span := tracer.Start(ctx, name)
+	defer span.End()
+
+	return fn(ctx, span)
+}
+
+func RecordError(ctx context.Context, span trace.Span, err error) error {
+	if err != proto.Nil {
+		span.RecordError(err)
+	}
+	return err
+}
diff --git a/vendor/github.com/go-redis/redis/v7/internal/util/safe.go b/vendor/github.com/go-redis/redis/v8/internal/util/safe.go
similarity index 100%
rename from vendor/github.com/go-redis/redis/v7/internal/util/safe.go
rename to vendor/github.com/go-redis/redis/v8/internal/util/safe.go
diff --git a/vendor/github.com/go-redis/redis/v7/internal/util/strconv.go b/vendor/github.com/go-redis/redis/v8/internal/util/strconv.go
similarity index 100%
rename from vendor/github.com/go-redis/redis/v7/internal/util/strconv.go
rename to vendor/github.com/go-redis/redis/v8/internal/util/strconv.go
diff --git a/vendor/github.com/go-redis/redis/v7/internal/util/unsafe.go b/vendor/github.com/go-redis/redis/v8/internal/util/unsafe.go
similarity index 100%
rename from vendor/github.com/go-redis/redis/v7/internal/util/unsafe.go
rename to vendor/github.com/go-redis/redis/v8/internal/util/unsafe.go
diff --git a/vendor/github.com/go-redis/redis/v7/iterator.go b/vendor/github.com/go-redis/redis/v8/iterator.go
similarity index 88%
rename from vendor/github.com/go-redis/redis/v7/iterator.go
rename to vendor/github.com/go-redis/redis/v8/iterator.go
index f9d3aab6d2..2f8bc2beda 100644
--- a/vendor/github.com/go-redis/redis/v7/iterator.go
+++ b/vendor/github.com/go-redis/redis/v8/iterator.go
@@ -1,6 +1,7 @@
 package redis
 
 import (
+	"context"
 	"sync"
 )
 
@@ -21,7 +22,7 @@ func (it *ScanIterator) Err() error {
 }
 
 // Next advances the cursor and returns true if more values can be read.
-func (it *ScanIterator) Next() bool {
+func (it *ScanIterator) Next(ctx context.Context) bool {
 	it.mu.Lock()
 	defer it.mu.Unlock()
 
@@ -43,13 +44,14 @@ func (it *ScanIterator) Next() bool {
 		}
 
 		// Fetch next page.
-		if it.cmd.args[0] == "scan" {
+		switch it.cmd.args[0] {
+		case "scan", "qscan":
 			it.cmd.args[1] = it.cmd.cursor
-		} else {
+		default:
 			it.cmd.args[2] = it.cmd.cursor
 		}
 
-		err := it.cmd.process(it.cmd)
+		err := it.cmd.process(ctx, it.cmd)
 		if err != nil {
 			return false
 		}
diff --git a/vendor/github.com/go-redis/redis/v7/options.go b/vendor/github.com/go-redis/redis/v8/options.go
similarity index 71%
rename from vendor/github.com/go-redis/redis/v7/options.go
rename to vendor/github.com/go-redis/redis/v8/options.go
index 47dcc29bf3..7a02002401 100644
--- a/vendor/github.com/go-redis/redis/v7/options.go
+++ b/vendor/github.com/go-redis/redis/v8/options.go
@@ -12,7 +12,10 @@ import (
 	"strings"
 	"time"
 
-	"github.com/go-redis/redis/v7/internal/pool"
+	"github.com/go-redis/redis/v8/internal"
+	"github.com/go-redis/redis/v8/internal/pool"
+	"go.opentelemetry.io/otel/label"
+	"go.opentelemetry.io/otel/trace"
 )
 
 // Limiter is the interface of a rate limiter or a circuit breaker.
@@ -26,6 +29,7 @@ type Limiter interface {
 	ReportResult(result error)
 }
 
+// Options keeps the settings to setup redis connection.
 type Options struct {
 	// The network type, either tcp or unix.
 	// Default is tcp.
@@ -38,21 +42,23 @@ type Options struct {
 	Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
 
 	// Hook that is called when new connection is established.
-	OnConnect func(*Conn) error
+	OnConnect func(ctx context.Context, cn *Conn) error
 
-	// Use the specified Username to authenticate the current connection with one of the connections defined in the ACL
-	// list when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
+	// Use the specified Username to authenticate the current connection
+	// with one of the connections defined in the ACL list when connecting
+	// to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
 	Username string
-
 	// Optional password. Must match the password specified in the
 	// requirepass server configuration option (if connecting to a Redis 5.0 instance, or lower),
-	// or the User Password when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
+	// or the User Password when connecting to a Redis 6.0 instance, or greater,
+	// that is using the Redis ACL system.
 	Password string
+
 	// Database to be selected after connecting to the server.
 	DB int
 
 	// Maximum number of retries before giving up.
-	// Default is to not retry failed commands.
+	// Default is 3 retries; -1 (not 0) disables retries.
 	MaxRetries int
 	// Minimum backoff between each retry.
 	// Default is 8 milliseconds; -1 disables backoff.
@@ -117,6 +123,9 @@ func (opt *Options) init() {
 			opt.Network = "tcp"
 		}
 	}
+	if opt.DialTimeout == 0 {
+		opt.DialTimeout = 5 * time.Second
+	}
 	if opt.Dialer == nil {
 		opt.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) {
 			netDialer := &net.Dialer{
@@ -132,9 +141,6 @@ func (opt *Options) init() {
 	if opt.PoolSize == 0 {
 		opt.PoolSize = 10 * runtime.NumCPU()
 	}
-	if opt.DialTimeout == 0 {
-		opt.DialTimeout = 5 * time.Second
-	}
 	switch opt.ReadTimeout {
 	case -1:
 		opt.ReadTimeout = 0
@@ -159,6 +165,8 @@ func (opt *Options) init() {
 
 	if opt.MaxRetries == -1 {
 		opt.MaxRetries = 0
+	} else if opt.MaxRetries == 0 {
+		opt.MaxRetries = 3
 	}
 	switch opt.MinRetryBackoff {
 	case -1:
@@ -180,26 +188,35 @@ func (opt *Options) clone() *Options {
 }
 
 // ParseURL parses an URL into Options that can be used to connect to Redis.
+// Scheme is required.
+// There are two connection types: by tcp socket and by unix socket.
+// Tcp connection:
+// 		redis://<user>:<password>@<host>:<port>/<db_number>
+// Unix connection:
+//		unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>
 func ParseURL(redisURL string) (*Options, error) {
-	o := &Options{Network: "tcp"}
 	u, err := url.Parse(redisURL)
 	if err != nil {
 		return nil, err
 	}
 
-	if u.Scheme != "redis" && u.Scheme != "rediss" {
-		return nil, errors.New("invalid redis URL scheme: " + u.Scheme)
+	switch u.Scheme {
+	case "redis", "rediss":
+		return setupTCPConn(u)
+	case "unix":
+		return setupUnixConn(u)
+	default:
+		return nil, fmt.Errorf("redis: invalid URL scheme: %s", u.Scheme)
 	}
+}
 
-	if u.User != nil {
-		o.Username = u.User.Username()
-		if p, ok := u.User.Password(); ok {
-			o.Password = p
-		}
-	}
+func setupTCPConn(u *url.URL) (*Options, error) {
+	o := &Options{Network: "tcp"}
+
+	o.Username, o.Password = getUserPassword(u)
 
 	if len(u.Query()) > 0 {
-		return nil, errors.New("no options supported")
+		return nil, errors.New("redis: no options supported")
 	}
 
 	h, p, err := net.SplitHostPort(u.Host)
@@ -222,22 +239,73 @@ func ParseURL(redisURL string) (*Options, error) {
 		o.DB = 0
 	case 1:
 		if o.DB, err = strconv.Atoi(f[0]); err != nil {
-			return nil, fmt.Errorf("invalid redis database number: %q", f[0])
+			return nil, fmt.Errorf("redis: invalid database number: %q", f[0])
 		}
 	default:
-		return nil, errors.New("invalid redis URL path: " + u.Path)
+		return nil, fmt.Errorf("redis: invalid URL path: %s", u.Path)
 	}
 
 	if u.Scheme == "rediss" {
 		o.TLSConfig = &tls.Config{ServerName: h}
 	}
+
 	return o, nil
 }
 
+func setupUnixConn(u *url.URL) (*Options, error) {
+	o := &Options{
+		Network: "unix",
+	}
+
+	if strings.TrimSpace(u.Path) == "" { // path is required with unix connection
+		return nil, errors.New("redis: empty unix socket path")
+	}
+	o.Addr = u.Path
+
+	o.Username, o.Password = getUserPassword(u)
+
+	dbStr := u.Query().Get("db")
+	if dbStr == "" {
+		return o, nil // if database is not set, connect to 0 db.
+	}
+
+	db, err := strconv.Atoi(dbStr)
+	if err != nil {
+		return nil, fmt.Errorf("redis: invalid database number: %w", err)
+	}
+	o.DB = db
+
+	return o, nil
+}
+
+func getUserPassword(u *url.URL) (string, string) {
+	var user, password string
+	if u.User != nil {
+		user = u.User.Username()
+		if p, ok := u.User.Password(); ok {
+			password = p
+		}
+	}
+	return user, password
+}
+
 func newConnPool(opt *Options) *pool.ConnPool {
 	return pool.NewConnPool(&pool.Options{
 		Dialer: func(ctx context.Context) (net.Conn, error) {
-			return opt.Dialer(ctx, opt.Network, opt.Addr)
+			var conn net.Conn
+			err := internal.WithSpan(ctx, "redis.dial", func(ctx context.Context, span trace.Span) error {
+				span.SetAttributes(
+					label.String("db.connection_string", opt.Addr),
+				)
+
+				var err error
+				conn, err = opt.Dialer(ctx, opt.Network, opt.Addr)
+				if err != nil {
+					_ = internal.RecordError(ctx, span, err)
+				}
+				return err
+			})
+			return conn, err
 		},
 		PoolSize:           opt.PoolSize,
 		MinIdleConns:       opt.MinIdleConns,
diff --git a/vendor/github.com/go-redis/redis/v7/pipeline.go b/vendor/github.com/go-redis/redis/v8/pipeline.go
similarity index 77%
rename from vendor/github.com/go-redis/redis/v7/pipeline.go
rename to vendor/github.com/go-redis/redis/v8/pipeline.go
index d48566a787..c6ec340998 100644
--- a/vendor/github.com/go-redis/redis/v7/pipeline.go
+++ b/vendor/github.com/go-redis/redis/v8/pipeline.go
@@ -4,7 +4,7 @@ import (
 	"context"
 	"sync"
 
-	"github.com/go-redis/redis/v7/internal/pool"
+	"github.com/go-redis/redis/v8/internal/pool"
 )
 
 type pipelineExecer func(context.Context, []Cmder) error
@@ -24,12 +24,11 @@ type pipelineExecer func(context.Context, []Cmder) error
 // depends of your batch size and/or use TxPipeline.
 type Pipeliner interface {
 	StatefulCmdable
-	Do(args ...interface{}) *Cmd
-	Process(cmd Cmder) error
+	Do(ctx context.Context, args ...interface{}) *Cmd
+	Process(ctx context.Context, cmd Cmder) error
 	Close() error
 	Discard() error
-	Exec() ([]Cmder, error)
-	ExecContext(ctx context.Context) ([]Cmder, error)
+	Exec(ctx context.Context) ([]Cmder, error)
 }
 
 var _ Pipeliner = (*Pipeline)(nil)
@@ -54,14 +53,14 @@ func (c *Pipeline) init() {
 	c.statefulCmdable = c.Process
 }
 
-func (c *Pipeline) Do(args ...interface{}) *Cmd {
-	cmd := NewCmd(args...)
-	_ = c.Process(cmd)
+func (c *Pipeline) Do(ctx context.Context, args ...interface{}) *Cmd {
+	cmd := NewCmd(ctx, args...)
+	_ = c.Process(ctx, cmd)
 	return cmd
 }
 
 // Process queues the cmd for later execution.
-func (c *Pipeline) Process(cmd Cmder) error {
+func (c *Pipeline) Process(ctx context.Context, cmd Cmder) error {
 	c.mu.Lock()
 	c.cmds = append(c.cmds, cmd)
 	c.mu.Unlock()
@@ -98,11 +97,7 @@ func (c *Pipeline) discard() error {
 //
 // Exec always returns list of commands and error of the first failed
 // command if any.
-func (c *Pipeline) Exec() ([]Cmder, error) {
-	return c.ExecContext(c.ctx)
-}
-
-func (c *Pipeline) ExecContext(ctx context.Context) ([]Cmder, error) {
+func (c *Pipeline) Exec(ctx context.Context) ([]Cmder, error) {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 
@@ -120,11 +115,11 @@ func (c *Pipeline) ExecContext(ctx context.Context) ([]Cmder, error) {
 	return cmds, c.exec(ctx, cmds)
 }
 
-func (c *Pipeline) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
+func (c *Pipeline) Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) {
 	if err := fn(c); err != nil {
 		return nil, err
 	}
-	cmds, err := c.Exec()
+	cmds, err := c.Exec(ctx)
 	_ = c.Close()
 	return cmds, err
 }
@@ -133,8 +128,8 @@ func (c *Pipeline) Pipeline() Pipeliner {
 	return c
 }
 
-func (c *Pipeline) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
-	return c.Pipelined(fn)
+func (c *Pipeline) TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) {
+	return c.Pipelined(ctx, fn)
 }
 
 func (c *Pipeline) TxPipeline() Pipeliner {
diff --git a/vendor/github.com/go-redis/redis/v7/pubsub.go b/vendor/github.com/go-redis/redis/v8/pubsub.go
similarity index 70%
rename from vendor/github.com/go-redis/redis/v7/pubsub.go
rename to vendor/github.com/go-redis/redis/v8/pubsub.go
index 26cde242be..c56270b443 100644
--- a/vendor/github.com/go-redis/redis/v7/pubsub.go
+++ b/vendor/github.com/go-redis/redis/v8/pubsub.go
@@ -8,12 +8,15 @@ import (
 	"sync"
 	"time"
 
-	"github.com/go-redis/redis/v7/internal"
-	"github.com/go-redis/redis/v7/internal/pool"
-	"github.com/go-redis/redis/v7/internal/proto"
+	"github.com/go-redis/redis/v8/internal"
+	"github.com/go-redis/redis/v8/internal/pool"
+	"github.com/go-redis/redis/v8/internal/proto"
 )
 
-const pingTimeout = 30 * time.Second
+const (
+	pingTimeout     = time.Second
+	chanSendTimeout = time.Minute
+)
 
 var errPingTimeout = errors.New("redis: ping timeout")
 
@@ -26,7 +29,7 @@ var errPingTimeout = errors.New("redis: ping timeout")
 type PubSub struct {
 	opt *Options
 
-	newConn   func([]string) (*pool.Conn, error)
+	newConn   func(ctx context.Context, channels []string) (*pool.Conn, error)
 	closeConn func(*pool.Conn) error
 
 	mu       sync.Mutex
@@ -55,14 +58,14 @@ func (c *PubSub) init() {
 	c.exit = make(chan struct{})
 }
 
-func (c *PubSub) connWithLock() (*pool.Conn, error) {
+func (c *PubSub) connWithLock(ctx context.Context) (*pool.Conn, error) {
 	c.mu.Lock()
-	cn, err := c.conn(nil)
+	cn, err := c.conn(ctx, nil)
 	c.mu.Unlock()
 	return cn, err
 }
 
-func (c *PubSub) conn(newChannels []string) (*pool.Conn, error) {
+func (c *PubSub) conn(ctx context.Context, newChannels []string) (*pool.Conn, error) {
 	if c.closed {
 		return nil, pool.ErrClosed
 	}
@@ -73,12 +76,12 @@ func (c *PubSub) conn(newChannels []string) (*pool.Conn, error) {
 	channels := mapKeys(c.channels)
 	channels = append(channels, newChannels...)
 
-	cn, err := c.newConn(channels)
+	cn, err := c.newConn(ctx, channels)
 	if err != nil {
 		return nil, err
 	}
 
-	if err := c.resubscribe(cn); err != nil {
+	if err := c.resubscribe(ctx, cn); err != nil {
 		_ = c.closeConn(cn)
 		return nil, err
 	}
@@ -93,15 +96,15 @@ func (c *PubSub) writeCmd(ctx context.Context, cn *pool.Conn, cmd Cmder) error {
 	})
 }
 
-func (c *PubSub) resubscribe(cn *pool.Conn) error {
+func (c *PubSub) resubscribe(ctx context.Context, cn *pool.Conn) error {
 	var firstErr error
 
 	if len(c.channels) > 0 {
-		firstErr = c._subscribe(cn, "subscribe", mapKeys(c.channels))
+		firstErr = c._subscribe(ctx, cn, "subscribe", mapKeys(c.channels))
 	}
 
 	if len(c.patterns) > 0 {
-		err := c._subscribe(cn, "psubscribe", mapKeys(c.patterns))
+		err := c._subscribe(ctx, cn, "psubscribe", mapKeys(c.patterns))
 		if err != nil && firstErr == nil {
 			firstErr = err
 		}
@@ -121,35 +124,40 @@ func mapKeys(m map[string]struct{}) []string {
 }
 
 func (c *PubSub) _subscribe(
-	cn *pool.Conn, redisCmd string, channels []string,
+	ctx context.Context, cn *pool.Conn, redisCmd string, channels []string,
 ) error {
 	args := make([]interface{}, 0, 1+len(channels))
 	args = append(args, redisCmd)
 	for _, channel := range channels {
 		args = append(args, channel)
 	}
-	cmd := NewSliceCmd(args...)
-	return c.writeCmd(context.TODO(), cn, cmd)
+	cmd := NewSliceCmd(ctx, args...)
+	return c.writeCmd(ctx, cn, cmd)
 }
 
-func (c *PubSub) releaseConnWithLock(cn *pool.Conn, err error, allowTimeout bool) {
+func (c *PubSub) releaseConnWithLock(
+	ctx context.Context,
+	cn *pool.Conn,
+	err error,
+	allowTimeout bool,
+) {
 	c.mu.Lock()
-	c.releaseConn(cn, err, allowTimeout)
+	c.releaseConn(ctx, cn, err, allowTimeout)
 	c.mu.Unlock()
 }
 
-func (c *PubSub) releaseConn(cn *pool.Conn, err error, allowTimeout bool) {
+func (c *PubSub) releaseConn(ctx context.Context, cn *pool.Conn, err error, allowTimeout bool) {
 	if c.cn != cn {
 		return
 	}
 	if isBadConn(err, allowTimeout) {
-		c.reconnect(err)
+		c.reconnect(ctx, err)
 	}
 }
 
-func (c *PubSub) reconnect(reason error) {
+func (c *PubSub) reconnect(ctx context.Context, reason error) {
 	_ = c.closeTheCn(reason)
-	_, _ = c.conn(nil)
+	_, _ = c.conn(ctx, nil)
 }
 
 func (c *PubSub) closeTheCn(reason error) error {
@@ -157,7 +165,7 @@ func (c *PubSub) closeTheCn(reason error) error {
 		return nil
 	}
 	if !c.closed {
-		internal.Logger.Printf("redis: discarding bad PubSub connection: %s", reason)
+		internal.Logger.Printf(c.getContext(), "redis: discarding bad PubSub connection: %s", reason)
 	}
 	err := c.closeConn(c.cn)
 	c.cn = nil
@@ -179,11 +187,11 @@ func (c *PubSub) Close() error {
 
 // Subscribe the client to the specified channels. It returns
 // empty subscription if there are no channels.
-func (c *PubSub) Subscribe(channels ...string) error {
+func (c *PubSub) Subscribe(ctx context.Context, channels ...string) error {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 
-	err := c.subscribe("subscribe", channels...)
+	err := c.subscribe(ctx, "subscribe", channels...)
 	if c.channels == nil {
 		c.channels = make(map[string]struct{})
 	}
@@ -195,11 +203,11 @@ func (c *PubSub) Subscribe(channels ...string) error {
 
 // PSubscribe the client to the given patterns. It returns
 // empty subscription if there are no patterns.
-func (c *PubSub) PSubscribe(patterns ...string) error {
+func (c *PubSub) PSubscribe(ctx context.Context, patterns ...string) error {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 
-	err := c.subscribe("psubscribe", patterns...)
+	err := c.subscribe(ctx, "psubscribe", patterns...)
 	if c.patterns == nil {
 		c.patterns = make(map[string]struct{})
 	}
@@ -211,55 +219,55 @@ func (c *PubSub) PSubscribe(patterns ...string) error {
 
 // Unsubscribe the client from the given channels, or from all of
 // them if none is given.
-func (c *PubSub) Unsubscribe(channels ...string) error {
+func (c *PubSub) Unsubscribe(ctx context.Context, channels ...string) error {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 
 	for _, channel := range channels {
 		delete(c.channels, channel)
 	}
-	err := c.subscribe("unsubscribe", channels...)
+	err := c.subscribe(ctx, "unsubscribe", channels...)
 	return err
 }
 
 // PUnsubscribe the client from the given patterns, or from all of
 // them if none is given.
-func (c *PubSub) PUnsubscribe(patterns ...string) error {
+func (c *PubSub) PUnsubscribe(ctx context.Context, patterns ...string) error {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 
 	for _, pattern := range patterns {
 		delete(c.patterns, pattern)
 	}
-	err := c.subscribe("punsubscribe", patterns...)
+	err := c.subscribe(ctx, "punsubscribe", patterns...)
 	return err
 }
 
-func (c *PubSub) subscribe(redisCmd string, channels ...string) error {
-	cn, err := c.conn(channels)
+func (c *PubSub) subscribe(ctx context.Context, redisCmd string, channels ...string) error {
+	cn, err := c.conn(ctx, channels)
 	if err != nil {
 		return err
 	}
 
-	err = c._subscribe(cn, redisCmd, channels)
-	c.releaseConn(cn, err, false)
+	err = c._subscribe(ctx, cn, redisCmd, channels)
+	c.releaseConn(ctx, cn, err, false)
 	return err
 }
 
-func (c *PubSub) Ping(payload ...string) error {
+func (c *PubSub) Ping(ctx context.Context, payload ...string) error {
 	args := []interface{}{"ping"}
 	if len(payload) == 1 {
 		args = append(args, payload[0])
 	}
-	cmd := NewCmd(args...)
+	cmd := NewCmd(ctx, args...)
 
-	cn, err := c.connWithLock()
+	cn, err := c.connWithLock(ctx)
 	if err != nil {
 		return err
 	}
 
-	err = c.writeCmd(context.TODO(), cn, cmd)
-	c.releaseConnWithLock(cn, err, false)
+	err = c.writeCmd(ctx, cn, cmd)
+	c.releaseConnWithLock(ctx, cn, err, false)
 	return err
 }
 
@@ -279,9 +287,10 @@ func (m *Subscription) String() string {
 
 // Message received as result of a PUBLISH command issued by another client.
 type Message struct {
-	Channel string
-	Pattern string
-	Payload string
+	Channel      string
+	Pattern      string
+	Payload      string
+	PayloadSlice []string
 }
 
 func (m *Message) String() string {
@@ -317,10 +326,24 @@ func (c *PubSub) newMessage(reply interface{}) (interface{}, error) {
 				Count:   int(reply[2].(int64)),
 			}, nil
 		case "message":
-			return &Message{
-				Channel: reply[1].(string),
-				Payload: reply[2].(string),
-			}, nil
+			switch payload := reply[2].(type) {
+			case string:
+				return &Message{
+					Channel: reply[1].(string),
+					Payload: payload,
+				}, nil
+			case []interface{}:
+				ss := make([]string, len(payload))
+				for i, s := range payload {
+					ss[i] = s.(string)
+				}
+				return &Message{
+					Channel:      reply[1].(string),
+					PayloadSlice: ss,
+				}, nil
+			default:
+				return nil, fmt.Errorf("redis: unsupported pubsub message payload: %T", payload)
+			}
 		case "pmessage":
 			return &Message{
 				Pattern: reply[1].(string),
@@ -342,21 +365,21 @@ func (c *PubSub) newMessage(reply interface{}) (interface{}, error) {
 // ReceiveTimeout acts like Receive but returns an error if message
 // is not received in time. This is low-level API and in most cases
 // Channel should be used instead.
-func (c *PubSub) ReceiveTimeout(timeout time.Duration) (interface{}, error) {
+func (c *PubSub) ReceiveTimeout(ctx context.Context, timeout time.Duration) (interface{}, error) {
 	if c.cmd == nil {
-		c.cmd = NewCmd()
+		c.cmd = NewCmd(ctx)
 	}
 
-	cn, err := c.connWithLock()
+	cn, err := c.connWithLock(ctx)
 	if err != nil {
 		return nil, err
 	}
 
-	err = cn.WithReader(context.TODO(), timeout, func(rd *proto.Reader) error {
+	err = cn.WithReader(ctx, timeout, func(rd *proto.Reader) error {
 		return c.cmd.readReply(rd)
 	})
 
-	c.releaseConnWithLock(cn, err, timeout > 0)
+	c.releaseConnWithLock(ctx, cn, err, timeout > 0)
 	if err != nil {
 		return nil, err
 	}
@@ -367,16 +390,16 @@ func (c *PubSub) ReceiveTimeout(timeout time.Duration) (interface{}, error) {
 // Receive returns a message as a Subscription, Message, Pong or error.
 // See PubSub example for details. This is low-level API and in most cases
 // Channel should be used instead.
-func (c *PubSub) Receive() (interface{}, error) {
-	return c.ReceiveTimeout(0)
+func (c *PubSub) Receive(ctx context.Context) (interface{}, error) {
+	return c.ReceiveTimeout(ctx, 0)
 }
 
 // ReceiveMessage returns a Message or error ignoring Subscription and Pong
 // messages. This is low-level API and in most cases Channel should be used
 // instead.
-func (c *PubSub) ReceiveMessage() (*Message, error) {
+func (c *PubSub) ReceiveMessage(ctx context.Context) (*Message, error) {
 	for {
-		msg, err := c.Receive()
+		msg, err := c.Receive(ctx)
 		if err != nil {
 			return nil, err
 		}
@@ -429,7 +452,7 @@ func (c *PubSub) ChannelSize(size int) <-chan *Message {
 // reconnections.
 //
 // ChannelWithSubscriptions can not be used together with Channel or ChannelSize.
-func (c *PubSub) ChannelWithSubscriptions(size int) <-chan interface{} {
+func (c *PubSub) ChannelWithSubscriptions(ctx context.Context, size int) <-chan interface{} {
 	c.chOnce.Do(func() {
 		c.initPing()
 		c.initAllChan(size)
@@ -445,10 +468,18 @@ func (c *PubSub) ChannelWithSubscriptions(size int) <-chan interface{} {
 	return c.allCh
 }
 
+func (c *PubSub) getContext() context.Context {
+	if c.cmd != nil {
+		return c.cmd.ctx
+	}
+	return context.Background()
+}
+
 func (c *PubSub) initPing() {
+	ctx := context.TODO()
 	c.ping = make(chan struct{}, 1)
 	go func() {
-		timer := time.NewTimer(pingTimeout)
+		timer := time.NewTimer(time.Minute)
 		timer.Stop()
 
 		healthy := true
@@ -461,7 +492,7 @@ func (c *PubSub) initPing() {
 					<-timer.C
 				}
 			case <-timer.C:
-				pingErr := c.Ping()
+				pingErr := c.Ping(ctx)
 				if healthy {
 					healthy = false
 				} else {
@@ -469,7 +500,7 @@ func (c *PubSub) initPing() {
 						pingErr = errPingTimeout
 					}
 					c.mu.Lock()
-					c.reconnect(pingErr)
+					c.reconnect(ctx, pingErr)
 					healthy = true
 					c.mu.Unlock()
 				}
@@ -482,21 +513,22 @@ func (c *PubSub) initPing() {
 
 // initMsgChan must be in sync with initAllChan.
 func (c *PubSub) initMsgChan(size int) {
+	ctx := context.TODO()
 	c.msgCh = make(chan *Message, size)
 	go func() {
-		timer := time.NewTimer(pingTimeout)
+		timer := time.NewTimer(time.Minute)
 		timer.Stop()
 
 		var errCount int
 		for {
-			msg, err := c.Receive()
+			msg, err := c.Receive(ctx)
 			if err != nil {
 				if err == pool.ErrClosed {
 					close(c.msgCh)
 					return
 				}
 				if errCount > 0 {
-					time.Sleep(c.retryBackoff(errCount))
+					time.Sleep(100 * time.Millisecond)
 				}
 				errCount++
 				continue
@@ -516,7 +548,7 @@ func (c *PubSub) initMsgChan(size int) {
 			case *Pong:
 				// Ignore.
 			case *Message:
-				timer.Reset(pingTimeout)
+				timer.Reset(chanSendTimeout)
 				select {
 				case c.msgCh <- msg:
 					if !timer.Stop() {
@@ -524,10 +556,14 @@ func (c *PubSub) initMsgChan(size int) {
 					}
 				case <-timer.C:
 					internal.Logger.Printf(
-						"redis: %s channel is full for %s (message is dropped)", c, pingTimeout)
+						c.getContext(),
+						"redis: %s channel is full for %s (message is dropped)",
+						c,
+						chanSendTimeout,
+					)
 				}
 			default:
-				internal.Logger.Printf("redis: unknown message type: %T", msg)
+				internal.Logger.Printf(c.getContext(), "redis: unknown message type: %T", msg)
 			}
 		}
 	}()
@@ -535,6 +571,7 @@ func (c *PubSub) initMsgChan(size int) {
 
 // initAllChan must be in sync with initMsgChan.
 func (c *PubSub) initAllChan(size int) {
+	ctx := context.TODO()
 	c.allCh = make(chan interface{}, size)
 	go func() {
 		timer := time.NewTimer(pingTimeout)
@@ -542,14 +579,14 @@ func (c *PubSub) initAllChan(size int) {
 
 		var errCount int
 		for {
-			msg, err := c.Receive()
+			msg, err := c.Receive(ctx)
 			if err != nil {
 				if err == pool.ErrClosed {
 					close(c.allCh)
 					return
 				}
 				if errCount > 0 {
-					time.Sleep(c.retryBackoff(errCount))
+					time.Sleep(100 * time.Millisecond)
 				}
 				errCount++
 				continue
@@ -571,7 +608,7 @@ func (c *PubSub) initAllChan(size int) {
 			case *Message:
 				c.sendMessage(msg, timer)
 			default:
-				internal.Logger.Printf("redis: unknown message type: %T", msg)
+				internal.Logger.Printf(c.getContext(), "redis: unknown message type: %T", msg)
 			}
 		}
 	}()
@@ -586,10 +623,7 @@ func (c *PubSub) sendMessage(msg interface{}, timer *time.Timer) {
 		}
 	case <-timer.C:
 		internal.Logger.Printf(
+			c.getContext(),
 			"redis: %s channel is full for %s (message is dropped)", c, pingTimeout)
 	}
 }
-
-func (c *PubSub) retryBackoff(attempt int) time.Duration {
-	return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
-}
diff --git a/vendor/github.com/go-redis/redis/v7/redis.go b/vendor/github.com/go-redis/redis/v8/redis.go
similarity index 67%
rename from vendor/github.com/go-redis/redis/v7/redis.go
rename to vendor/github.com/go-redis/redis/v8/redis.go
index 3d9dfed7db..712579d4ca 100644
--- a/vendor/github.com/go-redis/redis/v7/redis.go
+++ b/vendor/github.com/go-redis/redis/v8/redis.go
@@ -2,19 +2,22 @@ package redis
 
 import (
 	"context"
+	"errors"
 	"fmt"
-	"log"
+	"sync/atomic"
 	"time"
 
-	"github.com/go-redis/redis/v7/internal"
-	"github.com/go-redis/redis/v7/internal/pool"
-	"github.com/go-redis/redis/v7/internal/proto"
+	"github.com/go-redis/redis/v8/internal"
+	"github.com/go-redis/redis/v8/internal/pool"
+	"github.com/go-redis/redis/v8/internal/proto"
+	"go.opentelemetry.io/otel/label"
+	"go.opentelemetry.io/otel/trace"
 )
 
 // Nil reply returned by Redis when key does not exist.
 const Nil = proto.Nil
 
-func SetLogger(logger *log.Logger) {
+func SetLogger(logger internal.Logging) {
 	internal.Logger = logger
 }
 
@@ -49,92 +52,88 @@ func (hs *hooks) AddHook(hook Hook) {
 func (hs hooks) process(
 	ctx context.Context, cmd Cmder, fn func(context.Context, Cmder) error,
 ) error {
-	ctx, err := hs.beforeProcess(ctx, cmd)
-	if err != nil {
+	if len(hs.hooks) == 0 {
+		err := hs.withContext(ctx, func() error {
+			return fn(ctx, cmd)
+		})
 		cmd.SetErr(err)
 		return err
 	}
 
-	cmdErr := fn(ctx, cmd)
+	var hookIndex int
+	var retErr error
 
-	if err := hs.afterProcess(ctx, cmd); err != nil {
-		cmd.SetErr(err)
-		return err
-	}
-
-	return cmdErr
-}
-
-func (hs hooks) beforeProcess(ctx context.Context, cmd Cmder) (context.Context, error) {
-	for _, h := range hs.hooks {
-		var err error
-		ctx, err = h.BeforeProcess(ctx, cmd)
-		if err != nil {
-			return nil, err
+	for ; hookIndex < len(hs.hooks) && retErr == nil; hookIndex++ {
+		ctx, retErr = hs.hooks[hookIndex].BeforeProcess(ctx, cmd)
+		if retErr != nil {
+			cmd.SetErr(retErr)
 		}
 	}
-	return ctx, nil
-}
 
-func (hs hooks) afterProcess(ctx context.Context, cmd Cmder) error {
-	var firstErr error
-	for _, h := range hs.hooks {
-		err := h.AfterProcess(ctx, cmd)
-		if err != nil && firstErr == nil {
-			firstErr = err
+	if retErr == nil {
+		retErr = hs.withContext(ctx, func() error {
+			return fn(ctx, cmd)
+		})
+		cmd.SetErr(retErr)
+	}
+
+	for hookIndex--; hookIndex >= 0; hookIndex-- {
+		if err := hs.hooks[hookIndex].AfterProcess(ctx, cmd); err != nil {
+			retErr = err
+			cmd.SetErr(retErr)
 		}
 	}
-	return firstErr
+
+	return retErr
 }
 
 func (hs hooks) processPipeline(
 	ctx context.Context, cmds []Cmder, fn func(context.Context, []Cmder) error,
 ) error {
-	ctx, err := hs.beforeProcessPipeline(ctx, cmds)
-	if err != nil {
-		setCmdsErr(cmds, err)
+	if len(hs.hooks) == 0 {
+		err := hs.withContext(ctx, func() error {
+			return fn(ctx, cmds)
+		})
 		return err
 	}
 
-	cmdsErr := fn(ctx, cmds)
+	var hookIndex int
+	var retErr error
 
-	if err := hs.afterProcessPipeline(ctx, cmds); err != nil {
-		setCmdsErr(cmds, err)
-		return err
-	}
-
-	return cmdsErr
-}
-
-func (hs hooks) beforeProcessPipeline(ctx context.Context, cmds []Cmder) (context.Context, error) {
-	for _, h := range hs.hooks {
-		var err error
-		ctx, err = h.BeforeProcessPipeline(ctx, cmds)
-		if err != nil {
-			return nil, err
+	for ; hookIndex < len(hs.hooks) && retErr == nil; hookIndex++ {
+		ctx, retErr = hs.hooks[hookIndex].BeforeProcessPipeline(ctx, cmds)
+		if retErr != nil {
+			setCmdsErr(cmds, retErr)
 		}
 	}
-	return ctx, nil
-}
 
-func (hs hooks) afterProcessPipeline(ctx context.Context, cmds []Cmder) error {
-	var firstErr error
-	for _, h := range hs.hooks {
-		err := h.AfterProcessPipeline(ctx, cmds)
-		if err != nil && firstErr == nil {
-			firstErr = err
+	if retErr == nil {
+		retErr = hs.withContext(ctx, func() error {
+			return fn(ctx, cmds)
+		})
+	}
+
+	for hookIndex--; hookIndex >= 0; hookIndex-- {
+		if err := hs.hooks[hookIndex].AfterProcessPipeline(ctx, cmds); err != nil {
+			retErr = err
+			setCmdsErr(cmds, retErr)
 		}
 	}
-	return firstErr
+
+	return retErr
 }
 
 func (hs hooks) processTxPipeline(
 	ctx context.Context, cmds []Cmder, fn func(context.Context, []Cmder) error,
 ) error {
-	cmds = wrapMultiExec(cmds)
+	cmds = wrapMultiExec(ctx, cmds)
 	return hs.processPipeline(ctx, cmds, fn)
 }
 
+func (hs hooks) withContext(ctx context.Context, fn func() error) error {
+	return fn()
+}
+
 //------------------------------------------------------------------------------
 
 type baseClient struct {
@@ -201,6 +200,7 @@ func (c *baseClient) getConn(ctx context.Context) (*pool.Conn, error) {
 		}
 		return nil, err
 	}
+
 	return cn, nil
 }
 
@@ -210,10 +210,16 @@ func (c *baseClient) _getConn(ctx context.Context) (*pool.Conn, error) {
 		return nil, err
 	}
 
-	err = c.initConn(ctx, cn)
+	if cn.Inited {
+		return cn, nil
+	}
+
+	err = internal.WithSpan(ctx, "redis.init_conn", func(ctx context.Context, span trace.Span) error {
+		return c.initConn(ctx, cn)
+	})
 	if err != nil {
-		c.connPool.Remove(cn, err)
-		if err := internal.Unwrap(err); err != nil {
+		c.connPool.Remove(ctx, cn, err)
+		if err := errors.Unwrap(err); err != nil {
 			return nil, err
 		}
 		return nil, err
@@ -235,25 +241,24 @@ func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
 		return nil
 	}
 
-	connPool := pool.NewSingleConnPool(nil)
-	connPool.SetConn(cn)
+	connPool := pool.NewSingleConnPool(c.connPool, cn)
 	conn := newConn(ctx, c.opt, connPool)
 
-	_, err := conn.Pipelined(func(pipe Pipeliner) error {
+	_, err := conn.Pipelined(ctx, func(pipe Pipeliner) error {
 		if c.opt.Password != "" {
 			if c.opt.Username != "" {
-				pipe.AuthACL(c.opt.Username, c.opt.Password)
+				pipe.AuthACL(ctx, c.opt.Username, c.opt.Password)
 			} else {
-				pipe.Auth(c.opt.Password)
+				pipe.Auth(ctx, c.opt.Password)
 			}
 		}
 
 		if c.opt.DB > 0 {
-			pipe.Select(c.opt.DB)
+			pipe.Select(ctx, c.opt.DB)
 		}
 
 		if c.opt.readOnly {
-			pipe.ReadOnly()
+			pipe.ReadOnly(ctx)
 		}
 
 		return nil
@@ -263,76 +268,107 @@ func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
 	}
 
 	if c.opt.OnConnect != nil {
-		return c.opt.OnConnect(conn)
+		return c.opt.OnConnect(ctx, conn)
 	}
 	return nil
 }
 
-func (c *baseClient) releaseConn(cn *pool.Conn, err error) {
+func (c *baseClient) releaseConn(ctx context.Context, cn *pool.Conn, err error) {
 	if c.opt.Limiter != nil {
 		c.opt.Limiter.ReportResult(err)
 	}
 
 	if isBadConn(err, false) {
-		c.connPool.Remove(cn, err)
+		c.connPool.Remove(ctx, cn, err)
 	} else {
-		c.connPool.Put(cn)
+		c.connPool.Put(ctx, cn)
 	}
 }
 
 func (c *baseClient) withConn(
 	ctx context.Context, fn func(context.Context, *pool.Conn) error,
 ) error {
-	cn, err := c.getConn(ctx)
-	if err != nil {
-		return err
-	}
-	defer func() {
-		c.releaseConn(cn, err)
-	}()
+	return internal.WithSpan(ctx, "redis.with_conn", func(ctx context.Context, span trace.Span) error {
+		cn, err := c.getConn(ctx)
+		if err != nil {
+			return err
+		}
 
-	err = fn(ctx, cn)
-	return err
+		if span.IsRecording() {
+			if remoteAddr := cn.RemoteAddr(); remoteAddr != nil {
+				span.SetAttributes(label.String("net.peer.ip", remoteAddr.String()))
+			}
+		}
+
+		defer func() {
+			c.releaseConn(ctx, cn, err)
+		}()
+
+		done := ctx.Done()
+		if done == nil {
+			err = fn(ctx, cn)
+			return err
+		}
+
+		errc := make(chan error, 1)
+		go func() { errc <- fn(ctx, cn) }()
+
+		select {
+		case <-done:
+			_ = cn.Close()
+			// Wait for the goroutine to finish and send something.
+			<-errc
+
+			err = ctx.Err()
+			return err
+		case err = <-errc:
+			return err
+		}
+	})
 }
 
 func (c *baseClient) process(ctx context.Context, cmd Cmder) error {
-	err := c._process(ctx, cmd)
-	if err != nil {
-		cmd.SetErr(err)
-		return err
-	}
-	return nil
-}
-
-func (c *baseClient) _process(ctx context.Context, cmd Cmder) error {
 	var lastErr error
 	for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
-		if attempt > 0 {
-			if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil {
-				return err
-			}
-		}
+		attempt := attempt
 
-		retryTimeout := true
-		lastErr = c.withConn(ctx, func(ctx context.Context, cn *pool.Conn) error {
-			err := cn.WithWriter(ctx, c.opt.WriteTimeout, func(wr *proto.Writer) error {
-				return writeCmd(wr, cmd)
+		var retry bool
+		err := internal.WithSpan(ctx, "redis.process", func(ctx context.Context, span trace.Span) error {
+			if attempt > 0 {
+				if err := internal.Sleep(ctx, c.retryBackoff(attempt)); err != nil {
+					return err
+				}
+			}
+
+			retryTimeout := uint32(1)
+			err := c.withConn(ctx, func(ctx context.Context, cn *pool.Conn) error {
+				err := cn.WithWriter(ctx, c.opt.WriteTimeout, func(wr *proto.Writer) error {
+					return writeCmd(wr, cmd)
+				})
+				if err != nil {
+					return err
+				}
+
+				err = cn.WithReader(ctx, c.cmdTimeout(cmd), cmd.readReply)
+				if err != nil {
+					if cmd.readTimeout() == nil {
+						atomic.StoreUint32(&retryTimeout, 1)
+					}
+					return err
+				}
+
+				return nil
 			})
-			if err != nil {
-				return err
+			if err == nil {
+				return nil
 			}
-
-			err = cn.WithReader(ctx, c.cmdTimeout(cmd), cmd.readReply)
-			if err != nil {
-				retryTimeout = cmd.readTimeout() == nil
-				return err
-			}
-
-			return nil
+			retry = shouldRetry(err, atomic.LoadUint32(&retryTimeout) == 1)
+			return err
 		})
-		if lastErr == nil || !isRetryableError(lastErr, retryTimeout) {
-			return lastErr
+		if err == nil || !retry {
+			return err
 		}
+		lastErr = err
 	}
 	return lastErr
 }
@@ -411,7 +447,7 @@ func (c *baseClient) _generalProcessPipeline(
 			canRetry, err = p(ctx, cn, cmds)
 			return err
 		})
-		if lastErr == nil || !canRetry || !isRetryableError(lastErr, true) {
+		if lastErr == nil || !canRetry || !shouldRetry(lastErr, true) {
 			return lastErr
 		}
 	}
@@ -437,6 +473,7 @@ func (c *baseClient) pipelineProcessCmds(
 func pipelineReadCmds(rd *proto.Reader, cmds []Cmder) error {
 	for _, cmd := range cmds {
 		err := cmd.readReply(rd)
+		cmd.SetErr(err)
 		if err != nil && !isRedisError(err) {
 			return err
 		}
@@ -469,15 +506,15 @@ func (c *baseClient) txPipelineProcessCmds(
 	return false, err
 }
 
-func wrapMultiExec(cmds []Cmder) []Cmder {
+func wrapMultiExec(ctx context.Context, cmds []Cmder) []Cmder {
 	if len(cmds) == 0 {
 		panic("not reached")
 	}
-	cmds = append(cmds, make([]Cmder, 2)...)
-	copy(cmds[1:], cmds[:len(cmds)-2])
-	cmds[0] = NewStatusCmd("multi")
-	cmds[len(cmds)-1] = NewSliceCmd("exec")
-	return cmds
+	cmdCopy := make([]Cmder, len(cmds)+2)
+	cmdCopy[0] = NewStatusCmd(ctx, "multi")
+	copy(cmdCopy[1:], cmds)
+	cmdCopy[len(cmdCopy)-1] = NewSliceCmd(ctx, "exec")
+	return cmdCopy
 }
 
 func txPipelineReadQueued(rd *proto.Reader, statusCmd *StatusCmd, cmds []Cmder) error {
@@ -565,26 +602,18 @@ func (c *Client) WithContext(ctx context.Context) *Client {
 	return clone
 }
 
-func (c *Client) Conn() *Conn {
-	return newConn(c.ctx, c.opt, pool.NewSingleConnPool(c.connPool))
+func (c *Client) Conn(ctx context.Context) *Conn {
+	return newConn(ctx, c.opt, pool.NewStickyConnPool(c.connPool))
 }
 
 // Do creates a Cmd from the args and processes the cmd.
-func (c *Client) Do(args ...interface{}) *Cmd {
-	return c.DoContext(c.ctx, args...)
-}
-
-func (c *Client) DoContext(ctx context.Context, args ...interface{}) *Cmd {
-	cmd := NewCmd(args...)
-	_ = c.ProcessContext(ctx, cmd)
+func (c *Client) Do(ctx context.Context, args ...interface{}) *Cmd {
+	cmd := NewCmd(ctx, args...)
+	_ = c.Process(ctx, cmd)
 	return cmd
 }
 
-func (c *Client) Process(cmd Cmder) error {
-	return c.ProcessContext(c.ctx, cmd)
-}
-
-func (c *Client) ProcessContext(ctx context.Context, cmd Cmder) error {
+func (c *Client) Process(ctx context.Context, cmd Cmder) error {
 	return c.hooks.process(ctx, cmd, c.baseClient.process)
 }
 
@@ -609,8 +638,8 @@ func (c *Client) PoolStats() *PoolStats {
 	return (*PoolStats)(stats)
 }
 
-func (c *Client) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
-	return c.Pipeline().Pipelined(fn)
+func (c *Client) Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) {
+	return c.Pipeline().Pipelined(ctx, fn)
 }
 
 func (c *Client) Pipeline() Pipeliner {
@@ -622,8 +651,8 @@ func (c *Client) Pipeline() Pipeliner {
 	return &pipe
 }
 
-func (c *Client) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
-	return c.TxPipeline().Pipelined(fn)
+func (c *Client) TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) {
+	return c.TxPipeline().Pipelined(ctx, fn)
 }
 
 // TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.
@@ -640,8 +669,8 @@ func (c *Client) pubSub() *PubSub {
 	pubsub := &PubSub{
 		opt: c.opt,
 
-		newConn: func(channels []string) (*pool.Conn, error) {
-			return c.newConn(context.TODO())
+		newConn: func(ctx context.Context, channels []string) (*pool.Conn, error) {
+			return c.newConn(ctx)
 		},
 		closeConn: c.connPool.CloseConn,
 	}
@@ -675,20 +704,20 @@ func (c *Client) pubSub() *PubSub {
 //    }
 //
 //    ch := sub.Channel()
-func (c *Client) Subscribe(channels ...string) *PubSub {
+func (c *Client) Subscribe(ctx context.Context, channels ...string) *PubSub {
 	pubsub := c.pubSub()
 	if len(channels) > 0 {
-		_ = pubsub.Subscribe(channels...)
+		_ = pubsub.Subscribe(ctx, 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 {
+func (c *Client) PSubscribe(ctx context.Context, channels ...string) *PubSub {
 	pubsub := c.pubSub()
 	if len(channels) > 0 {
-		_ = pubsub.PSubscribe(channels...)
+		_ = pubsub.PSubscribe(ctx, channels...)
 	}
 	return pubsub
 }
@@ -699,6 +728,7 @@ type conn struct {
 	baseClient
 	cmdable
 	statefulCmdable
+	hooks // TODO: inherit hooks
 }
 
 // Conn is like Client, but its pool contains single connection.
@@ -722,16 +752,20 @@ func newConn(ctx context.Context, opt *Options, connPool pool.Pooler) *Conn {
 	return &c
 }
 
-func (c *Conn) Process(cmd Cmder) error {
-	return c.ProcessContext(c.ctx, cmd)
+func (c *Conn) Process(ctx context.Context, cmd Cmder) error {
+	return c.hooks.process(ctx, cmd, c.baseClient.process)
 }
 
-func (c *Conn) ProcessContext(ctx context.Context, cmd Cmder) error {
-	return c.baseClient.process(ctx, cmd)
+func (c *Conn) processPipeline(ctx context.Context, cmds []Cmder) error {
+	return c.hooks.processPipeline(ctx, cmds, c.baseClient.processPipeline)
 }
 
-func (c *Conn) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
-	return c.Pipeline().Pipelined(fn)
+func (c *Conn) processTxPipeline(ctx context.Context, cmds []Cmder) error {
+	return c.hooks.processTxPipeline(ctx, cmds, c.baseClient.processTxPipeline)
+}
+
+func (c *Conn) Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) {
+	return c.Pipeline().Pipelined(ctx, fn)
 }
 
 func (c *Conn) Pipeline() Pipeliner {
@@ -743,8 +777,8 @@ func (c *Conn) Pipeline() Pipeliner {
 	return &pipe
 }
 
-func (c *Conn) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
-	return c.TxPipeline().Pipelined(fn)
+func (c *Conn) TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) {
+	return c.TxPipeline().Pipelined(ctx, fn)
 }
 
 // TxPipeline acts like Pipeline, but wraps queued commands with MULTI/EXEC.
diff --git a/vendor/github.com/go-redis/redis/v7/result.go b/vendor/github.com/go-redis/redis/v8/result.go
similarity index 90%
rename from vendor/github.com/go-redis/redis/v7/result.go
rename to vendor/github.com/go-redis/redis/v8/result.go
index 5bec26ca95..24cfd49940 100644
--- a/vendor/github.com/go-redis/redis/v7/result.go
+++ b/vendor/github.com/go-redis/redis/v8/result.go
@@ -2,7 +2,7 @@ package redis
 
 import "time"
 
-// NewCmdResult returns a Cmd initialised with val and err for testing
+// NewCmdResult returns a Cmd initialised with val and err for testing.
 func NewCmdResult(val interface{}, err error) *Cmd {
 	var cmd Cmd
 	cmd.val = val
@@ -10,7 +10,7 @@ func NewCmdResult(val interface{}, err error) *Cmd {
 	return &cmd
 }
 
-// NewSliceResult returns a SliceCmd initialised with val and err for testing
+// NewSliceResult returns a SliceCmd initialised with val and err for testing.
 func NewSliceResult(val []interface{}, err error) *SliceCmd {
 	var cmd SliceCmd
 	cmd.val = val
@@ -18,7 +18,7 @@ func NewSliceResult(val []interface{}, err error) *SliceCmd {
 	return &cmd
 }
 
-// NewStatusResult returns a StatusCmd initialised with val and err for testing
+// NewStatusResult returns a StatusCmd initialised with val and err for testing.
 func NewStatusResult(val string, err error) *StatusCmd {
 	var cmd StatusCmd
 	cmd.val = val
@@ -26,7 +26,7 @@ func NewStatusResult(val string, err error) *StatusCmd {
 	return &cmd
 }
 
-// NewIntResult returns an IntCmd initialised with val and err for testing
+// NewIntResult returns an IntCmd initialised with val and err for testing.
 func NewIntResult(val int64, err error) *IntCmd {
 	var cmd IntCmd
 	cmd.val = val
@@ -34,7 +34,7 @@ func NewIntResult(val int64, err error) *IntCmd {
 	return &cmd
 }
 
-// NewDurationResult returns a DurationCmd initialised with val and err for testing
+// NewDurationResult returns a DurationCmd initialised with val and err for testing.
 func NewDurationResult(val time.Duration, err error) *DurationCmd {
 	var cmd DurationCmd
 	cmd.val = val
@@ -42,7 +42,7 @@ func NewDurationResult(val time.Duration, err error) *DurationCmd {
 	return &cmd
 }
 
-// NewBoolResult returns a BoolCmd initialised with val and err for testing
+// NewBoolResult returns a BoolCmd initialised with val and err for testing.
 func NewBoolResult(val bool, err error) *BoolCmd {
 	var cmd BoolCmd
 	cmd.val = val
@@ -50,7 +50,7 @@ func NewBoolResult(val bool, err error) *BoolCmd {
 	return &cmd
 }
 
-// NewStringResult returns a StringCmd initialised with val and err for testing
+// NewStringResult returns a StringCmd initialised with val and err for testing.
 func NewStringResult(val string, err error) *StringCmd {
 	var cmd StringCmd
 	cmd.val = val
@@ -58,7 +58,7 @@ func NewStringResult(val string, err error) *StringCmd {
 	return &cmd
 }
 
-// NewFloatResult returns a FloatCmd initialised with val and err for testing
+// NewFloatResult returns a FloatCmd initialised with val and err for testing.
 func NewFloatResult(val float64, err error) *FloatCmd {
 	var cmd FloatCmd
 	cmd.val = val
@@ -66,7 +66,7 @@ func NewFloatResult(val float64, err error) *FloatCmd {
 	return &cmd
 }
 
-// NewStringSliceResult returns a StringSliceCmd initialised with val and err for testing
+// NewStringSliceResult returns a StringSliceCmd initialised with val and err for testing.
 func NewStringSliceResult(val []string, err error) *StringSliceCmd {
 	var cmd StringSliceCmd
 	cmd.val = val
@@ -74,7 +74,7 @@ func NewStringSliceResult(val []string, err error) *StringSliceCmd {
 	return &cmd
 }
 
-// NewBoolSliceResult returns a BoolSliceCmd initialised with val and err for testing
+// NewBoolSliceResult returns a BoolSliceCmd initialised with val and err for testing.
 func NewBoolSliceResult(val []bool, err error) *BoolSliceCmd {
 	var cmd BoolSliceCmd
 	cmd.val = val
@@ -82,7 +82,7 @@ func NewBoolSliceResult(val []bool, err error) *BoolSliceCmd {
 	return &cmd
 }
 
-// NewStringStringMapResult returns a StringStringMapCmd initialised with val and err for testing
+// NewStringStringMapResult returns a StringStringMapCmd initialised with val and err for testing.
 func NewStringStringMapResult(val map[string]string, err error) *StringStringMapCmd {
 	var cmd StringStringMapCmd
 	cmd.val = val
@@ -90,7 +90,7 @@ func NewStringStringMapResult(val map[string]string, err error) *StringStringMap
 	return &cmd
 }
 
-// NewStringIntMapCmdResult returns a StringIntMapCmd initialised with val and err for testing
+// NewStringIntMapCmdResult returns a StringIntMapCmd initialised with val and err for testing.
 func NewStringIntMapCmdResult(val map[string]int64, err error) *StringIntMapCmd {
 	var cmd StringIntMapCmd
 	cmd.val = val
@@ -98,7 +98,7 @@ func NewStringIntMapCmdResult(val map[string]int64, err error) *StringIntMapCmd
 	return &cmd
 }
 
-// NewTimeCmdResult returns a TimeCmd initialised with val and err for testing
+// NewTimeCmdResult returns a TimeCmd initialised with val and err for testing.
 func NewTimeCmdResult(val time.Time, err error) *TimeCmd {
 	var cmd TimeCmd
 	cmd.val = val
@@ -106,7 +106,7 @@ func NewTimeCmdResult(val time.Time, err error) *TimeCmd {
 	return &cmd
 }
 
-// NewZSliceCmdResult returns a ZSliceCmd initialised with val and err for testing
+// NewZSliceCmdResult returns a ZSliceCmd initialised with val and err for testing.
 func NewZSliceCmdResult(val []Z, err error) *ZSliceCmd {
 	var cmd ZSliceCmd
 	cmd.val = val
@@ -114,7 +114,7 @@ func NewZSliceCmdResult(val []Z, err error) *ZSliceCmd {
 	return &cmd
 }
 
-// NewZWithKeyCmdResult returns a NewZWithKeyCmd initialised with val and err for testing
+// NewZWithKeyCmdResult returns a NewZWithKeyCmd initialised with val and err for testing.
 func NewZWithKeyCmdResult(val *ZWithKey, err error) *ZWithKeyCmd {
 	var cmd ZWithKeyCmd
 	cmd.val = val
@@ -122,7 +122,7 @@ func NewZWithKeyCmdResult(val *ZWithKey, err error) *ZWithKeyCmd {
 	return &cmd
 }
 
-// NewScanCmdResult returns a ScanCmd initialised with val and err for testing
+// NewScanCmdResult returns a ScanCmd initialised with val and err for testing.
 func NewScanCmdResult(keys []string, cursor uint64, err error) *ScanCmd {
 	var cmd ScanCmd
 	cmd.page = keys
@@ -131,7 +131,7 @@ func NewScanCmdResult(keys []string, cursor uint64, err error) *ScanCmd {
 	return &cmd
 }
 
-// NewClusterSlotsCmdResult returns a ClusterSlotsCmd initialised with val and err for testing
+// NewClusterSlotsCmdResult returns a ClusterSlotsCmd initialised with val and err for testing.
 func NewClusterSlotsCmdResult(val []ClusterSlot, err error) *ClusterSlotsCmd {
 	var cmd ClusterSlotsCmd
 	cmd.val = val
@@ -139,7 +139,7 @@ func NewClusterSlotsCmdResult(val []ClusterSlot, err error) *ClusterSlotsCmd {
 	return &cmd
 }
 
-// NewGeoLocationCmdResult returns a GeoLocationCmd initialised with val and err for testing
+// NewGeoLocationCmdResult returns a GeoLocationCmd initialised with val and err for testing.
 func NewGeoLocationCmdResult(val []GeoLocation, err error) *GeoLocationCmd {
 	var cmd GeoLocationCmd
 	cmd.locations = val
@@ -147,7 +147,7 @@ func NewGeoLocationCmdResult(val []GeoLocation, err error) *GeoLocationCmd {
 	return &cmd
 }
 
-// NewGeoPosCmdResult returns a GeoPosCmd initialised with val and err for testing
+// NewGeoPosCmdResult returns a GeoPosCmd initialised with val and err for testing.
 func NewGeoPosCmdResult(val []*GeoPos, err error) *GeoPosCmd {
 	var cmd GeoPosCmd
 	cmd.val = val
@@ -155,7 +155,7 @@ func NewGeoPosCmdResult(val []*GeoPos, err error) *GeoPosCmd {
 	return &cmd
 }
 
-// NewCommandsInfoCmdResult returns a CommandsInfoCmd initialised with val and err for testing
+// NewCommandsInfoCmdResult returns a CommandsInfoCmd initialised with val and err for testing.
 func NewCommandsInfoCmdResult(val map[string]*CommandInfo, err error) *CommandsInfoCmd {
 	var cmd CommandsInfoCmd
 	cmd.val = val
@@ -163,7 +163,7 @@ func NewCommandsInfoCmdResult(val map[string]*CommandInfo, err error) *CommandsI
 	return &cmd
 }
 
-// NewXMessageSliceCmdResult returns a XMessageSliceCmd initialised with val and err for testing
+// NewXMessageSliceCmdResult returns a XMessageSliceCmd initialised with val and err for testing.
 func NewXMessageSliceCmdResult(val []XMessage, err error) *XMessageSliceCmd {
 	var cmd XMessageSliceCmd
 	cmd.val = val
@@ -171,7 +171,7 @@ func NewXMessageSliceCmdResult(val []XMessage, err error) *XMessageSliceCmd {
 	return &cmd
 }
 
-// NewXStreamSliceCmdResult returns a XStreamSliceCmd initialised with val and err for testing
+// NewXStreamSliceCmdResult returns a XStreamSliceCmd initialised with val and err for testing.
 func NewXStreamSliceCmdResult(val []XStream, err error) *XStreamSliceCmd {
 	var cmd XStreamSliceCmd
 	cmd.val = val
diff --git a/vendor/github.com/go-redis/redis/v7/ring.go b/vendor/github.com/go-redis/redis/v8/ring.go
similarity index 68%
rename from vendor/github.com/go-redis/redis/v7/ring.go
rename to vendor/github.com/go-redis/redis/v8/ring.go
index 44fc623f8a..34d05f35ae 100644
--- a/vendor/github.com/go-redis/redis/v7/ring.go
+++ b/vendor/github.com/go-redis/redis/v8/ring.go
@@ -2,72 +2,73 @@ package redis
 
 import (
 	"context"
+	"crypto/tls"
 	"errors"
 	"fmt"
-	"math/rand"
+	"net"
 	"strconv"
 	"sync"
 	"sync/atomic"
 	"time"
 
-	"github.com/go-redis/redis/v7/internal"
-	"github.com/go-redis/redis/v7/internal/consistenthash"
-	"github.com/go-redis/redis/v7/internal/hashtag"
-	"github.com/go-redis/redis/v7/internal/pool"
+	"github.com/cespare/xxhash/v2"
+	"github.com/dgryski/go-rendezvous"
+	"github.com/go-redis/redis/v8/internal"
+	"github.com/go-redis/redis/v8/internal/hashtag"
+	"github.com/go-redis/redis/v8/internal/pool"
+	"github.com/go-redis/redis/v8/internal/rand"
 )
 
-// Hash is type of hash function used in consistent hash.
-type Hash consistenthash.Hash
-
 var errRingShardsDown = errors.New("redis: all ring shards are down")
 
+//------------------------------------------------------------------------------
+
+type ConsistentHash interface {
+	Get(string) string
+}
+
+type rendezvousWrapper struct {
+	*rendezvous.Rendezvous
+}
+
+func (w rendezvousWrapper) Get(key string) string {
+	return w.Lookup(key)
+}
+
+func newRendezvous(shards []string) ConsistentHash {
+	return rendezvousWrapper{rendezvous.New(shards, xxhash.Sum64String)}
+}
+
+//------------------------------------------------------------------------------
+
 // RingOptions are used to configure a ring client and should be
 // passed to NewRing.
 type RingOptions struct {
 	// Map of name => host:port addresses of ring shards.
 	Addrs map[string]string
 
-	// Map of name => password of ring shards, to allow different shards to have
-	// different passwords. It will be ignored if the Password field is set.
-	Passwords map[string]string
+	// NewClient creates a shard client with provided name and options.
+	NewClient func(name string, opt *Options) *Client
 
 	// Frequency of PING commands sent to check shards availability.
 	// Shard is considered down after 3 subsequent failed checks.
 	HeartbeatFrequency time.Duration
 
-	// Hash function used in consistent hash.
-	// Default is crc32.ChecksumIEEE.
-	Hash Hash
-
-	// Number of replicas in consistent hash.
-	// Default is 100 replicas.
+	// NewConsistentHash returns a consistent hash that is used
+	// to distribute keys across the shards.
 	//
-	// Higher number of replicas will provide less deviation, that is keys will be
-	// distributed to nodes more evenly.
-	//
-	// Following is deviation for common nreplicas:
-	//  --------------------------------------------------------
-	//  | nreplicas | standard error | 99% confidence interval |
-	//  |     10    |     0.3152     |      (0.37, 1.98)       |
-	//  |    100    |     0.0997     |      (0.76, 1.28)       |
-	//  |   1000    |     0.0316     |      (0.92, 1.09)       |
-	//  --------------------------------------------------------
-	//
-	//  See https://arxiv.org/abs/1406.2294 for reference
-	HashReplicas int
-
-	// NewClient creates a shard client with provided name and options.
-	NewClient func(name string, opt *Options) *Client
-
-	// Optional hook that is called when a new shard is created.
-	OnNewShard func(*Client)
+	// See https://medium.com/@dgryski/consistent-hashing-algorithmic-tradeoffs-ef6b8e2fcae8
+	// for consistent hashing algorithmic tradeoffs.
+	NewConsistentHash func(shards []string) ConsistentHash
 
 	// Following options are copied from Options struct.
 
-	OnConnect func(*Conn) error
+	Dialer    func(ctx context.Context, network, addr string) (net.Conn, error)
+	OnConnect func(ctx context.Context, cn *Conn) error
 
-	DB       int
+	Username string
 	Password string
+	DB       int
 
 	MaxRetries      int
 	MinRetryBackoff time.Duration
@@ -83,17 +84,31 @@ type RingOptions struct {
 	PoolTimeout        time.Duration
 	IdleTimeout        time.Duration
 	IdleCheckFrequency time.Duration
+
+	TLSConfig *tls.Config
+	Limiter   Limiter
 }
 
 func (opt *RingOptions) init() {
+	if opt.NewClient == nil {
+		opt.NewClient = func(name string, opt *Options) *Client {
+			return NewClient(opt)
+		}
+	}
+
 	if opt.HeartbeatFrequency == 0 {
 		opt.HeartbeatFrequency = 500 * time.Millisecond
 	}
 
-	if opt.HashReplicas == 0 {
-		opt.HashReplicas = 100
+	if opt.NewConsistentHash == nil {
+		opt.NewConsistentHash = newRendezvous
 	}
 
+	if opt.MaxRetries == -1 {
+		opt.MaxRetries = 0
+	} else if opt.MaxRetries == 0 {
+		opt.MaxRetries = 3
+	}
 	switch opt.MinRetryBackoff {
 	case -1:
 		opt.MinRetryBackoff = 0
@@ -108,12 +123,16 @@ func (opt *RingOptions) init() {
 	}
 }
 
-func (opt *RingOptions) clientOptions(shard string) *Options {
+func (opt *RingOptions) clientOptions() *Options {
 	return &Options{
+		Dialer:    opt.Dialer,
 		OnConnect: opt.OnConnect,
 
+		Username: opt.Username,
+		Password: opt.Password,
 		DB:       opt.DB,
-		Password: opt.getPassword(shard),
+
+		MaxRetries: -1,
 
 		DialTimeout:  opt.DialTimeout,
 		ReadTimeout:  opt.ReadTimeout,
@@ -125,14 +144,10 @@ func (opt *RingOptions) clientOptions(shard string) *Options {
 		PoolTimeout:        opt.PoolTimeout,
 		IdleTimeout:        opt.IdleTimeout,
 		IdleCheckFrequency: opt.IdleCheckFrequency,
-	}
-}
 
-func (opt *RingOptions) getPassword(shard string) string {
-	if opt.Password == "" {
-		return opt.Passwords[shard]
+		TLSConfig: opt.TLSConfig,
+		Limiter:   opt.Limiter,
 	}
-	return opt.Password
 }
 
 //------------------------------------------------------------------------------
@@ -142,6 +157,15 @@ type ringShard struct {
 	down   int32
 }
 
+func newRingShard(opt *RingOptions, name, addr string) *ringShard {
+	clopt := opt.clientOptions()
+	clopt.Addr = addr
+
+	return &ringShard{
+		Client: opt.NewClient(name, clopt),
+	}
+}
+
 func (shard *ringShard) String() string {
 	var state string
 	if shard.IsUp() {
@@ -182,41 +206,59 @@ func (shard *ringShard) Vote(up bool) bool {
 type ringShards struct {
 	opt *RingOptions
 
-	mu     sync.RWMutex
-	hash   *consistenthash.Map
-	shards map[string]*ringShard // read only
-	list   []*ringShard          // read only
-	len    int
-	closed bool
+	mu       sync.RWMutex
+	hash     ConsistentHash
+	shards   map[string]*ringShard // read only
+	list     []*ringShard          // read only
+	numShard int
+	closed   bool
 }
 
 func newRingShards(opt *RingOptions) *ringShards {
-	return &ringShards{
+	shards := make(map[string]*ringShard, len(opt.Addrs))
+	list := make([]*ringShard, 0, len(shards))
+
+	for name, addr := range opt.Addrs {
+		shard := newRingShard(opt, name, addr)
+		shards[name] = shard
+
+		list = append(list, shard)
+	}
+
+	c := &ringShards{
 		opt: opt,
 
-		hash:   newConsistentHash(opt),
-		shards: make(map[string]*ringShard),
+		shards: shards,
+		list:   list,
 	}
-}
+	c.rebalance()
 
-func (c *ringShards) Add(name string, cl *Client) {
-	shard := &ringShard{Client: cl}
-	c.hash.Add(name)
-	c.shards[name] = shard
-	c.list = append(c.list, shard)
+	return c
 }
 
 func (c *ringShards) List() []*ringShard {
+	var list []*ringShard
+
 	c.mu.RLock()
-	list := c.list
+	if !c.closed {
+		list = c.list
+	}
 	c.mu.RUnlock()
+
 	return list
 }
 
 func (c *ringShards) Hash(key string) string {
+	key = hashtag.Key(key)
+
+	var hash string
+
 	c.mu.RLock()
-	hash := c.hash.Get(key)
+	if c.numShard > 0 {
+		hash = c.hash.Get(key)
+	}
 	c.mu.RUnlock()
+
 	return hash
 }
 
@@ -230,6 +272,11 @@ func (c *ringShards) GetByKey(key string) (*ringShard, error) {
 		return nil, pool.ErrClosed
 	}
 
+	if c.numShard == 0 {
+		c.mu.RUnlock()
+		return nil, errRingShardsDown
+	}
+
 	hash := c.hash.Get(key)
 	if hash == "" {
 		c.mu.RUnlock()
@@ -242,13 +289,13 @@ func (c *ringShards) GetByKey(key string) (*ringShard, error) {
 	return shard, nil
 }
 
-func (c *ringShards) GetByHash(name string) (*ringShard, error) {
-	if name == "" {
+func (c *ringShards) GetByName(shardName string) (*ringShard, error) {
+	if shardName == "" {
 		return c.Random()
 	}
 
 	c.mu.RLock()
-	shard := c.shards[name]
+	shard := c.shards[shardName]
 	c.mu.RUnlock()
 	return shard, nil
 }
@@ -261,23 +308,16 @@ func (c *ringShards) Random() (*ringShard, error) {
 func (c *ringShards) Heartbeat(frequency time.Duration) {
 	ticker := time.NewTicker(frequency)
 	defer ticker.Stop()
+
+	ctx := context.Background()
 	for range ticker.C {
 		var rebalance bool
 
-		c.mu.RLock()
-
-		if c.closed {
-			c.mu.RUnlock()
-			break
-		}
-
-		shards := c.list
-		c.mu.RUnlock()
-
-		for _, shard := range shards {
-			err := shard.Client.Ping().Err()
-			if shard.Vote(err == nil || err == pool.ErrPoolTimeout) {
-				internal.Logger.Printf("ring shard state changed: %s", shard)
+		for _, shard := range c.List() {
+			err := shard.Client.Ping(ctx).Err()
+			isUp := err == nil || err == pool.ErrPoolTimeout
+			if shard.Vote(isUp) {
+				internal.Logger.Printf(context.Background(), "ring shard state changed: %s", shard)
 				rebalance = true
 			}
 		}
@@ -294,24 +334,25 @@ func (c *ringShards) rebalance() {
 	shards := c.shards
 	c.mu.RUnlock()
 
-	hash := newConsistentHash(c.opt)
-	var shardsNum int
+	liveShards := make([]string, 0, len(shards))
+
 	for name, shard := range shards {
 		if shard.IsUp() {
-			hash.Add(name)
-			shardsNum++
+			liveShards = append(liveShards, name)
 		}
 	}
 
+	hash := c.opt.NewConsistentHash(liveShards)
+
 	c.mu.Lock()
 	c.hash = hash
-	c.len = shardsNum
+	c.numShard = len(liveShards)
 	c.mu.Unlock()
 }
 
 func (c *ringShards) Len() int {
 	c.mu.RLock()
-	l := c.len
+	l := c.numShard
 	c.mu.RUnlock()
 	return l
 }
@@ -377,34 +418,15 @@ func NewRing(opt *RingOptions) *Ring {
 		},
 		ctx: context.Background(),
 	}
+
 	ring.cmdsInfoCache = newCmdsInfoCache(ring.cmdsInfo)
 	ring.cmdable = ring.Process
 
-	for name, addr := range opt.Addrs {
-		shard := newRingShard(opt, name, addr)
-		ring.shards.Add(name, shard)
-	}
-
 	go ring.shards.Heartbeat(opt.HeartbeatFrequency)
 
 	return &ring
 }
 
-func newRingShard(opt *RingOptions, name, addr string) *Client {
-	clopt := opt.clientOptions(name)
-	clopt.Addr = addr
-	var shard *Client
-	if opt.NewClient != nil {
-		shard = opt.NewClient(name, clopt)
-	} else {
-		shard = NewClient(clopt)
-	}
-	if opt.OnNewShard != nil {
-		opt.OnNewShard(shard)
-	}
-	return shard
-}
-
 func (c *Ring) Context() context.Context {
 	return c.ctx
 }
@@ -421,21 +443,13 @@ func (c *Ring) WithContext(ctx context.Context) *Ring {
 }
 
 // Do creates a Cmd from the args and processes the cmd.
-func (c *Ring) Do(args ...interface{}) *Cmd {
-	return c.DoContext(c.ctx, args...)
-}
-
-func (c *Ring) DoContext(ctx context.Context, args ...interface{}) *Cmd {
-	cmd := NewCmd(args...)
-	_ = c.ProcessContext(ctx, cmd)
+func (c *Ring) Do(ctx context.Context, args ...interface{}) *Cmd {
+	cmd := NewCmd(ctx, args...)
+	_ = c.Process(ctx, cmd)
 	return cmd
 }
 
-func (c *Ring) Process(cmd Cmder) error {
-	return c.ProcessContext(c.ctx, cmd)
-}
-
-func (c *Ring) ProcessContext(ctx context.Context, cmd Cmder) error {
+func (c *Ring) Process(ctx context.Context, cmd Cmder) error {
 	return c.hooks.process(ctx, cmd, c.process)
 }
 
@@ -469,36 +483,39 @@ func (c *Ring) Len() int {
 }
 
 // Subscribe subscribes the client to the specified channels.
-func (c *Ring) Subscribe(channels ...string) *PubSub {
+func (c *Ring) Subscribe(ctx context.Context, channels ...string) *PubSub {
 	if len(channels) == 0 {
 		panic("at least one channel is required")
 	}
 
 	shard, err := c.shards.GetByKey(channels[0])
 	if err != nil {
-		//TODO: return PubSub with sticky error
+		// TODO: return PubSub with sticky error
 		panic(err)
 	}
-	return shard.Client.Subscribe(channels...)
+	return shard.Client.Subscribe(ctx, channels...)
 }
 
 // PSubscribe subscribes the client to the given patterns.
-func (c *Ring) PSubscribe(channels ...string) *PubSub {
+func (c *Ring) PSubscribe(ctx context.Context, channels ...string) *PubSub {
 	if len(channels) == 0 {
 		panic("at least one channel is required")
 	}
 
 	shard, err := c.shards.GetByKey(channels[0])
 	if err != nil {
-		//TODO: return PubSub with sticky error
+		// TODO: return PubSub with sticky error
 		panic(err)
 	}
-	return shard.Client.PSubscribe(channels...)
+	return shard.Client.PSubscribe(ctx, channels...)
 }
 
 // ForEachShard concurrently calls the fn on each live shard in the ring.
 // It returns the first error if any.
-func (c *Ring) ForEachShard(fn func(client *Client) error) error {
+func (c *Ring) ForEachShard(
+	ctx context.Context,
+	fn func(ctx context.Context, client *Client) error,
+) error {
 	shards := c.shards.List()
 	var wg sync.WaitGroup
 	errCh := make(chan error, 1)
@@ -510,7 +527,7 @@ func (c *Ring) ForEachShard(fn func(client *Client) error) error {
 		wg.Add(1)
 		go func(shard *ringShard) {
 			defer wg.Done()
-			err := fn(shard.Client)
+			err := fn(ctx, shard.Client)
 			if err != nil {
 				select {
 				case errCh <- err:
@@ -529,11 +546,11 @@ func (c *Ring) ForEachShard(fn func(client *Client) error) error {
 	}
 }
 
-func (c *Ring) cmdsInfo() (map[string]*CommandInfo, error) {
+func (c *Ring) cmdsInfo(ctx context.Context) (map[string]*CommandInfo, error) {
 	shards := c.shards.List()
-	firstErr := errRingShardsDown
+	var firstErr error
 	for _, shard := range shards {
-		cmdsInfo, err := shard.Client.Command().Result()
+		cmdsInfo, err := shard.Client.Command(ctx).Result()
 		if err == nil {
 			return cmdsInfo, nil
 		}
@@ -541,23 +558,26 @@ func (c *Ring) cmdsInfo() (map[string]*CommandInfo, error) {
 			firstErr = err
 		}
 	}
+	if firstErr == nil {
+		return nil, errRingShardsDown
+	}
 	return nil, firstErr
 }
 
-func (c *Ring) cmdInfo(name string) *CommandInfo {
-	cmdsInfo, err := c.cmdsInfoCache.Get()
+func (c *Ring) cmdInfo(ctx context.Context, name string) *CommandInfo {
+	cmdsInfo, err := c.cmdsInfoCache.Get(ctx)
 	if err != nil {
 		return nil
 	}
 	info := cmdsInfo[name]
 	if info == nil {
-		internal.Logger.Printf("info for cmd=%s not found", name)
+		internal.Logger.Printf(c.Context(), "info for cmd=%s not found", name)
 	}
 	return info
 }
 
-func (c *Ring) cmdShard(cmd Cmder) (*ringShard, error) {
-	cmdInfo := c.cmdInfo(cmd.Name())
+func (c *Ring) cmdShard(ctx context.Context, cmd Cmder) (*ringShard, error) {
+	cmdInfo := c.cmdInfo(ctx, cmd.Name())
 	pos := cmdFirstKeyPos(cmd, cmdInfo)
 	if pos == 0 {
 		return c.shards.Random()
@@ -567,15 +587,6 @@ func (c *Ring) cmdShard(cmd Cmder) (*ringShard, error) {
 }
 
 func (c *Ring) process(ctx context.Context, cmd Cmder) error {
-	err := c._process(ctx, cmd)
-	if err != nil {
-		cmd.SetErr(err)
-		return err
-	}
-	return nil
-}
-
-func (c *Ring) _process(ctx context.Context, cmd Cmder) error {
 	var lastErr error
 	for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
 		if attempt > 0 {
@@ -584,21 +595,21 @@ func (c *Ring) _process(ctx context.Context, cmd Cmder) error {
 			}
 		}
 
-		shard, err := c.cmdShard(cmd)
+		shard, err := c.cmdShard(ctx, cmd)
 		if err != nil {
 			return err
 		}
 
-		lastErr = shard.Client.ProcessContext(ctx, cmd)
-		if lastErr == nil || !isRetryableError(lastErr, cmd.readTimeout() == nil) {
+		lastErr = shard.Client.Process(ctx, cmd)
+		if lastErr == nil || !shouldRetry(lastErr, cmd.readTimeout() == nil) {
 			return lastErr
 		}
 	}
 	return lastErr
 }
 
-func (c *Ring) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
-	return c.Pipeline().Pipelined(fn)
+func (c *Ring) Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) {
+	return c.Pipeline().Pipelined(ctx, fn)
 }
 
 func (c *Ring) Pipeline() Pipeliner {
@@ -616,8 +627,8 @@ func (c *Ring) processPipeline(ctx context.Context, cmds []Cmder) error {
 	})
 }
 
-func (c *Ring) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
-	return c.TxPipeline().Pipelined(fn)
+func (c *Ring) TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) {
+	return c.TxPipeline().Pipelined(ctx, fn)
 }
 
 func (c *Ring) TxPipeline() Pipeliner {
@@ -640,10 +651,10 @@ func (c *Ring) generalProcessPipeline(
 ) error {
 	cmdsMap := make(map[string][]Cmder)
 	for _, cmd := range cmds {
-		cmdInfo := c.cmdInfo(cmd.Name())
+		cmdInfo := c.cmdInfo(ctx, cmd.Name())
 		hash := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo))
 		if hash != "" {
-			hash = c.shards.Hash(hashtag.Key(hash))
+			hash = c.shards.Hash(hash)
 		}
 		cmdsMap[hash] = append(cmdsMap[hash], cmd)
 	}
@@ -665,30 +676,20 @@ func (c *Ring) generalProcessPipeline(
 func (c *Ring) processShardPipeline(
 	ctx context.Context, hash string, cmds []Cmder, tx bool,
 ) error {
-	//TODO: retry?
-	shard, err := c.shards.GetByHash(hash)
+	// TODO: retry?
+	shard, err := c.shards.GetByName(hash)
 	if err != nil {
 		setCmdsErr(cmds, err)
 		return err
 	}
 
 	if tx {
-		err = shard.Client.processTxPipeline(ctx, cmds)
-	} else {
-		err = shard.Client.processPipeline(ctx, cmds)
+		return shard.Client.processTxPipeline(ctx, cmds)
 	}
-	return err
+	return shard.Client.processPipeline(ctx, cmds)
 }
 
-// Close closes the ring client, releasing any open resources.
-//
-// It is rare to Close a Ring, as the Ring is meant to be long-lived
-// and shared between many goroutines.
-func (c *Ring) Close() error {
-	return c.shards.Close()
-}
-
-func (c *Ring) Watch(fn func(*Tx) error, keys ...string) error {
+func (c *Ring) Watch(ctx context.Context, fn func(*Tx) error, keys ...string) error {
 	if len(keys) == 0 {
 		return fmt.Errorf("redis: Watch requires at least one key")
 	}
@@ -718,9 +719,13 @@ func (c *Ring) Watch(fn func(*Tx) error, keys ...string) error {
 		}
 	}
 
-	return shards[0].Client.Watch(fn, keys...)
+	return shards[0].Client.Watch(ctx, fn, keys...)
 }
 
-func newConsistentHash(opt *RingOptions) *consistenthash.Map {
-	return consistenthash.New(opt.HashReplicas, consistenthash.Hash(opt.Hash))
+// Close closes the ring client, releasing any open resources.
+//
+// It is rare to Close a Ring, as the Ring is meant to be long-lived
+// and shared between many goroutines.
+func (c *Ring) Close() error {
+	return c.shards.Close()
 }
diff --git a/vendor/github.com/go-redis/redis/v8/script.go b/vendor/github.com/go-redis/redis/v8/script.go
new file mode 100644
index 0000000000..5cab18d617
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/script.go
@@ -0,0 +1,65 @@
+package redis
+
+import (
+	"context"
+	"crypto/sha1"
+	"encoding/hex"
+	"io"
+	"strings"
+)
+
+type Scripter interface {
+	Eval(ctx context.Context, script string, keys []string, args ...interface{}) *Cmd
+	EvalSha(ctx context.Context, sha1 string, keys []string, args ...interface{}) *Cmd
+	ScriptExists(ctx context.Context, hashes ...string) *BoolSliceCmd
+	ScriptLoad(ctx context.Context, script string) *StringCmd
+}
+
+var (
+	_ Scripter = (*Client)(nil)
+	_ Scripter = (*Ring)(nil)
+	_ Scripter = (*ClusterClient)(nil)
+)
+
+type Script struct {
+	src, hash string
+}
+
+func NewScript(src string) *Script {
+	h := sha1.New()
+	_, _ = io.WriteString(h, src)
+	return &Script{
+		src:  src,
+		hash: hex.EncodeToString(h.Sum(nil)),
+	}
+}
+
+func (s *Script) Hash() string {
+	return s.hash
+}
+
+func (s *Script) Load(ctx context.Context, c Scripter) *StringCmd {
+	return c.ScriptLoad(ctx, s.src)
+}
+
+func (s *Script) Exists(ctx context.Context, c Scripter) *BoolSliceCmd {
+	return c.ScriptExists(ctx, s.hash)
+}
+
+func (s *Script) Eval(ctx context.Context, c Scripter, keys []string, args ...interface{}) *Cmd {
+	return c.Eval(ctx, s.src, keys, args...)
+}
+
+func (s *Script) EvalSha(ctx context.Context, c Scripter, keys []string, args ...interface{}) *Cmd {
+	return c.EvalSha(ctx, s.hash, keys, args...)
+}
+
+// Run optimistically uses EVALSHA to run the script. If script does not exist
+// it is retried using EVAL.
+func (s *Script) Run(ctx context.Context, c Scripter, keys []string, args ...interface{}) *Cmd {
+	r := s.EvalSha(ctx, c, keys, args...)
+	if err := r.Err(); err != nil && strings.HasPrefix(err.Error(), "NOSCRIPT ") {
+		return s.Eval(ctx, c, keys, args...)
+	}
+	return r
+}
diff --git a/vendor/github.com/go-redis/redis/v8/sentinel.go b/vendor/github.com/go-redis/redis/v8/sentinel.go
new file mode 100644
index 0000000000..d785168ff1
--- /dev/null
+++ b/vendor/github.com/go-redis/redis/v8/sentinel.go
@@ -0,0 +1,738 @@
+package redis
+
+import (
+	"context"
+	"crypto/tls"
+	"errors"
+	"net"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/go-redis/redis/v8/internal"
+	"github.com/go-redis/redis/v8/internal/pool"
+	"github.com/go-redis/redis/v8/internal/rand"
+)
+
+//------------------------------------------------------------------------------
+
+// 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
+	// Sentinel password from "requirepass <password>" (if enabled) in Sentinel configuration
+	SentinelPassword string
+
+	// Allows routing read-only commands to the closest master or slave node.
+	// This option only works with NewFailoverClusterClient.
+	RouteByLatency bool
+	// Allows routing read-only commands to the random master or slave node.
+	// This option only works with NewFailoverClusterClient.
+	RouteRandomly bool
+
+	// Route all commands to slave read-only nodes.
+	SlaveOnly bool
+
+	// Following options are copied from Options struct.
+
+	Dialer    func(ctx context.Context, network, addr string) (net.Conn, error)
+	OnConnect func(ctx context.Context, cn *Conn) error
+
+	Username string
+	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) clientOptions() *Options {
+	return &Options{
+		Addr: "FailoverClient",
+
+		Dialer:    opt.Dialer,
+		OnConnect: opt.OnConnect,
+
+		DB:       opt.DB,
+		Username: opt.Username,
+		Password: opt.Password,
+
+		MaxRetries:      opt.MaxRetries,
+		MinRetryBackoff: opt.MinRetryBackoff,
+		MaxRetryBackoff: opt.MaxRetryBackoff,
+
+		DialTimeout:  opt.DialTimeout,
+		ReadTimeout:  opt.ReadTimeout,
+		WriteTimeout: opt.WriteTimeout,
+
+		PoolSize:           opt.PoolSize,
+		PoolTimeout:        opt.PoolTimeout,
+		IdleTimeout:        opt.IdleTimeout,
+		IdleCheckFrequency: opt.IdleCheckFrequency,
+		MinIdleConns:       opt.MinIdleConns,
+		MaxConnAge:         opt.MaxConnAge,
+
+		TLSConfig: opt.TLSConfig,
+	}
+}
+
+func (opt *FailoverOptions) sentinelOptions(addr string) *Options {
+	return &Options{
+		Addr: addr,
+
+		Dialer:    opt.Dialer,
+		OnConnect: opt.OnConnect,
+
+		DB:       0,
+		Password: opt.SentinelPassword,
+
+		MaxRetries:      opt.MaxRetries,
+		MinRetryBackoff: opt.MinRetryBackoff,
+		MaxRetryBackoff: opt.MaxRetryBackoff,
+
+		DialTimeout:  opt.DialTimeout,
+		ReadTimeout:  opt.ReadTimeout,
+		WriteTimeout: opt.WriteTimeout,
+
+		PoolSize:           opt.PoolSize,
+		PoolTimeout:        opt.PoolTimeout,
+		IdleTimeout:        opt.IdleTimeout,
+		IdleCheckFrequency: opt.IdleCheckFrequency,
+		MinIdleConns:       opt.MinIdleConns,
+		MaxConnAge:         opt.MaxConnAge,
+
+		TLSConfig: opt.TLSConfig,
+	}
+}
+
+func (opt *FailoverOptions) clusterOptions() *ClusterOptions {
+	return &ClusterOptions{
+		Dialer:    opt.Dialer,
+		OnConnect: opt.OnConnect,
+
+		Username: opt.Username,
+		Password: opt.Password,
+
+		MaxRedirects: opt.MaxRetries,
+
+		RouteByLatency: opt.RouteByLatency,
+		RouteRandomly:  opt.RouteRandomly,
+
+		MinRetryBackoff: opt.MinRetryBackoff,
+		MaxRetryBackoff: opt.MaxRetryBackoff,
+
+		DialTimeout:  opt.DialTimeout,
+		ReadTimeout:  opt.ReadTimeout,
+		WriteTimeout: opt.WriteTimeout,
+
+		PoolSize:           opt.PoolSize,
+		PoolTimeout:        opt.PoolTimeout,
+		IdleTimeout:        opt.IdleTimeout,
+		IdleCheckFrequency: opt.IdleCheckFrequency,
+		MinIdleConns:       opt.MinIdleConns,
+		MaxConnAge:         opt.MaxConnAge,
+
+		TLSConfig: opt.TLSConfig,
+	}
+}
+
+// NewFailoverClient returns a Redis client that uses Redis Sentinel
+// for automatic failover. It's safe for concurrent use by multiple
+// goroutines.
+func NewFailoverClient(failoverOpt *FailoverOptions) *Client {
+	if failoverOpt.RouteByLatency {
+		panic("to route commands by latency, use NewFailoverClusterClient")
+	}
+	if failoverOpt.RouteRandomly {
+		panic("to route commands randomly, use NewFailoverClusterClient")
+	}
+
+	sentinelAddrs := make([]string, len(failoverOpt.SentinelAddrs))
+	copy(sentinelAddrs, failoverOpt.SentinelAddrs)
+
+	failover := &sentinelFailover{
+		opt:           failoverOpt,
+		sentinelAddrs: sentinelAddrs,
+	}
+
+	opt := failoverOpt.clientOptions()
+	opt.Dialer = masterSlaveDialer(failover)
+	opt.init()
+
+	connPool := newConnPool(opt)
+
+	failover.mu.Lock()
+	failover.onFailover = func(ctx context.Context, addr string) {
+		_ = connPool.Filter(func(cn *pool.Conn) bool {
+			return cn.RemoteAddr().String() != addr
+		})
+	}
+	failover.mu.Unlock()
+
+	c := Client{
+		baseClient: newBaseClient(opt, connPool),
+		ctx:        context.Background(),
+	}
+	c.cmdable = c.Process
+	c.onClose = failover.Close
+
+	return &c
+}
+
+func masterSlaveDialer(
+	failover *sentinelFailover,
+) func(ctx context.Context, network, addr string) (net.Conn, error) {
+	return func(ctx context.Context, network, _ string) (net.Conn, error) {
+		var addr string
+		var err error
+
+		if failover.opt.SlaveOnly {
+			addr, err = failover.RandomSlaveAddr(ctx)
+		} else {
+			addr, err = failover.MasterAddr(ctx)
+			if err == nil {
+				failover.trySwitchMaster(ctx, addr)
+			}
+		}
+
+		if err != nil {
+			return nil, err
+		}
+		if failover.opt.Dialer != nil {
+			return failover.opt.Dialer(ctx, network, addr)
+		}
+		return net.DialTimeout("tcp", addr, failover.opt.DialTimeout)
+	}
+}
+
+//------------------------------------------------------------------------------
+
+// SentinelClient is a client for a Redis Sentinel.
+type SentinelClient struct {
+	*baseClient
+	hooks
+	ctx context.Context
+}
+
+func NewSentinelClient(opt *Options) *SentinelClient {
+	opt.init()
+	c := &SentinelClient{
+		baseClient: &baseClient{
+			opt:      opt,
+			connPool: newConnPool(opt),
+		},
+		ctx: context.Background(),
+	}
+	return c
+}
+
+func (c *SentinelClient) Context() context.Context {
+	return c.ctx
+}
+
+func (c *SentinelClient) WithContext(ctx context.Context) *SentinelClient {
+	if ctx == nil {
+		panic("nil context")
+	}
+	clone := *c
+	clone.ctx = ctx
+	return &clone
+}
+
+func (c *SentinelClient) Process(ctx context.Context, cmd Cmder) error {
+	return c.hooks.process(ctx, cmd, c.baseClient.process)
+}
+
+func (c *SentinelClient) pubSub() *PubSub {
+	pubsub := &PubSub{
+		opt: c.opt,
+
+		newConn: func(ctx context.Context, channels []string) (*pool.Conn, error) {
+			return c.newConn(ctx)
+		},
+		closeConn: c.connPool.CloseConn,
+	}
+	pubsub.init()
+	return pubsub
+}
+
+// Ping is used to test if a connection is still alive, or to
+// measure latency.
+func (c *SentinelClient) Ping(ctx context.Context) *StringCmd {
+	cmd := NewStringCmd(ctx, "ping")
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+// Subscribe subscribes the client to the specified channels.
+// Channels can be omitted to create empty subscription.
+func (c *SentinelClient) Subscribe(ctx context.Context, channels ...string) *PubSub {
+	pubsub := c.pubSub()
+	if len(channels) > 0 {
+		_ = pubsub.Subscribe(ctx, channels...)
+	}
+	return pubsub
+}
+
+// PSubscribe subscribes the client to the given patterns.
+// Patterns can be omitted to create empty subscription.
+func (c *SentinelClient) PSubscribe(ctx context.Context, channels ...string) *PubSub {
+	pubsub := c.pubSub()
+	if len(channels) > 0 {
+		_ = pubsub.PSubscribe(ctx, channels...)
+	}
+	return pubsub
+}
+
+func (c *SentinelClient) GetMasterAddrByName(ctx context.Context, name string) *StringSliceCmd {
+	cmd := NewStringSliceCmd(ctx, "sentinel", "get-master-addr-by-name", name)
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+func (c *SentinelClient) Sentinels(ctx context.Context, name string) *SliceCmd {
+	cmd := NewSliceCmd(ctx, "sentinel", "sentinels", name)
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+// Failover forces a failover as if the master was not reachable, and without
+// asking for agreement to other Sentinels.
+func (c *SentinelClient) Failover(ctx context.Context, name string) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "sentinel", "failover", name)
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+// Reset resets all the masters with matching name. The pattern argument is a
+// glob-style pattern. The reset process clears any previous state in a master
+// (including a failover in progress), and removes every slave and sentinel
+// already discovered and associated with the master.
+func (c *SentinelClient) Reset(ctx context.Context, pattern string) *IntCmd {
+	cmd := NewIntCmd(ctx, "sentinel", "reset", pattern)
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+// FlushConfig forces Sentinel to rewrite its configuration on disk, including
+// the current Sentinel state.
+func (c *SentinelClient) FlushConfig(ctx context.Context) *StatusCmd {
+	cmd := NewStatusCmd(ctx, "sentinel", "flushconfig")
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+// Master shows the state and info of the specified master.
+func (c *SentinelClient) Master(ctx context.Context, name string) *StringStringMapCmd {
+	cmd := NewStringStringMapCmd(ctx, "sentinel", "master", name)
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+// Masters shows a list of monitored masters and their state.
+func (c *SentinelClient) Masters(ctx context.Context) *SliceCmd {
+	cmd := NewSliceCmd(ctx, "sentinel", "masters")
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+// Slaves shows a list of slaves for the specified master and their state.
+func (c *SentinelClient) Slaves(ctx context.Context, name string) *SliceCmd {
+	cmd := NewSliceCmd(ctx, "sentinel", "slaves", name)
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+// CkQuorum checks if the current Sentinel configuration is able to reach the
+// quorum needed to failover a master, and the majority needed to authorize the
+// failover. This command should be used in monitoring systems to check if a
+// Sentinel deployment is ok.
+func (c *SentinelClient) CkQuorum(ctx context.Context, name string) *StringCmd {
+	cmd := NewStringCmd(ctx, "sentinel", "ckquorum", name)
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+// Monitor tells the Sentinel to start monitoring a new master with the specified
+// name, ip, port, and quorum.
+func (c *SentinelClient) Monitor(ctx context.Context, name, ip, port, quorum string) *StringCmd {
+	cmd := NewStringCmd(ctx, "sentinel", "monitor", name, ip, port, quorum)
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+// Set is used in order to change configuration parameters of a specific master.
+func (c *SentinelClient) Set(ctx context.Context, name, option, value string) *StringCmd {
+	cmd := NewStringCmd(ctx, "sentinel", "set", name, option, value)
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+// Remove is used in order to remove the specified master: the master will no
+// longer be monitored, and will totally be removed from the internal state of
+// the Sentinel.
+func (c *SentinelClient) Remove(ctx context.Context, name string) *StringCmd {
+	cmd := NewStringCmd(ctx, "sentinel", "remove", name)
+	_ = c.Process(ctx, cmd)
+	return cmd
+}
+
+//------------------------------------------------------------------------------
+
+type sentinelFailover struct {
+	opt *FailoverOptions
+
+	sentinelAddrs []string
+
+	onFailover func(ctx context.Context, addr string)
+	onUpdate   func(ctx context.Context)
+
+	mu          sync.RWMutex
+	_masterAddr string
+	sentinel    *SentinelClient
+	pubsub      *PubSub
+}
+
+func (c *sentinelFailover) Close() error {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	if c.sentinel != nil {
+		return c.closeSentinel()
+	}
+	return nil
+}
+
+func (c *sentinelFailover) closeSentinel() error {
+	firstErr := c.pubsub.Close()
+	c.pubsub = nil
+
+	err := c.sentinel.Close()
+	if err != nil && firstErr == nil {
+		firstErr = err
+	}
+	c.sentinel = nil
+
+	return firstErr
+}
+
+func (c *sentinelFailover) RandomSlaveAddr(ctx context.Context) (string, error) {
+	addresses, err := c.slaveAddrs(ctx)
+	if err != nil {
+		return "", err
+	}
+	if len(addresses) == 0 {
+		return c.MasterAddr(ctx)
+	}
+	return addresses[rand.Intn(len(addresses))], nil
+}
+
+func (c *sentinelFailover) MasterAddr(ctx context.Context) (string, error) {
+	c.mu.RLock()
+	sentinel := c.sentinel
+	c.mu.RUnlock()
+
+	if sentinel != nil {
+		addr := c.getMasterAddr(ctx, sentinel)
+		if addr != "" {
+			return addr, nil
+		}
+	}
+
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	if c.sentinel != nil {
+		addr := c.getMasterAddr(ctx, c.sentinel)
+		if addr != "" {
+			return addr, nil
+		}
+		_ = c.closeSentinel()
+	}
+
+	for i, sentinelAddr := range c.sentinelAddrs {
+		sentinel := NewSentinelClient(c.opt.sentinelOptions(sentinelAddr))
+
+		masterAddr, err := sentinel.GetMasterAddrByName(ctx, c.opt.MasterName).Result()
+		if err != nil {
+			internal.Logger.Printf(ctx, "sentinel: GetMasterAddrByName master=%q failed: %s",
+				c.opt.MasterName, err)
+			_ = sentinel.Close()
+			continue
+		}
+
+		// Push working sentinel to the top.
+		c.sentinelAddrs[0], c.sentinelAddrs[i] = c.sentinelAddrs[i], c.sentinelAddrs[0]
+		c.setSentinel(ctx, sentinel)
+
+		addr := net.JoinHostPort(masterAddr[0], masterAddr[1])
+		return addr, nil
+	}
+
+	return "", errors.New("redis: all sentinels specified in configuration are unreachable")
+}
+
+func (c *sentinelFailover) slaveAddrs(ctx context.Context) ([]string, error) {
+	c.mu.RLock()
+	sentinel := c.sentinel
+	c.mu.RUnlock()
+
+	if sentinel != nil {
+		addrs := c.getSlaveAddrs(ctx, sentinel)
+		if len(addrs) > 0 {
+			return addrs, nil
+		}
+	}
+
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	if c.sentinel != nil {
+		addrs := c.getSlaveAddrs(ctx, c.sentinel)
+		if len(addrs) > 0 {
+			return addrs, nil
+		}
+		_ = c.closeSentinel()
+	}
+
+	for i, sentinelAddr := range c.sentinelAddrs {
+		sentinel := NewSentinelClient(c.opt.sentinelOptions(sentinelAddr))
+
+		slaves, err := sentinel.Slaves(ctx, c.opt.MasterName).Result()
+		if err != nil {
+			internal.Logger.Printf(ctx, "sentinel: Slaves master=%q failed: %s",
+				c.opt.MasterName, err)
+			_ = sentinel.Close()
+			continue
+		}
+
+		// Push working sentinel to the top.
+		c.sentinelAddrs[0], c.sentinelAddrs[i] = c.sentinelAddrs[i], c.sentinelAddrs[0]
+		c.setSentinel(ctx, sentinel)
+
+		addrs := parseSlaveAddrs(slaves)
+		return addrs, nil
+	}
+
+	return []string{}, errors.New("redis: all sentinels specified in configuration are unreachable")
+}
+
+func (c *sentinelFailover) getMasterAddr(ctx context.Context, sentinel *SentinelClient) string {
+	addr, err := sentinel.GetMasterAddrByName(ctx, c.opt.MasterName).Result()
+	if err != nil {
+		internal.Logger.Printf(ctx, "sentinel: GetMasterAddrByName name=%q failed: %s",
+			c.opt.MasterName, err)
+		return ""
+	}
+	return net.JoinHostPort(addr[0], addr[1])
+}
+
+func (c *sentinelFailover) getSlaveAddrs(ctx context.Context, sentinel *SentinelClient) []string {
+	addrs, err := sentinel.Slaves(ctx, c.opt.MasterName).Result()
+	if err != nil {
+		internal.Logger.Printf(ctx, "sentinel: Slaves name=%q failed: %s",
+			c.opt.MasterName, err)
+		return []string{}
+	}
+	return parseSlaveAddrs(addrs)
+}
+
+func parseSlaveAddrs(addrs []interface{}) []string {
+	nodes := make([]string, 0, len(addrs))
+
+	for _, node := range addrs {
+		ip := ""
+		port := ""
+		flags := []string{}
+		lastkey := ""
+		isDown := false
+
+		for _, key := range node.([]interface{}) {
+			switch lastkey {
+			case "ip":
+				ip = key.(string)
+			case "port":
+				port = key.(string)
+			case "flags":
+				flags = strings.Split(key.(string), ",")
+			}
+			lastkey = key.(string)
+		}
+
+		for _, flag := range flags {
+			switch flag {
+			case "s_down", "o_down", "disconnected":
+				isDown = true
+			}
+		}
+
+		if !isDown {
+			nodes = append(nodes, net.JoinHostPort(ip, port))
+		}
+	}
+
+	return nodes
+}
+
+func (c *sentinelFailover) trySwitchMaster(ctx context.Context, addr string) {
+	c.mu.RLock()
+	currentAddr := c._masterAddr
+	c.mu.RUnlock()
+
+	if addr == currentAddr {
+		return
+	}
+
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	if addr == c._masterAddr {
+		return
+	}
+	c._masterAddr = addr
+
+	internal.Logger.Printf(ctx, "sentinel: new master=%q addr=%q",
+		c.opt.MasterName, addr)
+	if c.onFailover != nil {
+		c.onFailover(ctx, addr)
+	}
+}
+
+func (c *sentinelFailover) setSentinel(ctx context.Context, sentinel *SentinelClient) {
+	if c.sentinel != nil {
+		panic("not reached")
+	}
+	c.sentinel = sentinel
+	c.discoverSentinels(ctx)
+
+	c.pubsub = sentinel.Subscribe(ctx, "+switch-master", "+slave-reconf-done")
+	go c.listen(c.pubsub)
+}
+
+func (c *sentinelFailover) discoverSentinels(ctx context.Context) {
+	sentinels, err := c.sentinel.Sentinels(ctx, c.opt.MasterName).Result()
+	if err != nil {
+		internal.Logger.Printf(ctx, "sentinel: Sentinels master=%q failed: %s", c.opt.MasterName, err)
+		return
+	}
+	for _, sentinel := range sentinels {
+		vals := sentinel.([]interface{})
+		for i := 0; i < len(vals); i += 2 {
+			key := vals[i].(string)
+			if key == "name" {
+				sentinelAddr := vals[i+1].(string)
+				if !contains(c.sentinelAddrs, sentinelAddr) {
+					internal.Logger.Printf(ctx, "sentinel: discovered new sentinel=%q for master=%q",
+						sentinelAddr, c.opt.MasterName)
+					c.sentinelAddrs = append(c.sentinelAddrs, sentinelAddr)
+				}
+			}
+		}
+	}
+}
+
+func (c *sentinelFailover) listen(pubsub *PubSub) {
+	ctx := context.TODO()
+
+	if c.onUpdate != nil {
+		c.onUpdate(ctx)
+	}
+
+	ch := pubsub.Channel()
+	for msg := range ch {
+		if msg.Channel == "+switch-master" {
+			parts := strings.Split(msg.Payload, " ")
+			if parts[0] != c.opt.MasterName {
+				internal.Logger.Printf(pubsub.getContext(), "sentinel: ignore addr for master=%q", parts[0])
+				continue
+			}
+			addr := net.JoinHostPort(parts[3], parts[4])
+			c.trySwitchMaster(pubsub.getContext(), addr)
+		}
+
+		if c.onUpdate != nil {
+			c.onUpdate(ctx)
+		}
+	}
+}
+
+func contains(slice []string, str string) bool {
+	for _, s := range slice {
+		if s == str {
+			return true
+		}
+	}
+	return false
+}
+
+//------------------------------------------------------------------------------
+
+// NewFailoverClusterClient returns a client that supports routing read-only commands
+// to a slave node.
+func NewFailoverClusterClient(failoverOpt *FailoverOptions) *ClusterClient {
+	sentinelAddrs := make([]string, len(failoverOpt.SentinelAddrs))
+	copy(sentinelAddrs, failoverOpt.SentinelAddrs)
+
+	failover := &sentinelFailover{
+		opt:           failoverOpt,
+		sentinelAddrs: sentinelAddrs,
+	}
+
+	opt := failoverOpt.clusterOptions()
+	opt.ClusterSlots = func(ctx context.Context) ([]ClusterSlot, error) {
+		masterAddr, err := failover.MasterAddr(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		nodes := []ClusterNode{{
+			Addr: masterAddr,
+		}}
+
+		slaveAddrs, err := failover.slaveAddrs(ctx)
+		if err != nil {
+			return nil, err
+		}
+
+		for _, slaveAddr := range slaveAddrs {
+			nodes = append(nodes, ClusterNode{
+				Addr: slaveAddr,
+			})
+		}
+
+		slots := []ClusterSlot{
+			{
+				Start: 0,
+				End:   16383,
+				Nodes: nodes,
+			},
+		}
+		return slots, nil
+	}
+
+	c := NewClusterClient(opt)
+
+	failover.mu.Lock()
+	failover.onUpdate = func(ctx context.Context) {
+		c.ReloadState(ctx)
+	}
+	failover.mu.Unlock()
+
+	return c
+}
diff --git a/vendor/github.com/go-redis/redis/v7/tx.go b/vendor/github.com/go-redis/redis/v8/tx.go
similarity index 72%
rename from vendor/github.com/go-redis/redis/v7/tx.go
rename to vendor/github.com/go-redis/redis/v8/tx.go
index 9ae159015e..08d381a9d3 100644
--- a/vendor/github.com/go-redis/redis/v7/tx.go
+++ b/vendor/github.com/go-redis/redis/v8/tx.go
@@ -3,8 +3,8 @@ package redis
 import (
 	"context"
 
-	"github.com/go-redis/redis/v7/internal/pool"
-	"github.com/go-redis/redis/v7/internal/proto"
+	"github.com/go-redis/redis/v8/internal/pool"
+	"github.com/go-redis/redis/v8/internal/proto"
 )
 
 // TxFailedErr transaction redis failed.
@@ -26,7 +26,7 @@ func (c *Client) newTx(ctx context.Context) *Tx {
 	tx := Tx{
 		baseClient: baseClient{
 			opt:      c.opt,
-			connPool: pool.NewStickyConnPool(c.connPool.(*pool.ConnPool), true),
+			connPool: pool.NewStickyConnPool(c.connPool),
 		},
 		hooks: c.hooks.clone(),
 		ctx:   ctx,
@@ -55,11 +55,7 @@ func (c *Tx) WithContext(ctx context.Context) *Tx {
 	return &clone
 }
 
-func (c *Tx) Process(cmd Cmder) error {
-	return c.ProcessContext(c.ctx, cmd)
-}
-
-func (c *Tx) ProcessContext(ctx context.Context, cmd Cmder) error {
+func (c *Tx) Process(ctx context.Context, cmd Cmder) error {
 	return c.hooks.process(ctx, cmd, c.baseClient.process)
 }
 
@@ -67,52 +63,45 @@ func (c *Tx) ProcessContext(ctx context.Context, cmd Cmder) error {
 // for conditional execution if there are any keys.
 //
 // The transaction is automatically closed when fn exits.
-func (c *Client) Watch(fn func(*Tx) error, keys ...string) error {
-	return c.WatchContext(c.ctx, fn, keys...)
-}
-
-func (c *Client) WatchContext(ctx context.Context, fn func(*Tx) error, keys ...string) error {
+func (c *Client) Watch(ctx context.Context, fn func(*Tx) error, keys ...string) error {
 	tx := c.newTx(ctx)
+	defer tx.Close(ctx)
 	if len(keys) > 0 {
-		if err := tx.Watch(keys...).Err(); err != nil {
-			_ = tx.Close()
+		if err := tx.Watch(ctx, keys...).Err(); err != nil {
 			return err
 		}
 	}
-
-	err := fn(tx)
-	_ = tx.Close()
-	return err
+	return fn(tx)
 }
 
 // Close closes the transaction, releasing any open resources.
-func (c *Tx) Close() error {
-	_ = c.Unwatch().Err()
+func (c *Tx) Close(ctx context.Context) error {
+	_ = c.Unwatch(ctx).Err()
 	return c.baseClient.Close()
 }
 
 // Watch marks the keys to be watched for conditional execution
 // of a transaction.
-func (c *Tx) Watch(keys ...string) *StatusCmd {
+func (c *Tx) Watch(ctx context.Context, keys ...string) *StatusCmd {
 	args := make([]interface{}, 1+len(keys))
 	args[0] = "watch"
 	for i, key := range keys {
 		args[1+i] = key
 	}
-	cmd := NewStatusCmd(args...)
-	_ = c.Process(cmd)
+	cmd := NewStatusCmd(ctx, args...)
+	_ = c.Process(ctx, cmd)
 	return cmd
 }
 
 // Unwatch flushes all the previously watched keys for a transaction.
-func (c *Tx) Unwatch(keys ...string) *StatusCmd {
+func (c *Tx) Unwatch(ctx context.Context, keys ...string) *StatusCmd {
 	args := make([]interface{}, 1+len(keys))
 	args[0] = "unwatch"
 	for i, key := range keys {
 		args[1+i] = key
 	}
-	cmd := NewStatusCmd(args...)
-	_ = c.Process(cmd)
+	cmd := NewStatusCmd(ctx, args...)
+	_ = c.Process(ctx, cmd)
 	return cmd
 }
 
@@ -130,8 +119,8 @@ func (c *Tx) Pipeline() Pipeliner {
 
 // Pipelined executes commands queued in the fn outside of the transaction.
 // Use TxPipelined if you need transactional behavior.
-func (c *Tx) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
-	return c.Pipeline().Pipelined(fn)
+func (c *Tx) Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) {
+	return c.Pipeline().Pipelined(ctx, fn)
 }
 
 // TxPipelined executes commands queued in the fn in the transaction.
@@ -142,8 +131,8 @@ func (c *Tx) Pipelined(fn func(Pipeliner) error) ([]Cmder, error) {
 // Exec always returns list of commands. If transaction fails
 // TxFailedErr is returned. Otherwise Exec returns an error of the first
 // failed command or nil.
-func (c *Tx) TxPipelined(fn func(Pipeliner) error) ([]Cmder, error) {
-	return c.TxPipeline().Pipelined(fn)
+func (c *Tx) TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) {
+	return c.TxPipeline().Pipelined(ctx, fn)
 }
 
 // TxPipeline creates a pipeline. Usually it is more convenient to use TxPipelined.
diff --git a/vendor/github.com/go-redis/redis/v7/universal.go b/vendor/github.com/go-redis/redis/v8/universal.go
similarity index 82%
rename from vendor/github.com/go-redis/redis/v7/universal.go
rename to vendor/github.com/go-redis/redis/v8/universal.go
index 005ca682c5..5f0e1e33a5 100644
--- a/vendor/github.com/go-redis/redis/v7/universal.go
+++ b/vendor/github.com/go-redis/redis/v8/universal.go
@@ -20,23 +20,29 @@ type UniversalOptions struct {
 
 	// Common options.
 
-	Dialer             func(ctx context.Context, network, addr string) (net.Conn, error)
-	OnConnect          func(*Conn) error
-	Username           string
-	Password           string
-	MaxRetries         int
-	MinRetryBackoff    time.Duration
-	MaxRetryBackoff    time.Duration
-	DialTimeout        time.Duration
-	ReadTimeout        time.Duration
-	WriteTimeout       time.Duration
+	Dialer    func(ctx context.Context, network, addr string) (net.Conn, error)
+	OnConnect func(ctx context.Context, cn *Conn) error
+
+	Username         string
+	Password         string
+	SentinelPassword string
+
+	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
+
+	TLSConfig *tls.Config
 
 	// Only cluster clients.
 
@@ -100,9 +106,10 @@ func (o *UniversalOptions) Failover() *FailoverOptions {
 		Dialer:    o.Dialer,
 		OnConnect: o.OnConnect,
 
-		DB:       o.DB,
-		Username: o.Username,
-		Password: o.Password,
+		DB:               o.DB,
+		Username:         o.Username,
+		Password:         o.Password,
+		SentinelPassword: o.SentinelPassword,
 
 		MaxRetries:      o.MaxRetries,
 		MinRetryBackoff: o.MinRetryBackoff,
@@ -168,19 +175,20 @@ type UniversalClient interface {
 	Cmdable
 	Context() context.Context
 	AddHook(Hook)
-	Watch(fn func(*Tx) error, keys ...string) error
-	Do(args ...interface{}) *Cmd
-	DoContext(ctx context.Context, args ...interface{}) *Cmd
-	Process(cmd Cmder) error
-	ProcessContext(ctx context.Context, cmd Cmder) error
-	Subscribe(channels ...string) *PubSub
-	PSubscribe(channels ...string) *PubSub
+	Watch(ctx context.Context, fn func(*Tx) error, keys ...string) error
+	Do(ctx context.Context, args ...interface{}) *Cmd
+	Process(ctx context.Context, cmd Cmder) error
+	Subscribe(ctx context.Context, channels ...string) *PubSub
+	PSubscribe(ctx context.Context, channels ...string) *PubSub
 	Close() error
+	PoolStats() *PoolStats
 }
 
-var _ UniversalClient = (*Client)(nil)
-var _ UniversalClient = (*ClusterClient)(nil)
-var _ UniversalClient = (*Ring)(nil)
+var (
+	_ UniversalClient = (*Client)(nil)
+	_ UniversalClient = (*ClusterClient)(nil)
+	_ UniversalClient = (*Ring)(nil)
+)
 
 // NewUniversalClient returns a new multi client. The type of client returned depends
 // on the following three conditions:
diff --git a/vendor/go.opentelemetry.io/otel/.gitignore b/vendor/go.opentelemetry.io/otel/.gitignore
new file mode 100644
index 0000000000..0686068294
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/.gitignore
@@ -0,0 +1,23 @@
+.DS_Store
+Thumbs.db
+
+.tools/
+.idea/
+.vscode/
+*.iml
+*.so
+coverage.*
+
+gen/
+
+/example/grpc/client/client
+/example/grpc/server/server
+/example/http/client/client
+/example/http/server/server
+/example/jaeger/jaeger
+/example/namedtracer/namedtracer
+/example/opencensus/opencensus
+/example/prometheus/prometheus
+/example/prom-collector/prom-collector
+/example/zipkin/zipkin
+/example/otel-collector/otel-collector
diff --git a/vendor/go.opentelemetry.io/otel/.gitmodules b/vendor/go.opentelemetry.io/otel/.gitmodules
new file mode 100644
index 0000000000..38a1f56982
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "opentelemetry-proto"]
+	path = exporters/otlp/internal/opentelemetry-proto
+	url = https://github.com/open-telemetry/opentelemetry-proto
diff --git a/vendor/go.opentelemetry.io/otel/.golangci.yml b/vendor/go.opentelemetry.io/otel/.golangci.yml
new file mode 100644
index 0000000000..2ef168198c
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/.golangci.yml
@@ -0,0 +1,32 @@
+# See https://github.com/golangci/golangci-lint#config-file
+run:
+  issues-exit-code: 1 #Default
+  tests: true #Default
+
+linters:
+  enable:
+    - misspell
+    - goimports
+    - golint
+    - gofmt
+
+issues:
+  exclude-rules:
+    # helpers in tests often (rightfully) pass a *testing.T as their first argument
+    - path: _test\.go
+      text: "context.Context should be the first parameter of a function"
+      linters:
+        - golint
+    # Yes, they are, but it's okay in a test
+    - path: _test\.go
+      text: "exported func.*returns unexported type.*which can be annoying to use"
+      linters:
+        - golint
+
+linters-settings:
+  misspell:
+    locale: US
+    ignore-words:
+      - cancelled
+  goimports:
+    local-prefixes: go.opentelemetry.io
diff --git a/vendor/go.opentelemetry.io/otel/CHANGELOG.md b/vendor/go.opentelemetry.io/otel/CHANGELOG.md
new file mode 100644
index 0000000000..aedb792d16
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/CHANGELOG.md
@@ -0,0 +1,1054 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+
+This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [0.16.0] - 2020-01-13
+
+### Added
+
+- Add the `ReadOnlySpan` and `ReadWriteSpan` interfaces to provide better control for accessing span data. (#1360)
+- `NewGRPCDriver` function returns a `ProtocolDriver` that maintains a single gRPC connection to the collector. (#1369)
+- Added documentation about the project's versioning policy. (#1388)
+- Added `NewSplitDriver` for OTLP exporter that allows sending traces and metrics to different endpoints. (#1418)
+- Added codeql worfklow to GitHub Actions (#1428)
+- Added Gosec workflow to GitHub Actions (#1429)
+- Add new HTTP driver for OTLP exporter in `exporters/otlp/otlphttp`. Currently it only supports the binary protobuf payloads. (#1420)
+
+### Changed
+
+- Rename `internal/testing` to `internal/internaltest`. (#1449)
+- Rename `export.SpanData` to `export.SpanSnapshot` and use it only for exporting spans. (#1360)
+- Store the parent's full `SpanContext` rather than just its span ID in the `span` struct. (#1360)
+- Improve span duration accuracy. (#1360)
+- Migrated CI/CD from CircleCI to GitHub Actions (#1382)
+- Remove duplicate checkout from GitHub Actions workflow (#1407)
+- Metric `array` aggregator renamed `exact` to match its `aggregation.Kind` (#1412)
+- Metric `exact` aggregator includes per-point timestamps (#1412)
+- Metric stdout exporter uses MinMaxSumCount aggregator for ValueRecorder instruments (#1412)
+- `NewExporter` from `exporters/otlp` now takes a `ProtocolDriver` as a parameter. (#1369)
+- Many OTLP Exporter options became gRPC ProtocolDriver options. (#1369)
+- Unify endpoint API that related to OTel exporter. (#1401)
+- Optimize metric histogram aggregator to re-use its slice of buckets. (#1435)
+- Metric aggregator Count() and histogram Bucket.Counts are consistently `uint64`. (1430)
+- `SamplingResult` now passed a `Tracestate` from the parent `SpanContext` (#1432)
+- Moved gRPC driver for OTLP exporter to `exporters/otlp/otlpgrpc`. (#1420)
+- The `TraceContext` propagator now correctly propagates `TraceState` through the `SpanContext`. (#1447)
+- Metric Push and Pull Controller components are combined into a single "basic" Controller:
+  - `WithExporter()` and `Start()` to configure Push behavior
+  - `Start()` is optional; use `Collect()` and `ForEach()` for Pull behavior
+  - `Start()` and `Stop()` accept Context. (#1378)
+
+### Removed
+
+- Remove `errUninitializedSpan` as its only usage is now obsolete. (#1360)
+- Remove Metric export functionality related to quantiles and summary data points: this is not specified (#1412)
+- Remove DDSketch metric aggregator; our intention is to re-introduce this as an option of the histogram aggregator after [new OTLP histogram data types](https://github.com/open-telemetry/opentelemetry-proto/pull/226) are released (#1412)
+
+### Fixed
+
+- `BatchSpanProcessor.Shutdown()` will now shutdown underlying `export.SpanExporter`. (#1443)
+
+## [0.15.0] - 2020-12-10
+
+### Added
+
+- The `WithIDGenerator` `TracerProviderOption` is added to the `go.opentelemetry.io/otel/trace` package to configure an `IDGenerator` for the `TracerProvider`. (#1363)
+
+### Changed
+
+- The Zipkin exporter now uses the Span status code to determine. (#1328)
+- `NewExporter` and `Start` functions in `go.opentelemetry.io/otel/exporters/otlp` now receive `context.Context` as a first parameter. (#1357)
+- Move the OpenCensus example into `example` directory. (#1359)
+- Moved the SDK's `internal.IDGenerator` interface in to the `sdk/trace` package to enable support for externally-defined ID generators. (#1363)
+- Bump `github.com/google/go-cmp` from 0.5.3 to 0.5.4 (#1374)
+- Bump `github.com/golangci/golangci-lint` in `/internal/tools` (#1375)
+
+### Fixed
+
+- Metric SDK `SumObserver` and `UpDownSumObserver` instruments correctness fixes. (#1381)
+
+## [0.14.0] - 2020-11-19
+
+### Added
+
+- An `EventOption` and the related `NewEventConfig` function are added to the `go.opentelemetry.io/otel` package to configure Span events. (#1254)
+- A `TextMapPropagator` and associated `TextMapCarrier` are added to the `go.opentelemetry.io/otel/oteltest` package to test `TextMap` type propagators and their use. (#1259)
+- `SpanContextFromContext` returns `SpanContext` from context. (#1255)
+- `TraceState` has been added to `SpanContext`. (#1340)
+- `DeploymentEnvironmentKey` added to `go.opentelemetry.io/otel/semconv` package. (#1323)
+- Add an OpenCensus to OpenTelemetry tracing bridge. (#1305)
+- Add a parent context argument to `SpanProcessor.OnStart` to follow the specification. (#1333)
+- Add missing tests for `sdk/trace/attributes_map.go`. (#1337)
+
+### Changed
+
+- Move the `go.opentelemetry.io/otel/api/trace` package into `go.opentelemetry.io/otel/trace` with the following changes. (#1229) (#1307)
+  - `ID` has been renamed to `TraceID`.
+  - `IDFromHex` has been renamed to `TraceIDFromHex`.
+  - `EmptySpanContext` is removed.
+- Move the `go.opentelemetry.io/otel/api/trace/tracetest` package into `go.opentelemetry.io/otel/oteltest`. (#1229)
+- OTLP Exporter updates:
+  - supports OTLP v0.6.0 (#1230, #1354)
+  - supports configurable aggregation temporality (default: Cumulative, optional: Stateless). (#1296)
+- The Sampler is now called on local child spans. (#1233)
+- The `Kind` type from the `go.opentelemetry.io/otel/api/metric` package was renamed to `InstrumentKind` to more specifically describe what it is and avoid semantic ambiguity. (#1240)
+- The `MetricKind` method of the `Descriptor` type in the `go.opentelemetry.io/otel/api/metric` package was renamed to `Descriptor.InstrumentKind`.
+   This matches the returned type and fixes misuse of the term metric. (#1240)
+- Move test harness from the `go.opentelemetry.io/otel/api/apitest` package into `go.opentelemetry.io/otel/oteltest`. (#1241)
+- Move the `go.opentelemetry.io/otel/api/metric/metrictest` package into `go.opentelemetry.io/oteltest` as part of #964. (#1252)
+- Move the `go.opentelemetry.io/otel/api/metric` package into `go.opentelemetry.io/otel/metric` as part of #1303. (#1321)
+- Move the `go.opentelemetry.io/otel/api/metric/registry` package into `go.opentelemetry.io/otel/metric/registry` as a part of #1303. (#1316)
+- Move the `Number` type (together with related functions) from `go.opentelemetry.io/otel/api/metric` package into `go.opentelemetry.io/otel/metric/number` as a part of #1303. (#1316)
+- The function signature of the Span `AddEvent` method in `go.opentelemetry.io/otel` is updated to no longer take an unused context and instead take a required name and a variable number of `EventOption`s. (#1254)
+- The function signature of the Span `RecordError` method in `go.opentelemetry.io/otel` is updated to no longer take an unused context and instead take a required error value and a variable number of `EventOption`s. (#1254)
+- Move the `go.opentelemetry.io/otel/api/global` package to `go.opentelemetry.io/otel`. (#1262) (#1330)
+- Move the `Version` function from `go.opentelemetry.io/otel/sdk` to `go.opentelemetry.io/otel`. (#1330)
+- Rename correlation context header from `"otcorrelations"` to `"baggage"` to match the OpenTelemetry specification. (#1267)
+- Fix `Code.UnmarshalJSON` to work with valid JSON only. (#1276)
+- The `resource.New()` method changes signature to support builtin attributes and functional options, including `telemetry.sdk.*` and
+  `host.name` semantic conventions; the former method is renamed `resource.NewWithAttributes`. (#1235)
+- The Prometheus exporter now exports non-monotonic counters (i.e. `UpDownCounter`s) as gauges. (#1210)
+- Correct the `Span.End` method documentation in the `otel` API to state updates are not allowed on a span after it has ended. (#1310)
+- Updated span collection limits for attribute, event and link counts to 1000 (#1318)
+- Renamed `semconv.HTTPUrlKey` to `semconv.HTTPURLKey`. (#1338)
+
+### Removed
+
+- The `ErrInvalidHexID`, `ErrInvalidTraceIDLength`, `ErrInvalidSpanIDLength`, `ErrInvalidSpanIDLength`, or `ErrNilSpanID` from the `go.opentelemetry.io/otel` package are unexported now. (#1243)
+- The `AddEventWithTimestamp` method on the `Span` interface in `go.opentelemetry.io/otel` is removed due to its redundancy.
+   It is replaced by using the `AddEvent` method with a `WithTimestamp` option. (#1254)
+- The `MockSpan` and `MockTracer` types are removed from `go.opentelemetry.io/otel/oteltest`.
+   `Tracer` and `Span` from the same module should be used in their place instead. (#1306)
+- `WorkerCount` option is removed from `go.opentelemetry.io/otel/exporters/otlp`. (#1350)
+
+### Fixed
+
+- Rename `MergeItererator` to `MergeIterator` in the `go.opentelemetry.io/otel/label` package. (#1244)
+- The `go.opentelemetry.io/otel/api/global` packages global TextMapPropagator now delegates functionality to a globally set delegate for all previously returned propagators. (#1258)
+- Fix condition in `label.Any`. (#1299)
+- Fix global `TracerProvider` to pass options to its configured provider. (#1329)
+- Fix missing handler for `ExactKind` aggregator in OTLP metrics transformer (#1309)
+
+## [0.13.0] - 2020-10-08
+
+### Added
+
+- OTLP Metric exporter supports Histogram aggregation. (#1209)
+- The `Code` struct from the `go.opentelemetry.io/otel/codes` package now supports JSON marshaling and unmarshaling as well as implements the `Stringer` interface. (#1214)
+- A Baggage API to implement the OpenTelemetry specification. (#1217)
+- Add Shutdown method to sdk/trace/provider, shutdown processors in the order they were registered. (#1227)
+
+### Changed
+
+- Set default propagator to no-op propagator. (#1184)
+- The `HTTPSupplier`, `HTTPExtractor`, `HTTPInjector`, and `HTTPPropagator` from the `go.opentelemetry.io/otel/api/propagation` package were replaced with unified `TextMapCarrier` and `TextMapPropagator` in the `go.opentelemetry.io/otel/propagation` package. (#1212) (#1325)
+- The `New` function from the `go.opentelemetry.io/otel/api/propagation` package was replaced with `NewCompositeTextMapPropagator` in the `go.opentelemetry.io/otel` package. (#1212)
+- The status codes of the `go.opentelemetry.io/otel/codes` package have been updated to match the latest OpenTelemetry specification.
+   They now are `Unset`, `Error`, and `Ok`.
+   They no longer track the gRPC codes. (#1214)
+- The `StatusCode` field of the `SpanData` struct in the `go.opentelemetry.io/otel/sdk/export/trace` package now uses the codes package from this package instead of the gRPC project. (#1214)
+- Move the `go.opentelemetry.io/otel/api/baggage` package into `go.opentelemetry.io/otel/baggage`. (#1217) (#1325)
+- A `Shutdown` method of `SpanProcessor` and all its implementations receives a context and returns an error. (#1264)
+
+### Fixed
+
+- Copies of data from arrays and slices passed to `go.opentelemetry.io/otel/label.ArrayValue()` are now used in the returned `Value` instead of using the mutable data itself. (#1226)
+
+### Removed
+
+- The `ExtractHTTP` and `InjectHTTP` functions from the `go.opentelemetry.io/otel/api/propagation` package were removed. (#1212)
+- The `Propagators` interface from the `go.opentelemetry.io/otel/api/propagation` package was removed to conform to the OpenTelemetry specification.
+   The explicit `TextMapPropagator` type can be used in its place as this is the `Propagator` type the specification defines. (#1212)
+- The `SetAttribute` method of the `Span` from the `go.opentelemetry.io/otel/api/trace` package was removed given its redundancy with the `SetAttributes` method. (#1216)
+- The internal implementation of Baggage storage is removed in favor of using the new Baggage API functionality. (#1217)
+- Remove duplicate hostname key `HostHostNameKey` in Resource semantic conventions. (#1219)
+- Nested array/slice support has been removed. (#1226)
+
+## [0.12.0] - 2020-09-24
+
+### Added
+
+- A `SpanConfigure` function in `go.opentelemetry.io/otel/api/trace` to create a new `SpanConfig` from `SpanOption`s. (#1108)
+- In the `go.opentelemetry.io/otel/api/trace` package, `NewTracerConfig` was added to construct new `TracerConfig`s.
+   This addition was made to conform with our project option conventions. (#1155)
+- Instrumentation library information was added to the Zipkin exporter. (#1119)
+- The `SpanProcessor` interface now has a `ForceFlush()` method. (#1166)
+- More semantic conventions for k8s as resource attributes. (#1167)
+
+### Changed
+
+- Add reconnecting udp connection type to Jaeger exporter.
+   This change adds a new optional implementation of the udp conn interface used to detect changes to an agent's host dns record.
+   It then adopts the new destination address to ensure the exporter doesn't get stuck. This change was ported from jaegertracing/jaeger-client-go#520. (#1063)
+- Replace `StartOption` and `EndOption` in `go.opentelemetry.io/otel/api/trace` with `SpanOption`.
+   This change is matched by replacing the `StartConfig` and `EndConfig` with a unified `SpanConfig`. (#1108)
+- Replace the `LinkedTo` span option in `go.opentelemetry.io/otel/api/trace` with `WithLinks`.
+   This is be more consistent with our other option patterns, i.e. passing the item to be configured directly instead of its component parts, and provides a cleaner function signature. (#1108)
+- The `go.opentelemetry.io/otel/api/trace` `TracerOption` was changed to an interface to conform to project option conventions. (#1109)
+- Move the `B3` and `TraceContext` from within the `go.opentelemetry.io/otel/api/trace` package to their own `go.opentelemetry.io/otel/propagators` package.
+    This removal of the propagators is reflective of the OpenTelemetry specification for these propagators as well as cleans up the `go.opentelemetry.io/otel/api/trace` API. (#1118)
+- Rename Jaeger tags used for instrumentation library information to reflect changes in OpenTelemetry specification. (#1119)
+- Rename `ProbabilitySampler` to `TraceIDRatioBased` and change semantics to ignore parent span sampling status. (#1115)
+- Move `tools` package under `internal`. (#1141)
+- Move `go.opentelemetry.io/otel/api/correlation` package to `go.opentelemetry.io/otel/api/baggage`. (#1142)
+   The `correlation.CorrelationContext` propagator has been renamed `baggage.Baggage`.  Other exported functions and types are unchanged.
+- Rename `ParentOrElse` sampler to `ParentBased` and allow setting samplers depending on parent span. (#1153)
+- In the `go.opentelemetry.io/otel/api/trace` package, `SpanConfigure` was renamed to `NewSpanConfig`. (#1155)
+- Change `dependabot.yml` to add a `Skip Changelog` label to dependabot-sourced PRs. (#1161)
+- The [configuration style guide](https://github.com/open-telemetry/opentelemetry-go/blob/master/CONTRIBUTING.md#config) has been updated to
+   recommend the use of `newConfig()` instead of `configure()`. (#1163)
+- The `otlp.Config` type has been unexported and changed to `otlp.config`, along with its initializer. (#1163)
+- Ensure exported interface types include parameter names and update the
+   Style Guide to reflect this styling rule. (#1172)
+- Don't consider unset environment variable for resource detection to be an error. (#1170)
+- Rename `go.opentelemetry.io/otel/api/metric.ConfigureInstrument` to `NewInstrumentConfig` and
+  `go.opentelemetry.io/otel/api/metric.ConfigureMeter` to `NewMeterConfig`.
+- ValueObserver instruments use LastValue aggregator by default. (#1165)
+- OTLP Metric exporter supports LastValue aggregation. (#1165)
+- Move the `go.opentelemetry.io/otel/api/unit` package to `go.opentelemetry.io/otel/unit`. (#1185)
+- Rename `Provider` to `MeterProvider` in the `go.opentelemetry.io/otel/api/metric` package. (#1190)
+- Rename `NoopProvider` to `NoopMeterProvider` in the `go.opentelemetry.io/otel/api/metric` package. (#1190)
+- Rename `NewProvider` to `NewMeterProvider` in the `go.opentelemetry.io/otel/api/metric/metrictest` package. (#1190)
+- Rename `Provider` to `MeterProvider` in the `go.opentelemetry.io/otel/api/metric/registry` package. (#1190)
+- Rename `NewProvider` to `NewMeterProvider` in the `go.opentelemetry.io/otel/api/metri/registryc` package. (#1190)
+- Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/api/trace` package. (#1190)
+- Rename `NoopProvider` to `NoopTracerProvider` in the `go.opentelemetry.io/otel/api/trace` package. (#1190)
+- Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/api/trace/tracetest` package. (#1190)
+- Rename `NewProvider` to `NewTracerProvider` in the `go.opentelemetry.io/otel/api/trace/tracetest` package. (#1190)
+- Rename `WrapperProvider` to `WrapperTracerProvider` in the `go.opentelemetry.io/otel/bridge/opentracing` package. (#1190)
+- Rename `NewWrapperProvider` to `NewWrapperTracerProvider` in the `go.opentelemetry.io/otel/bridge/opentracing` package. (#1190)
+- Rename `Provider` method of the pull controller to `MeterProvider` in the `go.opentelemetry.io/otel/sdk/metric/controller/pull` package. (#1190)
+- Rename `Provider` method of the push controller to `MeterProvider` in the `go.opentelemetry.io/otel/sdk/metric/controller/push` package. (#1190)
+- Rename `ProviderOptions` to `TracerProviderConfig` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190)
+- Rename `ProviderOption` to `TracerProviderOption` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190)
+- Rename `Provider` to `TracerProvider` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190)
+- Rename `NewProvider` to `NewTracerProvider` in the `go.opentelemetry.io/otel/sdk/trace` package. (#1190)
+- Renamed `SamplingDecision` values to comply with OpenTelemetry specification change. (#1192)
+- Renamed Zipkin attribute names from `ot.status_code & ot.status_description` to `otel.status_code & otel.status_description`. (#1201)
+- The default SDK now invokes registered `SpanProcessor`s in the order they were registered with the `TracerProvider`. (#1195)
+- Add test of spans being processed by the `SpanProcessor`s in the order they were registered. (#1203)
+
+### Removed
+
+- Remove the B3 propagator from `go.opentelemetry.io/otel/propagators`. It is now located in the
+   `go.opentelemetry.io/contrib/propagators/` module. (#1191)
+- Remove the semantic convention for HTTP status text, `HTTPStatusTextKey` from package `go.opentelemetry.io/otel/semconv`. (#1194)
+
+### Fixed
+
+- Zipkin example no longer mentions `ParentSampler`, corrected to `ParentBased`. (#1171)
+- Fix missing shutdown processor in otel-collector example. (#1186)
+- Fix missing shutdown processor in basic and namedtracer examples. (#1197)
+
+## [0.11.0] - 2020-08-24
+
+### Added
+
+- Support for exporting array-valued attributes via OTLP. (#992)
+- `Noop` and `InMemory` `SpanBatcher` implementations to help with testing integrations. (#994)
+- Support for filtering metric label sets. (#1047)
+- A dimensionality-reducing metric Processor. (#1057)
+- Integration tests for more OTel Collector Attribute types. (#1062)
+- A new `WithSpanProcessor` `ProviderOption` is added to the `go.opentelemetry.io/otel/sdk/trace` package to create a `Provider` and automatically register the `SpanProcessor`. (#1078)
+
+### Changed
+
+- Rename `sdk/metric/processor/test` to `sdk/metric/processor/processortest`. (#1049)
+- Rename `sdk/metric/controller/test` to `sdk/metric/controller/controllertest`. (#1049)
+- Rename `api/testharness` to `api/apitest`. (#1049)
+- Rename `api/trace/testtrace` to `api/trace/tracetest`. (#1049)
+- Change Metric Processor to merge multiple observations. (#1024)
+- The `go.opentelemetry.io/otel/bridge/opentracing` bridge package has been made into its own module.
+   This removes the package dependencies of this bridge from the rest of the OpenTelemetry based project. (#1038)
+- Renamed `go.opentelemetry.io/otel/api/standard` package to `go.opentelemetry.io/otel/semconv` to avoid the ambiguous and generic name `standard` and better describe the package as containing OpenTelemetry semantic conventions. (#1016)
+- The environment variable used for resource detection has been changed from `OTEL_RESOURCE_LABELS` to `OTEL_RESOURCE_ATTRIBUTES` (#1042)
+- Replace `WithSyncer` with `WithBatcher` in examples. (#1044)
+- Replace the `google.golang.org/grpc/codes` dependency in the API with an equivalent `go.opentelemetry.io/otel/codes` package. (#1046)
+- Merge the `go.opentelemetry.io/otel/api/label` and `go.opentelemetry.io/otel/api/kv` into the new `go.opentelemetry.io/otel/label` package. (#1060)
+- Unify Callback Function Naming.
+   Rename `*Callback` with `*Func`. (#1061)
+- CI builds validate against last two versions of Go, dropping 1.13 and adding 1.15. (#1064)
+- The `go.opentelemetry.io/otel/sdk/export/trace` interfaces `SpanSyncer` and `SpanBatcher` have been replaced with a specification compliant `Exporter` interface.
+   This interface still supports the export of `SpanData`, but only as a slice.
+   Implementation are also required now to return any error from `ExportSpans` if one occurs as well as implement a `Shutdown` method for exporter clean-up. (#1078)
+- The `go.opentelemetry.io/otel/sdk/trace` `NewBatchSpanProcessor` function no longer returns an error.
+   If a `nil` exporter is passed as an argument to this function, instead of it returning an error, it now returns a `BatchSpanProcessor` that handles the export of `SpanData` by not taking any action. (#1078)
+- The `go.opentelemetry.io/otel/sdk/trace` `NewProvider` function to create a `Provider` no longer returns an error, instead only a `*Provider`.
+   This change is related to `NewBatchSpanProcessor` not returning an error which was the only error this function would return. (#1078)
+
+### Removed
+
+- Duplicate, unused API sampler interface. (#999)
+   Use the [`Sampler` interface](https://github.com/open-telemetry/opentelemetry-go/blob/v0.11.0/sdk/trace/sampling.go) provided by the SDK instead.
+- The `grpctrace` instrumentation was moved to the `go.opentelemetry.io/contrib` repository and out of this repository.
+   This move includes moving the `grpc` example to the `go.opentelemetry.io/contrib` as well. (#1027)
+- The `WithSpan` method of the `Tracer` interface.
+   The functionality this method provided was limited compared to what a user can provide themselves.
+   It was removed with the understanding that if there is sufficient user need it can be added back based on actual user usage. (#1043)
+- The `RegisterSpanProcessor` and `UnregisterSpanProcessor` functions.
+   These were holdovers from an approach prior to the TracerProvider design. They were not used anymore. (#1077)
+- The `oterror` package. (#1026)
+- The `othttp` and `httptrace` instrumentations were moved to `go.opentelemetry.io/contrib`. (#1032)
+
+### Fixed
+
+- The `semconv.HTTPServerMetricAttributesFromHTTPRequest()` function no longer generates the high-cardinality `http.request.content.length` label. (#1031)
+- Correct instrumentation version tag in Jaeger exporter. (#1037)
+- The SDK span will now set an error event if the `End` method is called during a panic (i.e. it was deferred). (#1043)
+- Move internally generated protobuf code from the `go.opentelemetry.io/otel` to the OTLP exporter to reduce dependency overhead. (#1050)
+- The `otel-collector` example referenced outdated collector processors. (#1006)
+
+## [0.10.0] - 2020-07-29
+
+This release migrates the default OpenTelemetry SDK into its own Go module, decoupling the SDK from the API and reducing dependencies for instrumentation packages.
+
+### Added
+
+- The Zipkin exporter now has `NewExportPipeline` and `InstallNewPipeline` constructor functions to match the common pattern.
+    These function build a new exporter with default SDK options and register the exporter with the `global` package respectively. (#944)
+- Add propagator option for gRPC instrumentation. (#986)
+- The `testtrace` package now tracks the `trace.SpanKind` for each span. (#987)
+
+### Changed
+
+- Replace the `RegisterGlobal` `Option` in the Jaeger exporter with an `InstallNewPipeline` constructor function.
+   This matches the other exporter constructor patterns and will register a new exporter after building it with default configuration. (#944)
+- The trace (`go.opentelemetry.io/otel/exporters/trace/stdout`) and metric (`go.opentelemetry.io/otel/exporters/metric/stdout`) `stdout` exporters are now merged into a single exporter at `go.opentelemetry.io/otel/exporters/stdout`.
+   This new exporter was made into its own Go module to follow the pattern of all exporters and decouple it from the `go.opentelemetry.io/otel` module. (#956, #963)
+- Move the `go.opentelemetry.io/otel/exporters/test` test package to `go.opentelemetry.io/otel/sdk/export/metric/metrictest`. (#962)
+- The `go.opentelemetry.io/otel/api/kv/value` package was merged into the parent `go.opentelemetry.io/otel/api/kv` package. (#968)
+  - `value.Bool` was replaced with `kv.BoolValue`.
+  - `value.Int64` was replaced with `kv.Int64Value`.
+  - `value.Uint64` was replaced with `kv.Uint64Value`.
+  - `value.Float64` was replaced with `kv.Float64Value`.
+  - `value.Int32` was replaced with `kv.Int32Value`.
+  - `value.Uint32` was replaced with `kv.Uint32Value`.
+  - `value.Float32` was replaced with `kv.Float32Value`.
+  - `value.String` was replaced with `kv.StringValue`.
+  - `value.Int` was replaced with `kv.IntValue`.
+  - `value.Uint` was replaced with `kv.UintValue`.
+  - `value.Array` was replaced with `kv.ArrayValue`.
+- Rename `Infer` to `Any` in the `go.opentelemetry.io/otel/api/kv` package. (#972)
+- Change `othttp` to use the `httpsnoop` package to wrap the `ResponseWriter` so that optional interfaces (`http.Hijacker`, `http.Flusher`, etc.) that are implemented by the original `ResponseWriter`are also implemented by the wrapped `ResponseWriter`. (#979)
+- Rename `go.opentelemetry.io/otel/sdk/metric/aggregator/test` package to `go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest`. (#980)
+- Make the SDK into its own Go module called `go.opentelemetry.io/otel/sdk`. (#985)
+- Changed the default trace `Sampler` from `AlwaysOn` to `ParentOrElse(AlwaysOn)`. (#989)
+
+### Removed
+
+- The `IndexedAttribute` function from the `go.opentelemetry.io/otel/api/label` package was removed in favor of `IndexedLabel` which it was synonymous with. (#970)
+
+### Fixed
+
+- Bump github.com/golangci/golangci-lint from 1.28.3 to 1.29.0 in /tools. (#953)
+- Bump github.com/google/go-cmp from 0.5.0 to 0.5.1. (#957)
+- Use `global.Handle` for span export errors in the OTLP exporter. (#946)
+- Correct Go language formatting in the README documentation. (#961)
+- Remove default SDK dependencies from the `go.opentelemetry.io/otel/api` package. (#977)
+- Remove default SDK dependencies from the `go.opentelemetry.io/otel/instrumentation` package. (#983)
+- Move documented examples for `go.opentelemetry.io/otel/instrumentation/grpctrace` interceptors into Go example tests. (#984)
+
+## [0.9.0] - 2020-07-20
+
+### Added
+
+- A new Resource Detector interface is included to allow resources to be automatically detected and included. (#939)
+- A Detector to automatically detect resources from an environment variable. (#939)
+- Github action to generate protobuf Go bindings locally in `internal/opentelemetry-proto-gen`. (#938)
+- OTLP .proto files from `open-telemetry/opentelemetry-proto` imported as a git submodule under `internal/opentelemetry-proto`.
+   References to `github.com/open-telemetry/opentelemetry-proto` changed to `go.opentelemetry.io/otel/internal/opentelemetry-proto-gen`. (#942)
+
+### Changed
+
+- Non-nil value `struct`s for key-value pairs will be marshalled using JSON rather than `Sprintf`. (#948)
+
+### Removed
+
+- Removed dependency on `github.com/open-telemetry/opentelemetry-collector`. (#943)
+
+## [0.8.0] - 2020-07-09
+
+### Added
+
+- The `B3Encoding` type to represent the B3 encoding(s) the B3 propagator can inject.
+   A value for HTTP supported encodings (Multiple Header: `MultipleHeader`, Single Header: `SingleHeader`) are included. (#882)
+- The `FlagsDeferred` trace flag to indicate if the trace sampling decision has been deferred. (#882)
+- The `FlagsDebug` trace flag to indicate if the trace is a debug trace. (#882)
+- Add `peer.service` semantic attribute. (#898)
+- Add database-specific semantic attributes. (#899)
+- Add semantic convention for `faas.coldstart` and `container.id`. (#909)
+- Add http content size semantic conventions. (#905)
+- Include `http.request_content_length` in HTTP request basic attributes. (#905)
+- Add semantic conventions for operating system process resource attribute keys. (#919)
+- The Jaeger exporter now has a `WithBatchMaxCount` option to specify the maximum number of spans sent in a batch. (#931)
+
+### Changed
+
+- Update `CONTRIBUTING.md` to ask for updates to `CHANGELOG.md` with each pull request. (#879)
+- Use lowercase header names for B3 Multiple Headers. (#881)
+- The B3 propagator `SingleHeader` field has been replaced with `InjectEncoding`.
+   This new field can be set to combinations of the `B3Encoding` bitmasks and will inject trace information in these encodings.
+   If no encoding is set, the propagator will default to `MultipleHeader` encoding. (#882)
+- The B3 propagator now extracts from either HTTP encoding of B3 (Single Header or Multiple Header) based on what is contained in the header.
+   Preference is given to Single Header encoding with Multiple Header being the fallback if Single Header is not found or is invalid.
+   This behavior change is made to dynamically support all correctly encoded traces received instead of having to guess the expected encoding prior to receiving. (#882)
+- Extend semantic conventions for RPC. (#900)
+- To match constant naming conventions in the `api/standard` package, the `FaaS*` key names are appended with a suffix of `Key`. (#920)
+  - `"api/standard".FaaSName` -> `FaaSNameKey`
+  - `"api/standard".FaaSID` -> `FaaSIDKey`
+  - `"api/standard".FaaSVersion` -> `FaaSVersionKey`
+  - `"api/standard".FaaSInstance` -> `FaaSInstanceKey`
+
+### Removed
+
+- The `FlagsUnused` trace flag is removed.
+   The purpose of this flag was to act as the inverse of `FlagsSampled`, the inverse of `FlagsSampled` is used instead. (#882)
+- The B3 header constants (`B3SingleHeader`, `B3DebugFlagHeader`, `B3TraceIDHeader`, `B3SpanIDHeader`, `B3SampledHeader`, `B3ParentSpanIDHeader`) are removed.
+   If B3 header keys are needed [the authoritative OpenZipkin package constants](https://pkg.go.dev/github.com/openzipkin/zipkin-go@v0.2.2/propagation/b3?tab=doc#pkg-constants) should be used instead. (#882)
+
+### Fixed
+
+- The B3 Single Header name is now correctly `b3` instead of the previous `X-B3`. (#881)
+- The B3 propagator now correctly supports sampling only values (`b3: 0`, `b3: 1`, or `b3: d`) for a Single B3 Header. (#882)
+- The B3 propagator now propagates the debug flag.
+   This removes the behavior of changing the debug flag into a set sampling bit.
+   Instead, this now follow the B3 specification and omits the `X-B3-Sampling` header. (#882)
+- The B3 propagator now tracks "unset" sampling state (meaning "defer the decision") and does not set the `X-B3-Sampling` header when injecting. (#882)
+- Bump github.com/itchyny/gojq from 0.10.3 to 0.10.4 in /tools. (#883)
+- Bump github.com/opentracing/opentracing-go from v1.1.1-0.20190913142402-a7454ce5950e to v1.2.0. (#885)
+- The tracing time conversion for OTLP spans is now correctly set to `UnixNano`. (#896)
+- Ensure span status is not set to `Unknown` when no HTTP status code is provided as it is assumed to be `200 OK`. (#908)
+- Ensure `httptrace.clientTracer` closes `http.headers` span. (#912)
+- Prometheus exporter will not apply stale updates or forget inactive metrics. (#903)
+- Add test for api.standard `HTTPClientAttributesFromHTTPRequest`. (#905)
+- Bump github.com/golangci/golangci-lint from 1.27.0 to 1.28.1 in /tools. (#901, #913)
+- Update otel-colector example to use the v0.5.0 collector. (#915)
+- The `grpctrace` instrumentation uses a span name conforming to the OpenTelemetry semantic conventions (does not contain a leading slash (`/`)). (#922)
+- The `grpctrace` instrumentation includes an `rpc.method` attribute now set to the gRPC method name. (#900, #922)
+- The `grpctrace` instrumentation `rpc.service` attribute now contains the package name if one exists.
+   This is in accordance with OpenTelemetry semantic conventions. (#922)
+- Correlation Context extractor will no longer insert an empty map into the returned context when no valid values are extracted. (#923)
+- Bump google.golang.org/api from 0.28.0 to 0.29.0 in /exporters/trace/jaeger. (#925)
+- Bump github.com/itchyny/gojq from 0.10.4 to 0.11.0 in /tools. (#926)
+- Bump github.com/golangci/golangci-lint from 1.28.1 to 1.28.2 in /tools. (#930)
+
+## [0.7.0] - 2020-06-26
+
+This release implements the v0.5.0 version of the OpenTelemetry specification.
+
+### Added
+
+- The othttp instrumentation now includes default metrics. (#861)
+- This CHANGELOG file to track all changes in the project going forward.
+- Support for array type attributes. (#798)
+- Apply transitive dependabot go.mod dependency updates as part of a new automatic Github workflow. (#844)
+- Timestamps are now passed to exporters for each export. (#835)
+- Add new `Accumulation` type to metric SDK to transport telemetry from `Accumulator`s to `Processor`s.
+   This replaces the prior `Record` `struct` use for this purpose. (#835)
+- New dependabot integration to automate package upgrades. (#814)
+- `Meter` and `Tracer` implementations accept instrumentation version version as an optional argument.
+   This instrumentation version is passed on to exporters. (#811) (#805) (#802)
+- The OTLP exporter includes the instrumentation version in telemetry it exports. (#811)
+- Environment variables for Jaeger exporter are supported. (#796)
+- New `aggregation.Kind` in the export metric API. (#808)
+- New example that uses OTLP and the collector. (#790)
+- Handle errors in the span `SetName` during span initialization. (#791)
+- Default service config to enable retries for retry-able failed requests in the OTLP exporter and an option to override this default. (#777)
+- New `go.opentelemetry.io/otel/api/oterror` package to uniformly support error handling and definitions for the project. (#778)
+- New `global` default implementation of the `go.opentelemetry.io/otel/api/oterror.Handler` interface to be used to handle errors prior to an user defined `Handler`.
+   There is also functionality for the user to register their `Handler` as well as a convenience function `Handle` to handle an error with this global `Handler`(#778)
+- Options to specify propagators for httptrace and grpctrace instrumentation. (#784)
+- The required `application/json` header for the Zipkin exporter is included in all exports. (#774)
+- Integrate HTTP semantics helpers from the contrib repository into the `api/standard` package. #769
+
+### Changed
+
+- Rename `Integrator` to `Processor` in the metric SDK. (#863)
+- Rename `AggregationSelector` to `AggregatorSelector`. (#859)
+- Rename `SynchronizedCopy` to `SynchronizedMove`. (#858)
+- Rename `simple` integrator to `basic` integrator. (#857)
+- Merge otlp collector examples. (#841)
+- Change the metric SDK to support cumulative, delta, and pass-through exporters directly.
+   With these changes, cumulative and delta specific exporters are able to request the correct kind of aggregation from the SDK. (#840)
+- The `Aggregator.Checkpoint` API is renamed to `SynchronizedCopy` and adds an argument, a different `Aggregator` into which the copy is stored. (#812)
+- The `export.Aggregator` contract is that `Update()` and `SynchronizedCopy()` are synchronized with each other.
+   All the aggregation interfaces (`Sum`, `LastValue`, ...) are not meant to be synchronized, as the caller is expected to synchronize aggregators at a higher level after the `Accumulator`.
+   Some of the `Aggregators` used unnecessary locking and that has been cleaned up. (#812)
+- Use of `metric.Number` was replaced by `int64` now that we use `sync.Mutex` in the `MinMaxSumCount` and `Histogram` `Aggregators`. (#812)
+- Replace `AlwaysParentSample` with `ParentSample(fallback)` to match the OpenTelemetry v0.5.0 specification. (#810)
+- Rename `sdk/export/metric/aggregator` to `sdk/export/metric/aggregation`. #808
+- Send configured headers with every request in the OTLP exporter, instead of just on connection creation. (#806)
+- Update error handling for any one off error handlers, replacing, instead, with the `global.Handle` function. (#791)
+- Rename `plugin` directory to `instrumentation` to match the OpenTelemetry specification. (#779)
+- Makes the argument order to Histogram and DDSketch `New()` consistent. (#781)
+
+### Removed
+
+- `Uint64NumberKind` and related functions from the API. (#864)
+- Context arguments from `Aggregator.Checkpoint` and `Integrator.Process` as they were unused. (#803)
+- `SpanID` is no longer included in parameters for sampling decision to match the OpenTelemetry specification. (#775)
+
+### Fixed
+
+- Upgrade OTLP exporter to opentelemetry-proto matching the opentelemetry-collector v0.4.0 release. (#866)
+- Allow changes to `go.sum` and `go.mod` when running dependabot tidy-up. (#871)
+- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1. (#824)
+- Bump github.com/prometheus/client_golang from 1.7.0 to 1.7.1 in /exporters/metric/prometheus. (#867)
+- Bump google.golang.org/grpc from 1.29.1 to 1.30.0 in /exporters/trace/jaeger. (#853)
+- Bump google.golang.org/grpc from 1.29.1 to 1.30.0 in /exporters/trace/zipkin. (#854)
+- Bumps github.com/golang/protobuf from 1.3.2 to 1.4.2 (#848)
+- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/otlp (#817)
+- Bump github.com/golangci/golangci-lint from 1.25.1 to 1.27.0 in /tools (#828)
+- Bump github.com/prometheus/client_golang from 1.5.0 to 1.7.0 in /exporters/metric/prometheus (#838)
+- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/trace/jaeger (#829)
+- Bump github.com/benbjohnson/clock from 1.0.0 to 1.0.3 (#815)
+- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/trace/zipkin (#823)
+- Bump github.com/itchyny/gojq from 0.10.1 to 0.10.3 in /tools (#830)
+- Bump github.com/stretchr/testify from 1.4.0 to 1.6.1 in /exporters/metric/prometheus (#822)
+- Bump google.golang.org/grpc from 1.27.1 to 1.29.1 in /exporters/trace/zipkin (#820)
+- Bump google.golang.org/grpc from 1.27.1 to 1.29.1 in /exporters/trace/jaeger (#831)
+- Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 (#836)
+- Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 in /exporters/trace/jaeger (#837)
+- Bump github.com/google/go-cmp from 0.4.0 to 0.5.0 in /exporters/otlp (#839)
+- Bump google.golang.org/api from 0.20.0 to 0.28.0 in /exporters/trace/jaeger (#843)
+- Set span status from HTTP status code in the othttp instrumentation. (#832)
+- Fixed typo in push controller comment. (#834)
+- The `Aggregator` testing has been updated and cleaned. (#812)
+- `metric.Number(0)` expressions are replaced by `0` where possible. (#812)
+- Fixed `global` `handler_test.go` test failure. #804
+- Fixed `BatchSpanProcessor.Shutdown` to wait until all spans are processed. (#766)
+- Fixed OTLP example's accidental early close of exporter. (#807)
+- Ensure zipkin exporter reads and closes response body. (#788)
+- Update instrumentation to use `api/standard` keys instead of custom keys. (#782)
+- Clean up tools and RELEASING documentation. (#762)
+
+## [0.6.0] - 2020-05-21
+
+### Added
+
+- Support for `Resource`s in the prometheus exporter. (#757)
+- New pull controller. (#751)
+- New `UpDownSumObserver` instrument. (#750)
+- OpenTelemetry collector demo. (#711)
+- New `SumObserver` instrument. (#747)
+- New `UpDownCounter` instrument. (#745)
+- New timeout `Option` and configuration function `WithTimeout` to the push controller. (#742)
+- New `api/standards` package to implement semantic conventions and standard key-value generation. (#731)
+
+### Changed
+
+- Rename `Register*` functions in the metric API to `New*` for all `Observer` instruments. (#761)
+- Use `[]float64` for histogram boundaries, not `[]metric.Number`. (#758)
+- Change OTLP example to use exporter as a trace `Syncer` instead of as an unneeded `Batcher`. (#756)
+- Replace `WithResourceAttributes()` with `WithResource()` in the trace SDK. (#754)
+- The prometheus exporter now uses the new pull controller. (#751)
+- Rename `ScheduleDelayMillis` to `BatchTimeout` in the trace `BatchSpanProcessor`.(#752)
+- Support use of synchronous instruments in asynchronous callbacks (#725)
+- Move `Resource` from the `Export` method parameter into the metric export `Record`. (#739)
+- Rename `Observer` instrument to `ValueObserver`. (#734)
+- The push controller now has a method (`Provider()`) to return a `metric.Provider` instead of the old `Meter` method that acted as a `metric.Provider`. (#738)
+- Replace `Measure` instrument by `ValueRecorder` instrument. (#732)
+- Rename correlation context header from `"Correlation-Context"` to `"otcorrelations"` to match the OpenTelemetry specification. 727)
+
+### Fixed
+
+- Ensure gRPC `ClientStream` override methods do not panic in grpctrace package. (#755)
+- Disable parts of `BatchSpanProcessor` test until a fix is found. (#743)
+- Fix `string` case in `kv` `Infer` function. (#746)
+- Fix panic in grpctrace client interceptors. (#740)
+- Refactor the `api/metrics` push controller and add `CheckpointSet` synchronization. (#737)
+- Rewrite span batch process queue batching logic. (#719)
+- Remove the push controller named Meter map. (#738)
+- Fix Histogram aggregator initial state (fix #735). (#736)
+- Ensure golang alpine image is running `golang-1.14` for examples. (#733)
+- Added test for grpctrace `UnaryInterceptorClient`. (#695)
+- Rearrange `api/metric` code layout. (#724)
+
+## [0.5.0] - 2020-05-13
+
+### Added
+
+- Batch `Observer` callback support. (#717)
+- Alias `api` types to root package of project. (#696)
+- Create basic `othttp.Transport` for simple client instrumentation. (#678)
+- `SetAttribute(string, interface{})` to the trace API. (#674)
+- Jaeger exporter option that allows user to specify custom http client. (#671)
+- `Stringer` and `Infer` methods to `key`s. (#662)
+
+### Changed
+
+- Rename `NewKey` in the `kv` package to just `Key`. (#721)
+- Move `core` and `key` to `kv` package. (#720)
+- Make the metric API `Meter` a `struct` so the abstract `MeterImpl` can be passed and simplify implementation. (#709)
+- Rename SDK `Batcher` to `Integrator` to match draft OpenTelemetry SDK specification. (#710)
+- Rename SDK `Ungrouped` integrator to `simple.Integrator` to match draft OpenTelemetry SDK specification. (#710)
+- Rename SDK `SDK` `struct` to `Accumulator` to match draft OpenTelemetry SDK specification. (#710)
+- Move `Number` from `core` to `api/metric` package. (#706)
+- Move `SpanContext` from `core` to `trace` package. (#692)
+- Change traceparent header from `Traceparent` to `traceparent` to implement the W3C specification. (#681)
+
+### Fixed
+
+- Update tooling to run generators in all submodules. (#705)
+- gRPC interceptor regexp to match methods without a service name. (#683)
+- Use a `const` for padding 64-bit B3 trace IDs. (#701)
+- Update `mockZipkin` listen address from `:0` to `127.0.0.1:0`. (#700)
+- Left-pad 64-bit B3 trace IDs with zero. (#698)
+- Propagate at least the first W3C tracestate header. (#694)
+- Remove internal `StateLocker` implementation. (#688)
+- Increase instance size CI system uses. (#690)
+- Add a `key` benchmark and use reflection in `key.Infer()`. (#679)
+- Fix internal `global` test by using `global.Meter` with `RecordBatch()`. (#680)
+- Reimplement histogram using mutex instead of `StateLocker`. (#669)
+- Switch `MinMaxSumCount` to a mutex lock implementation instead of `StateLocker`. (#667)
+- Update documentation to not include any references to `WithKeys`. (#672)
+- Correct misspelling. (#668)
+- Fix clobbering of the span context if extraction fails. (#656)
+- Bump `golangci-lint` and work around the corrupting bug. (#666) (#670)
+
+## [0.4.3] - 2020-04-24
+
+### Added
+
+- `Dockerfile` and `docker-compose.yml` to run example code. (#635)
+- New `grpctrace` package that provides gRPC client and server interceptors for both unary and stream connections. (#621)
+- New `api/label` package, providing common label set implementation. (#651)
+- Support for JSON marshaling of `Resources`. (#654)
+- `TraceID` and `SpanID` implementations for `Stringer` interface. (#642)
+- `RemoteAddrKey` in the othttp plugin to include the HTTP client address in top-level spans. (#627)
+- `WithSpanFormatter` option to the othttp plugin. (#617)
+- Updated README to include section for compatible libraries and include reference to the contrib repository. (#612)
+- The prometheus exporter now supports exporting histograms. (#601)
+- A `String` method to the `Resource` to return a hashable identifier for a now unique resource. (#613)
+- An `Iter` method to the `Resource` to return an array `AttributeIterator`. (#613)
+- An `Equal` method to the `Resource` test the equivalence of resources. (#613)
+- An iterable structure (`AttributeIterator`) for `Resource` attributes.
+
+### Changed
+
+- zipkin export's `NewExporter` now requires a `serviceName` argument to ensure this needed values is provided. (#644)
+- Pass `Resources` through the metrics export pipeline. (#659)
+
+### Removed
+
+- `WithKeys` option from the metric API. (#639)
+
+### Fixed
+
+- Use the `label.Set.Equivalent` value instead of an encoding in the batcher. (#658)
+- Correct typo `trace.Exporter` to `trace.SpanSyncer` in comments. (#653)
+- Use type names for return values in jaeger exporter. (#648)
+- Increase the visibility of the `api/key` package by updating comments and fixing usages locally. (#650)
+- `Checkpoint` only after `Update`; Keep records in the `sync.Map` longer. (#647)
+- Do not cache `reflect.ValueOf()` in metric Labels. (#649)
+- Batch metrics exported from the OTLP exporter based on `Resource` and labels. (#626)
+- Add error wrapping to the prometheus exporter. (#631)
+- Update the OTLP exporter batching of traces to use a unique `string` representation of an associated `Resource` as the batching key. (#623)
+- Update OTLP `SpanData` transform to only include the `ParentSpanID` if one exists. (#614)
+- Update `Resource` internal representation to uniquely and reliably identify resources. (#613)
+- Check return value from `CheckpointSet.ForEach` in prometheus exporter. (#622)
+- Ensure spans created by httptrace client tracer reflect operation structure. (#618)
+- Create a new recorder rather than reuse when multiple observations in same epoch for asynchronous instruments. #610
+- The default port the OTLP exporter uses to connect to the OpenTelemetry collector is updated to match the one the collector listens on by default. (#611)
+
+
+## [0.4.2] - 2020-03-31
+
+### Fixed
+
+- Fix `pre_release.sh` to update version in `sdk/opentelemetry.go`. (#607)
+- Fix time conversion from internal to OTLP in OTLP exporter. (#606)
+
+## [0.4.1] - 2020-03-31
+
+### Fixed
+
+- Update `tag.sh` to create signed tags. (#604)
+
+## [0.4.0] - 2020-03-30
+
+### Added
+
+- New API package `api/metric/registry` that exposes a `MeterImpl` wrapper for use by SDKs to generate unique instruments. (#580)
+- Script to verify examples after a new release. (#579)
+
+### Removed
+
+- The dogstatsd exporter due to lack of support.
+   This additionally removes support for statsd. (#591)
+- `LabelSet` from the metric API.
+   This is replaced by a `[]core.KeyValue` slice. (#595)
+- `Labels` from the metric API's `Meter` interface. (#595)
+
+### Changed
+
+- The metric `export.Labels` became an interface which the SDK implements and the `export` package provides a simple, immutable implementation of this interface intended for testing purposes. (#574)
+- Renamed `internal/metric.Meter` to `MeterImpl`. (#580)
+- Renamed `api/global/internal.obsImpl` to `asyncImpl`. (#580)
+
+### Fixed
+
+- Corrected missing return in mock span. (#582)
+- Update License header for all source files to match CNCF guidelines and include a test to ensure it is present. (#586) (#596)
+- Update to v0.3.0 of the OTLP in the OTLP exporter. (#588)
+- Update pre-release script to be compatible between GNU and BSD based systems. (#592)
+- Add a `RecordBatch` benchmark. (#594)
+- Moved span transforms of the OTLP exporter to the internal package. (#593)
+- Build both go-1.13 and go-1.14 in circleci to test for all supported versions of Go. (#569)
+- Removed unneeded allocation on empty labels in OLTP exporter. (#597)
+- Update `BatchedSpanProcessor` to process the queue until no data but respect max batch size. (#599)
+- Update project documentation godoc.org links to pkg.go.dev. (#602)
+
+## [0.3.0] - 2020-03-21
+
+This is a first official beta release, which provides almost fully complete metrics, tracing, and context propagation functionality.
+There is still a possibility of breaking changes.
+
+### Added
+
+- Add `Observer` metric instrument. (#474)
+- Add global `Propagators` functionality to enable deferred initialization for propagators registered before the first Meter SDK is installed. (#494)
+- Simplified export setup pipeline for the jaeger exporter to match other exporters. (#459)
+- The zipkin trace exporter. (#495)
+- The OTLP exporter to export metric and trace telemetry to the OpenTelemetry collector. (#497) (#544) (#545)
+- The `StatusMessage` field was add to the trace `Span`. (#524)
+- Context propagation in OpenTracing bridge in terms of OpenTelemetry context propagation. (#525)
+- The `Resource` type was added to the SDK. (#528)
+- The global API now supports a `Tracer` and `Meter` function as shortcuts to getting a global `*Provider` and calling these methods directly. (#538)
+- The metric API now defines a generic `MeterImpl` interface to support general purpose `Meter` construction.
+   Additionally, `SyncImpl` and `AsyncImpl` are added to support general purpose instrument construction. (#560)
+- A metric `Kind` is added to represent the `MeasureKind`, `ObserverKind`, and `CounterKind`. (#560)
+- Scripts to better automate the release process. (#576)
+
+### Changed
+
+- Default to to use `AlwaysSampler` instead of `ProbabilitySampler` to match OpenTelemetry specification. (#506)
+- Renamed `AlwaysSampleSampler` to `AlwaysOnSampler` in the trace API. (#511)
+- Renamed `NeverSampleSampler` to `AlwaysOffSampler` in the trace API. (#511)
+- The `Status` field of the `Span` was changed to `StatusCode` to disambiguate with the added `StatusMessage`. (#524)
+- Updated the trace `Sampler` interface conform to the OpenTelemetry specification. (#531)
+- Rename metric API `Options` to `Config`. (#541)
+- Rename metric `Counter` aggregator to be `Sum`. (#541)
+- Unify metric options into `Option` from instrument specific options. (#541)
+- The trace API's `TraceProvider` now support `Resource`s. (#545)
+- Correct error in zipkin module name. (#548)
+- The jaeger trace exporter now supports `Resource`s. (#551)
+- Metric SDK now supports `Resource`s.
+   The `WithResource` option was added to configure a `Resource` on creation and the `Resource` method was added to the metric `Descriptor` to return the associated `Resource`. (#552)
+- Replace `ErrNoLastValue` and `ErrEmptyDataSet` by `ErrNoData` in the metric SDK. (#557)
+- The stdout trace exporter now supports `Resource`s. (#558)
+- The metric `Descriptor` is now included at the API instead of the SDK. (#560)
+- Replace `Ordered` with an iterator in `export.Labels`. (#567)
+
+### Removed
+
+- The vendor specific Stackdriver. It is now hosted on 3rd party vendor infrastructure. (#452)
+- The `Unregister` method for metric observers as it is not in the OpenTelemetry specification. (#560)
+- `GetDescriptor` from the metric SDK. (#575)
+- The `Gauge` instrument from the metric API. (#537)
+
+### Fixed
+
+- Make histogram aggregator checkpoint consistent. (#438)
+- Update README with import instructions and how to build and test. (#505)
+- The default label encoding was updated to be unique. (#508)
+- Use `NewRoot` in the othttp plugin for public endpoints. (#513)
+- Fix data race in `BatchedSpanProcessor`. (#518)
+- Skip test-386 for Mac OS 10.15.x (Catalina and upwards). #521
+- Use a variable-size array to represent ordered labels in maps. (#523)
+- Update the OTLP protobuf and update changed import path. (#532)
+- Use `StateLocker` implementation in `MinMaxSumCount`. (#546)
+- Eliminate goroutine leak in histogram stress test. (#547)
+- Update OTLP exporter with latest protobuf. (#550)
+- Add filters to the othttp plugin. (#556)
+- Provide an implementation of the `Header*` filters that do not depend on Go 1.14. (#565)
+- Encode labels once during checkpoint.
+   The checkpoint function is executed in a single thread so we can do the encoding lazily before passing the encoded version of labels to the exporter.
+   This is a cheap and quick way to avoid encoding the labels on every collection interval. (#572)
+- Run coverage over all packages in `COVERAGE_MOD_DIR`. (#573)
+
+## [0.2.3] - 2020-03-04
+
+### Added
+
+- `RecordError` method on `Span`s in the trace API to Simplify adding error events to spans. (#473)
+- Configurable push frequency for exporters setup pipeline. (#504)
+
+### Changed
+
+- Rename the `exporter` directory to `exporters`.
+   The `go.opentelemetry.io/otel/exporter/trace/jaeger` package was mistakenly released with a `v1.0.0` tag instead of `v0.1.0`.
+   This resulted in all subsequent releases not becoming the default latest.
+   A consequence of this was that all `go get`s pulled in the incompatible `v0.1.0` release of that package when pulling in more recent packages from other otel packages.
+   Renaming the `exporter` directory to `exporters` fixes this issue by renaming the package and therefore clearing any existing dependency tags.
+   Consequentially, this action also renames *all* exporter packages. (#502)
+
+### Removed
+
+- The `CorrelationContextHeader` constant in the `correlation` package is no longer exported. (#503)
+
+## [0.2.2] - 2020-02-27
+
+### Added
+
+- `HTTPSupplier` interface in the propagation API to specify methods to retrieve and store a single value for a key to be associated with a carrier. (#467)
+- `HTTPExtractor` interface in the propagation API to extract information from an `HTTPSupplier` into a context. (#467)
+- `HTTPInjector` interface in the propagation API to inject information into an `HTTPSupplier.` (#467)
+- `Config` and configuring `Option` to the propagator API. (#467)
+- `Propagators` interface in the propagation API to contain the set of injectors and extractors for all supported carrier formats. (#467)
+- `HTTPPropagator` interface in the propagation API to inject and extract from an `HTTPSupplier.` (#467)
+- `WithInjectors` and `WithExtractors` functions to the propagator API to configure injectors and extractors to use. (#467)
+- `ExtractHTTP` and `InjectHTTP` functions to apply configured HTTP extractors and injectors to a passed context. (#467)
+- Histogram aggregator. (#433)
+- `DefaultPropagator` function and have it return `trace.TraceContext` as the default context propagator. (#456)
+- `AlwaysParentSample` sampler to the trace API. (#455)
+- `WithNewRoot` option function to the trace API to specify the created span should be considered a root span. (#451)
+
+
+### Changed
+
+- Renamed `WithMap` to `ContextWithMap` in the correlation package. (#481)
+- Renamed `FromContext` to `MapFromContext` in the correlation package. (#481)
+- Move correlation context propagation to correlation package. (#479)
+- Do not default to putting remote span context into links. (#480)
+- Propagators extrac
+- `Tracer.WithSpan` updated to accept `StartOptions`. (#472)
+- Renamed `MetricKind` to `Kind` to not stutter in the type usage. (#432)
+- Renamed the `export` package to `metric` to match directory structure. (#432)
+- Rename the `api/distributedcontext` package to `api/correlation`. (#444)
+- Rename the `api/propagators` package to `api/propagation`. (#444)
+- Move the propagators from the `propagators` package into the `trace` API package. (#444)
+- Update `Float64Gauge`, `Int64Gauge`, `Float64Counter`, `Int64Counter`, `Float64Measure`, and `Int64Measure` metric methods to use value receivers instead of pointers. (#462)
+- Moved all dependencies of tools package to a tools directory. (#466)
+
+### Removed
+
+- Binary propagators. (#467)
+- NOOP propagator. (#467)
+
+### Fixed
+
+- Upgraded `github.com/golangci/golangci-lint` from `v1.21.0` to `v1.23.6` in `tools/`. (#492)
+- Fix a possible nil-dereference crash (#478)
+- Correct comments for `InstallNewPipeline` in the stdout exporter. (#483)
+- Correct comments for `InstallNewPipeline` in the dogstatsd exporter. (#484)
+- Correct comments for `InstallNewPipeline` in the prometheus exporter. (#482)
+- Initialize `onError` based on `Config` in prometheus exporter. (#486)
+- Correct module name in prometheus exporter README. (#475)
+- Removed tracer name prefix from span names. (#430)
+- Fix `aggregator_test.go` import package comment. (#431)
+- Improved detail in stdout exporter. (#436)
+- Fix a dependency issue (generate target should depend on stringer, not lint target) in Makefile. (#442)
+- Reorders the Makefile targets within `precommit` target so we generate files and build the code before doing linting, so we can get much nicer errors about syntax errors from the compiler. (#442)
+- Reword function documentation in gRPC plugin. (#446)
+- Send the `span.kind` tag to Jaeger from the jaeger exporter. (#441)
+- Fix `metadataSupplier` in the jaeger exporter to overwrite the header if existing instead of appending to it. (#441)
+- Upgraded to Go 1.13 in CI. (#465)
+- Correct opentelemetry.io URL in trace SDK documentation. (#464)
+- Refactored reference counting logic in SDK determination of stale records. (#468)
+- Add call to `runtime.Gosched` in instrument `acquireHandle` logic to not block the collector. (#469)
+
+## [0.2.1.1] - 2020-01-13
+
+### Fixed
+
+- Use stateful batcher on Prometheus exporter fixing regresion introduced in #395. (#428)
+
+## [0.2.1] - 2020-01-08
+
+### Added
+
+- Global meter forwarding implementation.
+   This enables deferred initialization for metric instruments registered before the first Meter SDK is installed. (#392)
+- Global trace forwarding implementation.
+   This enables deferred initialization for tracers registered before the first Trace SDK is installed. (#406)
+- Standardize export pipeline creation in all exporters. (#395)
+- A testing, organization, and comments for 64-bit field alignment. (#418)
+- Script to tag all modules in the project. (#414)
+
+### Changed
+
+- Renamed `propagation` package to `propagators`. (#362)
+- Renamed `B3Propagator` propagator to `B3`. (#362)
+- Renamed `TextFormatPropagator` propagator to `TextFormat`. (#362)
+- Renamed `BinaryPropagator` propagator to `Binary`. (#362)
+- Renamed `BinaryFormatPropagator` propagator to `BinaryFormat`. (#362)
+- Renamed `NoopTextFormatPropagator` propagator to `NoopTextFormat`. (#362)
+- Renamed `TraceContextPropagator` propagator to `TraceContext`. (#362)
+- Renamed `SpanOption` to `StartOption` in the trace API. (#369)
+- Renamed `StartOptions` to `StartConfig` in the trace API. (#369)
+- Renamed `EndOptions` to `EndConfig` in the trace API. (#369)
+- `Number` now has a pointer receiver for its methods. (#375)
+- Renamed `CurrentSpan` to `SpanFromContext` in the trace API. (#379)
+- Renamed `SetCurrentSpan` to `ContextWithSpan` in the trace API. (#379)
+- Renamed `Message` in Event to `Name` in the trace API. (#389)
+- Prometheus exporter no longer aggregates metrics, instead it only exports them. (#385)
+- Renamed `HandleImpl` to `BoundInstrumentImpl` in the metric API. (#400)
+- Renamed `Float64CounterHandle` to `Float64CounterBoundInstrument` in the metric API. (#400)
+- Renamed `Int64CounterHandle` to `Int64CounterBoundInstrument` in the metric API. (#400)
+- Renamed `Float64GaugeHandle` to `Float64GaugeBoundInstrument` in the metric API. (#400)
+- Renamed `Int64GaugeHandle` to `Int64GaugeBoundInstrument` in the metric API. (#400)
+- Renamed `Float64MeasureHandle` to `Float64MeasureBoundInstrument` in the metric API. (#400)
+- Renamed `Int64MeasureHandle` to `Int64MeasureBoundInstrument` in the metric API. (#400)
+- Renamed `Release` method for bound instruments in the metric API to `Unbind`. (#400)
+- Renamed `AcquireHandle` method for bound instruments in the metric API to `Bind`. (#400)
+- Renamed the `File` option in the stdout exporter to `Writer`. (#404)
+- Renamed all `Options` to `Config` for all metric exports where this wasn't already the case.
+
+### Fixed
+
+- Aggregator import path corrected. (#421)
+- Correct links in README. (#368)
+- The README was updated to match latest code changes in its examples. (#374)
+- Don't capitalize error statements. (#375)
+- Fix ignored errors. (#375)
+- Fix ambiguous variable naming. (#375)
+- Removed unnecessary type casting. (#375)
+- Use named parameters. (#375)
+- Updated release schedule. (#378)
+- Correct http-stackdriver example module name. (#394)
+- Removed the `http.request` span in `httptrace` package. (#397)
+- Add comments in the metrics SDK (#399)
+- Initialize checkpoint when creating ddsketch aggregator to prevent panic when merging into a empty one. (#402) (#403)
+- Add documentation of compatible exporters in the README. (#405)
+- Typo fix. (#408)
+- Simplify span check logic in SDK tracer implementation. (#419)
+
+## [0.2.0] - 2019-12-03
+
+### Added
+
+- Unary gRPC tracing example. (#351)
+- Prometheus exporter. (#334)
+- Dogstatsd metrics exporter. (#326)
+
+### Changed
+
+- Rename `MaxSumCount` aggregation to `MinMaxSumCount` and add the `Min` interface for this aggregation. (#352)
+- Rename `GetMeter` to `Meter`. (#357)
+- Rename `HTTPTraceContextPropagator` to `TraceContextPropagator`. (#355)
+- Rename `HTTPB3Propagator` to `B3Propagator`. (#355)
+- Rename `HTTPTraceContextPropagator` to `TraceContextPropagator`. (#355)
+- Move `/global` package to `/api/global`. (#356)
+- Rename `GetTracer` to `Tracer`. (#347)
+
+### Removed
+
+- `SetAttribute` from the `Span` interface in the trace API. (#361)
+- `AddLink` from the `Span` interface in the trace API. (#349)
+- `Link` from the `Span` interface in the trace API. (#349)
+
+### Fixed
+
+- Exclude example directories from coverage report. (#365)
+- Lint make target now implements automatic fixes with `golangci-lint` before a second run to report the remaining issues. (#360)
+- Drop `GO111MODULE` environment variable in Makefile as Go 1.13 is the project specified minimum version and this is environment variable is not needed for that version of Go. (#359)
+- Run the race checker for all test. (#354)
+- Redundant commands in the Makefile are removed. (#354)
+- Split the `generate` and `lint` targets of the Makefile. (#354)
+- Renames `circle-ci` target to more generic `ci` in Makefile. (#354)
+- Add example Prometheus binary to gitignore. (#358)
+- Support negative numbers with the `MaxSumCount`. (#335)
+- Resolve race conditions in `push_test.go` identified in #339. (#340)
+- Use `/usr/bin/env bash` as a shebang in scripts rather than `/bin/bash`. (#336)
+- Trace benchmark now tests both `AlwaysSample` and `NeverSample`.
+   Previously it was testing `AlwaysSample` twice. (#325)
+- Trace benchmark now uses a `[]byte` for `TraceID` to fix failing test. (#325)
+- Added a trace benchmark to test variadic functions in `setAttribute` vs `setAttributes` (#325)
+- The `defaultkeys` batcher was only using the encoded label set as its map key while building a checkpoint.
+   This allowed distinct label sets through, but any metrics sharing a label set could be overwritten or merged incorrectly.
+   This was corrected. (#333)
+
+
+## [0.1.2] - 2019-11-18
+
+### Fixed
+
+- Optimized the `simplelru` map for attributes to reduce the number of allocations. (#328)
+- Removed unnecessary unslicing of parameters that are already a slice. (#324)
+
+## [0.1.1] - 2019-11-18
+
+This release contains a Metrics SDK with stdout exporter and supports basic aggregations such as counter, gauges, array, maxsumcount, and ddsketch.
+
+### Added
+
+- Metrics stdout export pipeline. (#265)
+- Array aggregation for raw measure metrics. (#282)
+- The core.Value now have a `MarshalJSON` method. (#281)
+
+### Removed
+
+- `WithService`, `WithResources`, and `WithComponent` methods of tracers. (#314)
+- Prefix slash in `Tracer.Start()` for the Jaeger example. (#292)
+
+### Changed
+
+- Allocation in LabelSet construction to reduce GC overhead. (#318)
+- `trace.WithAttributes` to append values instead of replacing (#315)
+- Use a formula for tolerance in sampling tests. (#298)
+- Move export types into trace and metric-specific sub-directories. (#289)
+- `SpanKind` back to being based on an `int` type. (#288)
+
+### Fixed
+
+- URL to OpenTelemetry website in README. (#323)
+- Name of othttp default tracer. (#321)
+- `ExportSpans` for the stackdriver exporter now handles `nil` context. (#294)
+- CI modules cache to correctly restore/save from/to the cache. (#316)
+- Fix metric SDK race condition between `LoadOrStore` and the assignment `rec.recorder = i.meter.exporter.AggregatorFor(rec)`. (#293)
+- README now reflects the new code structure introduced with these changes. (#291)
+- Make the basic example work. (#279)
+
+## [0.1.0] - 2019-11-04
+
+This is the first release of open-telemetry go library.
+It contains api and sdk for trace and meter.
+
+### Added
+
+- Initial OpenTelemetry trace and metric API prototypes.
+- Initial OpenTelemetry trace, metric, and export SDK packages.
+- A wireframe bridge to support compatibility with OpenTracing.
+- Example code for a basic, http-stackdriver, http, jaeger, and named tracer setup.
+- Exporters for Jaeger, Stackdriver, and stdout.
+- Propagators for binary, B3, and trace-context protocols.
+- Project information and guidelines in the form of a README and CONTRIBUTING.
+- Tools to build the project and a Makefile to automate the process.
+- Apache-2.0 license.
+- CircleCI build CI manifest files.
+- CODEOWNERS file to track owners of this project.
+
+
+[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v0.16.0...HEAD
+[0.16.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.16.0
+[0.15.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.15.0
+[0.14.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.14.0
+[0.13.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.13.0
+[0.12.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.12.0
+[0.11.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.11.0
+[0.10.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.10.0
+[0.9.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.9.0
+[0.8.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.8.0
+[0.7.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.7.0
+[0.6.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.6.0
+[0.5.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.5.0
+[0.4.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.3
+[0.4.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.2
+[0.4.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.1
+[0.4.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.4.0
+[0.3.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.3.0
+[0.2.3]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.3
+[0.2.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.2
+[0.2.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.1.1
+[0.2.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.1
+[0.2.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.2.0
+[0.1.2]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.2
+[0.1.1]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.1
+[0.1.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v0.1.0
diff --git a/vendor/go.opentelemetry.io/otel/CODEOWNERS b/vendor/go.opentelemetry.io/otel/CODEOWNERS
new file mode 100644
index 0000000000..086072cb17
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/CODEOWNERS
@@ -0,0 +1,17 @@
+#####################################################
+#
+# List of approvers for this repository
+#
+#####################################################
+#
+# Learn about membership in OpenTelemetry community:
+#  https://github.com/open-telemetry/community/blob/master/community-membership.md
+#
+#
+# Learn about CODEOWNERS file format:
+#  https://help.github.com/en/articles/about-code-owners
+#
+
+* @jmacd @lizthegrey @MrAlias @Aneurysm9 @evantorrie @XSAM @dashpole
+
+CODEOWNERS @MrAlias @Aneurysm9
diff --git a/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md b/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md
new file mode 100644
index 0000000000..b34f57cdeb
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md
@@ -0,0 +1,374 @@
+# Contributing to opentelemetry-go
+
+The Go special interest group (SIG) meets regularly. See the
+OpenTelemetry
+[community](https://github.com/open-telemetry/community#golang-sdk)
+repo for information on this and other language SIGs.
+
+See the [public meeting
+notes](https://docs.google.com/document/d/1A63zSWX0x2CyCK_LoNhmQC4rqhLpYXJzXbEPDUQ2n6w/edit#heading=h.9tngw7jdwd6b)
+for a summary description of past meetings. To request edit access,
+join the meeting or get in touch on
+[Gitter](https://gitter.im/open-telemetry/opentelemetry-go).
+
+## Development
+
+You can view and edit the source code by cloning this repository:
+
+```bash
+git clone https://github.com/open-telemetry/opentelemetry-go.git
+```
+
+Run `make test` to run the tests instead of `go test`. 
+
+There are some generated files checked into the repo. To make sure
+that the generated files are up-to-date, run `make` (or `make
+precommit` - the `precommit` target is the default).
+
+The `precommit` target also fixes the formatting of the code and
+checks the status of the go module files.
+
+If after running `make precommit` the output of `git status` contains
+`nothing to commit, working tree clean` then it means that everything
+is up-to-date and properly formatted.
+
+## Pull Requests
+
+### How to Send Pull Requests
+
+Everyone is welcome to contribute code to `opentelemetry-go` via
+GitHub pull requests (PRs).
+
+To create a new PR, fork the project in GitHub and clone the upstream
+repo:
+
+```sh
+$ go get -d go.opentelemetry.io/otel
+```
+
+(This may print some warning about "build constraints exclude all Go
+files", just ignore it.)
+
+This will put the project in `${GOPATH}/src/go.opentelemetry.io/otel`. You
+can alternatively use `git` directly with:
+
+```sh
+$ git clone https://github.com/open-telemetry/opentelemetry-go
+```
+
+(Note that `git clone` is *not* using the `go.opentelemetry.io/otel` name -
+that name is a kind of a redirector to GitHub that `go get` can
+understand, but `git` does not.)
+
+This would put the project in the `opentelemetry-go` directory in
+current working directory.
+
+Enter the newly created directory and add your fork as a new remote:
+
+```sh
+$ git remote add <YOUR_FORK> git@github.com:<YOUR_GITHUB_USERNAME>/opentelemetry-go
+```
+
+Check out a new branch, make modifications, run linters and tests, update
+`CHANGELOG.md`, and push the branch to your fork:
+
+```sh
+$ git checkout -b <YOUR_BRANCH_NAME>
+# edit files
+# update changelog
+$ make precommit
+$ git add -p
+$ git commit
+$ git push <YOUR_FORK> <YOUR_BRANCH_NAME>
+```
+
+Open a pull request against the main `opentelemetry-go` repo. Be sure to add the pull
+request ID to the entry you added to `CHANGELOG.md`.
+
+### How to Receive Comments
+
+* If the PR is not ready for review, please put `[WIP]` in the title,
+  tag it as `work-in-progress`, or mark it as
+  [`draft`](https://github.blog/2019-02-14-introducing-draft-pull-requests/).
+* Make sure CLA is signed and CI is clear.
+
+### How to Get PRs Merged
+
+A PR is considered to be **ready to merge** when:
+
+* It has received two approvals from Collaborators/Maintainers (at
+  different companies). This is not enforced through technical means
+  and a PR may be **ready to merge** with a single approval if the change
+  and its approach have been discussed and consensus reached.
+* Major feedbacks are resolved.
+* It has been open for review for at least one working day. This gives
+  people reasonable time to review.
+* Trivial changes (typo, cosmetic, doc, etc.) do not have to wait for
+  one day and may be merged with a single Maintainer's approval.
+* `CHANGELOG.md` has been updated to reflect what has been
+  added, changed, removed, or fixed.
+* Urgent fix can take exception as long as it has been actively
+  communicated.
+
+Any Maintainer can merge the PR once it is **ready to merge**.
+
+## Design Choices
+
+As with other OpenTelemetry clients, opentelemetry-go follows the
+[opentelemetry-specification](https://github.com/open-telemetry/opentelemetry-specification).
+
+It's especially valuable to read through the [library
+guidelines](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/library-guidelines.md).
+
+### Focus on Capabilities, Not Structure Compliance
+
+OpenTelemetry is an evolving specification, one where the desires and
+use cases are clear, but the method to satisfy those uses cases are
+not.
+
+As such, Contributions should provide functionality and behavior that
+conforms to the specification, but the interface and structure is
+flexible.
+
+It is preferable to have contributions follow the idioms of the
+language rather than conform to specific API names or argument
+patterns in the spec.
+
+For a deeper discussion, see:
+https://github.com/open-telemetry/opentelemetry-specification/issues/165
+
+## Style Guide
+
+One of the primary goals of this project is that it is actually used by
+developers. With this goal in mind the project strives to build
+user-friendly and idiomatic Go code adhering to the Go community's best
+practices.
+
+For a non-comprehensive but foundational overview of these best practices
+the [Effective Go](https://golang.org/doc/effective_go.html) documentation
+is an excellent starting place.
+
+As a convenience for developers building this project the `make precommit`
+will format, lint, validate, and in some cases fix the changes you plan to
+submit. This check will need to pass for your changes to be able to be
+merged.
+
+In addition to idiomatic Go, the project has adopted certain standards for
+implementations of common patterns. These standards should be followed as a
+default, and if they are not followed documentation needs to be included as
+to the reasons why.
+
+### Configuration
+
+When creating an instantiation function for a complex `struct` it is useful
+to allow variable number of options to be applied. However, the strong type
+system of Go restricts the function design options. There are a few ways to
+solve this problem, but we have landed on the following design.
+
+#### `config`
+
+Configuration should be held in a `struct` named `config`, or prefixed with
+specific type name this Configuration applies to if there are multiple
+`config` in the package. This `struct` must contain configuration options.
+
+```go
+// config contains configuration options for a thing.
+type config struct {
+    // options ...
+}
+```
+
+In general the `config` `struct` will not need to be used externally to the
+package and should be unexported. If, however, it is expected that the user
+will likely want to build custom options for the configuration, the `config`
+should be exported. Please, include in the documentation for the `config`
+how the user can extend the configuration.
+
+It is important that `config` are not shared across package boundaries.
+Meaning a `config` from one package should not be directly used by another.
+
+Optionally, it is common to include a `newConfig` function (with the same
+naming scheme). This function wraps any defaults setting and looping over
+all options to create a configured `config`.
+
+```go
+// newConfig returns an appropriately configured config.
+func newConfig([]Option) config {
+    // Set default values for config.
+    config := config{/* […] */}
+    for _, option := range options {
+        option.Apply(&config)
+    }
+    // Preform any validation here.
+    return config
+}
+```
+
+If validation of the `config` options is also preformed this can return an
+error as well that is expected to be handled by the instantiation function
+or propagated to the user.
+
+Given the design goal of not having the user need to work with the `config`,
+the `newConfig` function should also be unexported.
+
+#### `Option`
+
+To set the value of the options a `config` contains, a corresponding
+`Option` interface type should be used.
+
+```go
+type Option interface {
+  Apply(*config)
+}
+```
+
+The name of the interface should be prefixed in the same way the
+corresponding `config` is (if at all).
+
+#### Options
+
+All user configurable options for a `config` must have a related unexported
+implementation of the `Option` interface and an exported configuration
+function that wraps this implementation.
+
+The wrapping function name should be prefixed with `With*` (or in the
+special case of a boolean options `Without*`) and should have the following
+function signature.
+
+```go
+func With*(…) Option { … }
+```
+
+##### `bool` Options
+
+```go
+type defaultFalseOption bool
+
+func (o defaultFalseOption) Apply(c *config) {
+    c.Bool = bool(o)
+}
+
+// WithOption sets a T* to have an option included.
+func WithOption() Option {
+    return defaultFalseOption(true)
+}
+```
+
+```go
+type defaultTrueOption bool
+
+func (o defaultTrueOption) Apply(c *config) {
+    c.Bool = bool(o)
+}
+
+// WithoutOption sets a T* to have Bool option excluded.
+func WithoutOption() Option {
+    return defaultTrueOption(false)
+}
+````
+
+##### Declared Type Options
+
+```go
+type myTypeOption struct {
+    MyType MyType
+}
+
+func (o myTypeOption) Apply(c *config) {
+    c.MyType = o.MyType
+}
+
+// WithMyType sets T* to have include MyType.
+func WithMyType(t MyType) Option {
+    return myTypeOption{t}
+}
+```
+
+#### Instantiation
+
+Using this configuration pattern to configure instantiation with a `New*`
+function.
+
+```go
+func NewT*(options ...Option) T* {…}
+```
+
+Any required parameters can be declared before the variadic `options`.
+
+#### Dealing with Overlap
+
+Sometimes there are multiple complex `struct` that share common
+configuration and also have distinct configuration. To avoid repeated
+portions of `config`s, a common `config` can be used with the union of
+options being handled with the `Option` interface.
+
+For example.
+
+```go
+// config holds options for all animals.
+type config struct {
+	Weight      float64
+	Color       string
+	MaxAltitude float64
+}
+
+// DogOption apply Dog specific options.
+type DogOption interface {
+	ApplyDog(*config)
+}
+
+// BirdOption apply Bird specific options.
+type BirdOption interface {
+	ApplyBird(*config)
+}
+
+// Option apply options for all animals.
+type Option interface {
+	BirdOption
+	DogOption
+}
+
+type weightOption float64
+func (o weightOption) ApplyDog(c *config)  { c.Weight = float64(o) }
+func (o weightOption) ApplyBird(c *config) { c.Weight = float64(o) }
+func WithWeight(w float64) Option          { return weightOption(w) }
+
+type furColorOption string
+func (o furColorOption) ApplyDog(c *config) { c.Color = string(o) }
+func WithFurColor(c string) DogOption       { return furColorOption(c) }
+
+type maxAltitudeOption float64
+func (o maxAltitudeOption) ApplyBird(c *config) { c.MaxAltitude = float64(o) }
+func WithMaxAltitude(a float64) BirdOption      { return maxAltitudeOption(a) }
+
+func NewDog(name string, o ...DogOption) Dog    {…}
+func NewBird(name string, o ...BirdOption) Bird {…}
+```
+
+### Interface Type
+
+To allow other developers to better comprehend the code, it is important
+to ensure it is sufficiently documented. One simple measure that contributes
+to this aim is self-documenting by naming method parameters. Therefore,
+where appropriate, methods of every exported interface type should have
+their parameters appropriately named.
+
+## Approvers and Maintainers
+
+Approvers:
+
+- [Liz Fong-Jones](https://github.com/lizthegrey), Honeycomb
+- [Evan Torrie](https://github.com/evantorrie), Verizon Media
+- [Josh MacDonald](https://github.com/jmacd), LightStep
+- [Sam Xie](https://github.com/XSAM)
+- [David Ashpole](https://github.com/dashpole), Google
+
+Maintainers:
+
+- [Anthony Mirabella](https://github.com/Aneurysm9), Centene
+- [Tyler Yahn](https://github.com/MrAlias), New Relic
+
+### Become an Approver or a Maintainer
+
+See the [community membership document in OpenTelemetry community
+repo](https://github.com/open-telemetry/community/blob/master/community-membership.md).
diff --git a/vendor/go.opentelemetry.io/otel/LICENSE b/vendor/go.opentelemetry.io/otel/LICENSE
new file mode 100644
index 0000000000..261eeb9e9f
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/vendor/go.opentelemetry.io/otel/Makefile b/vendor/go.opentelemetry.io/otel/Makefile
new file mode 100644
index 0000000000..130be713a8
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/Makefile
@@ -0,0 +1,177 @@
+# Copyright The OpenTelemetry Authors
+#
+# 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.
+
+EXAMPLES := $(shell ./get_main_pkgs.sh ./example)
+TOOLS_MOD_DIR := ./internal/tools
+
+# All source code and documents. Used in spell check.
+ALL_DOCS := $(shell find . -name '*.md' -type f | sort)
+# All directories with go.mod files related to opentelemetry library. Used for building, testing and linting.
+ALL_GO_MOD_DIRS := $(filter-out $(TOOLS_MOD_DIR), $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example' | sort)) $(shell find ./example -type f -name 'go.mod' -exec dirname {} \; | sort)
+ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | egrep -v '^./example|^$(TOOLS_MOD_DIR)' | sort)
+
+# Mac OS Catalina 10.5.x doesn't support 386. Hence skip 386 test
+SKIP_386_TEST = false
+UNAME_S := $(shell uname -s)
+ifeq ($(UNAME_S),Darwin)
+	SW_VERS := $(shell sw_vers -productVersion)
+	ifeq ($(shell echo $(SW_VERS) | egrep '^(10.1[5-9]|1[1-9]|[2-9])'), $(SW_VERS))
+		SKIP_386_TEST = true
+	endif
+endif
+
+GOTEST_MIN = go test -timeout 30s
+GOTEST = $(GOTEST_MIN) -race
+GOTEST_WITH_COVERAGE = $(GOTEST) -coverprofile=coverage.out -covermode=atomic -coverpkg=./...
+
+.DEFAULT_GOAL := precommit
+
+.PHONY: precommit
+
+TOOLS_DIR := $(abspath ./.tools)
+
+$(TOOLS_DIR)/golangci-lint: $(TOOLS_MOD_DIR)/go.mod $(TOOLS_MOD_DIR)/go.sum $(TOOLS_MOD_DIR)/tools.go
+	cd $(TOOLS_MOD_DIR) && \
+	go build -o $(TOOLS_DIR)/golangci-lint github.com/golangci/golangci-lint/cmd/golangci-lint
+
+$(TOOLS_DIR)/misspell: $(TOOLS_MOD_DIR)/go.mod $(TOOLS_MOD_DIR)/go.sum $(TOOLS_MOD_DIR)/tools.go
+	cd $(TOOLS_MOD_DIR) && \
+	go build -o $(TOOLS_DIR)/misspell github.com/client9/misspell/cmd/misspell
+
+$(TOOLS_DIR)/stringer: $(TOOLS_MOD_DIR)/go.mod $(TOOLS_MOD_DIR)/go.sum $(TOOLS_MOD_DIR)/tools.go
+	cd $(TOOLS_MOD_DIR) && \
+	go build -o $(TOOLS_DIR)/stringer golang.org/x/tools/cmd/stringer
+
+$(TOOLS_DIR)/gojq: $(TOOLS_MOD_DIR)/go.mod $(TOOLS_MOD_DIR)/go.sum $(TOOLS_MOD_DIR)/tools.go
+	cd $(TOOLS_MOD_DIR) && \
+	go build -o $(TOOLS_DIR)/gojq github.com/itchyny/gojq/cmd/gojq
+
+precommit: dependabot-check license-check generate build lint examples test-benchmarks test
+
+.PHONY: test-with-coverage
+test-with-coverage:
+	set -e; \
+	printf "" > coverage.txt; \
+	for dir in $(ALL_COVERAGE_MOD_DIRS); do \
+	  echo "go test ./... + coverage in $${dir}"; \
+	  (cd "$${dir}" && \
+	 	$(GOTEST_WITH_COVERAGE) ./... && \
+		go tool cover -html=coverage.out -o coverage.html); \
+      [ -f "$${dir}/coverage.out" ] && cat "$${dir}/coverage.out" >> coverage.txt; \
+	done; \
+	sed -i.bak -e '2,$$ { /^mode: /d; }' coverage.txt
+
+
+.PHONY: ci
+ci: precommit check-clean-work-tree test-with-coverage test-386
+
+.PHONY: check-clean-work-tree
+check-clean-work-tree:
+	@if ! git diff --quiet; then \
+	  echo; \
+	  echo 'Working tree is not clean, did you forget to run "make precommit"?'; \
+	  echo; \
+	  git status; \
+	  exit 1; \
+	fi
+
+.PHONY: build
+build:
+	# TODO: Fix this on windows.
+	set -e; for dir in $(ALL_GO_MOD_DIRS); do \
+	  echo "compiling all packages in $${dir}"; \
+	  (cd "$${dir}" && \
+	    go build ./... && \
+	    go test -run xxxxxMatchNothingxxxxx ./... >/dev/null); \
+	done
+
+.PHONY: test
+test:
+	set -e; for dir in $(ALL_GO_MOD_DIRS); do \
+	  echo "go test ./... + race in $${dir}"; \
+	  (cd "$${dir}" && \
+	    $(GOTEST) ./...); \
+	done
+
+.PHONY: test-386
+test-386:
+	if [ $(SKIP_386_TEST) = true ] ; then \
+	  echo "skipping the test for GOARCH 386 as it is not supported on the current OS"; \
+	else \
+	  set -e; for dir in $(ALL_GO_MOD_DIRS); do \
+	    echo "go test ./... GOARCH 386 in $${dir}"; \
+	    (cd "$${dir}" && \
+	      GOARCH=386 $(GOTEST_MIN) ./...); \
+	  done; \
+	fi
+
+.PHONY: examples
+examples:
+	@set -e; for ex in $(EXAMPLES); do \
+	  echo "Building $${ex}"; \
+	  (cd "$${ex}" && \
+	   go build .); \
+	done
+
+.PHONY: test-benchmarks
+test-benchmarks:
+	@set -e; for dir in $(ALL_GO_MOD_DIRS); do \
+	  echo "test benchmarks in $${dir}"; \
+	  (cd "$${dir}" && go test -test.benchtime=1ms -run=NONE -bench=. ./...) > /dev/null; \
+	done
+
+.PHONY: lint
+lint: $(TOOLS_DIR)/golangci-lint $(TOOLS_DIR)/misspell
+	set -e; for dir in $(ALL_GO_MOD_DIRS); do \
+	  echo "golangci-lint in $${dir}"; \
+	  (cd "$${dir}" && \
+	    $(TOOLS_DIR)/golangci-lint run --fix && \
+	    $(TOOLS_DIR)/golangci-lint run); \
+	done
+	$(TOOLS_DIR)/misspell -w $(ALL_DOCS)
+	set -e; for dir in $(ALL_GO_MOD_DIRS) $(TOOLS_MOD_DIR); do \
+	  echo "go mod tidy in $${dir}"; \
+	  (cd "$${dir}" && \
+	    go mod tidy); \
+	done
+
+generate: $(TOOLS_DIR)/stringer
+	set -e; for dir in $(ALL_GO_MOD_DIRS); do \
+	  echo "running generators in $${dir}"; \
+	  (cd "$${dir}" && \
+	    PATH="$(TOOLS_DIR):$${PATH}" go generate ./...); \
+	done
+
+.PHONY: license-check
+license-check:
+	@licRes=$$(for f in $$(find . -type f \( -iname '*.go' -o -iname '*.sh' \) ! -path './vendor/*' ! -path './exporters/otlp/internal/opentelemetry-proto/*') ; do \
+	           awk '/Copyright The OpenTelemetry Authors|generated|GENERATED/ && NR<=3 { found=1; next } END { if (!found) print FILENAME }' $$f; \
+	   done); \
+	   if [ -n "$${licRes}" ]; then \
+	           echo "license header checking failed:"; echo "$${licRes}"; \
+	           exit 1; \
+	   fi
+
+.PHONY: dependabot-check
+dependabot-check:
+	@result=$$( \
+		for f in $$( find . -type f -name go.mod -exec dirname {} \; | sed 's/^.\/\?/\//' ); \
+			do grep -q "$$f" .github/dependabot.yml \
+			|| echo "$$f"; \
+		done; \
+	); \
+	if [ -n "$$result" ]; then \
+		echo "missing go.mod dependabot check:"; echo "$$result"; \
+		exit 1; \
+	fi
diff --git a/vendor/go.opentelemetry.io/otel/Makefile.proto b/vendor/go.opentelemetry.io/otel/Makefile.proto
new file mode 100644
index 0000000000..5e51af3a5c
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/Makefile.proto
@@ -0,0 +1,129 @@
+#   -*- mode: makefile; -*-
+# Copyright The OpenTelemetry Authors
+#
+# 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.
+#
+# This Makefile.proto has rules to generate go code for otlp
+# exporter. It does it by copying the proto files from
+# `exporters/otlp/internal/opentelemetry-proto` (which is a
+# submodule that needs to be checked out) into `gen/proto`, changing
+# the go_package option to a valid string, generating the go files and
+# finally copying the files into the module. The files are not
+# generated in place, because protoc generates a too-deep directory
+# structure.
+#
+# Currently, all the generated code is in
+# `exporters/otlp/internal/opentelemetry-proto-gen`.
+#
+# Prereqs: wget (for downloading the zip file with protoc binary),
+# unzip (for unpacking the archive), rsync (for copying back the
+# generated files).
+
+PROTOC_VERSION := 3.14.0
+
+TOOLS_DIR                       := $(abspath ./.tools)
+TOOLS_MOD_DIR                   := ./internal/tools
+PROTOBUF_VERSION                := v1
+OTEL_PROTO_SUBMODULE            := exporters/otlp/internal/opentelemetry-proto
+GEN_TEMP_DIR                    := gen
+SUBMODULE_PROTO_FILES           := $(wildcard $(OTEL_PROTO_SUBMODULE)/opentelemetry/proto/*/$(PROTOBUF_VERSION)/*.proto) $(wildcard $(OTEL_PROTO_SUBMODULE)/opentelemetry/proto/collector/*/$(PROTOBUF_VERSION)/*.proto)
+
+ifeq ($(strip $(SUBMODULE_PROTO_FILES)),)
+$(error Submodule at $(OTEL_PROTO_SUBMODULE) is not checked out, use "git submodule update --init")
+endif
+
+PROTOBUF_GEN_DIR   := exporters/otlp/internal/opentelemetry-proto-gen
+PROTOBUF_TEMP_DIR  := $(GEN_TEMP_DIR)/pb-go
+PROTO_SOURCE_DIR   := $(GEN_TEMP_DIR)/proto
+SOURCE_PROTO_FILES := $(subst $(OTEL_PROTO_SUBMODULE),$(PROTO_SOURCE_DIR),$(SUBMODULE_PROTO_FILES))
+
+.DEFAULT_GOAL := protobuf
+
+UNAME_S := $(shell uname -s)
+UNAME_M := $(shell uname -m)
+
+ifeq ($(UNAME_S),Linux)
+
+PROTOC_OS := linux
+PROTOC_ARCH := $(UNAME_M)
+
+else ifeq ($(UNAME_S),Darwin)
+
+PROTOC_OS := osx
+PROTOC_ARCH := x86_64
+
+endif
+
+PROTOC_ZIP_URL := https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_VERSION)-$(PROTOC_OS)-$(PROTOC_ARCH).zip
+
+$(TOOLS_DIR)/PROTOC_$(PROTOC_VERSION):
+	@rm -f "$(TOOLS_DIR)"/PROTOC_* && \
+	touch "$@"
+
+# Depend on a versioned file (like PROTOC_3.14.0), so when version
+# gets bumped, we will depend on a nonexistent file and thus download
+# a newer version.
+$(TOOLS_DIR)/protoc/bin/protoc: $(TOOLS_DIR)/PROTOC_$(PROTOC_VERSION)
+	echo "Fetching protoc $(PROTOC_VERSION)" && \
+	rm -rf $(TOOLS_DIR)/protoc && \
+	wget -O $(TOOLS_DIR)/protoc.zip $(PROTOC_ZIP_URL) && \
+	unzip $(TOOLS_DIR)/protoc.zip -d $(TOOLS_DIR)/protoc-tmp && \
+	rm $(TOOLS_DIR)/protoc.zip && \
+	touch $(TOOLS_DIR)/protoc-tmp/bin/protoc && \
+	mv $(TOOLS_DIR)/protoc-tmp $(TOOLS_DIR)/protoc
+
+$(TOOLS_DIR)/protoc-gen-gogofast: $(TOOLS_MOD_DIR)/go.mod $(TOOLS_MOD_DIR)/go.sum $(TOOLS_MOD_DIR)/tools.go
+	cd $(TOOLS_MOD_DIR) && \
+	go build -o $(TOOLS_DIR)/protoc-gen-gogofast github.com/gogo/protobuf/protoc-gen-gogofast && \
+	go mod tidy
+
+# Return a sed expression for replacing the go_package option in proto
+# file with a one that's valid for us.
+#
+# Example: $(call get-sed-expr,$(PROTOBUF_GEN_DIR))
+define get-sed-expr
+'s,go_package = "github.com/open-telemetry/opentelemetry-proto/gen/go,go_package = "go.opentelemetry.io/otel/$(1),'
+endef
+
+.PHONY: protobuf
+protobuf: protobuf-source gen-protobuf copy-protobufs
+
+.PHONY: protobuf-source
+protobuf-source: $(SOURCE_PROTO_FILES)
+
+# This copies proto files from submodule into $(PROTO_SOURCE_DIR),
+# thus satisfying the $(SOURCE_PROTO_FILES) prerequisite. The copies
+# have their package name replaced by go.opentelemetry.io/otel.
+$(PROTO_SOURCE_DIR)/%.proto: $(OTEL_PROTO_SUBMODULE)/%.proto
+	@ \
+	mkdir -p $(@D); \
+	sed -e $(call get-sed-expr,$(PROTOBUF_GEN_DIR)) "$<" >"$@.tmp"; \
+	mv "$@.tmp" "$@"
+
+.PHONY: gen-protobuf
+gen-protobuf: $(SOURCE_PROTO_FILES) $(TOOLS_DIR)/protoc-gen-gogofast $(TOOLS_DIR)/protoc/bin/protoc
+	@ \
+	mkdir -p "$(PROTOBUF_TEMP_DIR)"; \
+	set -e; for f in $^; do \
+	  if [[ "$${f}" == $(TOOLS_DIR)/* ]]; then continue; fi; \
+	  echo "protoc $${f#"$(PROTO_SOURCE_DIR)/"}"; \
+	  PATH="$(TOOLS_DIR):$${PATH}" $(TOOLS_DIR)/protoc/bin/protoc --proto_path="$(PROTO_SOURCE_DIR)" --gogofast_out="plugins=grpc:$(PROTOBUF_TEMP_DIR)" "$${f}"; \
+	done
+
+.PHONY: copy-protobufs
+copy-protobufs:
+	@rsync -a $(PROTOBUF_TEMP_DIR)/go.opentelemetry.io/otel/exporters .
+
+.PHONY: clean
+clean:
+	rm -rf $(GEN_TEMP_DIR)
diff --git a/vendor/go.opentelemetry.io/otel/README.md b/vendor/go.opentelemetry.io/otel/README.md
new file mode 100644
index 0000000000..4a15a2f42e
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/README.md
@@ -0,0 +1,71 @@
+# OpenTelemetry-Go
+
+[![CI](https://github.com/open-telemetry/opentelemetry-go/workflows/ci/badge.svg)](https://github.com/open-telemetry/opentelemetry-go/actions?query=workflow%3Aci+branch%3Amaster)
+[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel)](https://pkg.go.dev/go.opentelemetry.io/otel)
+[![Go Report Card](https://goreportcard.com/badge/go.opentelemetry.io/otel)](https://goreportcard.com/report/go.opentelemetry.io/otel)
+[![Gitter](https://badges.gitter.im/open-telemetry/opentelemetry-go.svg)](https://gitter.im/open-telemetry/opentelemetry-go?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+
+The Go [OpenTelemetry](https://opentelemetry.io/) implementation.
+
+## Project Status
+
+**Warning**: this project is currently in a pre-GA phase. Backwards
+incompatible changes may be introduced in subsequent minor version releases as
+we work to track the evolving OpenTelemetry specification and user feedback.
+
+Our progress towards a GA release candidate is tracked in [this project
+board](https://github.com/orgs/open-telemetry/projects/5). This release
+candidate will follow semantic versioning and will be released with a major
+version greater than zero.
+
+Progress and status specific to this repository is tracked in our local
+[project boards](https://github.com/open-telemetry/opentelemetry-go/projects)
+and
+[milestones](https://github.com/open-telemetry/opentelemetry-go/milestones).
+
+Project versioning information and stability guarantees can be found in the
+[versioning documentation](./VERSIONING.md).
+
+## Getting Started
+
+You can find a getting started guide on [opentelemetry.io](https://opentelemetry.io/docs/go/getting-started/).
+
+OpenTelemetry's goal is to provide a single set of APIs to capture distributed
+traces and metrics from your application and send them to an observability
+platform. This project allows you to do just that for applications written in
+Go. There are two steps to this process: instrument your application, and
+configure an exporter.
+
+### Instrumentation
+
+To start capturing distributed traces and metric events from your application
+it first needs to be instrumented. The easiest way to do this is by using an
+instrumentation library for your code. Be sure to check out [the officially
+supported instrumentation
+libraries](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/master/instrumentation).
+
+If you need to extend the telemetry an instrumentation library provides or want
+to build your own instrumentation for your application directly you will need
+to use the
+[go.opentelemetry.io/otel/api](https://pkg.go.dev/go.opentelemetry.io/otel/api)
+package. The included [examples](./example/) are a good way to see some
+practical uses of this process.
+
+### Export
+
+Now that your application is instrumented to collect telemetry, it needs an
+export pipeline to send that telemetry to an observability platform.
+
+You can find officially supported exporters [here](./exporters/) and in the
+companion [contrib
+repository](https://github.com/open-telemetry/opentelemetry-go-contrib/tree/master/exporters/metric).
+Additionally, there are many vendor specific or 3rd party exporters for
+OpenTelemetry. These exporters are broken down by
+[trace](https://pkg.go.dev/go.opentelemetry.io/otel/sdk/export/trace?tab=importedby)
+and
+[metric](https://pkg.go.dev/go.opentelemetry.io/otel/sdk/export/metric?tab=importedby)
+support.
+
+## Contributing
+
+See the [contributing documentation](CONTRIBUTING.md).
diff --git a/vendor/go.opentelemetry.io/otel/RELEASING.md b/vendor/go.opentelemetry.io/otel/RELEASING.md
new file mode 100644
index 0000000000..0fba7f1756
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/RELEASING.md
@@ -0,0 +1,81 @@
+# Release Process
+
+## Pre-Release
+
+Update go.mod for submodules to depend on the new release which will happen in the next step.
+
+1. Run the pre-release script. It creates a branch `pre_release_<new tag>` that will contain all release changes.
+
+    ```
+    ./pre_release.sh -t <new tag>
+    ```
+
+2. Verify the changes.
+
+    ```
+    git diff master
+    ```
+
+    This should have changed the version for all modules to be `<new tag>`.
+
+3. Update the [Changelog](./CHANGELOG.md).
+   - Make sure all relevant changes for this release are included and are in language that non-contributors to the project can understand.
+       To verify this, you can look directly at the commits since the `<last tag>`.
+
+       ```
+       git --no-pager log --pretty=oneline "<last tag>..HEAD"
+       ```
+
+   - Move all the `Unreleased` changes into a new section following the title scheme (`[<new tag>] - <date of release>`).
+   - Update all the appropriate links at the bottom.
+
+4. Push the changes to upstream and create a Pull Request on GitHub.
+    Be sure to include the curated changes from the [Changelog](./CHANGELOG.md) in the description.
+
+
+## Tag
+
+Once the Pull Request with all the version changes has been approved and merged it is time to tag the merged commit.
+
+***IMPORTANT***: It is critical you use the same tag that you used in the Pre-Release step!
+Failure to do so will leave things in a broken state.
+
+***IMPORTANT***: [There is currently no way to remove an incorrectly tagged version of a Go module](https://github.com/golang/go/issues/34189).
+It is critical you make sure the version you push upstream is correct.
+[Failure to do so will lead to minor emergencies and tough to work around](https://github.com/open-telemetry/opentelemetry-go/issues/331).
+
+1. Run the tag.sh script using the `<commit-hash>` of the commit on the master branch for the merged Pull Request.
+
+    ```
+    ./tag.sh <new tag> <commit-hash>
+    ```
+
+2. Push tags to the upstream remote (not your fork: `github.com/open-telemetry/opentelemetry-go.git`).
+    Make sure you push all sub-modules as well.
+
+    ```
+    git push upstream <new tag>
+    git push upstream <submodules-path/new tag>
+    ...
+    ```
+
+## Release
+
+Finally create a Release for the new `<new tag>` on GitHub.
+The release body should include all the release notes from the Changelog for this release.
+Additionally, the `tag.sh` script generates commit logs since last release which can be used to supplement the release notes.
+
+## Verify Examples
+
+After releasing verify that examples build outside of the repository.
+
+```
+./verify_examples.sh
+```
+
+The script copies examples into a different directory removes any `replace` declarations in `go.mod` and builds them.
+This ensures they build with the published release, not the local copy.
+
+## Contrib Repository
+
+Once verified be sure to [make a release for the `contrib` repository](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/master/RELEASING.md) that uses this release.
diff --git a/vendor/go.opentelemetry.io/otel/VERSIONING.md b/vendor/go.opentelemetry.io/otel/VERSIONING.md
new file mode 100644
index 0000000000..3579b794ee
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/VERSIONING.md
@@ -0,0 +1,217 @@
+# Versioning
+
+This document describes the versioning policy for this repository. This policy
+is designed so the following goals can be achieved.
+
+**Users are provided a codebase of value that is stable and secure.**
+
+## Policy
+
+* Versioning of this project will be idiomatic of a Go project using [Go
+  modules](https://github.com/golang/go/wiki/Modules).
+  * [Semantic import
+    versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning)
+    will be used.
+    * Versions will comply with [semver 2.0](https://semver.org/spec/v2.0.0.html).
+    * If a module is version `v2` or higher, the major version of the module
+      must be included as a `/vN` at the end of the module paths used in
+      `go.mod` files (e.g., `module go.opentelemetry.io/otel/v2`, `require
+      go.opentelemetry.io/otel/v2 v2.0.1`) and in the package import path
+      (e.g., `import "go.opentelemetry.io/otel/v2/trace"`). This includes the
+      paths used in `go get` commands (e.g., `go get
+      go.opentelemetry.io/otel/v2@v2.0.1`.  Note there is both a `/v2` and a
+      `@v2.0.1` in that example. One way to think about it is that the module
+      name now includes the `/v2`, so include `/v2` whenever you are using the
+      module name).
+    * If a module is version `v0` or `v1`, do not include the major version in
+      either the module path or the import path.
+  * Modules will be used to encapsulate signals and components.
+    * Experimental modules still under active development will be versioned at
+      `v0` to imply the stability guarantee defined by
+      [semver](https://semver.org/spec/v2.0.0.html#spec-item-4).
+
+      > Major version zero (0.y.z) is for initial development. Anything MAY
+      > change at any time. The public API SHOULD NOT be considered stable.
+
+    * Mature modules for which we guarantee a stable public API will be versioned
+      with a major version greater than `v0`.
+      * The decision to make a module stable will be made on a case-by-case
+        basis by the maintainers of this project.
+    * Experimental modules will start their versioning at `v0.0.0` and will
+      increment their minor version when backwards incompatible changes are
+      released and increment their patch version when backwards compatible
+      changes are released.
+    * All stable modules that use the same major version number will use the
+      same entire version number.
+      * Stable modules may be released with an incremented minor or patch
+        version even though that module has not been changed, but rather so
+        that it will remain at the same version as other stable modules that
+        did undergo change.
+      * When an experimental module becomes stable a new stable module version
+        will be released and will include this now stable module. The new
+        stable module version will be an increment of the minor version number
+        and will be applied to all existing stable modules as well as the newly
+        stable module being released.
+* Versioning of the associated [contrib
+  repository](https://github.com/open-telemetry/opentelemetry-go-contrib) of
+  this project will be idiomatic of a Go project using [Go
+  modules](https://github.com/golang/go/wiki/Modules).
+  * [Semantic import
+    versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning)
+    will be used.
+    * Versions will comply with [semver 2.0](https://semver.org/spec/v2.0.0.html).
+    * If a module is version `v2` or higher, the
+      major version of the module must be included as a `/vN` at the end of the
+      module paths used in `go.mod` files (e.g., `module
+      go.opentelemetry.io/contrib/instrumentation/host/v2`, `require
+      go.opentelemetry.io/contrib/instrumentation/host/v2 v2.0.1`) and in the
+      package import path (e.g., `import
+      "go.opentelemetry.io/contrib/instrumentation/host/v2"`). This includes
+      the paths used in `go get` commands (e.g., `go get
+      go.opentelemetry.io/contrib/instrumentation/host/v2@v2.0.1`.  Note there
+      is both a `/v2` and a `@v2.0.1` in that example. One way to think about
+      it is that the module name now includes the `/v2`, so include `/v2`
+      whenever you are using the module name).
+    * If a module is version `v0` or `v1`, do not include the major version
+      in either the module path or the import path.
+  * In addition to public APIs, telemetry produced by stable instrumentation
+    will remain stable and backwards compatible. This is to avoid breaking
+    alerts and dashboard.
+  * Modules will be used to encapsulate instrumentation, detectors, exporters,
+    propagators, and any other independent sets of related components.
+    * Experimental modules still under active development will be versioned at
+      `v0` to imply the stability guarantee defined by
+      [semver](https://semver.org/spec/v2.0.0.html#spec-item-4).
+
+      > Major version zero (0.y.z) is for initial development. Anything MAY
+      > change at any time. The public API SHOULD NOT be considered stable.
+
+    * Mature modules for which we guarantee a stable public API and telemetry will
+      be versioned with a major version greater than `v0`.
+    * Experimental modules will start their versioning at `v0.0.0` and will
+      increment their minor version when backwards incompatible changes are
+      released and increment their patch version when backwards compatible
+      changes are released.
+    * Stable contrib modules cannot depend on experimental modules from this
+      project.
+    * All stable contrib modules of the same major version with this project
+      will use the same entire version as this project.
+      * Stable modules may be released with an incremented minor or patch
+        version even though that module's code has not been changed. Instead
+        the only change that will have been included is to have updated that
+        modules dependency on this project's stable APIs.
+      * When an experimental module in contrib becomes stable a new stable
+        module version will be released and will include this now stable
+        module. The new stable module version will be an increment of the minor
+        version number and will be applied to all existing stable contrib
+        modules, this project's modules, and the newly stable module being
+        released.
+  * Contrib modules will be kept up to date with this project's releases.
+    * Due to the dependency contrib modules will implicitly have on this
+      project's modules the release of stable contrib modules to match the
+      released version number will be staggered after this project's release.
+      There is no explicit time guarantee for how long after this projects
+      release the contrib release will be. Effort should be made to keep them
+      as close in time as possible.
+    * No additional stable release in this project can be made until the
+      contrib repository has a matching stable release.
+    * No release can be made in the contrib repository after this project's
+      stable release except for a stable release of the contrib repository.
+* GitHub releases will be made for all releases.
+* Go modules will be made available at Go package mirrors.
+
+## Example Versioning Lifecycle
+
+To better understand the implementation of the above policy the following
+example is provided. This project is simplified to include only the following
+modules and their versions:
+
+* `otel`: `v0.14.0`
+* `otel/trace`: `v0.14.0`
+* `otel/metric`: `v0.14.0`
+* `otel/baggage`: `v0.14.0`
+* `otel/sdk/trace`: `v0.14.0`
+* `otel/sdk/metric`: `v0.14.0`
+
+These modules have been developed to a point where the `otel/trace`,
+`otel/baggage`, and `otel/sdk/trace` modules have reached a point that they
+should be considered for a stable release. The `otel/metric` and
+`otel/sdk/metric` are still under active development and the `otel` module
+depends on both `otel/trace` and `otel/metric`.
+
+The `otel` package is refactored to remove its dependencies on `otel/metric` so
+it can be released as stable as well. With that done the following release
+candidates are made:
+
+* `otel`: `v1.0.0-rc.1`
+* `otel/trace`: `v1.0.0-rc.1`
+* `otel/baggage`: `v1.0.0-rc.1`
+* `otel/sdk/trace`: `v1.0.0-rc.1`
+
+The `otel/metric` and `otel/sdk/metric` modules remain at `v0.14.0`.
+
+A few minor issues are discovered in the `otel/trace` package. These issues are
+resolved with some minor, but backwards incompatible, changes and are released
+as a second release candidate:
+
+* `otel`: `v1.0.0-rc.2`
+* `otel/trace`: `v1.0.0-rc.2`
+* `otel/baggage`: `v1.0.0-rc.2`
+* `otel/sdk/trace`: `v1.0.0-rc.2`
+
+Notice that all module version numbers are incremented to adhere to our
+versioning policy.
+
+After these release candidates have been evaluated to satisfaction, they are
+released as version `v1.0.0`.
+
+* `otel`: `v1.0.0`
+* `otel/trace`: `v1.0.0`
+* `otel/baggage`: `v1.0.0`
+* `otel/sdk/trace`: `v1.0.0`
+
+Since both the `go` utility and the Go module system support [the semantic
+versioning definition of
+precedence](https://semver.org/spec/v2.0.0.html#spec-item-11), this release
+will correctly be interpreted as the successor to the previous release
+candidates.
+
+Active development of this project continues. The `otel/metric` module now has
+backwards incompatible changes to its API that need to be released and the
+`otel/baggage` module has a minor bug fix that needs to be released. The
+following release is made:
+
+* `otel`: `v1.0.1`
+* `otel/trace`: `v1.0.1`
+* `otel/metric`: `v0.15.0`
+* `otel/baggage`: `v1.0.1`
+* `otel/sdk/trace`: `v1.0.1`
+* `otel/sdk/metric`: `v0.15.0`
+
+Notice that, again, all stable module versions are incremented in unison and
+the `otel/sdk/metric` package, which depends on the `otel/metric` package, also
+bumped its version. This bump of the `otel/sdk/metric` package makes sense
+given their coupling, though it is not explicitly required by our versioning
+policy.
+
+As we progress, the `otel/metric` and `otel/sdk/metric` packages have reached a
+point where they should be evaluated for stability. The `otel` module is
+reintegrated with the `otel/metric` package and the following release is made:
+
+* `otel`: `v1.1.0-rc.1`
+* `otel/trace`: `v1.1.0-rc.1`
+* `otel/metric`: `v1.1.0-rc.1`
+* `otel/baggage`: `v1.1.0-rc.1`
+* `otel/sdk/trace`: `v1.1.0-rc.1`
+* `otel/sdk/metric`: `v1.1.0-rc.1`
+
+All the modules are evaluated and determined to a viable stable release. They
+are then released as version `v1.1.0` (the minor version is incremented to
+indicate the addition of new signal).
+
+* `otel`: `v1.1.0`
+* `otel/trace`: `v1.1.0`
+* `otel/metric`: `v1.1.0`
+* `otel/baggage`: `v1.1.0`
+* `otel/sdk/trace`: `v1.1.0`
+* `otel/sdk/metric`: `v1.1.0`
diff --git a/vendor/go.opentelemetry.io/otel/codes/codes.go b/vendor/go.opentelemetry.io/otel/codes/codes.go
new file mode 100644
index 0000000000..064a9279fd
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/codes/codes.go
@@ -0,0 +1,106 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 codes // import "go.opentelemetry.io/otel/codes"
+
+import (
+	"encoding/json"
+	"fmt"
+	"strconv"
+)
+
+const (
+	// Unset is the default status code.
+	Unset Code = 0
+	// Error indicates the operation contains an error.
+	Error Code = 1
+	// Ok indicates operation has been validated by an Application developers
+	// or Operator to have completed successfully, or contain no error.
+	Ok Code = 2
+
+	maxCode = 3
+)
+
+// Code is an 32-bit representation of a status state.
+type Code uint32
+
+var codeToStr = map[Code]string{
+	Unset: "Unset",
+	Error: "Error",
+	Ok:    "Ok",
+}
+
+var strToCode = map[string]Code{
+	`"Unset"`: Unset,
+	`"Error"`: Error,
+	`"Ok"`:    Ok,
+}
+
+// String returns the Code as a string.
+func (c Code) String() string {
+	return codeToStr[c]
+}
+
+// UnmarshalJSON unmarshals b into the Code.
+//
+// This is based on the functionality in the gRPC codes package:
+// https://github.com/grpc/grpc-go/blob/bb64fee312b46ebee26be43364a7a966033521b1/codes/codes.go#L218-L244
+func (c *Code) UnmarshalJSON(b []byte) error {
+	// From json.Unmarshaler: By convention, to approximate the behavior of
+	// Unmarshal itself, Unmarshalers implement UnmarshalJSON([]byte("null")) as
+	// a no-op.
+	if string(b) == "null" {
+		return nil
+	}
+	if c == nil {
+		return fmt.Errorf("nil receiver passed to UnmarshalJSON")
+	}
+
+	var x interface{}
+	if err := json.Unmarshal(b, &x); err != nil {
+		return err
+	}
+	switch x.(type) {
+	case string:
+		if jc, ok := strToCode[string(b)]; ok {
+			*c = jc
+			return nil
+		}
+		return fmt.Errorf("invalid code: %q", string(b))
+	case float64:
+		if ci, err := strconv.ParseUint(string(b), 10, 32); err == nil {
+			if ci >= maxCode {
+				return fmt.Errorf("invalid code: %q", ci)
+			}
+
+			*c = Code(ci)
+			return nil
+		}
+		return fmt.Errorf("invalid code: %q", string(b))
+	default:
+		return fmt.Errorf("invalid code: %q", string(b))
+	}
+}
+
+// MarshalJSON returns c as the JSON encoding of c.
+func (c *Code) MarshalJSON() ([]byte, error) {
+	if c == nil {
+		return []byte("null"), nil
+	}
+	str, ok := codeToStr[*c]
+	if !ok {
+		return nil, fmt.Errorf("invalid code: %d", *c)
+	}
+	return []byte(fmt.Sprintf("%q", str)), nil
+}
diff --git a/vendor/go.opentelemetry.io/otel/codes/doc.go b/vendor/go.opentelemetry.io/otel/codes/doc.go
new file mode 100644
index 0000000000..368f8818fd
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/codes/doc.go
@@ -0,0 +1,25 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 codes defines the canonical error codes used by OpenTelemetry.
+
+This package is currently in a pre-GA phase. Backwards incompatible changes
+may be introduced in subsequent minor version releases as we work to track
+the evolving OpenTelemetry specification and user feedback.
+
+It conforms to [the OpenTelemetry
+specification](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#statuscanonicalcode).
+*/
+package codes // import "go.opentelemetry.io/otel/codes"
diff --git a/vendor/go.opentelemetry.io/otel/doc.go b/vendor/go.opentelemetry.io/otel/doc.go
new file mode 100644
index 0000000000..771ce81cc2
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/doc.go
@@ -0,0 +1,38 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 otel provides global access to the OpenTelemetry API. The subpackages of
+the otel package provide an implementation of the OpenTelemetry API.
+
+This package is currently in a pre-GA phase. Backwards incompatible changes
+may be introduced in subsequent minor version releases as we work to track the
+evolving OpenTelemetry specification and user feedback.
+
+The provided API is used to instrument code and measure data about that code's
+performance and operation. The measured data, by default, is not processed or
+transmitted anywhere. An implementation of the OpenTelemetry SDK, like the
+default SDK implementation (go.opentelemetry.io/otel/sdk), and associated
+exporters are used to process and transport this data.
+
+To read the getting started guide, see https://opentelemetry.io/docs/go/getting-started/.
+
+To read more about tracing, see go.opentelemetry.io/otel/trace.
+
+To read more about metrics, see go.opentelemetry.io/otel/metric.
+
+To read more about propagation, see go.opentelemetry.io/otel/propagation and
+go.opentelemetry.io/otel/baggage.
+*/
+package otel // import "go.opentelemetry.io/otel"
diff --git a/vendor/go.opentelemetry.io/otel/error_handler.go b/vendor/go.opentelemetry.io/otel/error_handler.go
new file mode 100644
index 0000000000..ac42f8be07
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/error_handler.go
@@ -0,0 +1,22 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 otel // import "go.opentelemetry.io/otel"
+
+// ErrorHandler handles irremediable events.
+type ErrorHandler interface {
+	// Handle handles any error deemed irremediable by an OpenTelemetry
+	// component.
+	Handle(error)
+}
diff --git a/vendor/go.opentelemetry.io/otel/get_main_pkgs.sh b/vendor/go.opentelemetry.io/otel/get_main_pkgs.sh
new file mode 100644
index 0000000000..9a58fb1d37
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/get_main_pkgs.sh
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+
+# Copyright The OpenTelemetry Authors
+#
+# 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.
+
+set -euo pipefail
+
+top_dir='.'
+if [[ $# -gt 0 ]]; then
+    top_dir="${1}"
+fi
+
+p=$(pwd)
+mod_dirs=()
+
+# Note `mapfile` does not exist in older bash versions:
+# https://stackoverflow.com/questions/41475261/need-alternative-to-readarray-mapfile-for-script-on-older-version-of-bash
+
+while IFS= read -r line; do
+    mod_dirs+=("$line")
+done < <(find "${top_dir}" -type f -name 'go.mod' -exec dirname {} \; | sort)
+
+for mod_dir in "${mod_dirs[@]}"; do
+    cd "${mod_dir}"
+
+    while IFS= read -r line; do
+        echo ".${line#${p}}"
+    done < <(go list --find -f '{{.Name}}|{{.Dir}}' ./... | grep '^main|' | cut -f 2- -d '|')
+    cd "${p}"
+done
diff --git a/vendor/go.opentelemetry.io/otel/go.mod b/vendor/go.opentelemetry.io/otel/go.mod
new file mode 100644
index 0000000000..1847746362
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/go.mod
@@ -0,0 +1,8 @@
+module go.opentelemetry.io/otel
+
+go 1.14
+
+require (
+	github.com/google/go-cmp v0.5.4
+	github.com/stretchr/testify v1.6.1
+)
diff --git a/vendor/go.opentelemetry.io/otel/go.sum b/vendor/go.opentelemetry.io/otel/go.sum
new file mode 100644
index 0000000000..76002a62e4
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/go.sum
@@ -0,0 +1,15 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+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=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/vendor/go.opentelemetry.io/otel/handler.go b/vendor/go.opentelemetry.io/otel/handler.go
new file mode 100644
index 0000000000..27e1caa30d
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/handler.go
@@ -0,0 +1,89 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 otel // import "go.opentelemetry.io/otel"
+
+import (
+	"log"
+	"os"
+	"sync"
+	"sync/atomic"
+)
+
+var (
+	// globalErrorHandler provides an ErrorHandler that can be used
+	// throughout an OpenTelemetry instrumented project. When a user
+	// specified ErrorHandler is registered (`SetErrorHandler`) all calls to
+	// `Handle` and will be delegated to the registered ErrorHandler.
+	globalErrorHandler = &loggingErrorHandler{
+		l: log.New(os.Stderr, "", log.LstdFlags),
+	}
+
+	// delegateErrorHandlerOnce ensures that a user provided ErrorHandler is
+	// only ever registered once.
+	delegateErrorHandlerOnce sync.Once
+
+	// Comiple time check that loggingErrorHandler implements ErrorHandler.
+	_ ErrorHandler = (*loggingErrorHandler)(nil)
+)
+
+// loggingErrorHandler logs all errors to STDERR.
+type loggingErrorHandler struct {
+	delegate atomic.Value
+
+	l *log.Logger
+}
+
+// setDelegate sets the ErrorHandler delegate if one is not already set.
+func (h *loggingErrorHandler) setDelegate(d ErrorHandler) {
+	if h.delegate.Load() != nil {
+		// Delegate already registered
+		return
+	}
+	h.delegate.Store(d)
+}
+
+// Handle implements ErrorHandler.
+func (h *loggingErrorHandler) Handle(err error) {
+	if d := h.delegate.Load(); d != nil {
+		d.(ErrorHandler).Handle(err)
+		return
+	}
+	h.l.Print(err)
+}
+
+// GetErrorHandler returns the global ErrorHandler instance. If no ErrorHandler
+// instance has been set (`SetErrorHandler`), the default ErrorHandler which
+// logs errors to STDERR is returned.
+func GetErrorHandler() ErrorHandler {
+	return globalErrorHandler
+}
+
+// SetErrorHandler sets the global ErrorHandler to be h.
+func SetErrorHandler(h ErrorHandler) {
+	delegateErrorHandlerOnce.Do(func() {
+		current := GetErrorHandler()
+		if current == h {
+			return
+		}
+		if internalHandler, ok := current.(*loggingErrorHandler); ok {
+			internalHandler.setDelegate(h)
+		}
+	})
+}
+
+// Handle is a convience function for ErrorHandler().Handle(err)
+func Handle(err error) {
+	GetErrorHandler().Handle(err)
+}
diff --git a/vendor/go.opentelemetry.io/otel/internal/baggage/baggage.go b/vendor/go.opentelemetry.io/otel/internal/baggage/baggage.go
new file mode 100644
index 0000000000..6b5c0c2d93
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/internal/baggage/baggage.go
@@ -0,0 +1,338 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 baggage provides types and functions to manage W3C Baggage.
+package baggage
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/label"
+)
+
+type rawMap map[label.Key]label.Value
+type keySet map[label.Key]struct{}
+
+// Map is an immutable storage for correlations.
+type Map struct {
+	m rawMap
+}
+
+// MapUpdate contains information about correlation changes to be
+// made.
+type MapUpdate struct {
+	// DropSingleK contains a single key to be dropped from
+	// correlations. Use this to avoid an overhead of a slice
+	// allocation if there is only one key to drop.
+	DropSingleK label.Key
+	// DropMultiK contains all the keys to be dropped from
+	// correlations.
+	DropMultiK []label.Key
+
+	// SingleKV contains a single key-value pair to be added to
+	// correlations. Use this to avoid an overhead of a slice
+	// allocation if there is only one key-value pair to add.
+	SingleKV label.KeyValue
+	// MultiKV contains all the key-value pairs to be added to
+	// correlations.
+	MultiKV []label.KeyValue
+}
+
+func newMap(raw rawMap) Map {
+	return Map{
+		m: raw,
+	}
+}
+
+// NewEmptyMap creates an empty correlations map.
+func NewEmptyMap() Map {
+	return newMap(nil)
+}
+
+// NewMap creates a map with the contents of the update applied. In
+// this function, having an update with DropSingleK or DropMultiK
+// makes no sense - those fields are effectively ignored.
+func NewMap(update MapUpdate) Map {
+	return NewEmptyMap().Apply(update)
+}
+
+// Apply creates a copy of the map with the contents of the update
+// applied. Apply will first drop the keys from DropSingleK and
+// DropMultiK, then add key-value pairs from SingleKV and MultiKV.
+func (m Map) Apply(update MapUpdate) Map {
+	delSet, addSet := getModificationSets(update)
+	mapSize := getNewMapSize(m.m, delSet, addSet)
+
+	r := make(rawMap, mapSize)
+	for k, v := range m.m {
+		// do not copy items we want to drop
+		if _, ok := delSet[k]; ok {
+			continue
+		}
+		// do not copy items we would overwrite
+		if _, ok := addSet[k]; ok {
+			continue
+		}
+		r[k] = v
+	}
+	if update.SingleKV.Key.Defined() {
+		r[update.SingleKV.Key] = update.SingleKV.Value
+	}
+	for _, kv := range update.MultiKV {
+		r[kv.Key] = kv.Value
+	}
+	if len(r) == 0 {
+		r = nil
+	}
+	return newMap(r)
+}
+
+func getModificationSets(update MapUpdate) (delSet, addSet keySet) {
+	deletionsCount := len(update.DropMultiK)
+	if update.DropSingleK.Defined() {
+		deletionsCount++
+	}
+	if deletionsCount > 0 {
+		delSet = make(map[label.Key]struct{}, deletionsCount)
+		for _, k := range update.DropMultiK {
+			delSet[k] = struct{}{}
+		}
+		if update.DropSingleK.Defined() {
+			delSet[update.DropSingleK] = struct{}{}
+		}
+	}
+
+	additionsCount := len(update.MultiKV)
+	if update.SingleKV.Key.Defined() {
+		additionsCount++
+	}
+	if additionsCount > 0 {
+		addSet = make(map[label.Key]struct{}, additionsCount)
+		for _, k := range update.MultiKV {
+			addSet[k.Key] = struct{}{}
+		}
+		if update.SingleKV.Key.Defined() {
+			addSet[update.SingleKV.Key] = struct{}{}
+		}
+	}
+
+	return
+}
+
+func getNewMapSize(m rawMap, delSet, addSet keySet) int {
+	mapSizeDiff := 0
+	for k := range addSet {
+		if _, ok := m[k]; !ok {
+			mapSizeDiff++
+		}
+	}
+	for k := range delSet {
+		if _, ok := m[k]; ok {
+			if _, inAddSet := addSet[k]; !inAddSet {
+				mapSizeDiff--
+			}
+		}
+	}
+	return len(m) + mapSizeDiff
+}
+
+// Value gets a value from correlations map and returns a boolean
+// value indicating whether the key exist in the map.
+func (m Map) Value(k label.Key) (label.Value, bool) {
+	value, ok := m.m[k]
+	return value, ok
+}
+
+// HasValue returns a boolean value indicating whether the key exist
+// in the map.
+func (m Map) HasValue(k label.Key) bool {
+	_, has := m.Value(k)
+	return has
+}
+
+// Len returns a length of the map.
+func (m Map) Len() int {
+	return len(m.m)
+}
+
+// Foreach calls a passed callback once on each key-value pair until
+// all the key-value pairs of the map were iterated or the callback
+// returns false, whichever happens first.
+func (m Map) Foreach(f func(label.KeyValue) bool) {
+	for k, v := range m.m {
+		if !f(label.KeyValue{
+			Key:   k,
+			Value: v,
+		}) {
+			return
+		}
+	}
+}
+
+type correlationsType struct{}
+
+// SetHookFunc describes a type of a callback that is called when
+// storing baggage in the context.
+type SetHookFunc func(context.Context) context.Context
+
+// GetHookFunc describes a type of a callback that is called when
+// getting baggage from the context.
+type GetHookFunc func(context.Context, Map) Map
+
+// value under this key is either of type Map or correlationsData
+var correlationsKey = &correlationsType{}
+
+type correlationsData struct {
+	m       Map
+	setHook SetHookFunc
+	getHook GetHookFunc
+}
+
+func (d correlationsData) isHookless() bool {
+	return d.setHook == nil && d.getHook == nil
+}
+
+type hookKind int
+
+const (
+	hookKindSet hookKind = iota
+	hookKindGet
+)
+
+func (d *correlationsData) overrideHook(kind hookKind, setHook SetHookFunc, getHook GetHookFunc) {
+	switch kind {
+	case hookKindSet:
+		d.setHook = setHook
+	case hookKindGet:
+		d.getHook = getHook
+	}
+}
+
+// ContextWithSetHook installs a hook function that will be invoked
+// every time ContextWithMap is called. To avoid unnecessary callback
+// invocations (recursive or not), the callback can temporarily clear
+// the hooks from the context with the ContextWithNoHooks function.
+//
+// Note that NewContext also calls ContextWithMap, so the hook will be
+// invoked.
+//
+// Passing nil SetHookFunc creates a context with no set hook to call.
+//
+// This function should not be used by applications or libraries. It
+// is mostly for interoperation with other observability APIs.
+func ContextWithSetHook(ctx context.Context, hook SetHookFunc) context.Context {
+	return contextWithHook(ctx, hookKindSet, hook, nil)
+}
+
+// ContextWithGetHook installs a hook function that will be invoked
+// every time MapFromContext is called. To avoid unnecessary callback
+// invocations (recursive or not), the callback can temporarily clear
+// the hooks from the context with the ContextWithNoHooks function.
+//
+// Note that NewContext also calls MapFromContext, so the hook will be
+// invoked.
+//
+// Passing nil GetHookFunc creates a context with no get hook to call.
+//
+// This function should not be used by applications or libraries. It
+// is mostly for interoperation with other observability APIs.
+func ContextWithGetHook(ctx context.Context, hook GetHookFunc) context.Context {
+	return contextWithHook(ctx, hookKindGet, nil, hook)
+}
+
+func contextWithHook(ctx context.Context, kind hookKind, setHook SetHookFunc, getHook GetHookFunc) context.Context {
+	switch v := ctx.Value(correlationsKey).(type) {
+	case correlationsData:
+		v.overrideHook(kind, setHook, getHook)
+		if v.isHookless() {
+			return context.WithValue(ctx, correlationsKey, v.m)
+		}
+		return context.WithValue(ctx, correlationsKey, v)
+	case Map:
+		return contextWithOneHookAndMap(ctx, kind, setHook, getHook, v)
+	default:
+		m := NewEmptyMap()
+		return contextWithOneHookAndMap(ctx, kind, setHook, getHook, m)
+	}
+}
+
+func contextWithOneHookAndMap(ctx context.Context, kind hookKind, setHook SetHookFunc, getHook GetHookFunc, m Map) context.Context {
+	d := correlationsData{m: m}
+	d.overrideHook(kind, setHook, getHook)
+	if d.isHookless() {
+		return ctx
+	}
+	return context.WithValue(ctx, correlationsKey, d)
+}
+
+// ContextWithNoHooks creates a context with all the hooks
+// disabled. Also returns old set and get hooks. This function can be
+// used to temporarily clear the context from hooks and then reinstate
+// them by calling ContextWithSetHook and ContextWithGetHook functions
+// passing the hooks returned by this function.
+//
+// This function should not be used by applications or libraries. It
+// is mostly for interoperation with other observability APIs.
+func ContextWithNoHooks(ctx context.Context) (context.Context, SetHookFunc, GetHookFunc) {
+	switch v := ctx.Value(correlationsKey).(type) {
+	case correlationsData:
+		return context.WithValue(ctx, correlationsKey, v.m), v.setHook, v.getHook
+	default:
+		return ctx, nil, nil
+	}
+}
+
+// ContextWithMap returns a context with the Map entered into it.
+func ContextWithMap(ctx context.Context, m Map) context.Context {
+	switch v := ctx.Value(correlationsKey).(type) {
+	case correlationsData:
+		v.m = m
+		ctx = context.WithValue(ctx, correlationsKey, v)
+		if v.setHook != nil {
+			ctx = v.setHook(ctx)
+		}
+		return ctx
+	default:
+		return context.WithValue(ctx, correlationsKey, m)
+	}
+}
+
+// ContextWithNoCorrelationData returns a context stripped of correlation
+// data.
+func ContextWithNoCorrelationData(ctx context.Context) context.Context {
+	return context.WithValue(ctx, correlationsKey, nil)
+}
+
+// NewContext returns a context with the map from passed context
+// updated with the passed key-value pairs.
+func NewContext(ctx context.Context, keyvalues ...label.KeyValue) context.Context {
+	return ContextWithMap(ctx, MapFromContext(ctx).Apply(MapUpdate{
+		MultiKV: keyvalues,
+	}))
+}
+
+// MapFromContext gets the current Map from a Context.
+func MapFromContext(ctx context.Context) Map {
+	switch v := ctx.Value(correlationsKey).(type) {
+	case correlationsData:
+		if v.getHook != nil {
+			return v.getHook(ctx, v.m)
+		}
+		return v.m
+	case Map:
+		return v
+	default:
+		return NewEmptyMap()
+	}
+}
diff --git a/vendor/go.opentelemetry.io/otel/internal/global/meter.go b/vendor/go.opentelemetry.io/otel/internal/global/meter.go
new file mode 100644
index 0000000000..8b288df780
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/internal/global/meter.go
@@ -0,0 +1,348 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 global
+
+import (
+	"context"
+	"sync"
+	"sync/atomic"
+	"unsafe"
+
+	"go.opentelemetry.io/otel/label"
+	"go.opentelemetry.io/otel/metric"
+	"go.opentelemetry.io/otel/metric/number"
+	"go.opentelemetry.io/otel/metric/registry"
+)
+
+// This file contains the forwarding implementation of MeterProvider used as
+// the default global instance.  Metric events using instruments provided by
+// this implementation are no-ops until the first Meter implementation is set
+// as the global provider.
+//
+// The implementation here uses Mutexes to maintain a list of active Meters in
+// the MeterProvider and Instruments in each Meter, under the assumption that
+// these interfaces are not performance-critical.
+//
+// We have the invariant that setDelegate() will be called before a new
+// MeterProvider implementation is registered as the global provider.  Mutexes
+// in the MeterProvider and Meters ensure that each instrument has a delegate
+// before the global provider is set.
+//
+// Bound instrument operations are implemented by delegating to the
+// instrument after it is registered, with a sync.Once initializer to
+// protect against races with Release().
+//
+// Metric uniqueness checking is implemented by calling the exported
+// methods of the api/metric/registry package.
+
+type meterKey struct {
+	Name, Version string
+}
+
+type meterProvider struct {
+	delegate metric.MeterProvider
+
+	// lock protects `delegate` and `meters`.
+	lock sync.Mutex
+
+	// meters maintains a unique entry for every named Meter
+	// that has been registered through the global instance.
+	meters map[meterKey]*meterEntry
+}
+
+type meterImpl struct {
+	delegate unsafe.Pointer // (*metric.MeterImpl)
+
+	lock       sync.Mutex
+	syncInsts  []*syncImpl
+	asyncInsts []*asyncImpl
+}
+
+type meterEntry struct {
+	unique metric.MeterImpl
+	impl   meterImpl
+}
+
+type instrument struct {
+	descriptor metric.Descriptor
+}
+
+type syncImpl struct {
+	delegate unsafe.Pointer // (*metric.SyncImpl)
+
+	instrument
+}
+
+type asyncImpl struct {
+	delegate unsafe.Pointer // (*metric.AsyncImpl)
+
+	instrument
+
+	runner metric.AsyncRunner
+}
+
+// SyncImpler is implemented by all of the sync metric
+// instruments.
+type SyncImpler interface {
+	SyncImpl() metric.SyncImpl
+}
+
+// AsyncImpler is implemented by all of the async
+// metric instruments.
+type AsyncImpler interface {
+	AsyncImpl() metric.AsyncImpl
+}
+
+type syncHandle struct {
+	delegate unsafe.Pointer // (*metric.BoundInstrumentImpl)
+
+	inst   *syncImpl
+	labels []label.KeyValue
+
+	initialize sync.Once
+}
+
+var _ metric.MeterProvider = &meterProvider{}
+var _ metric.MeterImpl = &meterImpl{}
+var _ metric.InstrumentImpl = &syncImpl{}
+var _ metric.BoundSyncImpl = &syncHandle{}
+var _ metric.AsyncImpl = &asyncImpl{}
+
+func (inst *instrument) Descriptor() metric.Descriptor {
+	return inst.descriptor
+}
+
+// MeterProvider interface and delegation
+
+func newMeterProvider() *meterProvider {
+	return &meterProvider{
+		meters: map[meterKey]*meterEntry{},
+	}
+}
+
+func (p *meterProvider) setDelegate(provider metric.MeterProvider) {
+	p.lock.Lock()
+	defer p.lock.Unlock()
+
+	p.delegate = provider
+	for key, entry := range p.meters {
+		entry.impl.setDelegate(key.Name, key.Version, provider)
+	}
+	p.meters = nil
+}
+
+func (p *meterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
+	p.lock.Lock()
+	defer p.lock.Unlock()
+
+	if p.delegate != nil {
+		return p.delegate.Meter(instrumentationName, opts...)
+	}
+
+	key := meterKey{
+		Name:    instrumentationName,
+		Version: metric.NewMeterConfig(opts...).InstrumentationVersion,
+	}
+	entry, ok := p.meters[key]
+	if !ok {
+		entry = &meterEntry{}
+		entry.unique = registry.NewUniqueInstrumentMeterImpl(&entry.impl)
+		p.meters[key] = entry
+
+	}
+	return metric.WrapMeterImpl(entry.unique, key.Name, metric.WithInstrumentationVersion(key.Version))
+}
+
+// Meter interface and delegation
+
+func (m *meterImpl) setDelegate(name, version string, provider metric.MeterProvider) {
+	m.lock.Lock()
+	defer m.lock.Unlock()
+
+	d := new(metric.MeterImpl)
+	*d = provider.Meter(name, metric.WithInstrumentationVersion(version)).MeterImpl()
+	m.delegate = unsafe.Pointer(d)
+
+	for _, inst := range m.syncInsts {
+		inst.setDelegate(*d)
+	}
+	m.syncInsts = nil
+	for _, obs := range m.asyncInsts {
+		obs.setDelegate(*d)
+	}
+	m.asyncInsts = nil
+}
+
+func (m *meterImpl) NewSyncInstrument(desc metric.Descriptor) (metric.SyncImpl, error) {
+	m.lock.Lock()
+	defer m.lock.Unlock()
+
+	if meterPtr := (*metric.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil {
+		return (*meterPtr).NewSyncInstrument(desc)
+	}
+
+	inst := &syncImpl{
+		instrument: instrument{
+			descriptor: desc,
+		},
+	}
+	m.syncInsts = append(m.syncInsts, inst)
+	return inst, nil
+}
+
+// Synchronous delegation
+
+func (inst *syncImpl) setDelegate(d metric.MeterImpl) {
+	implPtr := new(metric.SyncImpl)
+
+	var err error
+	*implPtr, err = d.NewSyncInstrument(inst.descriptor)
+
+	if err != nil {
+		// TODO: There is no standard way to deliver this error to the user.
+		// See https://github.com/open-telemetry/opentelemetry-go/issues/514
+		// Note that the default SDK will not generate any errors yet, this is
+		// only for added safety.
+		panic(err)
+	}
+
+	atomic.StorePointer(&inst.delegate, unsafe.Pointer(implPtr))
+}
+
+func (inst *syncImpl) Implementation() interface{} {
+	if implPtr := (*metric.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil {
+		return (*implPtr).Implementation()
+	}
+	return inst
+}
+
+func (inst *syncImpl) Bind(labels []label.KeyValue) metric.BoundSyncImpl {
+	if implPtr := (*metric.SyncImpl)(atomic.LoadPointer(&inst.delegate)); implPtr != nil {
+		return (*implPtr).Bind(labels)
+	}
+	return &syncHandle{
+		inst:   inst,
+		labels: labels,
+	}
+}
+
+func (bound *syncHandle) Unbind() {
+	bound.initialize.Do(func() {})
+
+	implPtr := (*metric.BoundSyncImpl)(atomic.LoadPointer(&bound.delegate))
+
+	if implPtr == nil {
+		return
+	}
+
+	(*implPtr).Unbind()
+}
+
+// Async delegation
+
+func (m *meterImpl) NewAsyncInstrument(
+	desc metric.Descriptor,
+	runner metric.AsyncRunner,
+) (metric.AsyncImpl, error) {
+
+	m.lock.Lock()
+	defer m.lock.Unlock()
+
+	if meterPtr := (*metric.MeterImpl)(atomic.LoadPointer(&m.delegate)); meterPtr != nil {
+		return (*meterPtr).NewAsyncInstrument(desc, runner)
+	}
+
+	inst := &asyncImpl{
+		instrument: instrument{
+			descriptor: desc,
+		},
+		runner: runner,
+	}
+	m.asyncInsts = append(m.asyncInsts, inst)
+	return inst, nil
+}
+
+func (obs *asyncImpl) Implementation() interface{} {
+	if implPtr := (*metric.AsyncImpl)(atomic.LoadPointer(&obs.delegate)); implPtr != nil {
+		return (*implPtr).Implementation()
+	}
+	return obs
+}
+
+func (obs *asyncImpl) setDelegate(d metric.MeterImpl) {
+	implPtr := new(metric.AsyncImpl)
+
+	var err error
+	*implPtr, err = d.NewAsyncInstrument(obs.descriptor, obs.runner)
+
+	if err != nil {
+		// TODO: There is no standard way to deliver this error to the user.
+		// See https://github.com/open-telemetry/opentelemetry-go/issues/514
+		// Note that the default SDK will not generate any errors yet, this is
+		// only for added safety.
+		panic(err)
+	}
+
+	atomic.StorePointer(&obs.delegate, unsafe.Pointer(implPtr))
+}
+
+// Metric updates
+
+func (m *meterImpl) RecordBatch(ctx context.Context, labels []label.KeyValue, measurements ...metric.Measurement) {
+	if delegatePtr := (*metric.MeterImpl)(atomic.LoadPointer(&m.delegate)); delegatePtr != nil {
+		(*delegatePtr).RecordBatch(ctx, labels, measurements...)
+	}
+}
+
+func (inst *syncImpl) RecordOne(ctx context.Context, number number.Number, labels []label.KeyValue) {
+	if instPtr := (*metric.SyncImpl)(atomic.LoadPointer(&inst.delegate)); instPtr != nil {
+		(*instPtr).RecordOne(ctx, number, labels)
+	}
+}
+
+// Bound instrument initialization
+
+func (bound *syncHandle) RecordOne(ctx context.Context, number number.Number) {
+	instPtr := (*metric.SyncImpl)(atomic.LoadPointer(&bound.inst.delegate))
+	if instPtr == nil {
+		return
+	}
+	var implPtr *metric.BoundSyncImpl
+	bound.initialize.Do(func() {
+		implPtr = new(metric.BoundSyncImpl)
+		*implPtr = (*instPtr).Bind(bound.labels)
+		atomic.StorePointer(&bound.delegate, unsafe.Pointer(implPtr))
+	})
+	if implPtr == nil {
+		implPtr = (*metric.BoundSyncImpl)(atomic.LoadPointer(&bound.delegate))
+	}
+	// This may still be nil if instrument was created and bound
+	// without a delegate, then the instrument was set to have a
+	// delegate and unbound.
+	if implPtr == nil {
+		return
+	}
+	(*implPtr).RecordOne(ctx, number)
+}
+
+func AtomicFieldOffsets() map[string]uintptr {
+	return map[string]uintptr{
+		"meterProvider.delegate": unsafe.Offsetof(meterProvider{}.delegate),
+		"meterImpl.delegate":     unsafe.Offsetof(meterImpl{}.delegate),
+		"syncImpl.delegate":      unsafe.Offsetof(syncImpl{}.delegate),
+		"asyncImpl.delegate":     unsafe.Offsetof(asyncImpl{}.delegate),
+		"syncHandle.delegate":    unsafe.Offsetof(syncHandle{}.delegate),
+	}
+}
diff --git a/vendor/go.opentelemetry.io/otel/internal/global/propagator.go b/vendor/go.opentelemetry.io/otel/internal/global/propagator.go
new file mode 100644
index 0000000000..1c8b8589b0
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/internal/global/propagator.go
@@ -0,0 +1,82 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 global
+
+import (
+	"context"
+	"sync"
+
+	"go.opentelemetry.io/otel/propagation"
+)
+
+// textMapPropagator is a default TextMapPropagator that delegates calls to a
+// registered delegate if one is set, otherwise it defaults to delegating the
+// calls to a the default no-op propagation.TextMapPropagator.
+type textMapPropagator struct {
+	mtx      sync.Mutex
+	once     sync.Once
+	delegate propagation.TextMapPropagator
+	noop     propagation.TextMapPropagator
+}
+
+// Compile-time guarantee that textMapPropagator implements the
+// propagation.TextMapPropagator interface.
+var _ propagation.TextMapPropagator = (*textMapPropagator)(nil)
+
+func newTextMapPropagator() *textMapPropagator {
+	return &textMapPropagator{
+		noop: propagation.NewCompositeTextMapPropagator(),
+	}
+}
+
+// SetDelegate sets a delegate propagation.TextMapPropagator that all calls are
+// forwarded to. Delegation can only be performed once, all subsequent calls
+// perform no delegation.
+func (p *textMapPropagator) SetDelegate(delegate propagation.TextMapPropagator) {
+	if delegate == nil {
+		return
+	}
+
+	p.mtx.Lock()
+	p.once.Do(func() { p.delegate = delegate })
+	p.mtx.Unlock()
+}
+
+// effectiveDelegate returns the current delegate of p if one is set,
+// otherwise the default noop TextMapPropagator is returned. This method
+// can be called concurrently.
+func (p *textMapPropagator) effectiveDelegate() propagation.TextMapPropagator {
+	p.mtx.Lock()
+	defer p.mtx.Unlock()
+	if p.delegate != nil {
+		return p.delegate
+	}
+	return p.noop
+}
+
+// Inject set cross-cutting concerns from the Context into the carrier.
+func (p *textMapPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
+	p.effectiveDelegate().Inject(ctx, carrier)
+}
+
+// Extract reads cross-cutting concerns from the carrier into a Context.
+func (p *textMapPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
+	return p.effectiveDelegate().Extract(ctx, carrier)
+}
+
+// Fields returns the keys whose values are set with Inject.
+func (p *textMapPropagator) Fields() []string {
+	return p.effectiveDelegate().Fields()
+}
diff --git a/vendor/go.opentelemetry.io/otel/internal/global/state.go b/vendor/go.opentelemetry.io/otel/internal/global/state.go
new file mode 100644
index 0000000000..f3bf003510
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/internal/global/state.go
@@ -0,0 +1,143 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 global
+
+import (
+	"sync"
+	"sync/atomic"
+
+	"go.opentelemetry.io/otel/metric"
+	"go.opentelemetry.io/otel/propagation"
+	"go.opentelemetry.io/otel/trace"
+)
+
+type (
+	tracerProviderHolder struct {
+		tp trace.TracerProvider
+	}
+
+	meterProviderHolder struct {
+		mp metric.MeterProvider
+	}
+
+	propagatorsHolder struct {
+		tm propagation.TextMapPropagator
+	}
+)
+
+var (
+	globalTracer      = defaultTracerValue()
+	globalMeter       = defaultMeterValue()
+	globalPropagators = defaultPropagatorsValue()
+
+	delegateMeterOnce             sync.Once
+	delegateTraceOnce             sync.Once
+	delegateTextMapPropagatorOnce sync.Once
+)
+
+// TracerProvider is the internal implementation for global.TracerProvider.
+func TracerProvider() trace.TracerProvider {
+	return globalTracer.Load().(tracerProviderHolder).tp
+}
+
+// SetTracerProvider is the internal implementation for global.SetTracerProvider.
+func SetTracerProvider(tp trace.TracerProvider) {
+	delegateTraceOnce.Do(func() {
+		current := TracerProvider()
+		if current == tp {
+			// Setting the provider to the prior default is nonsense, panic.
+			// Panic is acceptable because we are likely still early in the
+			// process lifetime.
+			panic("invalid TracerProvider, the global instance cannot be reinstalled")
+		} else if def, ok := current.(*tracerProvider); ok {
+			def.setDelegate(tp)
+		}
+
+	})
+	globalTracer.Store(tracerProviderHolder{tp: tp})
+}
+
+// MeterProvider is the internal implementation for global.MeterProvider.
+func MeterProvider() metric.MeterProvider {
+	return globalMeter.Load().(meterProviderHolder).mp
+}
+
+// SetMeterProvider is the internal implementation for global.SetMeterProvider.
+func SetMeterProvider(mp metric.MeterProvider) {
+	delegateMeterOnce.Do(func() {
+		current := MeterProvider()
+
+		if current == mp {
+			// Setting the provider to the prior default is nonsense, panic.
+			// Panic is acceptable because we are likely still early in the
+			// process lifetime.
+			panic("invalid MeterProvider, the global instance cannot be reinstalled")
+		} else if def, ok := current.(*meterProvider); ok {
+			def.setDelegate(mp)
+		}
+	})
+	globalMeter.Store(meterProviderHolder{mp: mp})
+}
+
+// TextMapPropagator is the internal implementation for global.TextMapPropagator.
+func TextMapPropagator() propagation.TextMapPropagator {
+	return globalPropagators.Load().(propagatorsHolder).tm
+}
+
+// SetTextMapPropagator is the internal implementation for global.SetTextMapPropagator.
+func SetTextMapPropagator(p propagation.TextMapPropagator) {
+	// For the textMapPropagator already returned by TextMapPropagator
+	// delegate to p.
+	delegateTextMapPropagatorOnce.Do(func() {
+		if current := TextMapPropagator(); current == p {
+			// Setting the provider to the prior default is nonsense, panic.
+			// Panic is acceptable because we are likely still early in the
+			// process lifetime.
+			panic("invalid TextMapPropagator, the global instance cannot be reinstalled")
+		} else if def, ok := current.(*textMapPropagator); ok {
+			def.SetDelegate(p)
+		}
+	})
+	// Return p when subsequent calls to TextMapPropagator are made.
+	globalPropagators.Store(propagatorsHolder{tm: p})
+}
+
+func defaultTracerValue() *atomic.Value {
+	v := &atomic.Value{}
+	v.Store(tracerProviderHolder{tp: &tracerProvider{}})
+	return v
+}
+
+func defaultMeterValue() *atomic.Value {
+	v := &atomic.Value{}
+	v.Store(meterProviderHolder{mp: newMeterProvider()})
+	return v
+}
+
+func defaultPropagatorsValue() *atomic.Value {
+	v := &atomic.Value{}
+	v.Store(propagatorsHolder{tm: newTextMapPropagator()})
+	return v
+}
+
+// ResetForTest restores the initial global state, for testing purposes.
+func ResetForTest() {
+	globalTracer = defaultTracerValue()
+	globalMeter = defaultMeterValue()
+	globalPropagators = defaultPropagatorsValue()
+	delegateMeterOnce = sync.Once{}
+	delegateTraceOnce = sync.Once{}
+	delegateTextMapPropagatorOnce = sync.Once{}
+}
diff --git a/vendor/go.opentelemetry.io/otel/internal/global/trace.go b/vendor/go.opentelemetry.io/otel/internal/global/trace.go
new file mode 100644
index 0000000000..7b6993153f
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/internal/global/trace.go
@@ -0,0 +1,128 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 global
+
+/*
+This file contains the forwarding implementation of the TracerProvider used as
+the default global instance. Prior to initialization of an SDK, Tracers
+returned by the global TracerProvider will provide no-op functionality. This
+means that all Span created prior to initialization are no-op Spans.
+
+Once an SDK has been initialized, all provided no-op Tracers are swapped for
+Tracers provided by the SDK defined TracerProvider. However, any Span started
+prior to this initialization does not change its behavior. Meaning, the Span
+remains a no-op Span.
+
+The implementation to track and swap Tracers locks all new Tracer creation
+until the swap is complete. This assumes that this operation is not
+performance-critical. If that assumption is incorrect, be sure to configure an
+SDK prior to any Tracer creation.
+*/
+
+import (
+	"context"
+	"sync"
+
+	"go.opentelemetry.io/otel/internal/trace/noop"
+	"go.opentelemetry.io/otel/trace"
+)
+
+// tracerProvider is a placeholder for a configured SDK TracerProvider.
+//
+// All TracerProvider functionality is forwarded to a delegate once
+// configured.
+type tracerProvider struct {
+	mtx     sync.Mutex
+	tracers []*tracer
+
+	delegate trace.TracerProvider
+}
+
+// Compile-time guarantee that tracerProvider implements the TracerProvider
+// interface.
+var _ trace.TracerProvider = &tracerProvider{}
+
+// setDelegate configures p to delegate all TracerProvider functionality to
+// provider.
+//
+// All Tracers provided prior to this function call are switched out to be
+// Tracers provided by provider.
+//
+// Delegation only happens on the first call to this method. All subsequent
+// calls result in no delegation changes.
+func (p *tracerProvider) setDelegate(provider trace.TracerProvider) {
+	if p.delegate != nil {
+		return
+	}
+
+	p.mtx.Lock()
+	defer p.mtx.Unlock()
+
+	p.delegate = provider
+	for _, t := range p.tracers {
+		t.setDelegate(provider)
+	}
+
+	p.tracers = nil
+}
+
+// Tracer implements TracerProvider.
+func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer {
+	p.mtx.Lock()
+	defer p.mtx.Unlock()
+
+	if p.delegate != nil {
+		return p.delegate.Tracer(name, opts...)
+	}
+
+	t := &tracer{name: name, opts: opts}
+	p.tracers = append(p.tracers, t)
+	return t
+}
+
+// tracer is a placeholder for a trace.Tracer.
+//
+// All Tracer functionality is forwarded to a delegate once configured.
+// Otherwise, all functionality is forwarded to a NoopTracer.
+type tracer struct {
+	once sync.Once
+	name string
+	opts []trace.TracerOption
+
+	delegate trace.Tracer
+}
+
+// Compile-time guarantee that tracer implements the trace.Tracer interface.
+var _ trace.Tracer = &tracer{}
+
+// setDelegate configures t to delegate all Tracer functionality to Tracers
+// created by provider.
+//
+// All subsequent calls to the Tracer methods will be passed to the delegate.
+//
+// Delegation only happens on the first call to this method. All subsequent
+// calls result in no delegation changes.
+func (t *tracer) setDelegate(provider trace.TracerProvider) {
+	t.once.Do(func() { t.delegate = provider.Tracer(t.name, t.opts...) })
+}
+
+// Start implements trace.Tracer by forwarding the call to t.delegate if
+// set, otherwise it forwards the call to a NoopTracer.
+func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanOption) (context.Context, trace.Span) {
+	if t.delegate != nil {
+		return t.delegate.Start(ctx, name, opts...)
+	}
+	return noop.Tracer.Start(ctx, name, opts...)
+}
diff --git a/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go b/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go
new file mode 100644
index 0000000000..dae825ed8b
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/internal/rawhelpers.go
@@ -0,0 +1,91 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 internal
+
+import (
+	"math"
+	"unsafe"
+)
+
+func BoolToRaw(b bool) uint64 {
+	if b {
+		return 1
+	}
+	return 0
+}
+
+func RawToBool(r uint64) bool {
+	return r != 0
+}
+
+func Int64ToRaw(i int64) uint64 {
+	return uint64(i)
+}
+
+func RawToInt64(r uint64) int64 {
+	return int64(r)
+}
+
+func Uint64ToRaw(u uint64) uint64 {
+	return u
+}
+
+func RawToUint64(r uint64) uint64 {
+	return r
+}
+
+func Float64ToRaw(f float64) uint64 {
+	return math.Float64bits(f)
+}
+
+func RawToFloat64(r uint64) float64 {
+	return math.Float64frombits(r)
+}
+
+func Int32ToRaw(i int32) uint64 {
+	return uint64(i)
+}
+
+func RawToInt32(r uint64) int32 {
+	return int32(r)
+}
+
+func Uint32ToRaw(u uint32) uint64 {
+	return uint64(u)
+}
+
+func RawToUint32(r uint64) uint32 {
+	return uint32(r)
+}
+
+func Float32ToRaw(f float32) uint64 {
+	return Uint32ToRaw(math.Float32bits(f))
+}
+
+func RawToFloat32(r uint64) float32 {
+	return math.Float32frombits(RawToUint32(r))
+}
+
+func RawPtrToFloat64Ptr(r *uint64) *float64 {
+	return (*float64)(unsafe.Pointer(r))
+}
+
+func RawPtrToInt64Ptr(r *uint64) *int64 {
+	return (*int64)(unsafe.Pointer(r))
+}
+
+func RawPtrToUint64Ptr(r *uint64) *uint64 {
+	return r
+}
diff --git a/vendor/go.opentelemetry.io/otel/internal/trace/noop/noop.go b/vendor/go.opentelemetry.io/otel/internal/trace/noop/noop.go
new file mode 100644
index 0000000000..765c21a289
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/internal/trace/noop/noop.go
@@ -0,0 +1,35 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 noop provides noop tracing implementations for tracer and span.
+package noop
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/trace"
+)
+
+var (
+	// Tracer is a noop tracer that starts noop spans.
+	Tracer trace.Tracer
+
+	// Span is a noop Span.
+	Span trace.Span
+)
+
+func init() {
+	Tracer = trace.NewNoopTracerProvider().Tracer("")
+	_, Span = Tracer.Start(context.Background(), "")
+}
diff --git a/vendor/go.opentelemetry.io/otel/label/doc.go b/vendor/go.opentelemetry.io/otel/label/doc.go
new file mode 100644
index 0000000000..abdffdb46f
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/label/doc.go
@@ -0,0 +1,20 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 label provides key and value labels.
+//
+// This package is currently in a pre-GA phase. Backwards incompatible changes
+// may be introduced in subsequent minor version releases as we work to track
+// the evolving OpenTelemetry specification and user feedback.
+package label // import "go.opentelemetry.io/otel/label"
diff --git a/vendor/go.opentelemetry.io/otel/label/encoder.go b/vendor/go.opentelemetry.io/otel/label/encoder.go
new file mode 100644
index 0000000000..4620873df7
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/label/encoder.go
@@ -0,0 +1,150 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 label // import "go.opentelemetry.io/otel/label"
+
+import (
+	"bytes"
+	"sync"
+	"sync/atomic"
+)
+
+type (
+	// Encoder is a mechanism for serializing a label set into a
+	// specific string representation that supports caching, to
+	// avoid repeated serialization. An example could be an
+	// exporter encoding the label set into a wire representation.
+	Encoder interface {
+		// Encode returns the serialized encoding of the label
+		// set using its Iterator.  This result may be cached
+		// by a label.Set.
+		Encode(iterator Iterator) string
+
+		// ID returns a value that is unique for each class of
+		// label encoder.  Label encoders allocate these using
+		// `NewEncoderID`.
+		ID() EncoderID
+	}
+
+	// EncoderID is used to identify distinct Encoder
+	// implementations, for caching encoded results.
+	EncoderID struct {
+		value uint64
+	}
+
+	// defaultLabelEncoder uses a sync.Pool of buffers to reduce
+	// the number of allocations used in encoding labels.  This
+	// implementation encodes a comma-separated list of key=value,
+	// with '/'-escaping of '=', ',', and '\'.
+	defaultLabelEncoder struct {
+		// pool is a pool of labelset builders.  The buffers in this
+		// pool grow to a size that most label encodings will not
+		// allocate new memory.
+		pool sync.Pool // *bytes.Buffer
+	}
+)
+
+// escapeChar is used to ensure uniqueness of the label encoding where
+// keys or values contain either '=' or ','.  Since there is no parser
+// needed for this encoding and its only requirement is to be unique,
+// this choice is arbitrary.  Users will see these in some exporters
+// (e.g., stdout), so the backslash ('\') is used as a conventional choice.
+const escapeChar = '\\'
+
+var (
+	_ Encoder = &defaultLabelEncoder{}
+
+	// encoderIDCounter is for generating IDs for other label
+	// encoders.
+	encoderIDCounter uint64
+
+	defaultEncoderOnce     sync.Once
+	defaultEncoderID       = NewEncoderID()
+	defaultEncoderInstance *defaultLabelEncoder
+)
+
+// NewEncoderID returns a unique label encoder ID. It should be
+// called once per each type of label encoder. Preferably in init() or
+// in var definition.
+func NewEncoderID() EncoderID {
+	return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)}
+}
+
+// DefaultEncoder returns a label encoder that encodes labels
+// in such a way that each escaped label's key is followed by an equal
+// sign and then by an escaped label's value. All key-value pairs are
+// separated by a comma.
+//
+// Escaping is done by prepending a backslash before either a
+// backslash, equal sign or a comma.
+func DefaultEncoder() Encoder {
+	defaultEncoderOnce.Do(func() {
+		defaultEncoderInstance = &defaultLabelEncoder{
+			pool: sync.Pool{
+				New: func() interface{} {
+					return &bytes.Buffer{}
+				},
+			},
+		}
+	})
+	return defaultEncoderInstance
+}
+
+// Encode is a part of an implementation of the LabelEncoder
+// interface.
+func (d *defaultLabelEncoder) Encode(iter Iterator) string {
+	buf := d.pool.Get().(*bytes.Buffer)
+	defer d.pool.Put(buf)
+	buf.Reset()
+
+	for iter.Next() {
+		i, keyValue := iter.IndexedLabel()
+		if i > 0 {
+			_, _ = buf.WriteRune(',')
+		}
+		copyAndEscape(buf, string(keyValue.Key))
+
+		_, _ = buf.WriteRune('=')
+
+		if keyValue.Value.Type() == STRING {
+			copyAndEscape(buf, keyValue.Value.AsString())
+		} else {
+			_, _ = buf.WriteString(keyValue.Value.Emit())
+		}
+	}
+	return buf.String()
+}
+
+// ID is a part of an implementation of the LabelEncoder interface.
+func (*defaultLabelEncoder) ID() EncoderID {
+	return defaultEncoderID
+}
+
+// copyAndEscape escapes `=`, `,` and its own escape character (`\`),
+// making the default encoding unique.
+func copyAndEscape(buf *bytes.Buffer, val string) {
+	for _, ch := range val {
+		switch ch {
+		case '=', ',', escapeChar:
+			buf.WriteRune(escapeChar)
+		}
+		buf.WriteRune(ch)
+	}
+}
+
+// Valid returns true if this encoder ID was allocated by
+// `NewEncoderID`.  Invalid encoder IDs will not be cached.
+func (id EncoderID) Valid() bool {
+	return id.value != 0
+}
diff --git a/vendor/go.opentelemetry.io/otel/label/iterator.go b/vendor/go.opentelemetry.io/otel/label/iterator.go
new file mode 100644
index 0000000000..a968e76682
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/label/iterator.go
@@ -0,0 +1,143 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 label // import "go.opentelemetry.io/otel/label"
+
+// Iterator allows iterating over the set of labels in order,
+// sorted by key.
+type Iterator struct {
+	storage *Set
+	idx     int
+}
+
+// MergeIterator supports iterating over two sets of labels while
+// eliminating duplicate values from the combined set.  The first
+// iterator value takes precedence.
+type MergeIterator struct {
+	one     oneIterator
+	two     oneIterator
+	current KeyValue
+}
+
+type oneIterator struct {
+	iter  Iterator
+	done  bool
+	label KeyValue
+}
+
+// Next moves the iterator to the next position. Returns false if there
+// are no more labels.
+func (i *Iterator) Next() bool {
+	i.idx++
+	return i.idx < i.Len()
+}
+
+// Label returns current KeyValue. Must be called only after Next returns
+// true.
+func (i *Iterator) Label() KeyValue {
+	kv, _ := i.storage.Get(i.idx)
+	return kv
+}
+
+// Attribute is a synonym for Label().
+func (i *Iterator) Attribute() KeyValue {
+	return i.Label()
+}
+
+// IndexedLabel returns current index and label. Must be called only
+// after Next returns true.
+func (i *Iterator) IndexedLabel() (int, KeyValue) {
+	return i.idx, i.Label()
+}
+
+// Len returns a number of labels in the iterator's `*Set`.
+func (i *Iterator) Len() int {
+	return i.storage.Len()
+}
+
+// ToSlice is a convenience function that creates a slice of labels
+// from the passed iterator. The iterator is set up to start from the
+// beginning before creating the slice.
+func (i *Iterator) ToSlice() []KeyValue {
+	l := i.Len()
+	if l == 0 {
+		return nil
+	}
+	i.idx = -1
+	slice := make([]KeyValue, 0, l)
+	for i.Next() {
+		slice = append(slice, i.Label())
+	}
+	return slice
+}
+
+// NewMergeIterator returns a MergeIterator for merging two label sets
+// Duplicates are resolved by taking the value from the first set.
+func NewMergeIterator(s1, s2 *Set) MergeIterator {
+	mi := MergeIterator{
+		one: makeOne(s1.Iter()),
+		two: makeOne(s2.Iter()),
+	}
+	return mi
+}
+
+func makeOne(iter Iterator) oneIterator {
+	oi := oneIterator{
+		iter: iter,
+	}
+	oi.advance()
+	return oi
+}
+
+func (oi *oneIterator) advance() {
+	if oi.done = !oi.iter.Next(); !oi.done {
+		oi.label = oi.iter.Label()
+	}
+}
+
+// Next returns true if there is another label available.
+func (m *MergeIterator) Next() bool {
+	if m.one.done && m.two.done {
+		return false
+	}
+	if m.one.done {
+		m.current = m.two.label
+		m.two.advance()
+		return true
+	}
+	if m.two.done {
+		m.current = m.one.label
+		m.one.advance()
+		return true
+	}
+	if m.one.label.Key == m.two.label.Key {
+		m.current = m.one.label // first iterator label value wins
+		m.one.advance()
+		m.two.advance()
+		return true
+	}
+	if m.one.label.Key < m.two.label.Key {
+		m.current = m.one.label
+		m.one.advance()
+		return true
+	}
+	m.current = m.two.label
+	m.two.advance()
+	return true
+}
+
+// Label returns the current value after Next() returns true.
+func (m *MergeIterator) Label() KeyValue {
+	return m.current
+}
diff --git a/vendor/go.opentelemetry.io/otel/label/key.go b/vendor/go.opentelemetry.io/otel/label/key.go
new file mode 100644
index 0000000000..732fa2523a
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/label/key.go
@@ -0,0 +1,169 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 label // import "go.opentelemetry.io/otel/label"
+
+// Key represents the key part in key-value pairs. It's a string. The
+// allowed character set in the key depends on the use of the key.
+type Key string
+
+// Bool creates a KeyValue instance with a BOOL Value.
+//
+// If creating both key and a bool value at the same time, then
+// instead of calling Key(name).Bool(value) consider using a
+// convenience function provided by the api/key package -
+// key.Bool(name, value).
+func (k Key) Bool(v bool) KeyValue {
+	return KeyValue{
+		Key:   k,
+		Value: BoolValue(v),
+	}
+}
+
+// Int64 creates a KeyValue instance with an INT64 Value.
+//
+// If creating both key and an int64 value at the same time, then
+// instead of calling Key(name).Int64(value) consider using a
+// convenience function provided by the api/key package -
+// key.Int64(name, value).
+func (k Key) Int64(v int64) KeyValue {
+	return KeyValue{
+		Key:   k,
+		Value: Int64Value(v),
+	}
+}
+
+// Uint64 creates a KeyValue instance with a UINT64 Value.
+//
+// If creating both key and a uint64 value at the same time, then
+// instead of calling Key(name).Uint64(value) consider using a
+// convenience function provided by the api/key package -
+// key.Uint64(name, value).
+func (k Key) Uint64(v uint64) KeyValue {
+	return KeyValue{
+		Key:   k,
+		Value: Uint64Value(v),
+	}
+}
+
+// Float64 creates a KeyValue instance with a FLOAT64 Value.
+//
+// If creating both key and a float64 value at the same time, then
+// instead of calling Key(name).Float64(value) consider using a
+// convenience function provided by the api/key package -
+// key.Float64(name, value).
+func (k Key) Float64(v float64) KeyValue {
+	return KeyValue{
+		Key:   k,
+		Value: Float64Value(v),
+	}
+}
+
+// Int32 creates a KeyValue instance with an INT32 Value.
+//
+// If creating both key and an int32 value at the same time, then
+// instead of calling Key(name).Int32(value) consider using a
+// convenience function provided by the api/key package -
+// key.Int32(name, value).
+func (k Key) Int32(v int32) KeyValue {
+	return KeyValue{
+		Key:   k,
+		Value: Int32Value(v),
+	}
+}
+
+// Uint32 creates a KeyValue instance with a UINT32 Value.
+//
+// If creating both key and a uint32 value at the same time, then
+// instead of calling Key(name).Uint32(value) consider using a
+// convenience function provided by the api/key package -
+// key.Uint32(name, value).
+func (k Key) Uint32(v uint32) KeyValue {
+	return KeyValue{
+		Key:   k,
+		Value: Uint32Value(v),
+	}
+}
+
+// Float32 creates a KeyValue instance with a FLOAT32 Value.
+//
+// If creating both key and a float32 value at the same time, then
+// instead of calling Key(name).Float32(value) consider using a
+// convenience function provided by the api/key package -
+// key.Float32(name, value).
+func (k Key) Float32(v float32) KeyValue {
+	return KeyValue{
+		Key:   k,
+		Value: Float32Value(v),
+	}
+}
+
+// String creates a KeyValue instance with a STRING Value.
+//
+// If creating both key and a string value at the same time, then
+// instead of calling Key(name).String(value) consider using a
+// convenience function provided by the api/key package -
+// key.String(name, value).
+func (k Key) String(v string) KeyValue {
+	return KeyValue{
+		Key:   k,
+		Value: StringValue(v),
+	}
+}
+
+// Int creates a KeyValue instance with either an INT32 or an INT64
+// Value, depending on whether the int type is 32 or 64 bits wide.
+//
+// If creating both key and an int value at the same time, then
+// instead of calling Key(name).Int(value) consider using a
+// convenience function provided by the api/key package -
+// key.Int(name, value).
+func (k Key) Int(v int) KeyValue {
+	return KeyValue{
+		Key:   k,
+		Value: IntValue(v),
+	}
+}
+
+// Uint creates a KeyValue instance with either a UINT32 or a UINT64
+// Value, depending on whether the uint type is 32 or 64 bits wide.
+//
+// If creating both key and a uint value at the same time, then
+// instead of calling Key(name).Uint(value) consider using a
+// convenience function provided by the api/key package -
+// key.Uint(name, value).
+func (k Key) Uint(v uint) KeyValue {
+	return KeyValue{
+		Key:   k,
+		Value: UintValue(v),
+	}
+}
+
+// Defined returns true for non-empty keys.
+func (k Key) Defined() bool {
+	return len(k) != 0
+}
+
+// Array creates a KeyValue instance with a ARRAY Value.
+//
+// If creating both key and a array value at the same time, then
+// instead of calling Key(name).String(value) consider using a
+// convenience function provided by the api/key package -
+// key.Array(name, value).
+func (k Key) Array(v interface{}) KeyValue {
+	return KeyValue{
+		Key:   k,
+		Value: ArrayValue(v),
+	}
+}
diff --git a/vendor/go.opentelemetry.io/otel/label/kv.go b/vendor/go.opentelemetry.io/otel/label/kv.go
new file mode 100644
index 0000000000..0520f4f41a
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/label/kv.go
@@ -0,0 +1,144 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 label // import "go.opentelemetry.io/otel/label"
+
+import (
+	"encoding/json"
+	"fmt"
+	"reflect"
+)
+
+// KeyValue holds a key and value pair.
+type KeyValue struct {
+	Key   Key
+	Value Value
+}
+
+// Bool creates a new key-value pair with a passed name and a bool
+// value.
+func Bool(k string, v bool) KeyValue {
+	return Key(k).Bool(v)
+}
+
+// Int64 creates a new key-value pair with a passed name and an int64
+// value.
+func Int64(k string, v int64) KeyValue {
+	return Key(k).Int64(v)
+}
+
+// Uint64 creates a new key-value pair with a passed name and a uint64
+// value.
+func Uint64(k string, v uint64) KeyValue {
+	return Key(k).Uint64(v)
+}
+
+// Float64 creates a new key-value pair with a passed name and a float64
+// value.
+func Float64(k string, v float64) KeyValue {
+	return Key(k).Float64(v)
+}
+
+// Int32 creates a new key-value pair with a passed name and an int32
+// value.
+func Int32(k string, v int32) KeyValue {
+	return Key(k).Int32(v)
+}
+
+// Uint32 creates a new key-value pair with a passed name and a uint32
+// value.
+func Uint32(k string, v uint32) KeyValue {
+	return Key(k).Uint32(v)
+}
+
+// Float32 creates a new key-value pair with a passed name and a float32
+// value.
+func Float32(k string, v float32) KeyValue {
+	return Key(k).Float32(v)
+}
+
+// String creates a new key-value pair with a passed name and a string
+// value.
+func String(k, v string) KeyValue {
+	return Key(k).String(v)
+}
+
+// Stringer creates a new key-value pair with a passed name and a string
+// value generated by the passed Stringer interface.
+func Stringer(k string, v fmt.Stringer) KeyValue {
+	return Key(k).String(v.String())
+}
+
+// Int creates a new key-value pair instance with a passed name and
+// either an int32 or an int64 value, depending on whether the int
+// type is 32 or 64 bits wide.
+func Int(k string, v int) KeyValue {
+	return Key(k).Int(v)
+}
+
+// Uint creates a new key-value pair instance with a passed name and
+// either an uint32 or an uint64 value, depending on whether the uint
+// type is 32 or 64 bits wide.
+func Uint(k string, v uint) KeyValue {
+	return Key(k).Uint(v)
+}
+
+// Array creates a new key-value pair with a passed name and a array.
+// Only arrays of primitive type are supported.
+func Array(k string, v interface{}) KeyValue {
+	return Key(k).Array(v)
+}
+
+// Any creates a new key-value pair instance with a passed name and
+// automatic type inference. This is slower, and not type-safe.
+func Any(k string, value interface{}) KeyValue {
+	if value == nil {
+		return String(k, "<nil>")
+	}
+
+	if stringer, ok := value.(fmt.Stringer); ok {
+		return String(k, stringer.String())
+	}
+
+	rv := reflect.ValueOf(value)
+
+	switch rv.Kind() {
+	case reflect.Array, reflect.Slice:
+		return Array(k, value)
+	case reflect.Bool:
+		return Bool(k, rv.Bool())
+	case reflect.Int, reflect.Int8, reflect.Int16:
+		return Int(k, int(rv.Int()))
+	case reflect.Int32:
+		return Int32(k, int32(rv.Int()))
+	case reflect.Int64:
+		return Int64(k, int64(rv.Int()))
+	case reflect.Uint, reflect.Uint8, reflect.Uint16:
+		return Uint(k, uint(rv.Uint()))
+	case reflect.Uint32:
+		return Uint32(k, uint32(rv.Uint()))
+	case reflect.Uint64, reflect.Uintptr:
+		return Uint64(k, rv.Uint())
+	case reflect.Float32:
+		return Float32(k, float32(rv.Float()))
+	case reflect.Float64:
+		return Float64(k, rv.Float())
+	case reflect.String:
+		return String(k, rv.String())
+	}
+	if b, err := json.Marshal(value); b != nil && err == nil {
+		return String(k, string(b))
+	}
+	return String(k, fmt.Sprint(value))
+}
diff --git a/vendor/go.opentelemetry.io/otel/label/set.go b/vendor/go.opentelemetry.io/otel/label/set.go
new file mode 100644
index 0000000000..2d2f004504
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/label/set.go
@@ -0,0 +1,471 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 label // import "go.opentelemetry.io/otel/label"
+
+import (
+	"encoding/json"
+	"reflect"
+	"sort"
+	"sync"
+)
+
+type (
+	// Set is the representation for a distinct label set.  It
+	// manages an immutable set of labels, with an internal cache
+	// for storing label encodings.
+	//
+	// This type supports the `Equivalent` method of comparison
+	// using values of type `Distinct`.
+	//
+	// This type is used to implement:
+	// 1. Metric labels
+	// 2. Resource sets
+	// 3. Correlation map (TODO)
+	Set struct {
+		equivalent Distinct
+
+		lock     sync.Mutex
+		encoders [maxConcurrentEncoders]EncoderID
+		encoded  [maxConcurrentEncoders]string
+	}
+
+	// Distinct wraps a variable-size array of `KeyValue`,
+	// constructed with keys in sorted order.  This can be used as
+	// a map key or for equality checking between Sets.
+	Distinct struct {
+		iface interface{}
+	}
+
+	// Filter supports removing certain labels from label sets.
+	// When the filter returns true, the label will be kept in
+	// the filtered label set.  When the filter returns false, the
+	// label is excluded from the filtered label set, and the
+	// label instead appears in the `removed` list of excluded labels.
+	Filter func(KeyValue) bool
+
+	// Sortable implements `sort.Interface`, used for sorting
+	// `KeyValue`.  This is an exported type to support a
+	// memory optimization.  A pointer to one of these is needed
+	// for the call to `sort.Stable()`, which the caller may
+	// provide in order to avoid an allocation.  See
+	// `NewSetWithSortable()`.
+	Sortable []KeyValue
+)
+
+var (
+	// keyValueType is used in `computeDistinctReflect`.
+	keyValueType = reflect.TypeOf(KeyValue{})
+
+	// emptySet is returned for empty label sets.
+	emptySet = &Set{
+		equivalent: Distinct{
+			iface: [0]KeyValue{},
+		},
+	}
+)
+
+const maxConcurrentEncoders = 3
+
+// EmptySet returns a reference to a Set with no elements.
+//
+// This is a convenience provided for optimized calling utility.
+func EmptySet() *Set {
+	return emptySet
+}
+
+// reflect abbreviates `reflect.ValueOf`.
+func (d Distinct) reflect() reflect.Value {
+	return reflect.ValueOf(d.iface)
+}
+
+// Valid returns true if this value refers to a valid `*Set`.
+func (d Distinct) Valid() bool {
+	return d.iface != nil
+}
+
+// Len returns the number of labels in this set.
+func (l *Set) Len() int {
+	if l == nil || !l.equivalent.Valid() {
+		return 0
+	}
+	return l.equivalent.reflect().Len()
+}
+
+// Get returns the KeyValue at ordered position `idx` in this set.
+func (l *Set) Get(idx int) (KeyValue, bool) {
+	if l == nil {
+		return KeyValue{}, false
+	}
+	value := l.equivalent.reflect()
+
+	if idx >= 0 && idx < value.Len() {
+		// Note: The Go compiler successfully avoids an allocation for
+		// the interface{} conversion here:
+		return value.Index(idx).Interface().(KeyValue), true
+	}
+
+	return KeyValue{}, false
+}
+
+// Value returns the value of a specified key in this set.
+func (l *Set) Value(k Key) (Value, bool) {
+	if l == nil {
+		return Value{}, false
+	}
+	rValue := l.equivalent.reflect()
+	vlen := rValue.Len()
+
+	idx := sort.Search(vlen, func(idx int) bool {
+		return rValue.Index(idx).Interface().(KeyValue).Key >= k
+	})
+	if idx >= vlen {
+		return Value{}, false
+	}
+	keyValue := rValue.Index(idx).Interface().(KeyValue)
+	if k == keyValue.Key {
+		return keyValue.Value, true
+	}
+	return Value{}, false
+}
+
+// HasValue tests whether a key is defined in this set.
+func (l *Set) HasValue(k Key) bool {
+	if l == nil {
+		return false
+	}
+	_, ok := l.Value(k)
+	return ok
+}
+
+// Iter returns an iterator for visiting the labels in this set.
+func (l *Set) Iter() Iterator {
+	return Iterator{
+		storage: l,
+		idx:     -1,
+	}
+}
+
+// ToSlice returns the set of labels belonging to this set, sorted,
+// where keys appear no more than once.
+func (l *Set) ToSlice() []KeyValue {
+	iter := l.Iter()
+	return iter.ToSlice()
+}
+
+// Equivalent returns a value that may be used as a map key.  The
+// Distinct type guarantees that the result will equal the equivalent
+// Distinct value of any label set with the same elements as this,
+// where sets are made unique by choosing the last value in the input
+// for any given key.
+func (l *Set) Equivalent() Distinct {
+	if l == nil || !l.equivalent.Valid() {
+		return emptySet.equivalent
+	}
+	return l.equivalent
+}
+
+// Equals returns true if the argument set is equivalent to this set.
+func (l *Set) Equals(o *Set) bool {
+	return l.Equivalent() == o.Equivalent()
+}
+
+// Encoded returns the encoded form of this set, according to
+// `encoder`.  The result will be cached in this `*Set`.
+func (l *Set) Encoded(encoder Encoder) string {
+	if l == nil || encoder == nil {
+		return ""
+	}
+
+	id := encoder.ID()
+	if !id.Valid() {
+		// Invalid IDs are not cached.
+		return encoder.Encode(l.Iter())
+	}
+
+	var lookup *string
+	l.lock.Lock()
+	for idx := 0; idx < maxConcurrentEncoders; idx++ {
+		if l.encoders[idx] == id {
+			lookup = &l.encoded[idx]
+			break
+		}
+	}
+	l.lock.Unlock()
+
+	if lookup != nil {
+		return *lookup
+	}
+
+	r := encoder.Encode(l.Iter())
+
+	l.lock.Lock()
+	defer l.lock.Unlock()
+
+	for idx := 0; idx < maxConcurrentEncoders; idx++ {
+		if l.encoders[idx] == id {
+			return l.encoded[idx]
+		}
+		if !l.encoders[idx].Valid() {
+			l.encoders[idx] = id
+			l.encoded[idx] = r
+			return r
+		}
+	}
+
+	// TODO: This is a performance cliff.  Find a way for this to
+	// generate a warning.
+	return r
+}
+
+func empty() Set {
+	return Set{
+		equivalent: emptySet.equivalent,
+	}
+}
+
+// NewSet returns a new `Set`.  See the documentation for
+// `NewSetWithSortableFiltered` for more details.
+//
+// Except for empty sets, this method adds an additional allocation
+// compared with calls that include a `*Sortable`.
+func NewSet(kvs ...KeyValue) Set {
+	// Check for empty set.
+	if len(kvs) == 0 {
+		return empty()
+	}
+	s, _ := NewSetWithSortableFiltered(kvs, new(Sortable), nil)
+	return s //nolint
+}
+
+// NewSetWithSortable returns a new `Set`.  See the documentation for
+// `NewSetWithSortableFiltered` for more details.
+//
+// This call includes a `*Sortable` option as a memory optimization.
+func NewSetWithSortable(kvs []KeyValue, tmp *Sortable) Set {
+	// Check for empty set.
+	if len(kvs) == 0 {
+		return empty()
+	}
+	s, _ := NewSetWithSortableFiltered(kvs, tmp, nil)
+	return s //nolint
+}
+
+// NewSetWithFiltered returns a new `Set`.  See the documentation for
+// `NewSetWithSortableFiltered` for more details.
+//
+// This call includes a `Filter` to include/exclude label keys from
+// the return value.  Excluded keys are returned as a slice of label
+// values.
+func NewSetWithFiltered(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
+	// Check for empty set.
+	if len(kvs) == 0 {
+		return empty(), nil
+	}
+	return NewSetWithSortableFiltered(kvs, new(Sortable), filter)
+}
+
+// NewSetWithSortableFiltered returns a new `Set`.
+//
+// Duplicate keys are eliminated by taking the last value.  This
+// re-orders the input slice so that unique last-values are contiguous
+// at the end of the slice.
+//
+// This ensures the following:
+//
+// - Last-value-wins semantics
+// - Caller sees the reordering, but doesn't lose values
+// - Repeated call preserve last-value wins.
+//
+// Note that methods are defined on `*Set`, although this returns `Set`.
+// Callers can avoid memory allocations by:
+//
+// - allocating a `Sortable` for use as a temporary in this method
+// - allocating a `Set` for storing the return value of this
+//   constructor.
+//
+// The result maintains a cache of encoded labels, by label.EncoderID.
+// This value should not be copied after its first use.
+//
+// The second `[]KeyValue` return value is a list of labels that were
+// excluded by the Filter (if non-nil).
+func NewSetWithSortableFiltered(kvs []KeyValue, tmp *Sortable, filter Filter) (Set, []KeyValue) {
+	// Check for empty set.
+	if len(kvs) == 0 {
+		return empty(), nil
+	}
+
+	*tmp = kvs
+
+	// Stable sort so the following de-duplication can implement
+	// last-value-wins semantics.
+	sort.Stable(tmp)
+
+	*tmp = nil
+
+	position := len(kvs) - 1
+	offset := position - 1
+
+	// The requirements stated above require that the stable
+	// result be placed in the end of the input slice, while
+	// overwritten values are swapped to the beginning.
+	//
+	// De-duplicate with last-value-wins semantics.  Preserve
+	// duplicate values at the beginning of the input slice.
+	for ; offset >= 0; offset-- {
+		if kvs[offset].Key == kvs[position].Key {
+			continue
+		}
+		position--
+		kvs[offset], kvs[position] = kvs[position], kvs[offset]
+	}
+	if filter != nil {
+		return filterSet(kvs[position:], filter)
+	}
+	return Set{
+		equivalent: computeDistinct(kvs[position:]),
+	}, nil
+}
+
+// filterSet reorders `kvs` so that included keys are contiguous at
+// the end of the slice, while excluded keys precede the included keys.
+func filterSet(kvs []KeyValue, filter Filter) (Set, []KeyValue) {
+	var excluded []KeyValue
+
+	// Move labels that do not match the filter so
+	// they're adjacent before calling computeDistinct().
+	distinctPosition := len(kvs)
+
+	// Swap indistinct keys forward and distinct keys toward the
+	// end of the slice.
+	offset := len(kvs) - 1
+	for ; offset >= 0; offset-- {
+		if filter(kvs[offset]) {
+			distinctPosition--
+			kvs[offset], kvs[distinctPosition] = kvs[distinctPosition], kvs[offset]
+			continue
+		}
+	}
+	excluded = kvs[:distinctPosition]
+
+	return Set{
+		equivalent: computeDistinct(kvs[distinctPosition:]),
+	}, excluded
+}
+
+// Filter returns a filtered copy of this `Set`.  See the
+// documentation for `NewSetWithSortableFiltered` for more details.
+func (l *Set) Filter(re Filter) (Set, []KeyValue) {
+	if re == nil {
+		return Set{
+			equivalent: l.equivalent,
+		}, nil
+	}
+
+	// Note: This could be refactored to avoid the temporary slice
+	// allocation, if it proves to be expensive.
+	return filterSet(l.ToSlice(), re)
+}
+
+// computeDistinct returns a `Distinct` using either the fixed- or
+// reflect-oriented code path, depending on the size of the input.
+// The input slice is assumed to already be sorted and de-duplicated.
+func computeDistinct(kvs []KeyValue) Distinct {
+	iface := computeDistinctFixed(kvs)
+	if iface == nil {
+		iface = computeDistinctReflect(kvs)
+	}
+	return Distinct{
+		iface: iface,
+	}
+}
+
+// computeDistinctFixed computes a `Distinct` for small slices.  It
+// returns nil if the input is too large for this code path.
+func computeDistinctFixed(kvs []KeyValue) interface{} {
+	switch len(kvs) {
+	case 1:
+		ptr := new([1]KeyValue)
+		copy((*ptr)[:], kvs)
+		return *ptr
+	case 2:
+		ptr := new([2]KeyValue)
+		copy((*ptr)[:], kvs)
+		return *ptr
+	case 3:
+		ptr := new([3]KeyValue)
+		copy((*ptr)[:], kvs)
+		return *ptr
+	case 4:
+		ptr := new([4]KeyValue)
+		copy((*ptr)[:], kvs)
+		return *ptr
+	case 5:
+		ptr := new([5]KeyValue)
+		copy((*ptr)[:], kvs)
+		return *ptr
+	case 6:
+		ptr := new([6]KeyValue)
+		copy((*ptr)[:], kvs)
+		return *ptr
+	case 7:
+		ptr := new([7]KeyValue)
+		copy((*ptr)[:], kvs)
+		return *ptr
+	case 8:
+		ptr := new([8]KeyValue)
+		copy((*ptr)[:], kvs)
+		return *ptr
+	case 9:
+		ptr := new([9]KeyValue)
+		copy((*ptr)[:], kvs)
+		return *ptr
+	case 10:
+		ptr := new([10]KeyValue)
+		copy((*ptr)[:], kvs)
+		return *ptr
+	default:
+		return nil
+	}
+}
+
+// computeDistinctReflect computes a `Distinct` using reflection,
+// works for any size input.
+func computeDistinctReflect(kvs []KeyValue) interface{} {
+	at := reflect.New(reflect.ArrayOf(len(kvs), keyValueType)).Elem()
+	for i, keyValue := range kvs {
+		*(at.Index(i).Addr().Interface().(*KeyValue)) = keyValue
+	}
+	return at.Interface()
+}
+
+// MarshalJSON returns the JSON encoding of the `*Set`.
+func (l *Set) MarshalJSON() ([]byte, error) {
+	return json.Marshal(l.equivalent.iface)
+}
+
+// Len implements `sort.Interface`.
+func (l *Sortable) Len() int {
+	return len(*l)
+}
+
+// Swap implements `sort.Interface`.
+func (l *Sortable) Swap(i, j int) {
+	(*l)[i], (*l)[j] = (*l)[j], (*l)[i]
+}
+
+// Less implements `sort.Interface`.
+func (l *Sortable) Less(i, j int) bool {
+	return (*l)[i].Key < (*l)[j].Key
+}
diff --git a/vendor/go.opentelemetry.io/otel/label/type_string.go b/vendor/go.opentelemetry.io/otel/label/type_string.go
new file mode 100644
index 0000000000..62afeb60af
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/label/type_string.go
@@ -0,0 +1,32 @@
+// Code generated by "stringer -type=Type"; DO NOT EDIT.
+
+package label
+
+import "strconv"
+
+func _() {
+	// An "invalid array index" compiler error signifies that the constant values have changed.
+	// Re-run the stringer command to generate them again.
+	var x [1]struct{}
+	_ = x[INVALID-0]
+	_ = x[BOOL-1]
+	_ = x[INT32-2]
+	_ = x[INT64-3]
+	_ = x[UINT32-4]
+	_ = x[UINT64-5]
+	_ = x[FLOAT32-6]
+	_ = x[FLOAT64-7]
+	_ = x[STRING-8]
+	_ = x[ARRAY-9]
+}
+
+const _Type_name = "INVALIDBOOLINT32INT64UINT32UINT64FLOAT32FLOAT64STRINGARRAY"
+
+var _Type_index = [...]uint8{0, 7, 11, 16, 21, 27, 33, 40, 47, 53, 58}
+
+func (i Type) String() string {
+	if i < 0 || i >= Type(len(_Type_index)-1) {
+		return "Type(" + strconv.FormatInt(int64(i), 10) + ")"
+	}
+	return _Type_name[_Type_index[i]:_Type_index[i+1]]
+}
diff --git a/vendor/go.opentelemetry.io/otel/label/value.go b/vendor/go.opentelemetry.io/otel/label/value.go
new file mode 100644
index 0000000000..6cc0bee80c
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/label/value.go
@@ -0,0 +1,300 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 label // import "go.opentelemetry.io/otel/label"
+
+import (
+	"encoding/json"
+	"fmt"
+	"reflect"
+	"strconv"
+	"unsafe"
+
+	"go.opentelemetry.io/otel/internal"
+)
+
+//go:generate stringer -type=Type
+
+// Type describes the type of the data Value holds.
+type Type int
+
+// Value represents the value part in key-value pairs.
+type Value struct {
+	vtype    Type
+	numeric  uint64
+	stringly string
+	// TODO Lazy value type?
+
+	array interface{}
+}
+
+const (
+	// INVALID is used for a Value with no value set.
+	INVALID Type = iota
+	// BOOL is a boolean Type Value.
+	BOOL
+	// INT32 is a 32-bit signed integral Type Value.
+	INT32
+	// INT64 is a 64-bit signed integral Type Value.
+	INT64
+	// UINT32 is a 32-bit unsigned integral Type Value.
+	UINT32
+	// UINT64 is a 64-bit unsigned integral Type Value.
+	UINT64
+	// FLOAT32 is a 32-bit floating point Type Value.
+	FLOAT32
+	// FLOAT64 is a 64-bit floating point Type Value.
+	FLOAT64
+	// STRING is a string Type Value.
+	STRING
+	// ARRAY is an array Type Value used to store 1-dimensional slices or
+	// arrays of bool, int, int32, int64, uint, uint32, uint64, float,
+	// float32, float64, or string types.
+	ARRAY
+)
+
+// BoolValue creates a BOOL Value.
+func BoolValue(v bool) Value {
+	return Value{
+		vtype:   BOOL,
+		numeric: internal.BoolToRaw(v),
+	}
+}
+
+// Int64Value creates an INT64 Value.
+func Int64Value(v int64) Value {
+	return Value{
+		vtype:   INT64,
+		numeric: internal.Int64ToRaw(v),
+	}
+}
+
+// Uint64Value creates a UINT64 Value.
+func Uint64Value(v uint64) Value {
+	return Value{
+		vtype:   UINT64,
+		numeric: internal.Uint64ToRaw(v),
+	}
+}
+
+// Float64Value creates a FLOAT64 Value.
+func Float64Value(v float64) Value {
+	return Value{
+		vtype:   FLOAT64,
+		numeric: internal.Float64ToRaw(v),
+	}
+}
+
+// Int32Value creates an INT32 Value.
+func Int32Value(v int32) Value {
+	return Value{
+		vtype:   INT32,
+		numeric: internal.Int32ToRaw(v),
+	}
+}
+
+// Uint32Value creates a UINT32 Value.
+func Uint32Value(v uint32) Value {
+	return Value{
+		vtype:   UINT32,
+		numeric: internal.Uint32ToRaw(v),
+	}
+}
+
+// Float32Value creates a FLOAT32 Value.
+func Float32Value(v float32) Value {
+	return Value{
+		vtype:   FLOAT32,
+		numeric: internal.Float32ToRaw(v),
+	}
+}
+
+// StringValue creates a STRING Value.
+func StringValue(v string) Value {
+	return Value{
+		vtype:    STRING,
+		stringly: v,
+	}
+}
+
+// IntValue creates either an INT32 or an INT64 Value, depending on whether
+// the int type is 32 or 64 bits wide.
+func IntValue(v int) Value {
+	if unsafe.Sizeof(v) == 4 {
+		return Int32Value(int32(v))
+	}
+	return Int64Value(int64(v))
+}
+
+// UintValue creates either a UINT32 or a UINT64 Value, depending on whether
+// the uint type is 32 or 64 bits wide.
+func UintValue(v uint) Value {
+	if unsafe.Sizeof(v) == 4 {
+		return Uint32Value(uint32(v))
+	}
+	return Uint64Value(uint64(v))
+}
+
+// ArrayValue creates an ARRAY value from an array or slice.
+// Only arrays or slices of bool, int, int32, int64, uint, uint32, uint64,
+// float, float32, float64, or string types are allowed. Specifically, arrays
+// and slices can not contain other arrays, slices, structs, or non-standard
+// types. If the passed value is not an array or slice of these types an
+// INVALID value is returned.
+func ArrayValue(v interface{}) Value {
+	switch reflect.TypeOf(v).Kind() {
+	case reflect.Array, reflect.Slice:
+		// get array type regardless of dimensions
+		typ := reflect.TypeOf(v).Elem()
+		kind := typ.Kind()
+		switch kind {
+		case reflect.Bool, reflect.Int, reflect.Int32, reflect.Int64,
+			reflect.Float32, reflect.Float64, reflect.String,
+			reflect.Uint, reflect.Uint32, reflect.Uint64:
+			val := reflect.ValueOf(v)
+			length := val.Len()
+			frozen := reflect.Indirect(reflect.New(reflect.ArrayOf(length, typ)))
+			reflect.Copy(frozen, val)
+			return Value{
+				vtype: ARRAY,
+				array: frozen.Interface(),
+			}
+		default:
+			return Value{vtype: INVALID}
+		}
+	}
+	return Value{vtype: INVALID}
+}
+
+// Type returns a type of the Value.
+func (v Value) Type() Type {
+	return v.vtype
+}
+
+// AsBool returns the bool value. Make sure that the Value's type is
+// BOOL.
+func (v Value) AsBool() bool {
+	return internal.RawToBool(v.numeric)
+}
+
+// AsInt32 returns the int32 value. Make sure that the Value's type is
+// INT32.
+func (v Value) AsInt32() int32 {
+	return internal.RawToInt32(v.numeric)
+}
+
+// AsInt64 returns the int64 value. Make sure that the Value's type is
+// INT64.
+func (v Value) AsInt64() int64 {
+	return internal.RawToInt64(v.numeric)
+}
+
+// AsUint32 returns the uint32 value. Make sure that the Value's type
+// is UINT32.
+func (v Value) AsUint32() uint32 {
+	return internal.RawToUint32(v.numeric)
+}
+
+// AsUint64 returns the uint64 value. Make sure that the Value's type is
+// UINT64.
+func (v Value) AsUint64() uint64 {
+	return internal.RawToUint64(v.numeric)
+}
+
+// AsFloat32 returns the float32 value. Make sure that the Value's
+// type is FLOAT32.
+func (v Value) AsFloat32() float32 {
+	return internal.RawToFloat32(v.numeric)
+}
+
+// AsFloat64 returns the float64 value. Make sure that the Value's
+// type is FLOAT64.
+func (v Value) AsFloat64() float64 {
+	return internal.RawToFloat64(v.numeric)
+}
+
+// AsString returns the string value. Make sure that the Value's type
+// is STRING.
+func (v Value) AsString() string {
+	return v.stringly
+}
+
+// AsArray returns the array Value as an interface{}.
+func (v Value) AsArray() interface{} {
+	return v.array
+}
+
+type unknownValueType struct{}
+
+// AsInterface returns Value's data as interface{}.
+func (v Value) AsInterface() interface{} {
+	switch v.Type() {
+	case ARRAY:
+		return v.AsArray()
+	case BOOL:
+		return v.AsBool()
+	case INT32:
+		return v.AsInt32()
+	case INT64:
+		return v.AsInt64()
+	case UINT32:
+		return v.AsUint32()
+	case UINT64:
+		return v.AsUint64()
+	case FLOAT32:
+		return v.AsFloat32()
+	case FLOAT64:
+		return v.AsFloat64()
+	case STRING:
+		return v.stringly
+	}
+	return unknownValueType{}
+}
+
+// Emit returns a string representation of Value's data.
+func (v Value) Emit() string {
+	switch v.Type() {
+	case ARRAY:
+		return fmt.Sprint(v.array)
+	case BOOL:
+		return strconv.FormatBool(v.AsBool())
+	case INT32:
+		return strconv.FormatInt(int64(v.AsInt32()), 10)
+	case INT64:
+		return strconv.FormatInt(v.AsInt64(), 10)
+	case UINT32:
+		return strconv.FormatUint(uint64(v.AsUint32()), 10)
+	case UINT64:
+		return strconv.FormatUint(v.AsUint64(), 10)
+	case FLOAT32:
+		return fmt.Sprint(v.AsFloat32())
+	case FLOAT64:
+		return fmt.Sprint(v.AsFloat64())
+	case STRING:
+		return v.stringly
+	default:
+		return "unknown"
+	}
+}
+
+// MarshalJSON returns the JSON encoding of the Value.
+func (v Value) MarshalJSON() ([]byte, error) {
+	var jsonVal struct {
+		Type  string
+		Value interface{}
+	}
+	jsonVal.Type = v.Type().String()
+	jsonVal.Value = v.AsInterface()
+	return json.Marshal(jsonVal)
+}
diff --git a/vendor/go.opentelemetry.io/otel/metric.go b/vendor/go.opentelemetry.io/otel/metric.go
new file mode 100644
index 0000000000..6d285b911a
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric.go
@@ -0,0 +1,49 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 otel // import "go.opentelemetry.io/otel"
+
+import (
+	"go.opentelemetry.io/otel/internal/global"
+	"go.opentelemetry.io/otel/metric"
+)
+
+// Meter creates an implementation of the Meter interface from the global
+// MeterProvider. The instrumentationName must be the name of the library
+// providing instrumentation. This name may be the same as the instrumented
+// code only if that code provides built-in instrumentation. If the
+// instrumentationName is empty, then a implementation defined default name
+// will be used instead.
+//
+// This is short for MeterProvider().Meter(name)
+func Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
+	return GetMeterProvider().Meter(instrumentationName, opts...)
+}
+
+// GetMeterProvider returns the registered global meter provider.  If
+// none is registered then a default meter provider is returned that
+// forwards the Meter interface to the first registered Meter.
+//
+// Use the meter provider to create a named meter. E.g.
+//     meter := global.MeterProvider().Meter("example.com/foo")
+// or
+//     meter := global.Meter("example.com/foo")
+func GetMeterProvider() metric.MeterProvider {
+	return global.MeterProvider()
+}
+
+// SetMeterProvider registers `mp` as the global meter provider.
+func SetMeterProvider(mp metric.MeterProvider) {
+	global.SetMeterProvider(mp)
+}
diff --git a/vendor/go.opentelemetry.io/otel/metric/config.go b/vendor/go.opentelemetry.io/otel/metric/config.go
new file mode 100644
index 0000000000..02f0ff8e0c
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric/config.go
@@ -0,0 +1,128 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 metric // import "go.opentelemetry.io/otel/metric"
+
+import (
+	"go.opentelemetry.io/otel/unit"
+)
+
+// InstrumentConfig contains options for metric instrument descriptors.
+type InstrumentConfig struct {
+	// Description describes the instrument in human-readable terms.
+	Description string
+	// Unit describes the measurement unit for a instrument.
+	Unit unit.Unit
+	// InstrumentationName is the name of the library providing
+	// instrumentation.
+	InstrumentationName string
+	// InstrumentationVersion is the version of the library providing
+	// instrumentation.
+	InstrumentationVersion string
+}
+
+// InstrumentOption is an interface for applying metric instrument options.
+type InstrumentOption interface {
+	// ApplyMeter is used to set a InstrumentOption value of a
+	// InstrumentConfig.
+	ApplyInstrument(*InstrumentConfig)
+}
+
+// NewInstrumentConfig creates a new InstrumentConfig
+// and applies all the given options.
+func NewInstrumentConfig(opts ...InstrumentOption) InstrumentConfig {
+	var config InstrumentConfig
+	for _, o := range opts {
+		o.ApplyInstrument(&config)
+	}
+	return config
+}
+
+// WithDescription applies provided description.
+func WithDescription(desc string) InstrumentOption {
+	return descriptionOption(desc)
+}
+
+type descriptionOption string
+
+func (d descriptionOption) ApplyInstrument(config *InstrumentConfig) {
+	config.Description = string(d)
+}
+
+// WithUnit applies provided unit.
+func WithUnit(unit unit.Unit) InstrumentOption {
+	return unitOption(unit)
+}
+
+type unitOption unit.Unit
+
+func (u unitOption) ApplyInstrument(config *InstrumentConfig) {
+	config.Unit = unit.Unit(u)
+}
+
+// WithInstrumentationName sets the instrumentation name.
+func WithInstrumentationName(name string) InstrumentOption {
+	return instrumentationNameOption(name)
+}
+
+type instrumentationNameOption string
+
+func (i instrumentationNameOption) ApplyInstrument(config *InstrumentConfig) {
+	config.InstrumentationName = string(i)
+}
+
+// MeterConfig contains options for Meters.
+type MeterConfig struct {
+	// InstrumentationVersion is the version of the library providing
+	// instrumentation.
+	InstrumentationVersion string
+}
+
+// MeterOption is an interface for applying Meter options.
+type MeterOption interface {
+	// ApplyMeter is used to set a MeterOption value of a MeterConfig.
+	ApplyMeter(*MeterConfig)
+}
+
+// NewMeterConfig creates a new MeterConfig and applies
+// all the given options.
+func NewMeterConfig(opts ...MeterOption) MeterConfig {
+	var config MeterConfig
+	for _, o := range opts {
+		o.ApplyMeter(&config)
+	}
+	return config
+}
+
+// InstrumentationOption is an interface for applying instrumentation specific
+// options.
+type InstrumentationOption interface {
+	InstrumentOption
+	MeterOption
+}
+
+// WithInstrumentationVersion sets the instrumentation version.
+func WithInstrumentationVersion(version string) InstrumentationOption {
+	return instrumentationVersionOption(version)
+}
+
+type instrumentationVersionOption string
+
+func (i instrumentationVersionOption) ApplyMeter(config *MeterConfig) {
+	config.InstrumentationVersion = string(i)
+}
+
+func (i instrumentationVersionOption) ApplyInstrument(config *InstrumentConfig) {
+	config.InstrumentationVersion = string(i)
+}
diff --git a/vendor/go.opentelemetry.io/otel/metric/doc.go b/vendor/go.opentelemetry.io/otel/metric/doc.go
new file mode 100644
index 0000000000..7889ff000f
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric/doc.go
@@ -0,0 +1,67 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 metric provides an implementation of the metrics part of the
+OpenTelemetry API.
+
+This package is currently in a pre-GA phase. Backwards incompatible changes
+may be introduced in subsequent minor version releases as we work to track the
+evolving OpenTelemetry specification and user feedback.
+
+Measurements can be made about an operation being performed or the state of a
+system in general. These measurements can be crucial to the reliable operation
+of code and provide valuable insights about the inner workings of a system.
+
+Measurements are made using instruments provided by this package. The type of
+instrument used will depend on the type of measurement being made and of what
+part of a system is being measured.
+
+Instruments are categorized as Synchronous or Asynchronous and independently
+as Adding or Grouping. Synchronous instruments are called by the user with a
+Context. Asynchronous instruments are called by the SDK during collection.
+Additive instruments are semantically intended for capturing a sum. Grouping
+instruments are intended for capturing a distribution.
+
+Additive instruments may be monotonic, in which case they are non-decreasing
+and naturally define a rate.
+
+The synchronous instrument names are:
+
+  Counter:           additive, monotonic
+  UpDownCounter:     additive
+  ValueRecorder:     grouping
+
+and the asynchronous instruments are:
+
+  SumObserver:       additive, monotonic
+  UpDownSumObserver: additive
+  ValueObserver:     grouping
+
+All instruments are provided with support for either float64 or int64 input
+values.
+
+An instrument is created using a Meter. Additionally, a Meter is used to
+record batches of synchronous measurements or asynchronous observations. A
+Meter is obtained using a MeterProvider. A Meter, like a Tracer, is unique to
+the instrumentation it instruments and must be named and versioned when
+created with a MeterProvider with the name and version of the instrumentation
+library.
+
+Instrumentation should be designed to accept a MeterProvider from which it can
+create its own unique Meter. Alternatively, the registered global
+MeterProvider from the go.opentelemetry.io/otel package can be used as a
+default.
+*/
+package metric // import "go.opentelemetry.io/otel/metric"
diff --git a/vendor/go.opentelemetry.io/otel/metric/instrumentkind_string.go b/vendor/go.opentelemetry.io/otel/metric/instrumentkind_string.go
new file mode 100644
index 0000000000..2805e22500
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric/instrumentkind_string.go
@@ -0,0 +1,28 @@
+// Code generated by "stringer -type=InstrumentKind"; DO NOT EDIT.
+
+package metric
+
+import "strconv"
+
+func _() {
+	// An "invalid array index" compiler error signifies that the constant values have changed.
+	// Re-run the stringer command to generate them again.
+	var x [1]struct{}
+	_ = x[ValueRecorderInstrumentKind-0]
+	_ = x[ValueObserverInstrumentKind-1]
+	_ = x[CounterInstrumentKind-2]
+	_ = x[UpDownCounterInstrumentKind-3]
+	_ = x[SumObserverInstrumentKind-4]
+	_ = x[UpDownSumObserverInstrumentKind-5]
+}
+
+const _InstrumentKind_name = "ValueRecorderInstrumentKindValueObserverInstrumentKindCounterInstrumentKindUpDownCounterInstrumentKindSumObserverInstrumentKindUpDownSumObserverInstrumentKind"
+
+var _InstrumentKind_index = [...]uint8{0, 27, 54, 75, 102, 127, 158}
+
+func (i InstrumentKind) String() string {
+	if i < 0 || i >= InstrumentKind(len(_InstrumentKind_index)-1) {
+		return "InstrumentKind(" + strconv.FormatInt(int64(i), 10) + ")"
+	}
+	return _InstrumentKind_name[_InstrumentKind_index[i]:_InstrumentKind_index[i+1]]
+}
diff --git a/vendor/go.opentelemetry.io/otel/metric/metric.go b/vendor/go.opentelemetry.io/otel/metric/metric.go
new file mode 100644
index 0000000000..0b988abba8
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric/metric.go
@@ -0,0 +1,577 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 metric // import "go.opentelemetry.io/otel/metric"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/label"
+	"go.opentelemetry.io/otel/metric/number"
+	"go.opentelemetry.io/otel/unit"
+)
+
+// MeterProvider supports named Meter instances.
+type MeterProvider interface {
+	// Meter creates an implementation of the Meter interface.
+	// The instrumentationName must be the name of the library providing
+	// instrumentation. This name may be the same as the instrumented code
+	// only if that code provides built-in instrumentation. If the
+	// instrumentationName is empty, then a implementation defined default
+	// name will be used instead.
+	Meter(instrumentationName string, opts ...MeterOption) Meter
+}
+
+// Meter is the creator of metric instruments.
+//
+// An uninitialized Meter is a no-op implementation.
+type Meter struct {
+	impl          MeterImpl
+	name, version string
+}
+
+// RecordBatch atomically records a batch of measurements.
+func (m Meter) RecordBatch(ctx context.Context, ls []label.KeyValue, ms ...Measurement) {
+	if m.impl == nil {
+		return
+	}
+	m.impl.RecordBatch(ctx, ls, ms...)
+}
+
+// NewBatchObserver creates a new BatchObserver that supports
+// making batches of observations for multiple instruments.
+func (m Meter) NewBatchObserver(callback BatchObserverFunc) BatchObserver {
+	return BatchObserver{
+		meter:  m,
+		runner: newBatchAsyncRunner(callback),
+	}
+}
+
+// NewInt64Counter creates a new integer Counter instrument with the
+// given name, customized with options.  May return an error if the
+// name is invalid (e.g., empty) or improperly registered (e.g.,
+// duplicate registration).
+func (m Meter) NewInt64Counter(name string, options ...InstrumentOption) (Int64Counter, error) {
+	return wrapInt64CounterInstrument(
+		m.newSync(name, CounterInstrumentKind, number.Int64Kind, options))
+}
+
+// NewFloat64Counter creates a new floating point Counter with the
+// given name, customized with options.  May return an error if the
+// name is invalid (e.g., empty) or improperly registered (e.g.,
+// duplicate registration).
+func (m Meter) NewFloat64Counter(name string, options ...InstrumentOption) (Float64Counter, error) {
+	return wrapFloat64CounterInstrument(
+		m.newSync(name, CounterInstrumentKind, number.Float64Kind, options))
+}
+
+// NewInt64UpDownCounter creates a new integer UpDownCounter instrument with the
+// given name, customized with options.  May return an error if the
+// name is invalid (e.g., empty) or improperly registered (e.g.,
+// duplicate registration).
+func (m Meter) NewInt64UpDownCounter(name string, options ...InstrumentOption) (Int64UpDownCounter, error) {
+	return wrapInt64UpDownCounterInstrument(
+		m.newSync(name, UpDownCounterInstrumentKind, number.Int64Kind, options))
+}
+
+// NewFloat64UpDownCounter creates a new floating point UpDownCounter with the
+// given name, customized with options.  May return an error if the
+// name is invalid (e.g., empty) or improperly registered (e.g.,
+// duplicate registration).
+func (m Meter) NewFloat64UpDownCounter(name string, options ...InstrumentOption) (Float64UpDownCounter, error) {
+	return wrapFloat64UpDownCounterInstrument(
+		m.newSync(name, UpDownCounterInstrumentKind, number.Float64Kind, options))
+}
+
+// NewInt64ValueRecorder creates a new integer ValueRecorder instrument with the
+// given name, customized with options.  May return an error if the
+// name is invalid (e.g., empty) or improperly registered (e.g.,
+// duplicate registration).
+func (m Meter) NewInt64ValueRecorder(name string, opts ...InstrumentOption) (Int64ValueRecorder, error) {
+	return wrapInt64ValueRecorderInstrument(
+		m.newSync(name, ValueRecorderInstrumentKind, number.Int64Kind, opts))
+}
+
+// NewFloat64ValueRecorder creates a new floating point ValueRecorder with the
+// given name, customized with options.  May return an error if the
+// name is invalid (e.g., empty) or improperly registered (e.g.,
+// duplicate registration).
+func (m Meter) NewFloat64ValueRecorder(name string, opts ...InstrumentOption) (Float64ValueRecorder, error) {
+	return wrapFloat64ValueRecorderInstrument(
+		m.newSync(name, ValueRecorderInstrumentKind, number.Float64Kind, opts))
+}
+
+// NewInt64ValueObserver creates a new integer ValueObserver instrument
+// with the given name, running a given callback, and customized with
+// options.  May return an error if the name is invalid (e.g., empty)
+// or improperly registered (e.g., duplicate registration).
+func (m Meter) NewInt64ValueObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64ValueObserver, error) {
+	if callback == nil {
+		return wrapInt64ValueObserverInstrument(NoopAsync{}, nil)
+	}
+	return wrapInt64ValueObserverInstrument(
+		m.newAsync(name, ValueObserverInstrumentKind, number.Int64Kind, opts,
+			newInt64AsyncRunner(callback)))
+}
+
+// NewFloat64ValueObserver creates a new floating point ValueObserver with
+// the given name, running a given callback, and customized with
+// options.  May return an error if the name is invalid (e.g., empty)
+// or improperly registered (e.g., duplicate registration).
+func (m Meter) NewFloat64ValueObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64ValueObserver, error) {
+	if callback == nil {
+		return wrapFloat64ValueObserverInstrument(NoopAsync{}, nil)
+	}
+	return wrapFloat64ValueObserverInstrument(
+		m.newAsync(name, ValueObserverInstrumentKind, number.Float64Kind, opts,
+			newFloat64AsyncRunner(callback)))
+}
+
+// NewInt64SumObserver creates a new integer SumObserver instrument
+// with the given name, running a given callback, and customized with
+// options.  May return an error if the name is invalid (e.g., empty)
+// or improperly registered (e.g., duplicate registration).
+func (m Meter) NewInt64SumObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64SumObserver, error) {
+	if callback == nil {
+		return wrapInt64SumObserverInstrument(NoopAsync{}, nil)
+	}
+	return wrapInt64SumObserverInstrument(
+		m.newAsync(name, SumObserverInstrumentKind, number.Int64Kind, opts,
+			newInt64AsyncRunner(callback)))
+}
+
+// NewFloat64SumObserver creates a new floating point SumObserver with
+// the given name, running a given callback, and customized with
+// options.  May return an error if the name is invalid (e.g., empty)
+// or improperly registered (e.g., duplicate registration).
+func (m Meter) NewFloat64SumObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64SumObserver, error) {
+	if callback == nil {
+		return wrapFloat64SumObserverInstrument(NoopAsync{}, nil)
+	}
+	return wrapFloat64SumObserverInstrument(
+		m.newAsync(name, SumObserverInstrumentKind, number.Float64Kind, opts,
+			newFloat64AsyncRunner(callback)))
+}
+
+// NewInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument
+// with the given name, running a given callback, and customized with
+// options.  May return an error if the name is invalid (e.g., empty)
+// or improperly registered (e.g., duplicate registration).
+func (m Meter) NewInt64UpDownSumObserver(name string, callback Int64ObserverFunc, opts ...InstrumentOption) (Int64UpDownSumObserver, error) {
+	if callback == nil {
+		return wrapInt64UpDownSumObserverInstrument(NoopAsync{}, nil)
+	}
+	return wrapInt64UpDownSumObserverInstrument(
+		m.newAsync(name, UpDownSumObserverInstrumentKind, number.Int64Kind, opts,
+			newInt64AsyncRunner(callback)))
+}
+
+// NewFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with
+// the given name, running a given callback, and customized with
+// options.  May return an error if the name is invalid (e.g., empty)
+// or improperly registered (e.g., duplicate registration).
+func (m Meter) NewFloat64UpDownSumObserver(name string, callback Float64ObserverFunc, opts ...InstrumentOption) (Float64UpDownSumObserver, error) {
+	if callback == nil {
+		return wrapFloat64UpDownSumObserverInstrument(NoopAsync{}, nil)
+	}
+	return wrapFloat64UpDownSumObserverInstrument(
+		m.newAsync(name, UpDownSumObserverInstrumentKind, number.Float64Kind, opts,
+			newFloat64AsyncRunner(callback)))
+}
+
+// NewInt64ValueObserver creates a new integer ValueObserver instrument
+// with the given name, running in a batch callback, and customized with
+// options.  May return an error if the name is invalid (e.g., empty)
+// or improperly registered (e.g., duplicate registration).
+func (b BatchObserver) NewInt64ValueObserver(name string, opts ...InstrumentOption) (Int64ValueObserver, error) {
+	if b.runner == nil {
+		return wrapInt64ValueObserverInstrument(NoopAsync{}, nil)
+	}
+	return wrapInt64ValueObserverInstrument(
+		b.meter.newAsync(name, ValueObserverInstrumentKind, number.Int64Kind, opts, b.runner))
+}
+
+// NewFloat64ValueObserver creates a new floating point ValueObserver with
+// the given name, running in a batch callback, and customized with
+// options.  May return an error if the name is invalid (e.g., empty)
+// or improperly registered (e.g., duplicate registration).
+func (b BatchObserver) NewFloat64ValueObserver(name string, opts ...InstrumentOption) (Float64ValueObserver, error) {
+	if b.runner == nil {
+		return wrapFloat64ValueObserverInstrument(NoopAsync{}, nil)
+	}
+	return wrapFloat64ValueObserverInstrument(
+		b.meter.newAsync(name, ValueObserverInstrumentKind, number.Float64Kind, opts,
+			b.runner))
+}
+
+// NewInt64SumObserver creates a new integer SumObserver instrument
+// with the given name, running in a batch callback, and customized with
+// options.  May return an error if the name is invalid (e.g., empty)
+// or improperly registered (e.g., duplicate registration).
+func (b BatchObserver) NewInt64SumObserver(name string, opts ...InstrumentOption) (Int64SumObserver, error) {
+	if b.runner == nil {
+		return wrapInt64SumObserverInstrument(NoopAsync{}, nil)
+	}
+	return wrapInt64SumObserverInstrument(
+		b.meter.newAsync(name, SumObserverInstrumentKind, number.Int64Kind, opts, b.runner))
+}
+
+// NewFloat64SumObserver creates a new floating point SumObserver with
+// the given name, running in a batch callback, and customized with
+// options.  May return an error if the name is invalid (e.g., empty)
+// or improperly registered (e.g., duplicate registration).
+func (b BatchObserver) NewFloat64SumObserver(name string, opts ...InstrumentOption) (Float64SumObserver, error) {
+	if b.runner == nil {
+		return wrapFloat64SumObserverInstrument(NoopAsync{}, nil)
+	}
+	return wrapFloat64SumObserverInstrument(
+		b.meter.newAsync(name, SumObserverInstrumentKind, number.Float64Kind, opts,
+			b.runner))
+}
+
+// NewInt64UpDownSumObserver creates a new integer UpDownSumObserver instrument
+// with the given name, running in a batch callback, and customized with
+// options.  May return an error if the name is invalid (e.g., empty)
+// or improperly registered (e.g., duplicate registration).
+func (b BatchObserver) NewInt64UpDownSumObserver(name string, opts ...InstrumentOption) (Int64UpDownSumObserver, error) {
+	if b.runner == nil {
+		return wrapInt64UpDownSumObserverInstrument(NoopAsync{}, nil)
+	}
+	return wrapInt64UpDownSumObserverInstrument(
+		b.meter.newAsync(name, UpDownSumObserverInstrumentKind, number.Int64Kind, opts, b.runner))
+}
+
+// NewFloat64UpDownSumObserver creates a new floating point UpDownSumObserver with
+// the given name, running in a batch callback, and customized with
+// options.  May return an error if the name is invalid (e.g., empty)
+// or improperly registered (e.g., duplicate registration).
+func (b BatchObserver) NewFloat64UpDownSumObserver(name string, opts ...InstrumentOption) (Float64UpDownSumObserver, error) {
+	if b.runner == nil {
+		return wrapFloat64UpDownSumObserverInstrument(NoopAsync{}, nil)
+	}
+	return wrapFloat64UpDownSumObserverInstrument(
+		b.meter.newAsync(name, UpDownSumObserverInstrumentKind, number.Float64Kind, opts,
+			b.runner))
+}
+
+// MeterImpl returns the underlying MeterImpl of this Meter.
+func (m Meter) MeterImpl() MeterImpl {
+	return m.impl
+}
+
+// newAsync constructs one new asynchronous instrument.
+func (m Meter) newAsync(
+	name string,
+	mkind InstrumentKind,
+	nkind number.Kind,
+	opts []InstrumentOption,
+	runner AsyncRunner,
+) (
+	AsyncImpl,
+	error,
+) {
+	if m.impl == nil {
+		return NoopAsync{}, nil
+	}
+	desc := NewDescriptor(name, mkind, nkind, opts...)
+	desc.config.InstrumentationName = m.name
+	desc.config.InstrumentationVersion = m.version
+	return m.impl.NewAsyncInstrument(desc, runner)
+}
+
+// newSync constructs one new synchronous instrument.
+func (m Meter) newSync(
+	name string,
+	metricKind InstrumentKind,
+	numberKind number.Kind,
+	opts []InstrumentOption,
+) (
+	SyncImpl,
+	error,
+) {
+	if m.impl == nil {
+		return NoopSync{}, nil
+	}
+	desc := NewDescriptor(name, metricKind, numberKind, opts...)
+	desc.config.InstrumentationName = m.name
+	desc.config.InstrumentationVersion = m.version
+	return m.impl.NewSyncInstrument(desc)
+}
+
+// MeterMust is a wrapper for Meter interfaces that panics when any
+// instrument constructor encounters an error.
+type MeterMust struct {
+	meter Meter
+}
+
+// BatchObserverMust is a wrapper for BatchObserver that panics when
+// any instrument constructor encounters an error.
+type BatchObserverMust struct {
+	batch BatchObserver
+}
+
+// Must constructs a MeterMust implementation from a Meter, allowing
+// the application to panic when any instrument constructor yields an
+// error.
+func Must(meter Meter) MeterMust {
+	return MeterMust{meter: meter}
+}
+
+// NewInt64Counter calls `Meter.NewInt64Counter` and returns the
+// instrument, panicking if it encounters an error.
+func (mm MeterMust) NewInt64Counter(name string, cos ...InstrumentOption) Int64Counter {
+	if inst, err := mm.meter.NewInt64Counter(name, cos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewFloat64Counter calls `Meter.NewFloat64Counter` and returns the
+// instrument, panicking if it encounters an error.
+func (mm MeterMust) NewFloat64Counter(name string, cos ...InstrumentOption) Float64Counter {
+	if inst, err := mm.meter.NewFloat64Counter(name, cos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewInt64UpDownCounter calls `Meter.NewInt64UpDownCounter` and returns the
+// instrument, panicking if it encounters an error.
+func (mm MeterMust) NewInt64UpDownCounter(name string, cos ...InstrumentOption) Int64UpDownCounter {
+	if inst, err := mm.meter.NewInt64UpDownCounter(name, cos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewFloat64UpDownCounter calls `Meter.NewFloat64UpDownCounter` and returns the
+// instrument, panicking if it encounters an error.
+func (mm MeterMust) NewFloat64UpDownCounter(name string, cos ...InstrumentOption) Float64UpDownCounter {
+	if inst, err := mm.meter.NewFloat64UpDownCounter(name, cos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewInt64ValueRecorder calls `Meter.NewInt64ValueRecorder` and returns the
+// instrument, panicking if it encounters an error.
+func (mm MeterMust) NewInt64ValueRecorder(name string, mos ...InstrumentOption) Int64ValueRecorder {
+	if inst, err := mm.meter.NewInt64ValueRecorder(name, mos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewFloat64ValueRecorder calls `Meter.NewFloat64ValueRecorder` and returns the
+// instrument, panicking if it encounters an error.
+func (mm MeterMust) NewFloat64ValueRecorder(name string, mos ...InstrumentOption) Float64ValueRecorder {
+	if inst, err := mm.meter.NewFloat64ValueRecorder(name, mos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewInt64ValueObserver calls `Meter.NewInt64ValueObserver` and
+// returns the instrument, panicking if it encounters an error.
+func (mm MeterMust) NewInt64ValueObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64ValueObserver {
+	if inst, err := mm.meter.NewInt64ValueObserver(name, callback, oos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewFloat64ValueObserver calls `Meter.NewFloat64ValueObserver` and
+// returns the instrument, panicking if it encounters an error.
+func (mm MeterMust) NewFloat64ValueObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64ValueObserver {
+	if inst, err := mm.meter.NewFloat64ValueObserver(name, callback, oos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewInt64SumObserver calls `Meter.NewInt64SumObserver` and
+// returns the instrument, panicking if it encounters an error.
+func (mm MeterMust) NewInt64SumObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64SumObserver {
+	if inst, err := mm.meter.NewInt64SumObserver(name, callback, oos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewFloat64SumObserver calls `Meter.NewFloat64SumObserver` and
+// returns the instrument, panicking if it encounters an error.
+func (mm MeterMust) NewFloat64SumObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64SumObserver {
+	if inst, err := mm.meter.NewFloat64SumObserver(name, callback, oos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewInt64UpDownSumObserver calls `Meter.NewInt64UpDownSumObserver` and
+// returns the instrument, panicking if it encounters an error.
+func (mm MeterMust) NewInt64UpDownSumObserver(name string, callback Int64ObserverFunc, oos ...InstrumentOption) Int64UpDownSumObserver {
+	if inst, err := mm.meter.NewInt64UpDownSumObserver(name, callback, oos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewFloat64UpDownSumObserver calls `Meter.NewFloat64UpDownSumObserver` and
+// returns the instrument, panicking if it encounters an error.
+func (mm MeterMust) NewFloat64UpDownSumObserver(name string, callback Float64ObserverFunc, oos ...InstrumentOption) Float64UpDownSumObserver {
+	if inst, err := mm.meter.NewFloat64UpDownSumObserver(name, callback, oos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewBatchObserver returns a wrapper around BatchObserver that panics
+// when any instrument constructor returns an error.
+func (mm MeterMust) NewBatchObserver(callback BatchObserverFunc) BatchObserverMust {
+	return BatchObserverMust{
+		batch: mm.meter.NewBatchObserver(callback),
+	}
+}
+
+// NewInt64ValueObserver calls `BatchObserver.NewInt64ValueObserver` and
+// returns the instrument, panicking if it encounters an error.
+func (bm BatchObserverMust) NewInt64ValueObserver(name string, oos ...InstrumentOption) Int64ValueObserver {
+	if inst, err := bm.batch.NewInt64ValueObserver(name, oos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewFloat64ValueObserver calls `BatchObserver.NewFloat64ValueObserver` and
+// returns the instrument, panicking if it encounters an error.
+func (bm BatchObserverMust) NewFloat64ValueObserver(name string, oos ...InstrumentOption) Float64ValueObserver {
+	if inst, err := bm.batch.NewFloat64ValueObserver(name, oos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewInt64SumObserver calls `BatchObserver.NewInt64SumObserver` and
+// returns the instrument, panicking if it encounters an error.
+func (bm BatchObserverMust) NewInt64SumObserver(name string, oos ...InstrumentOption) Int64SumObserver {
+	if inst, err := bm.batch.NewInt64SumObserver(name, oos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewFloat64SumObserver calls `BatchObserver.NewFloat64SumObserver` and
+// returns the instrument, panicking if it encounters an error.
+func (bm BatchObserverMust) NewFloat64SumObserver(name string, oos ...InstrumentOption) Float64SumObserver {
+	if inst, err := bm.batch.NewFloat64SumObserver(name, oos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewInt64UpDownSumObserver calls `BatchObserver.NewInt64UpDownSumObserver` and
+// returns the instrument, panicking if it encounters an error.
+func (bm BatchObserverMust) NewInt64UpDownSumObserver(name string, oos ...InstrumentOption) Int64UpDownSumObserver {
+	if inst, err := bm.batch.NewInt64UpDownSumObserver(name, oos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// NewFloat64UpDownSumObserver calls `BatchObserver.NewFloat64UpDownSumObserver` and
+// returns the instrument, panicking if it encounters an error.
+func (bm BatchObserverMust) NewFloat64UpDownSumObserver(name string, oos ...InstrumentOption) Float64UpDownSumObserver {
+	if inst, err := bm.batch.NewFloat64UpDownSumObserver(name, oos...); err != nil {
+		panic(err)
+	} else {
+		return inst
+	}
+}
+
+// Descriptor contains all the settings that describe an instrument,
+// including its name, metric kind, number kind, and the configurable
+// options.
+type Descriptor struct {
+	name           string
+	instrumentKind InstrumentKind
+	numberKind     number.Kind
+	config         InstrumentConfig
+}
+
+// NewDescriptor returns a Descriptor with the given contents.
+func NewDescriptor(name string, ikind InstrumentKind, nkind number.Kind, opts ...InstrumentOption) Descriptor {
+	return Descriptor{
+		name:           name,
+		instrumentKind: ikind,
+		numberKind:     nkind,
+		config:         NewInstrumentConfig(opts...),
+	}
+}
+
+// Name returns the metric instrument's name.
+func (d Descriptor) Name() string {
+	return d.name
+}
+
+// InstrumentKind returns the specific kind of instrument.
+func (d Descriptor) InstrumentKind() InstrumentKind {
+	return d.instrumentKind
+}
+
+// Description provides a human-readable description of the metric
+// instrument.
+func (d Descriptor) Description() string {
+	return d.config.Description
+}
+
+// Unit describes the units of the metric instrument.  Unitless
+// metrics return the empty string.
+func (d Descriptor) Unit() unit.Unit {
+	return d.config.Unit
+}
+
+// NumberKind returns whether this instrument is declared over int64,
+// float64, or uint64 values.
+func (d Descriptor) NumberKind() number.Kind {
+	return d.numberKind
+}
+
+// InstrumentationName returns the name of the library that provided
+// instrumentation for this instrument.
+func (d Descriptor) InstrumentationName() string {
+	return d.config.InstrumentationName
+}
+
+// InstrumentationVersion returns the version of the library that provided
+// instrumentation for this instrument.
+func (d Descriptor) InstrumentationVersion() string {
+	return d.config.InstrumentationVersion
+}
diff --git a/vendor/go.opentelemetry.io/otel/metric/metric_instrument.go b/vendor/go.opentelemetry.io/otel/metric/metric_instrument.go
new file mode 100644
index 0000000000..6e2f9ce955
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric/metric_instrument.go
@@ -0,0 +1,777 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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.
+
+//go:generate stringer -type=InstrumentKind
+
+package metric // import "go.opentelemetry.io/otel/metric"
+
+import (
+	"context"
+	"errors"
+
+	"go.opentelemetry.io/otel/label"
+	"go.opentelemetry.io/otel/metric/number"
+)
+
+// ErrSDKReturnedNilImpl is returned when a new `MeterImpl` returns nil.
+var ErrSDKReturnedNilImpl = errors.New("SDK returned a nil implementation")
+
+// InstrumentKind describes the kind of instrument.
+type InstrumentKind int8
+
+const (
+	// ValueRecorderInstrumentKind indicates a ValueRecorder instrument.
+	ValueRecorderInstrumentKind InstrumentKind = iota
+	// ValueObserverInstrumentKind indicates an ValueObserver instrument.
+	ValueObserverInstrumentKind
+
+	// CounterInstrumentKind indicates a Counter instrument.
+	CounterInstrumentKind
+	// UpDownCounterInstrumentKind indicates a UpDownCounter instrument.
+	UpDownCounterInstrumentKind
+
+	// SumObserverInstrumentKind indicates a SumObserver instrument.
+	SumObserverInstrumentKind
+	// UpDownSumObserverInstrumentKind indicates a UpDownSumObserver
+	// instrument.
+	UpDownSumObserverInstrumentKind
+)
+
+// Synchronous returns whether this is a synchronous kind of instrument.
+func (k InstrumentKind) Synchronous() bool {
+	switch k {
+	case CounterInstrumentKind, UpDownCounterInstrumentKind, ValueRecorderInstrumentKind:
+		return true
+	}
+	return false
+}
+
+// Asynchronous returns whether this is an asynchronous kind of instrument.
+func (k InstrumentKind) Asynchronous() bool {
+	return !k.Synchronous()
+}
+
+// Adding returns whether this kind of instrument adds its inputs (as opposed to Grouping).
+func (k InstrumentKind) Adding() bool {
+	switch k {
+	case CounterInstrumentKind, UpDownCounterInstrumentKind, SumObserverInstrumentKind, UpDownSumObserverInstrumentKind:
+		return true
+	}
+	return false
+}
+
+// Grouping returns whether this kind of instrument groups its inputs (as opposed to Adding).
+func (k InstrumentKind) Grouping() bool {
+	return !k.Adding()
+}
+
+// Monotonic returns whether this kind of instrument exposes a non-decreasing sum.
+func (k InstrumentKind) Monotonic() bool {
+	switch k {
+	case CounterInstrumentKind, SumObserverInstrumentKind:
+		return true
+	}
+	return false
+}
+
+// PrecomputedSum returns whether this kind of instrument receives precomputed sums.
+func (k InstrumentKind) PrecomputedSum() bool {
+	return k.Adding() && k.Asynchronous()
+}
+
+// Observation is used for reporting an asynchronous  batch of metric
+// values. Instances of this type should be created by asynchronous
+// instruments (e.g., Int64ValueObserver.Observation()).
+type Observation struct {
+	// number needs to be aligned for 64-bit atomic operations.
+	number     number.Number
+	instrument AsyncImpl
+}
+
+// Int64ObserverFunc is a type of callback that integral
+// observers run.
+type Int64ObserverFunc func(context.Context, Int64ObserverResult)
+
+// Float64ObserverFunc is a type of callback that floating point
+// observers run.
+type Float64ObserverFunc func(context.Context, Float64ObserverResult)
+
+// BatchObserverFunc is a callback argument for use with any
+// Observer instrument that will be reported as a batch of
+// observations.
+type BatchObserverFunc func(context.Context, BatchObserverResult)
+
+// Int64ObserverResult is passed to an observer callback to capture
+// observations for one asynchronous integer metric instrument.
+type Int64ObserverResult struct {
+	instrument AsyncImpl
+	function   func([]label.KeyValue, ...Observation)
+}
+
+// Float64ObserverResult is passed to an observer callback to capture
+// observations for one asynchronous floating point metric instrument.
+type Float64ObserverResult struct {
+	instrument AsyncImpl
+	function   func([]label.KeyValue, ...Observation)
+}
+
+// BatchObserverResult is passed to a batch observer callback to
+// capture observations for multiple asynchronous instruments.
+type BatchObserverResult struct {
+	function func([]label.KeyValue, ...Observation)
+}
+
+// Observe captures a single integer value from the associated
+// instrument callback, with the given labels.
+func (ir Int64ObserverResult) Observe(value int64, labels ...label.KeyValue) {
+	ir.function(labels, Observation{
+		instrument: ir.instrument,
+		number:     number.NewInt64Number(value),
+	})
+}
+
+// Observe captures a single floating point value from the associated
+// instrument callback, with the given labels.
+func (fr Float64ObserverResult) Observe(value float64, labels ...label.KeyValue) {
+	fr.function(labels, Observation{
+		instrument: fr.instrument,
+		number:     number.NewFloat64Number(value),
+	})
+}
+
+// Observe captures a multiple observations from the associated batch
+// instrument callback, with the given labels.
+func (br BatchObserverResult) Observe(labels []label.KeyValue, obs ...Observation) {
+	br.function(labels, obs...)
+}
+
+// AsyncRunner is expected to convert into an AsyncSingleRunner or an
+// AsyncBatchRunner.  SDKs will encounter an error if the AsyncRunner
+// does not satisfy one of these interfaces.
+type AsyncRunner interface {
+	// AnyRunner() is a non-exported method with no functional use
+	// other than to make this a non-empty interface.
+	AnyRunner()
+}
+
+// AsyncSingleRunner is an interface implemented by single-observer
+// callbacks.
+type AsyncSingleRunner interface {
+	// Run accepts a single instrument and function for capturing
+	// observations of that instrument.  Each call to the function
+	// receives one captured observation.  (The function accepts
+	// multiple observations so the same implementation can be
+	// used for batch runners.)
+	Run(ctx context.Context, single AsyncImpl, capture func([]label.KeyValue, ...Observation))
+
+	AsyncRunner
+}
+
+// AsyncBatchRunner is an interface implemented by batch-observer
+// callbacks.
+type AsyncBatchRunner interface {
+	// Run accepts a function for capturing observations of
+	// multiple instruments.
+	Run(ctx context.Context, capture func([]label.KeyValue, ...Observation))
+
+	AsyncRunner
+}
+
+var _ AsyncSingleRunner = (*Int64ObserverFunc)(nil)
+var _ AsyncSingleRunner = (*Float64ObserverFunc)(nil)
+var _ AsyncBatchRunner = (*BatchObserverFunc)(nil)
+
+// newInt64AsyncRunner returns a single-observer callback for integer Observer instruments.
+func newInt64AsyncRunner(c Int64ObserverFunc) AsyncSingleRunner {
+	return &c
+}
+
+// newFloat64AsyncRunner returns a single-observer callback for floating point Observer instruments.
+func newFloat64AsyncRunner(c Float64ObserverFunc) AsyncSingleRunner {
+	return &c
+}
+
+// newBatchAsyncRunner returns a batch-observer callback use with multiple Observer instruments.
+func newBatchAsyncRunner(c BatchObserverFunc) AsyncBatchRunner {
+	return &c
+}
+
+// AnyRunner implements AsyncRunner.
+func (*Int64ObserverFunc) AnyRunner() {}
+
+// AnyRunner implements AsyncRunner.
+func (*Float64ObserverFunc) AnyRunner() {}
+
+// AnyRunner implements AsyncRunner.
+func (*BatchObserverFunc) AnyRunner() {}
+
+// Run implements AsyncSingleRunner.
+func (i *Int64ObserverFunc) Run(ctx context.Context, impl AsyncImpl, function func([]label.KeyValue, ...Observation)) {
+	(*i)(ctx, Int64ObserverResult{
+		instrument: impl,
+		function:   function,
+	})
+}
+
+// Run implements AsyncSingleRunner.
+func (f *Float64ObserverFunc) Run(ctx context.Context, impl AsyncImpl, function func([]label.KeyValue, ...Observation)) {
+	(*f)(ctx, Float64ObserverResult{
+		instrument: impl,
+		function:   function,
+	})
+}
+
+// Run implements AsyncBatchRunner.
+func (b *BatchObserverFunc) Run(ctx context.Context, function func([]label.KeyValue, ...Observation)) {
+	(*b)(ctx, BatchObserverResult{
+		function: function,
+	})
+}
+
+// wrapInt64ValueObserverInstrument converts an AsyncImpl into Int64ValueObserver.
+func wrapInt64ValueObserverInstrument(asyncInst AsyncImpl, err error) (Int64ValueObserver, error) {
+	common, err := checkNewAsync(asyncInst, err)
+	return Int64ValueObserver{asyncInstrument: common}, err
+}
+
+// wrapFloat64ValueObserverInstrument converts an AsyncImpl into Float64ValueObserver.
+func wrapFloat64ValueObserverInstrument(asyncInst AsyncImpl, err error) (Float64ValueObserver, error) {
+	common, err := checkNewAsync(asyncInst, err)
+	return Float64ValueObserver{asyncInstrument: common}, err
+}
+
+// wrapInt64SumObserverInstrument converts an AsyncImpl into Int64SumObserver.
+func wrapInt64SumObserverInstrument(asyncInst AsyncImpl, err error) (Int64SumObserver, error) {
+	common, err := checkNewAsync(asyncInst, err)
+	return Int64SumObserver{asyncInstrument: common}, err
+}
+
+// wrapFloat64SumObserverInstrument converts an AsyncImpl into Float64SumObserver.
+func wrapFloat64SumObserverInstrument(asyncInst AsyncImpl, err error) (Float64SumObserver, error) {
+	common, err := checkNewAsync(asyncInst, err)
+	return Float64SumObserver{asyncInstrument: common}, err
+}
+
+// wrapInt64UpDownSumObserverInstrument converts an AsyncImpl into Int64UpDownSumObserver.
+func wrapInt64UpDownSumObserverInstrument(asyncInst AsyncImpl, err error) (Int64UpDownSumObserver, error) {
+	common, err := checkNewAsync(asyncInst, err)
+	return Int64UpDownSumObserver{asyncInstrument: common}, err
+}
+
+// wrapFloat64UpDownSumObserverInstrument converts an AsyncImpl into Float64UpDownSumObserver.
+func wrapFloat64UpDownSumObserverInstrument(asyncInst AsyncImpl, err error) (Float64UpDownSumObserver, error) {
+	common, err := checkNewAsync(asyncInst, err)
+	return Float64UpDownSumObserver{asyncInstrument: common}, err
+}
+
+// BatchObserver represents an Observer callback that can report
+// observations for multiple instruments.
+type BatchObserver struct {
+	meter  Meter
+	runner AsyncBatchRunner
+}
+
+// Int64ValueObserver is a metric that captures a set of int64 values at a
+// point in time.
+type Int64ValueObserver struct {
+	asyncInstrument
+}
+
+// Float64ValueObserver is a metric that captures a set of float64 values
+// at a point in time.
+type Float64ValueObserver struct {
+	asyncInstrument
+}
+
+// Int64SumObserver is a metric that captures a precomputed sum of
+// int64 values at a point in time.
+type Int64SumObserver struct {
+	asyncInstrument
+}
+
+// Float64SumObserver is a metric that captures a precomputed sum of
+// float64 values at a point in time.
+type Float64SumObserver struct {
+	asyncInstrument
+}
+
+// Int64UpDownSumObserver is a metric that captures a precomputed sum of
+// int64 values at a point in time.
+type Int64UpDownSumObserver struct {
+	asyncInstrument
+}
+
+// Float64UpDownSumObserver is a metric that captures a precomputed sum of
+// float64 values at a point in time.
+type Float64UpDownSumObserver struct {
+	asyncInstrument
+}
+
+// Observation returns an Observation, a BatchObserverFunc
+// argument, for an asynchronous integer instrument.
+// This returns an implementation-level object for use by the SDK,
+// users should not refer to this.
+func (i Int64ValueObserver) Observation(v int64) Observation {
+	return Observation{
+		number:     number.NewInt64Number(v),
+		instrument: i.instrument,
+	}
+}
+
+// Observation returns an Observation, a BatchObserverFunc
+// argument, for an asynchronous integer instrument.
+// This returns an implementation-level object for use by the SDK,
+// users should not refer to this.
+func (f Float64ValueObserver) Observation(v float64) Observation {
+	return Observation{
+		number:     number.NewFloat64Number(v),
+		instrument: f.instrument,
+	}
+}
+
+// Observation returns an Observation, a BatchObserverFunc
+// argument, for an asynchronous integer instrument.
+// This returns an implementation-level object for use by the SDK,
+// users should not refer to this.
+func (i Int64SumObserver) Observation(v int64) Observation {
+	return Observation{
+		number:     number.NewInt64Number(v),
+		instrument: i.instrument,
+	}
+}
+
+// Observation returns an Observation, a BatchObserverFunc
+// argument, for an asynchronous integer instrument.
+// This returns an implementation-level object for use by the SDK,
+// users should not refer to this.
+func (f Float64SumObserver) Observation(v float64) Observation {
+	return Observation{
+		number:     number.NewFloat64Number(v),
+		instrument: f.instrument,
+	}
+}
+
+// Observation returns an Observation, a BatchObserverFunc
+// argument, for an asynchronous integer instrument.
+// This returns an implementation-level object for use by the SDK,
+// users should not refer to this.
+func (i Int64UpDownSumObserver) Observation(v int64) Observation {
+	return Observation{
+		number:     number.NewInt64Number(v),
+		instrument: i.instrument,
+	}
+}
+
+// Observation returns an Observation, a BatchObserverFunc
+// argument, for an asynchronous integer instrument.
+// This returns an implementation-level object for use by the SDK,
+// users should not refer to this.
+func (f Float64UpDownSumObserver) Observation(v float64) Observation {
+	return Observation{
+		number:     number.NewFloat64Number(v),
+		instrument: f.instrument,
+	}
+}
+
+// Measurement is used for reporting a synchronous batch of metric
+// values. Instances of this type should be created by synchronous
+// instruments (e.g., Int64Counter.Measurement()).
+type Measurement struct {
+	// number needs to be aligned for 64-bit atomic operations.
+	number     number.Number
+	instrument SyncImpl
+}
+
+// syncInstrument contains a SyncImpl.
+type syncInstrument struct {
+	instrument SyncImpl
+}
+
+// syncBoundInstrument contains a BoundSyncImpl.
+type syncBoundInstrument struct {
+	boundInstrument BoundSyncImpl
+}
+
+// asyncInstrument contains a AsyncImpl.
+type asyncInstrument struct {
+	instrument AsyncImpl
+}
+
+// SyncImpl returns the instrument that created this measurement.
+// This returns an implementation-level object for use by the SDK,
+// users should not refer to this.
+func (m Measurement) SyncImpl() SyncImpl {
+	return m.instrument
+}
+
+// Number returns a number recorded in this measurement.
+func (m Measurement) Number() number.Number {
+	return m.number
+}
+
+// AsyncImpl returns the instrument that created this observation.
+// This returns an implementation-level object for use by the SDK,
+// users should not refer to this.
+func (m Observation) AsyncImpl() AsyncImpl {
+	return m.instrument
+}
+
+// Number returns a number recorded in this observation.
+func (m Observation) Number() number.Number {
+	return m.number
+}
+
+// AsyncImpl implements AsyncImpl.
+func (a asyncInstrument) AsyncImpl() AsyncImpl {
+	return a.instrument
+}
+
+// SyncImpl returns the implementation object for synchronous instruments.
+func (s syncInstrument) SyncImpl() SyncImpl {
+	return s.instrument
+}
+
+func (s syncInstrument) bind(labels []label.KeyValue) syncBoundInstrument {
+	return newSyncBoundInstrument(s.instrument.Bind(labels))
+}
+
+func (s syncInstrument) float64Measurement(value float64) Measurement {
+	return newMeasurement(s.instrument, number.NewFloat64Number(value))
+}
+
+func (s syncInstrument) int64Measurement(value int64) Measurement {
+	return newMeasurement(s.instrument, number.NewInt64Number(value))
+}
+
+func (s syncInstrument) directRecord(ctx context.Context, number number.Number, labels []label.KeyValue) {
+	s.instrument.RecordOne(ctx, number, labels)
+}
+
+func (h syncBoundInstrument) directRecord(ctx context.Context, number number.Number) {
+	h.boundInstrument.RecordOne(ctx, number)
+}
+
+// Unbind calls SyncImpl.Unbind.
+func (h syncBoundInstrument) Unbind() {
+	h.boundInstrument.Unbind()
+}
+
+// checkNewAsync receives an AsyncImpl and potential
+// error, and returns the same types, checking for and ensuring that
+// the returned interface is not nil.
+func checkNewAsync(instrument AsyncImpl, err error) (asyncInstrument, error) {
+	if instrument == nil {
+		if err == nil {
+			err = ErrSDKReturnedNilImpl
+		}
+		instrument = NoopAsync{}
+	}
+	return asyncInstrument{
+		instrument: instrument,
+	}, err
+}
+
+// checkNewSync receives an SyncImpl and potential
+// error, and returns the same types, checking for and ensuring that
+// the returned interface is not nil.
+func checkNewSync(instrument SyncImpl, err error) (syncInstrument, error) {
+	if instrument == nil {
+		if err == nil {
+			err = ErrSDKReturnedNilImpl
+		}
+		// Note: an alternate behavior would be to synthesize a new name
+		// or group all duplicately-named instruments of a certain type
+		// together and use a tag for the original name, e.g.,
+		//   name = 'invalid.counter.int64'
+		//   label = 'original-name=duplicate-counter-name'
+		instrument = NoopSync{}
+	}
+	return syncInstrument{
+		instrument: instrument,
+	}, err
+}
+
+func newSyncBoundInstrument(boundInstrument BoundSyncImpl) syncBoundInstrument {
+	return syncBoundInstrument{
+		boundInstrument: boundInstrument,
+	}
+}
+
+func newMeasurement(instrument SyncImpl, number number.Number) Measurement {
+	return Measurement{
+		instrument: instrument,
+		number:     number,
+	}
+}
+
+// wrapInt64CounterInstrument converts a SyncImpl into Int64Counter.
+func wrapInt64CounterInstrument(syncInst SyncImpl, err error) (Int64Counter, error) {
+	common, err := checkNewSync(syncInst, err)
+	return Int64Counter{syncInstrument: common}, err
+}
+
+// wrapFloat64CounterInstrument converts a SyncImpl into Float64Counter.
+func wrapFloat64CounterInstrument(syncInst SyncImpl, err error) (Float64Counter, error) {
+	common, err := checkNewSync(syncInst, err)
+	return Float64Counter{syncInstrument: common}, err
+}
+
+// wrapInt64UpDownCounterInstrument converts a SyncImpl into Int64UpDownCounter.
+func wrapInt64UpDownCounterInstrument(syncInst SyncImpl, err error) (Int64UpDownCounter, error) {
+	common, err := checkNewSync(syncInst, err)
+	return Int64UpDownCounter{syncInstrument: common}, err
+}
+
+// wrapFloat64UpDownCounterInstrument converts a SyncImpl into Float64UpDownCounter.
+func wrapFloat64UpDownCounterInstrument(syncInst SyncImpl, err error) (Float64UpDownCounter, error) {
+	common, err := checkNewSync(syncInst, err)
+	return Float64UpDownCounter{syncInstrument: common}, err
+}
+
+// wrapInt64ValueRecorderInstrument converts a SyncImpl into Int64ValueRecorder.
+func wrapInt64ValueRecorderInstrument(syncInst SyncImpl, err error) (Int64ValueRecorder, error) {
+	common, err := checkNewSync(syncInst, err)
+	return Int64ValueRecorder{syncInstrument: common}, err
+}
+
+// wrapFloat64ValueRecorderInstrument converts a SyncImpl into Float64ValueRecorder.
+func wrapFloat64ValueRecorderInstrument(syncInst SyncImpl, err error) (Float64ValueRecorder, error) {
+	common, err := checkNewSync(syncInst, err)
+	return Float64ValueRecorder{syncInstrument: common}, err
+}
+
+// Float64Counter is a metric that accumulates float64 values.
+type Float64Counter struct {
+	syncInstrument
+}
+
+// Int64Counter is a metric that accumulates int64 values.
+type Int64Counter struct {
+	syncInstrument
+}
+
+// BoundFloat64Counter is a bound instrument for Float64Counter.
+//
+// It inherits the Unbind function from syncBoundInstrument.
+type BoundFloat64Counter struct {
+	syncBoundInstrument
+}
+
+// BoundInt64Counter is a boundInstrument for Int64Counter.
+//
+// It inherits the Unbind function from syncBoundInstrument.
+type BoundInt64Counter struct {
+	syncBoundInstrument
+}
+
+// Bind creates a bound instrument for this counter. The labels are
+// associated with values recorded via subsequent calls to Record.
+func (c Float64Counter) Bind(labels ...label.KeyValue) (h BoundFloat64Counter) {
+	h.syncBoundInstrument = c.bind(labels)
+	return
+}
+
+// Bind creates a bound instrument for this counter. The labels are
+// associated with values recorded via subsequent calls to Record.
+func (c Int64Counter) Bind(labels ...label.KeyValue) (h BoundInt64Counter) {
+	h.syncBoundInstrument = c.bind(labels)
+	return
+}
+
+// Measurement creates a Measurement object to use with batch
+// recording.
+func (c Float64Counter) Measurement(value float64) Measurement {
+	return c.float64Measurement(value)
+}
+
+// Measurement creates a Measurement object to use with batch
+// recording.
+func (c Int64Counter) Measurement(value int64) Measurement {
+	return c.int64Measurement(value)
+}
+
+// Add adds the value to the counter's sum. The labels should contain
+// the keys and values to be associated with this value.
+func (c Float64Counter) Add(ctx context.Context, value float64, labels ...label.KeyValue) {
+	c.directRecord(ctx, number.NewFloat64Number(value), labels)
+}
+
+// Add adds the value to the counter's sum. The labels should contain
+// the keys and values to be associated with this value.
+func (c Int64Counter) Add(ctx context.Context, value int64, labels ...label.KeyValue) {
+	c.directRecord(ctx, number.NewInt64Number(value), labels)
+}
+
+// Add adds the value to the counter's sum using the labels
+// previously bound to this counter via Bind()
+func (b BoundFloat64Counter) Add(ctx context.Context, value float64) {
+	b.directRecord(ctx, number.NewFloat64Number(value))
+}
+
+// Add adds the value to the counter's sum using the labels
+// previously bound to this counter via Bind()
+func (b BoundInt64Counter) Add(ctx context.Context, value int64) {
+	b.directRecord(ctx, number.NewInt64Number(value))
+}
+
+// Float64UpDownCounter is a metric instrument that sums floating
+// point values.
+type Float64UpDownCounter struct {
+	syncInstrument
+}
+
+// Int64UpDownCounter is a metric instrument that sums integer values.
+type Int64UpDownCounter struct {
+	syncInstrument
+}
+
+// BoundFloat64UpDownCounter is a bound instrument for Float64UpDownCounter.
+//
+// It inherits the Unbind function from syncBoundInstrument.
+type BoundFloat64UpDownCounter struct {
+	syncBoundInstrument
+}
+
+// BoundInt64UpDownCounter is a boundInstrument for Int64UpDownCounter.
+//
+// It inherits the Unbind function from syncBoundInstrument.
+type BoundInt64UpDownCounter struct {
+	syncBoundInstrument
+}
+
+// Bind creates a bound instrument for this counter. The labels are
+// associated with values recorded via subsequent calls to Record.
+func (c Float64UpDownCounter) Bind(labels ...label.KeyValue) (h BoundFloat64UpDownCounter) {
+	h.syncBoundInstrument = c.bind(labels)
+	return
+}
+
+// Bind creates a bound instrument for this counter. The labels are
+// associated with values recorded via subsequent calls to Record.
+func (c Int64UpDownCounter) Bind(labels ...label.KeyValue) (h BoundInt64UpDownCounter) {
+	h.syncBoundInstrument = c.bind(labels)
+	return
+}
+
+// Measurement creates a Measurement object to use with batch
+// recording.
+func (c Float64UpDownCounter) Measurement(value float64) Measurement {
+	return c.float64Measurement(value)
+}
+
+// Measurement creates a Measurement object to use with batch
+// recording.
+func (c Int64UpDownCounter) Measurement(value int64) Measurement {
+	return c.int64Measurement(value)
+}
+
+// Add adds the value to the counter's sum. The labels should contain
+// the keys and values to be associated with this value.
+func (c Float64UpDownCounter) Add(ctx context.Context, value float64, labels ...label.KeyValue) {
+	c.directRecord(ctx, number.NewFloat64Number(value), labels)
+}
+
+// Add adds the value to the counter's sum. The labels should contain
+// the keys and values to be associated with this value.
+func (c Int64UpDownCounter) Add(ctx context.Context, value int64, labels ...label.KeyValue) {
+	c.directRecord(ctx, number.NewInt64Number(value), labels)
+}
+
+// Add adds the value to the counter's sum using the labels
+// previously bound to this counter via Bind()
+func (b BoundFloat64UpDownCounter) Add(ctx context.Context, value float64) {
+	b.directRecord(ctx, number.NewFloat64Number(value))
+}
+
+// Add adds the value to the counter's sum using the labels
+// previously bound to this counter via Bind()
+func (b BoundInt64UpDownCounter) Add(ctx context.Context, value int64) {
+	b.directRecord(ctx, number.NewInt64Number(value))
+}
+
+// Float64ValueRecorder is a metric that records float64 values.
+type Float64ValueRecorder struct {
+	syncInstrument
+}
+
+// Int64ValueRecorder is a metric that records int64 values.
+type Int64ValueRecorder struct {
+	syncInstrument
+}
+
+// BoundFloat64ValueRecorder is a bound instrument for Float64ValueRecorder.
+//
+// It inherits the Unbind function from syncBoundInstrument.
+type BoundFloat64ValueRecorder struct {
+	syncBoundInstrument
+}
+
+// BoundInt64ValueRecorder is a bound instrument for Int64ValueRecorder.
+//
+// It inherits the Unbind function from syncBoundInstrument.
+type BoundInt64ValueRecorder struct {
+	syncBoundInstrument
+}
+
+// Bind creates a bound instrument for this ValueRecorder. The labels are
+// associated with values recorded via subsequent calls to Record.
+func (c Float64ValueRecorder) Bind(labels ...label.KeyValue) (h BoundFloat64ValueRecorder) {
+	h.syncBoundInstrument = c.bind(labels)
+	return
+}
+
+// Bind creates a bound instrument for this ValueRecorder. The labels are
+// associated with values recorded via subsequent calls to Record.
+func (c Int64ValueRecorder) Bind(labels ...label.KeyValue) (h BoundInt64ValueRecorder) {
+	h.syncBoundInstrument = c.bind(labels)
+	return
+}
+
+// Measurement creates a Measurement object to use with batch
+// recording.
+func (c Float64ValueRecorder) Measurement(value float64) Measurement {
+	return c.float64Measurement(value)
+}
+
+// Measurement creates a Measurement object to use with batch
+// recording.
+func (c Int64ValueRecorder) Measurement(value int64) Measurement {
+	return c.int64Measurement(value)
+}
+
+// Record adds a new value to the list of ValueRecorder's records. The
+// labels should contain the keys and values to be associated with
+// this value.
+func (c Float64ValueRecorder) Record(ctx context.Context, value float64, labels ...label.KeyValue) {
+	c.directRecord(ctx, number.NewFloat64Number(value), labels)
+}
+
+// Record adds a new value to the ValueRecorder's distribution. The
+// labels should contain the keys and values to be associated with
+// this value.
+func (c Int64ValueRecorder) Record(ctx context.Context, value int64, labels ...label.KeyValue) {
+	c.directRecord(ctx, number.NewInt64Number(value), labels)
+}
+
+// Record adds a new value to the ValueRecorder's distribution using the labels
+// previously bound to the ValueRecorder via Bind().
+func (b BoundFloat64ValueRecorder) Record(ctx context.Context, value float64) {
+	b.directRecord(ctx, number.NewFloat64Number(value))
+}
+
+// Record adds a new value to the ValueRecorder's distribution using the labels
+// previously bound to the ValueRecorder via Bind().
+func (b BoundInt64ValueRecorder) Record(ctx context.Context, value int64) {
+	b.directRecord(ctx, number.NewInt64Number(value))
+}
diff --git a/vendor/go.opentelemetry.io/otel/metric/metric_noop.go b/vendor/go.opentelemetry.io/otel/metric/metric_noop.go
new file mode 100644
index 0000000000..5cf26fc249
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric/metric_noop.go
@@ -0,0 +1,59 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 metric // import "go.opentelemetry.io/otel/metric"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/label"
+	"go.opentelemetry.io/otel/metric/number"
+)
+
+type NoopMeterProvider struct{}
+
+type noopInstrument struct{}
+type noopBoundInstrument struct{}
+type NoopSync struct{ noopInstrument }
+type NoopAsync struct{ noopInstrument }
+
+var _ MeterProvider = NoopMeterProvider{}
+var _ SyncImpl = NoopSync{}
+var _ BoundSyncImpl = noopBoundInstrument{}
+var _ AsyncImpl = NoopAsync{}
+
+func (NoopMeterProvider) Meter(_ string, _ ...MeterOption) Meter {
+	return Meter{}
+}
+
+func (noopInstrument) Implementation() interface{} {
+	return nil
+}
+
+func (noopInstrument) Descriptor() Descriptor {
+	return Descriptor{}
+}
+
+func (noopBoundInstrument) RecordOne(context.Context, number.Number) {
+}
+
+func (noopBoundInstrument) Unbind() {
+}
+
+func (NoopSync) Bind([]label.KeyValue) BoundSyncImpl {
+	return noopBoundInstrument{}
+}
+
+func (NoopSync) RecordOne(context.Context, number.Number, []label.KeyValue) {
+}
diff --git a/vendor/go.opentelemetry.io/otel/metric/metric_sdkapi.go b/vendor/go.opentelemetry.io/otel/metric/metric_sdkapi.go
new file mode 100644
index 0000000000..77d6adb173
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric/metric_sdkapi.go
@@ -0,0 +1,95 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 metric // import "go.opentelemetry.io/otel/metric"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/label"
+	"go.opentelemetry.io/otel/metric/number"
+)
+
+// MeterImpl is the interface an SDK must implement to supply a Meter
+// implementation.
+type MeterImpl interface {
+	// RecordBatch atomically records a batch of measurements.
+	RecordBatch(ctx context.Context, labels []label.KeyValue, measurement ...Measurement)
+
+	// NewSyncInstrument returns a newly constructed
+	// synchronous instrument implementation or an error, should
+	// one occur.
+	NewSyncInstrument(descriptor Descriptor) (SyncImpl, error)
+
+	// NewAsyncInstrument returns a newly constructed
+	// asynchronous instrument implementation or an error, should
+	// one occur.
+	NewAsyncInstrument(
+		descriptor Descriptor,
+		runner AsyncRunner,
+	) (AsyncImpl, error)
+}
+
+// InstrumentImpl is a common interface for synchronous and
+// asynchronous instruments.
+type InstrumentImpl interface {
+	// Implementation returns the underlying implementation of the
+	// instrument, which allows the implementation to gain access
+	// to its own representation especially from a `Measurement`.
+	Implementation() interface{}
+
+	// Descriptor returns a copy of the instrument's Descriptor.
+	Descriptor() Descriptor
+}
+
+// SyncImpl is the implementation-level interface to a generic
+// synchronous instrument (e.g., ValueRecorder and Counter instruments).
+type SyncImpl interface {
+	InstrumentImpl
+
+	// Bind creates an implementation-level bound instrument,
+	// binding a label set with this instrument implementation.
+	Bind(labels []label.KeyValue) BoundSyncImpl
+
+	// RecordOne captures a single synchronous metric event.
+	RecordOne(ctx context.Context, number number.Number, labels []label.KeyValue)
+}
+
+// BoundSyncImpl is the implementation-level interface to a
+// generic bound synchronous instrument
+type BoundSyncImpl interface {
+
+	// RecordOne captures a single synchronous metric event.
+	RecordOne(ctx context.Context, number number.Number)
+
+	// Unbind frees the resources associated with this bound instrument. It
+	// does not affect the metric this bound instrument was created through.
+	Unbind()
+}
+
+// AsyncImpl is an implementation-level interface to an
+// asynchronous instrument (e.g., Observer instruments).
+type AsyncImpl interface {
+	InstrumentImpl
+}
+
+// WrapMeterImpl constructs a `Meter` implementation from a
+// `MeterImpl` implementation.
+func WrapMeterImpl(impl MeterImpl, instrumentationName string, opts ...MeterOption) Meter {
+	return Meter{
+		impl:    impl,
+		name:    instrumentationName,
+		version: NewMeterConfig(opts...).InstrumentationVersion,
+	}
+}
diff --git a/vendor/go.opentelemetry.io/otel/metric/number/doc.go b/vendor/go.opentelemetry.io/otel/metric/number/doc.go
new file mode 100644
index 0000000000..0649ff875e
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric/number/doc.go
@@ -0,0 +1,23 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 number provides a number abstraction for instruments that
+either support int64 or float64 input values.
+
+This package is currently in a pre-GA phase. Backwards incompatible changes
+may be introduced in subsequent minor version releases as we work to track the
+evolving OpenTelemetry specification and user feedback.
+*/
+package number // import "go.opentelemetry.io/otel/metric/number"
diff --git a/vendor/go.opentelemetry.io/otel/metric/number/kind_string.go b/vendor/go.opentelemetry.io/otel/metric/number/kind_string.go
new file mode 100644
index 0000000000..6288c7ea29
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric/number/kind_string.go
@@ -0,0 +1,24 @@
+// Code generated by "stringer -type=Kind"; DO NOT EDIT.
+
+package number
+
+import "strconv"
+
+func _() {
+	// An "invalid array index" compiler error signifies that the constant values have changed.
+	// Re-run the stringer command to generate them again.
+	var x [1]struct{}
+	_ = x[Int64Kind-0]
+	_ = x[Float64Kind-1]
+}
+
+const _Kind_name = "Int64KindFloat64Kind"
+
+var _Kind_index = [...]uint8{0, 9, 20}
+
+func (i Kind) String() string {
+	if i < 0 || i >= Kind(len(_Kind_index)-1) {
+		return "Kind(" + strconv.FormatInt(int64(i), 10) + ")"
+	}
+	return _Kind_name[_Kind_index[i]:_Kind_index[i+1]]
+}
diff --git a/vendor/go.opentelemetry.io/otel/metric/number/number.go b/vendor/go.opentelemetry.io/otel/metric/number/number.go
new file mode 100644
index 0000000000..3ec95e2014
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric/number/number.go
@@ -0,0 +1,538 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 number // import "go.opentelemetry.io/otel/metric/number"
+
+//go:generate stringer -type=Kind
+
+import (
+	"fmt"
+	"math"
+	"sync/atomic"
+
+	"go.opentelemetry.io/otel/internal"
+)
+
+// Kind describes the data type of the Number.
+type Kind int8
+
+const (
+	// Int64Kind means that the Number stores int64.
+	Int64Kind Kind = iota
+	// Float64Kind means that the Number stores float64.
+	Float64Kind
+)
+
+// Zero returns a zero value for a given Kind
+func (k Kind) Zero() Number {
+	switch k {
+	case Int64Kind:
+		return NewInt64Number(0)
+	case Float64Kind:
+		return NewFloat64Number(0.)
+	default:
+		return Number(0)
+	}
+}
+
+// Minimum returns the minimum representable value
+// for a given Kind
+func (k Kind) Minimum() Number {
+	switch k {
+	case Int64Kind:
+		return NewInt64Number(math.MinInt64)
+	case Float64Kind:
+		return NewFloat64Number(-1. * math.MaxFloat64)
+	default:
+		return Number(0)
+	}
+}
+
+// Maximum returns the maximum representable value
+// for a given Kind
+func (k Kind) Maximum() Number {
+	switch k {
+	case Int64Kind:
+		return NewInt64Number(math.MaxInt64)
+	case Float64Kind:
+		return NewFloat64Number(math.MaxFloat64)
+	default:
+		return Number(0)
+	}
+}
+
+// Number represents either an integral or a floating point value. It
+// needs to be accompanied with a source of Kind that describes
+// the actual type of the value stored within Number.
+type Number uint64
+
+// - constructors
+
+// NewNumberFromRaw creates a new Number from a raw value.
+func NewNumberFromRaw(r uint64) Number {
+	return Number(r)
+}
+
+// NewInt64Number creates an integral Number.
+func NewInt64Number(i int64) Number {
+	return NewNumberFromRaw(internal.Int64ToRaw(i))
+}
+
+// NewFloat64Number creates a floating point Number.
+func NewFloat64Number(f float64) Number {
+	return NewNumberFromRaw(internal.Float64ToRaw(f))
+}
+
+// NewNumberSignChange returns a number with the same magnitude and
+// the opposite sign.  `kind` must describe the kind of number in `nn`.
+func NewNumberSignChange(kind Kind, nn Number) Number {
+	switch kind {
+	case Int64Kind:
+		return NewInt64Number(-nn.AsInt64())
+	case Float64Kind:
+		return NewFloat64Number(-nn.AsFloat64())
+	}
+	return nn
+}
+
+// - as x
+
+// AsNumber gets the Number.
+func (n *Number) AsNumber() Number {
+	return *n
+}
+
+// AsRaw gets the uninterpreted raw value. Might be useful for some
+// atomic operations.
+func (n *Number) AsRaw() uint64 {
+	return uint64(*n)
+}
+
+// AsInt64 assumes that the value contains an int64 and returns it as
+// such.
+func (n *Number) AsInt64() int64 {
+	return internal.RawToInt64(n.AsRaw())
+}
+
+// AsFloat64 assumes that the measurement value contains a float64 and
+// returns it as such.
+func (n *Number) AsFloat64() float64 {
+	return internal.RawToFloat64(n.AsRaw())
+}
+
+// - as x atomic
+
+// AsNumberAtomic gets the Number atomically.
+func (n *Number) AsNumberAtomic() Number {
+	return NewNumberFromRaw(n.AsRawAtomic())
+}
+
+// AsRawAtomic gets the uninterpreted raw value atomically. Might be
+// useful for some atomic operations.
+func (n *Number) AsRawAtomic() uint64 {
+	return atomic.LoadUint64(n.AsRawPtr())
+}
+
+// AsInt64Atomic assumes that the number contains an int64 and returns
+// it as such atomically.
+func (n *Number) AsInt64Atomic() int64 {
+	return atomic.LoadInt64(n.AsInt64Ptr())
+}
+
+// AsFloat64Atomic assumes that the measurement value contains a
+// float64 and returns it as such atomically.
+func (n *Number) AsFloat64Atomic() float64 {
+	return internal.RawToFloat64(n.AsRawAtomic())
+}
+
+// - as x ptr
+
+// AsRawPtr gets the pointer to the raw, uninterpreted raw
+// value. Might be useful for some atomic operations.
+func (n *Number) AsRawPtr() *uint64 {
+	return (*uint64)(n)
+}
+
+// AsInt64Ptr assumes that the number contains an int64 and returns a
+// pointer to it.
+func (n *Number) AsInt64Ptr() *int64 {
+	return internal.RawPtrToInt64Ptr(n.AsRawPtr())
+}
+
+// AsFloat64Ptr assumes that the number contains a float64 and returns a
+// pointer to it.
+func (n *Number) AsFloat64Ptr() *float64 {
+	return internal.RawPtrToFloat64Ptr(n.AsRawPtr())
+}
+
+// - coerce
+
+// CoerceToInt64 casts the number to int64. May result in
+// data/precision loss.
+func (n *Number) CoerceToInt64(kind Kind) int64 {
+	switch kind {
+	case Int64Kind:
+		return n.AsInt64()
+	case Float64Kind:
+		return int64(n.AsFloat64())
+	default:
+		// you get what you deserve
+		return 0
+	}
+}
+
+// CoerceToFloat64 casts the number to float64. May result in
+// data/precision loss.
+func (n *Number) CoerceToFloat64(kind Kind) float64 {
+	switch kind {
+	case Int64Kind:
+		return float64(n.AsInt64())
+	case Float64Kind:
+		return n.AsFloat64()
+	default:
+		// you get what you deserve
+		return 0
+	}
+}
+
+// - set
+
+// SetNumber sets the number to the passed number. Both should be of
+// the same kind.
+func (n *Number) SetNumber(nn Number) {
+	*n.AsRawPtr() = nn.AsRaw()
+}
+
+// SetRaw sets the number to the passed raw value. Both number and the
+// raw number should represent the same kind.
+func (n *Number) SetRaw(r uint64) {
+	*n.AsRawPtr() = r
+}
+
+// SetInt64 assumes that the number contains an int64 and sets it to
+// the passed value.
+func (n *Number) SetInt64(i int64) {
+	*n.AsInt64Ptr() = i
+}
+
+// SetFloat64 assumes that the number contains a float64 and sets it
+// to the passed value.
+func (n *Number) SetFloat64(f float64) {
+	*n.AsFloat64Ptr() = f
+}
+
+// - set atomic
+
+// SetNumberAtomic sets the number to the passed number
+// atomically. Both should be of the same kind.
+func (n *Number) SetNumberAtomic(nn Number) {
+	atomic.StoreUint64(n.AsRawPtr(), nn.AsRaw())
+}
+
+// SetRawAtomic sets the number to the passed raw value
+// atomically. Both number and the raw number should represent the
+// same kind.
+func (n *Number) SetRawAtomic(r uint64) {
+	atomic.StoreUint64(n.AsRawPtr(), r)
+}
+
+// SetInt64Atomic assumes that the number contains an int64 and sets
+// it to the passed value atomically.
+func (n *Number) SetInt64Atomic(i int64) {
+	atomic.StoreInt64(n.AsInt64Ptr(), i)
+}
+
+// SetFloat64Atomic assumes that the number contains a float64 and
+// sets it to the passed value atomically.
+func (n *Number) SetFloat64Atomic(f float64) {
+	atomic.StoreUint64(n.AsRawPtr(), internal.Float64ToRaw(f))
+}
+
+// - swap
+
+// SwapNumber sets the number to the passed number and returns the old
+// number. Both this number and the passed number should be of the
+// same kind.
+func (n *Number) SwapNumber(nn Number) Number {
+	old := *n
+	n.SetNumber(nn)
+	return old
+}
+
+// SwapRaw sets the number to the passed raw value and returns the old
+// raw value. Both number and the raw number should represent the same
+// kind.
+func (n *Number) SwapRaw(r uint64) uint64 {
+	old := n.AsRaw()
+	n.SetRaw(r)
+	return old
+}
+
+// SwapInt64 assumes that the number contains an int64, sets it to the
+// passed value and returns the old int64 value.
+func (n *Number) SwapInt64(i int64) int64 {
+	old := n.AsInt64()
+	n.SetInt64(i)
+	return old
+}
+
+// SwapFloat64 assumes that the number contains an float64, sets it to
+// the passed value and returns the old float64 value.
+func (n *Number) SwapFloat64(f float64) float64 {
+	old := n.AsFloat64()
+	n.SetFloat64(f)
+	return old
+}
+
+// - swap atomic
+
+// SwapNumberAtomic sets the number to the passed number and returns
+// the old number atomically. Both this number and the passed number
+// should be of the same kind.
+func (n *Number) SwapNumberAtomic(nn Number) Number {
+	return NewNumberFromRaw(atomic.SwapUint64(n.AsRawPtr(), nn.AsRaw()))
+}
+
+// SwapRawAtomic sets the number to the passed raw value and returns
+// the old raw value atomically. Both number and the raw number should
+// represent the same kind.
+func (n *Number) SwapRawAtomic(r uint64) uint64 {
+	return atomic.SwapUint64(n.AsRawPtr(), r)
+}
+
+// SwapInt64Atomic assumes that the number contains an int64, sets it
+// to the passed value and returns the old int64 value atomically.
+func (n *Number) SwapInt64Atomic(i int64) int64 {
+	return atomic.SwapInt64(n.AsInt64Ptr(), i)
+}
+
+// SwapFloat64Atomic assumes that the number contains an float64, sets
+// it to the passed value and returns the old float64 value
+// atomically.
+func (n *Number) SwapFloat64Atomic(f float64) float64 {
+	return internal.RawToFloat64(atomic.SwapUint64(n.AsRawPtr(), internal.Float64ToRaw(f)))
+}
+
+// - add
+
+// AddNumber assumes that this and the passed number are of the passed
+// kind and adds the passed number to this number.
+func (n *Number) AddNumber(kind Kind, nn Number) {
+	switch kind {
+	case Int64Kind:
+		n.AddInt64(nn.AsInt64())
+	case Float64Kind:
+		n.AddFloat64(nn.AsFloat64())
+	}
+}
+
+// AddRaw assumes that this number and the passed raw value are of the
+// passed kind and adds the passed raw value to this number.
+func (n *Number) AddRaw(kind Kind, r uint64) {
+	n.AddNumber(kind, NewNumberFromRaw(r))
+}
+
+// AddInt64 assumes that the number contains an int64 and adds the
+// passed int64 to it.
+func (n *Number) AddInt64(i int64) {
+	*n.AsInt64Ptr() += i
+}
+
+// AddFloat64 assumes that the number contains a float64 and adds the
+// passed float64 to it.
+func (n *Number) AddFloat64(f float64) {
+	*n.AsFloat64Ptr() += f
+}
+
+// - add atomic
+
+// AddNumberAtomic assumes that this and the passed number are of the
+// passed kind and adds the passed number to this number atomically.
+func (n *Number) AddNumberAtomic(kind Kind, nn Number) {
+	switch kind {
+	case Int64Kind:
+		n.AddInt64Atomic(nn.AsInt64())
+	case Float64Kind:
+		n.AddFloat64Atomic(nn.AsFloat64())
+	}
+}
+
+// AddRawAtomic assumes that this number and the passed raw value are
+// of the passed kind and adds the passed raw value to this number
+// atomically.
+func (n *Number) AddRawAtomic(kind Kind, r uint64) {
+	n.AddNumberAtomic(kind, NewNumberFromRaw(r))
+}
+
+// AddInt64Atomic assumes that the number contains an int64 and adds
+// the passed int64 to it atomically.
+func (n *Number) AddInt64Atomic(i int64) {
+	atomic.AddInt64(n.AsInt64Ptr(), i)
+}
+
+// AddFloat64Atomic assumes that the number contains a float64 and
+// adds the passed float64 to it atomically.
+func (n *Number) AddFloat64Atomic(f float64) {
+	for {
+		o := n.AsFloat64Atomic()
+		if n.CompareAndSwapFloat64(o, o+f) {
+			break
+		}
+	}
+}
+
+// - compare and swap (atomic only)
+
+// CompareAndSwapNumber does the atomic CAS operation on this
+// number. This number and passed old and new numbers should be of the
+// same kind.
+func (n *Number) CompareAndSwapNumber(on, nn Number) bool {
+	return atomic.CompareAndSwapUint64(n.AsRawPtr(), on.AsRaw(), nn.AsRaw())
+}
+
+// CompareAndSwapRaw does the atomic CAS operation on this
+// number. This number and passed old and new raw values should be of
+// the same kind.
+func (n *Number) CompareAndSwapRaw(or, nr uint64) bool {
+	return atomic.CompareAndSwapUint64(n.AsRawPtr(), or, nr)
+}
+
+// CompareAndSwapInt64 assumes that this number contains an int64 and
+// does the atomic CAS operation on it.
+func (n *Number) CompareAndSwapInt64(oi, ni int64) bool {
+	return atomic.CompareAndSwapInt64(n.AsInt64Ptr(), oi, ni)
+}
+
+// CompareAndSwapFloat64 assumes that this number contains a float64 and
+// does the atomic CAS operation on it.
+func (n *Number) CompareAndSwapFloat64(of, nf float64) bool {
+	return atomic.CompareAndSwapUint64(n.AsRawPtr(), internal.Float64ToRaw(of), internal.Float64ToRaw(nf))
+}
+
+// - compare
+
+// CompareNumber compares two Numbers given their kind.  Both numbers
+// should have the same kind.  This returns:
+//    0 if the numbers are equal
+//    -1 if the subject `n` is less than the argument `nn`
+//    +1 if the subject `n` is greater than the argument `nn`
+func (n *Number) CompareNumber(kind Kind, nn Number) int {
+	switch kind {
+	case Int64Kind:
+		return n.CompareInt64(nn.AsInt64())
+	case Float64Kind:
+		return n.CompareFloat64(nn.AsFloat64())
+	default:
+		// you get what you deserve
+		return 0
+	}
+}
+
+// CompareRaw compares two numbers, where one is input as a raw
+// uint64, interpreting both values as a `kind` of number.
+func (n *Number) CompareRaw(kind Kind, r uint64) int {
+	return n.CompareNumber(kind, NewNumberFromRaw(r))
+}
+
+// CompareInt64 assumes that the Number contains an int64 and performs
+// a comparison between the value and the other value. It returns the
+// typical result of the compare function: -1 if the value is less
+// than the other, 0 if both are equal, 1 if the value is greater than
+// the other.
+func (n *Number) CompareInt64(i int64) int {
+	this := n.AsInt64()
+	if this < i {
+		return -1
+	} else if this > i {
+		return 1
+	}
+	return 0
+}
+
+// CompareFloat64 assumes that the Number contains a float64 and
+// performs a comparison between the value and the other value. It
+// returns the typical result of the compare function: -1 if the value
+// is less than the other, 0 if both are equal, 1 if the value is
+// greater than the other.
+//
+// Do not compare NaN values.
+func (n *Number) CompareFloat64(f float64) int {
+	this := n.AsFloat64()
+	if this < f {
+		return -1
+	} else if this > f {
+		return 1
+	}
+	return 0
+}
+
+// - relations to zero
+
+// IsPositive returns true if the actual value is greater than zero.
+func (n *Number) IsPositive(kind Kind) bool {
+	return n.compareWithZero(kind) > 0
+}
+
+// IsNegative returns true if the actual value is less than zero.
+func (n *Number) IsNegative(kind Kind) bool {
+	return n.compareWithZero(kind) < 0
+}
+
+// IsZero returns true if the actual value is equal to zero.
+func (n *Number) IsZero(kind Kind) bool {
+	return n.compareWithZero(kind) == 0
+}
+
+// - misc
+
+// Emit returns a string representation of the raw value of the
+// Number. A %d is used for integral values, %f for floating point
+// values.
+func (n *Number) Emit(kind Kind) string {
+	switch kind {
+	case Int64Kind:
+		return fmt.Sprintf("%d", n.AsInt64())
+	case Float64Kind:
+		return fmt.Sprintf("%f", n.AsFloat64())
+	default:
+		return ""
+	}
+}
+
+// AsInterface returns the number as an interface{}, typically used
+// for Kind-correct JSON conversion.
+func (n *Number) AsInterface(kind Kind) interface{} {
+	switch kind {
+	case Int64Kind:
+		return n.AsInt64()
+	case Float64Kind:
+		return n.AsFloat64()
+	default:
+		return math.NaN()
+	}
+}
+
+// - private stuff
+
+func (n *Number) compareWithZero(kind Kind) int {
+	switch kind {
+	case Int64Kind:
+		return n.CompareInt64(0)
+	case Float64Kind:
+		return n.CompareFloat64(0.)
+	default:
+		// you get what you deserve
+		return 0
+	}
+}
diff --git a/vendor/go.opentelemetry.io/otel/metric/registry/doc.go b/vendor/go.opentelemetry.io/otel/metric/registry/doc.go
new file mode 100644
index 0000000000..a53ba45545
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric/registry/doc.go
@@ -0,0 +1,24 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 registry provides a non-standalone implementation of
+MeterProvider that adds uniqueness checking for instrument descriptors
+on top of other MeterProvider it wraps.
+
+This package is currently in a pre-GA phase. Backwards incompatible changes
+may be introduced in subsequent minor version releases as we work to track the
+evolving OpenTelemetry specification and user feedback.
+*/
+package registry // import "go.opentelemetry.io/otel/metric/registry"
diff --git a/vendor/go.opentelemetry.io/otel/metric/registry/registry.go b/vendor/go.opentelemetry.io/otel/metric/registry/registry.go
new file mode 100644
index 0000000000..f1d9819c31
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/metric/registry/registry.go
@@ -0,0 +1,170 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 registry // import "go.opentelemetry.io/otel/metric/registry"
+
+import (
+	"context"
+	"fmt"
+	"sync"
+
+	"go.opentelemetry.io/otel/label"
+	"go.opentelemetry.io/otel/metric"
+)
+
+// MeterProvider is a standard MeterProvider for wrapping `MeterImpl`
+type MeterProvider struct {
+	impl metric.MeterImpl
+}
+
+var _ metric.MeterProvider = (*MeterProvider)(nil)
+
+// uniqueInstrumentMeterImpl implements the metric.MeterImpl interface, adding
+// uniqueness checking for instrument descriptors.  Use NewUniqueInstrumentMeter
+// to wrap an implementation with uniqueness checking.
+type uniqueInstrumentMeterImpl struct {
+	lock  sync.Mutex
+	impl  metric.MeterImpl
+	state map[key]metric.InstrumentImpl
+}
+
+var _ metric.MeterImpl = (*uniqueInstrumentMeterImpl)(nil)
+
+type key struct {
+	instrumentName         string
+	instrumentationName    string
+	InstrumentationVersion string
+}
+
+// NewMeterProvider returns a new provider that implements instrument
+// name-uniqueness checking.
+func NewMeterProvider(impl metric.MeterImpl) *MeterProvider {
+	return &MeterProvider{
+		impl: NewUniqueInstrumentMeterImpl(impl),
+	}
+}
+
+// Meter implements MeterProvider.
+func (p *MeterProvider) Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter {
+	return metric.WrapMeterImpl(p.impl, instrumentationName, opts...)
+}
+
+// ErrMetricKindMismatch is the standard error for mismatched metric
+// instrument definitions.
+var ErrMetricKindMismatch = fmt.Errorf(
+	"A metric was already registered by this name with another kind or number type")
+
+// NewUniqueInstrumentMeterImpl returns a wrapped metric.MeterImpl with
+// the addition of uniqueness checking.
+func NewUniqueInstrumentMeterImpl(impl metric.MeterImpl) metric.MeterImpl {
+	return &uniqueInstrumentMeterImpl{
+		impl:  impl,
+		state: map[key]metric.InstrumentImpl{},
+	}
+}
+
+// RecordBatch implements metric.MeterImpl.
+func (u *uniqueInstrumentMeterImpl) RecordBatch(ctx context.Context, labels []label.KeyValue, ms ...metric.Measurement) {
+	u.impl.RecordBatch(ctx, labels, ms...)
+}
+
+func keyOf(descriptor metric.Descriptor) key {
+	return key{
+		descriptor.Name(),
+		descriptor.InstrumentationName(),
+		descriptor.InstrumentationVersion(),
+	}
+}
+
+// NewMetricKindMismatchError formats an error that describes a
+// mismatched metric instrument definition.
+func NewMetricKindMismatchError(desc metric.Descriptor) error {
+	return fmt.Errorf("Metric was %s (%s %s)registered as a %s %s: %w",
+		desc.Name(),
+		desc.InstrumentationName(),
+		desc.InstrumentationVersion(),
+		desc.NumberKind(),
+		desc.InstrumentKind(),
+		ErrMetricKindMismatch)
+}
+
+// Compatible determines whether two metric.Descriptors are considered
+// the same for the purpose of uniqueness checking.
+func Compatible(candidate, existing metric.Descriptor) bool {
+	return candidate.InstrumentKind() == existing.InstrumentKind() &&
+		candidate.NumberKind() == existing.NumberKind()
+}
+
+// checkUniqueness returns an ErrMetricKindMismatch error if there is
+// a conflict between a descriptor that was already registered and the
+// `descriptor` argument.  If there is an existing compatible
+// registration, this returns the already-registered instrument.  If
+// there is no conflict and no prior registration, returns (nil, nil).
+func (u *uniqueInstrumentMeterImpl) checkUniqueness(descriptor metric.Descriptor) (metric.InstrumentImpl, error) {
+	impl, ok := u.state[keyOf(descriptor)]
+	if !ok {
+		return nil, nil
+	}
+
+	if !Compatible(descriptor, impl.Descriptor()) {
+		return nil, NewMetricKindMismatchError(impl.Descriptor())
+	}
+
+	return impl, nil
+}
+
+// NewSyncInstrument implements metric.MeterImpl.
+func (u *uniqueInstrumentMeterImpl) NewSyncInstrument(descriptor metric.Descriptor) (metric.SyncImpl, error) {
+	u.lock.Lock()
+	defer u.lock.Unlock()
+
+	impl, err := u.checkUniqueness(descriptor)
+
+	if err != nil {
+		return nil, err
+	} else if impl != nil {
+		return impl.(metric.SyncImpl), nil
+	}
+
+	syncInst, err := u.impl.NewSyncInstrument(descriptor)
+	if err != nil {
+		return nil, err
+	}
+	u.state[keyOf(descriptor)] = syncInst
+	return syncInst, nil
+}
+
+// NewAsyncInstrument implements metric.MeterImpl.
+func (u *uniqueInstrumentMeterImpl) NewAsyncInstrument(
+	descriptor metric.Descriptor,
+	runner metric.AsyncRunner,
+) (metric.AsyncImpl, error) {
+	u.lock.Lock()
+	defer u.lock.Unlock()
+
+	impl, err := u.checkUniqueness(descriptor)
+
+	if err != nil {
+		return nil, err
+	} else if impl != nil {
+		return impl.(metric.AsyncImpl), nil
+	}
+
+	asyncInst, err := u.impl.NewAsyncInstrument(descriptor, runner)
+	if err != nil {
+		return nil, err
+	}
+	u.state[keyOf(descriptor)] = asyncInst
+	return asyncInst, nil
+}
diff --git a/vendor/go.opentelemetry.io/otel/pre_release.sh b/vendor/go.opentelemetry.io/otel/pre_release.sh
new file mode 100644
index 0000000000..34290a1c82
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/pre_release.sh
@@ -0,0 +1,95 @@
+#!/bin/bash
+
+# Copyright The OpenTelemetry Authors
+#
+# 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.
+
+set -e
+
+help()
+{
+   printf "\n"
+   printf "Usage: $0 -t tag\n"
+   printf "\t-t Unreleased tag. Update all go.mod with this tag.\n"
+   exit 1 # Exit script after printing help
+}
+
+while getopts "t:" opt
+do
+   case "$opt" in
+      t ) TAG="$OPTARG" ;;
+      ? ) help ;; # Print help
+   esac
+done
+
+# Print help in case parameters are empty
+if [ -z "$TAG" ]
+then
+   printf "Tag is missing\n";
+   help
+fi
+
+# Validate semver
+SEMVER_REGEX="^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?$"
+if [[ "${TAG}" =~ ${SEMVER_REGEX} ]]; then
+	printf "${TAG} is valid semver tag.\n"
+else
+	printf "${TAG} is not a valid semver tag.\n"
+	exit -1
+fi
+
+TAG_FOUND=`git tag --list ${TAG}`
+if [[ ${TAG_FOUND} = ${TAG} ]] ; then
+        printf "Tag ${TAG} already exists\n"
+        exit -1
+fi
+
+# Get version for version.go
+OTEL_VERSION=$(echo "${TAG}" | grep -o '^v[0-9]\+\.[0-9]\+\.[0-9]\+')
+# Strip leading v
+OTEL_VERSION="${OTEL_VERSION#v}"
+
+cd $(dirname $0)
+
+if ! git diff --quiet; then \
+	printf "Working tree is not clean, can't proceed with the release process\n"
+	git status
+	git diff
+	exit 1
+fi
+
+# Update version.go
+cp ./version.go ./version.go.bak
+sed "s/\(return \"\)[0-9]*\.[0-9]*\.[0-9]*\"/\1${OTEL_VERSION}\"/" ./version.go.bak >./version.go
+rm -f ./version.go.bak
+
+# Update go.mod
+git checkout -b pre_release_${TAG} master
+PACKAGE_DIRS=$(find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; | egrep -v 'tools' | sed 's/^\.\///' | sort)
+
+for dir in $PACKAGE_DIRS; do
+	cp "${dir}/go.mod" "${dir}/go.mod.bak"
+	sed "s/opentelemetry.io\/otel\([^ ]*\) v[0-9]*\.[0-9]*\.[0-9]/opentelemetry.io\/otel\1 ${TAG}/" "${dir}/go.mod.bak" >"${dir}/go.mod"
+	rm -f "${dir}/go.mod.bak"
+done
+
+# Run lint to update go.sum
+make lint
+
+# Add changes and commit.
+git add .
+make ci
+git commit -m "Prepare for releasing $TAG"
+
+printf "Now run following to verify the changes.\ngit diff master\n"
+printf "\nThen push the changes to upstream\n"
diff --git a/vendor/go.opentelemetry.io/otel/propagation.go b/vendor/go.opentelemetry.io/otel/propagation.go
new file mode 100644
index 0000000000..d29aaa32c0
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/propagation.go
@@ -0,0 +1,31 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 otel // import "go.opentelemetry.io/otel"
+
+import (
+	"go.opentelemetry.io/otel/internal/global"
+	"go.opentelemetry.io/otel/propagation"
+)
+
+// GetTextMapPropagator returns the global TextMapPropagator. If none has been
+// set, a No-Op TextMapPropagator is returned.
+func GetTextMapPropagator() propagation.TextMapPropagator {
+	return global.TextMapPropagator()
+}
+
+// SetTextMapPropagator sets propagator as the global TextMapPropagator.
+func SetTextMapPropagator(propagator propagation.TextMapPropagator) {
+	global.SetTextMapPropagator(propagator)
+}
diff --git a/vendor/go.opentelemetry.io/otel/propagation/baggage.go b/vendor/go.opentelemetry.io/otel/propagation/baggage.go
new file mode 100644
index 0000000000..2c416d7fbd
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/propagation/baggage.go
@@ -0,0 +1,111 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 propagation // import "go.opentelemetry.io/otel/propagation"
+
+import (
+	"context"
+	"net/url"
+	"strings"
+
+	"go.opentelemetry.io/otel/internal/baggage"
+	"go.opentelemetry.io/otel/label"
+)
+
+const baggageHeader = "baggage"
+
+// Baggage is a propagator that supports the W3C Baggage format.
+//
+// This propagates user-defined baggage associated with a trace. The complete
+// specification is defined at https://w3c.github.io/baggage/.
+type Baggage struct{}
+
+var _ TextMapPropagator = Baggage{}
+
+// Inject sets baggage key-values from ctx into the carrier.
+func (b Baggage) Inject(ctx context.Context, carrier TextMapCarrier) {
+	baggageMap := baggage.MapFromContext(ctx)
+	firstIter := true
+	var headerValueBuilder strings.Builder
+	baggageMap.Foreach(func(kv label.KeyValue) bool {
+		if !firstIter {
+			headerValueBuilder.WriteRune(',')
+		}
+		firstIter = false
+		headerValueBuilder.WriteString(url.QueryEscape(strings.TrimSpace((string)(kv.Key))))
+		headerValueBuilder.WriteRune('=')
+		headerValueBuilder.WriteString(url.QueryEscape(strings.TrimSpace(kv.Value.Emit())))
+		return true
+	})
+	if headerValueBuilder.Len() > 0 {
+		headerString := headerValueBuilder.String()
+		carrier.Set(baggageHeader, headerString)
+	}
+}
+
+// Extract returns a copy of parent with the baggage from the carrier added.
+func (b Baggage) Extract(parent context.Context, carrier TextMapCarrier) context.Context {
+	bVal := carrier.Get(baggageHeader)
+	if bVal == "" {
+		return parent
+	}
+
+	baggageValues := strings.Split(bVal, ",")
+	keyValues := make([]label.KeyValue, 0, len(baggageValues))
+	for _, baggageValue := range baggageValues {
+		valueAndProps := strings.Split(baggageValue, ";")
+		if len(valueAndProps) < 1 {
+			continue
+		}
+		nameValue := strings.Split(valueAndProps[0], "=")
+		if len(nameValue) < 2 {
+			continue
+		}
+		name, err := url.QueryUnescape(nameValue[0])
+		if err != nil {
+			continue
+		}
+		trimmedName := strings.TrimSpace(name)
+		value, err := url.QueryUnescape(nameValue[1])
+		if err != nil {
+			continue
+		}
+		trimmedValue := strings.TrimSpace(value)
+
+		// TODO (skaris): properties defiend https://w3c.github.io/correlation-context/, are currently
+		// just put as part of the value.
+		var trimmedValueWithProps strings.Builder
+		trimmedValueWithProps.WriteString(trimmedValue)
+		for _, prop := range valueAndProps[1:] {
+			trimmedValueWithProps.WriteRune(';')
+			trimmedValueWithProps.WriteString(prop)
+		}
+
+		keyValues = append(keyValues, label.String(trimmedName, trimmedValueWithProps.String()))
+	}
+
+	if len(keyValues) > 0 {
+		// Only update the context if valid values were found
+		return baggage.ContextWithMap(parent, baggage.NewMap(baggage.MapUpdate{
+			MultiKV: keyValues,
+		}))
+	}
+
+	return parent
+}
+
+// Fields returns the keys who's values are set with Inject.
+func (b Baggage) Fields() []string {
+	return []string{baggageHeader}
+}
diff --git a/vendor/go.opentelemetry.io/otel/propagation/doc.go b/vendor/go.opentelemetry.io/otel/propagation/doc.go
new file mode 100644
index 0000000000..89573f1baa
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/propagation/doc.go
@@ -0,0 +1,28 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 propagation contains OpenTelemetry context propagators.
+
+This package is currently in a pre-GA phase. Backwards incompatible changes
+may be introduced in subsequent minor version releases as we work to track the
+evolving OpenTelemetry specification and user feedback.
+
+OpenTelemetry propagators are used to extract and inject context data from and
+into messages exchanged by applications. The propagator supported by this
+package is the W3C Trace Context encoding
+(https://www.w3.org/TR/trace-context/), and W3C Baggage
+(https://w3c.github.io/baggage/).
+*/
+package propagation // import "go.opentelemetry.io/otel/propagation"
diff --git a/vendor/go.opentelemetry.io/otel/propagation/propagation.go b/vendor/go.opentelemetry.io/otel/propagation/propagation.go
new file mode 100644
index 0000000000..c867f86b8f
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/propagation/propagation.go
@@ -0,0 +1,78 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 propagation // import "go.opentelemetry.io/otel/propagation"
+
+import "context"
+
+// TextMapCarrier is the storage medium used by a TextMapPropagator.
+type TextMapCarrier interface {
+	// Get returns the value associated with the passed key.
+	Get(key string) string
+	// Set stores the key-value pair.
+	Set(key string, value string)
+}
+
+// TextMapPropagator propagates cross-cutting concerns as key-value text
+// pairs within a carrier that travels in-band across process boundaries.
+type TextMapPropagator interface {
+	// Inject set cross-cutting concerns from the Context into the carrier.
+	Inject(ctx context.Context, carrier TextMapCarrier)
+	// Extract reads cross-cutting concerns from the carrier into a Context.
+	Extract(ctx context.Context, carrier TextMapCarrier) context.Context
+	// Fields returns the keys who's values are set with Inject.
+	Fields() []string
+}
+
+type compositeTextMapPropagator []TextMapPropagator
+
+func (p compositeTextMapPropagator) Inject(ctx context.Context, carrier TextMapCarrier) {
+	for _, i := range p {
+		i.Inject(ctx, carrier)
+	}
+}
+
+func (p compositeTextMapPropagator) Extract(ctx context.Context, carrier TextMapCarrier) context.Context {
+	for _, i := range p {
+		ctx = i.Extract(ctx, carrier)
+	}
+	return ctx
+}
+
+func (p compositeTextMapPropagator) Fields() []string {
+	unique := make(map[string]struct{})
+	for _, i := range p {
+		for _, k := range i.Fields() {
+			unique[k] = struct{}{}
+		}
+	}
+
+	fields := make([]string, 0, len(unique))
+	for k := range unique {
+		fields = append(fields, k)
+	}
+	return fields
+}
+
+// NewCompositeTextMapPropagator returns a unified TextMapPropagator from the
+// group of passed TextMapPropagator. This allows different cross-cutting
+// concerns to be propagates in a unified manner.
+//
+// The returned TextMapPropagator will inject and extract cross-cutting
+// concerns in the order the TextMapPropagators were provided. Additionally,
+// the Fields method will return a de-duplicated slice of the keys that are
+// set with the Inject method.
+func NewCompositeTextMapPropagator(p ...TextMapPropagator) TextMapPropagator {
+	return compositeTextMapPropagator(p)
+}
diff --git a/vendor/go.opentelemetry.io/otel/propagation/trace_context.go b/vendor/go.opentelemetry.io/otel/propagation/trace_context.go
new file mode 100644
index 0000000000..fa18b8540a
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/propagation/trace_context.go
@@ -0,0 +1,169 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 propagation // import "go.opentelemetry.io/otel/propagation"
+
+import (
+	"context"
+	"encoding/hex"
+	"fmt"
+	"regexp"
+	"strings"
+
+	"go.opentelemetry.io/otel/label"
+	"go.opentelemetry.io/otel/trace"
+)
+
+const (
+	supportedVersion  = 0
+	maxVersion        = 254
+	traceparentHeader = "traceparent"
+	tracestateHeader  = "tracestate"
+)
+
+// TraceContext is a propagator that supports the W3C Trace Context format
+// (https://www.w3.org/TR/trace-context/)
+//
+// This propagator will propagate the traceparent and tracestate headers to
+// guarantee traces are not broken. It is up to the users of this propagator
+// to choose if they want to participate in a trace by modifying the
+// traceparent header and relevant parts of the tracestate header containing
+// their proprietary information.
+type TraceContext struct{}
+
+var _ TextMapPropagator = TraceContext{}
+var traceCtxRegExp = regexp.MustCompile("^(?P<version>[0-9a-f]{2})-(?P<traceID>[a-f0-9]{32})-(?P<spanID>[a-f0-9]{16})-(?P<traceFlags>[a-f0-9]{2})(?:-.*)?$")
+
+// Inject set tracecontext from the Context into the carrier.
+func (tc TraceContext) Inject(ctx context.Context, carrier TextMapCarrier) {
+	sc := trace.SpanContextFromContext(ctx)
+	if !sc.IsValid() {
+		return
+	}
+
+	carrier.Set(tracestateHeader, sc.TraceState.String())
+
+	h := fmt.Sprintf("%.2x-%s-%s-%.2x",
+		supportedVersion,
+		sc.TraceID,
+		sc.SpanID,
+		sc.TraceFlags&trace.FlagsSampled)
+	carrier.Set(traceparentHeader, h)
+}
+
+// Extract reads tracecontext from the carrier into a returned Context.
+func (tc TraceContext) Extract(ctx context.Context, carrier TextMapCarrier) context.Context {
+	sc := tc.extract(carrier)
+	if !sc.IsValid() {
+		return ctx
+	}
+	return trace.ContextWithRemoteSpanContext(ctx, sc)
+}
+
+func (tc TraceContext) extract(carrier TextMapCarrier) trace.SpanContext {
+	h := carrier.Get(traceparentHeader)
+	if h == "" {
+		return trace.SpanContext{}
+	}
+
+	matches := traceCtxRegExp.FindStringSubmatch(h)
+
+	if len(matches) == 0 {
+		return trace.SpanContext{}
+	}
+
+	if len(matches) < 5 { // four subgroups plus the overall match
+		return trace.SpanContext{}
+	}
+
+	if len(matches[1]) != 2 {
+		return trace.SpanContext{}
+	}
+	ver, err := hex.DecodeString(matches[1])
+	if err != nil {
+		return trace.SpanContext{}
+	}
+	version := int(ver[0])
+	if version > maxVersion {
+		return trace.SpanContext{}
+	}
+
+	if version == 0 && len(matches) != 5 { // four subgroups plus the overall match
+		return trace.SpanContext{}
+	}
+
+	if len(matches[2]) != 32 {
+		return trace.SpanContext{}
+	}
+
+	var sc trace.SpanContext
+
+	sc.TraceID, err = trace.TraceIDFromHex(matches[2][:32])
+	if err != nil {
+		return trace.SpanContext{}
+	}
+
+	if len(matches[3]) != 16 {
+		return trace.SpanContext{}
+	}
+	sc.SpanID, err = trace.SpanIDFromHex(matches[3])
+	if err != nil {
+		return trace.SpanContext{}
+	}
+
+	if len(matches[4]) != 2 {
+		return trace.SpanContext{}
+	}
+	opts, err := hex.DecodeString(matches[4])
+	if err != nil || len(opts) < 1 || (version == 0 && opts[0] > 2) {
+		return trace.SpanContext{}
+	}
+	// Clear all flags other than the trace-context supported sampling bit.
+	sc.TraceFlags = opts[0] & trace.FlagsSampled
+
+	sc.TraceState = parseTraceState(carrier.Get(tracestateHeader))
+
+	if !sc.IsValid() {
+		return trace.SpanContext{}
+	}
+
+	return sc
+}
+
+// Fields returns the keys who's values are set with Inject.
+func (tc TraceContext) Fields() []string {
+	return []string{traceparentHeader, tracestateHeader}
+}
+
+func parseTraceState(in string) trace.TraceState {
+	if in == "" {
+		return trace.TraceState{}
+	}
+
+	kvs := []label.KeyValue{}
+	for _, entry := range strings.Split(in, ",") {
+		parts := strings.SplitN(entry, "=", 2)
+		if len(parts) != 2 {
+			// Parse failure, abort!
+			return trace.TraceState{}
+		}
+		kvs = append(kvs, label.String(parts[0], parts[1]))
+	}
+
+	// Ignoring error here as "failure to parse tracestate MUST NOT
+	// affect the parsing of traceparent."
+	// https://www.w3.org/TR/trace-context/#tracestate-header
+	ts, _ := trace.TraceStateFromKeyValues(kvs...)
+	return ts
+}
diff --git a/vendor/go.opentelemetry.io/otel/tag.sh b/vendor/go.opentelemetry.io/otel/tag.sh
new file mode 100644
index 0000000000..70767c7037
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/tag.sh
@@ -0,0 +1,178 @@
+#!/usr/bin/env bash
+
+# Copyright The OpenTelemetry Authors
+#
+# 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.
+
+readonly PROGNAME=$(basename "$0")
+readonly PROGDIR=$(readlink -m "$(dirname "$0")")
+
+readonly EXCLUDE_PACKAGES="internal/tools"
+readonly SEMVER_REGEX="v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?(\\+[0-9A-Za-z-]+(\\.[0-9A-Za-z-]+)*)?"
+
+usage() {
+    cat <<- EOF
+Usage: $PROGNAME [OPTIONS] SEMVER_TAG COMMIT_HASH
+
+Creates git tag for all Go packages in project.
+
+OPTIONS:
+  -h --help        Show this help.
+
+ARGUMENTS:
+  SEMVER_TAG       Semantic version to tag with.
+  COMMIT_HASH      Git commit hash to tag.
+EOF
+}
+
+cmdline() {
+    local arg commit
+
+    for arg
+    do
+        local delim=""
+        case "$arg" in
+            # Translate long form options to short form.
+            --help)           args="${args}-h ";;
+            # Pass through for everything else.
+            *) [[ "${arg:0:1}" == "-" ]] || delim="\""
+                args="${args}${delim}${arg}${delim} ";;
+        esac
+    done
+
+    # Reset and process short form options.
+    eval set -- "$args"
+
+    while getopts "h" OPTION
+    do
+         case $OPTION in
+         h)
+             usage
+             exit 0
+             ;;
+         *)
+             echo "unknown option: $OPTION"
+             usage
+             exit 1
+             ;;
+        esac
+    done
+
+    # Positional arguments.
+    shift $((OPTIND-1))
+    readonly TAG="$1"
+    if [ -z "$TAG" ]
+    then
+        echo "missing SEMVER_TAG"
+        usage
+        exit 1
+    fi
+    if [[ ! "$TAG" =~ $SEMVER_REGEX ]]
+    then
+        printf "invalid semantic version: %s\n" "$TAG"
+        exit 2
+    fi
+    if [[ "$( git tag --list "$TAG" )" ]]
+    then
+        printf "tag already exists: %s\n" "$TAG"
+        exit 2
+    fi
+
+    shift
+    commit="$1"
+    if [ -z "$commit" ]
+    then
+        echo "missing COMMIT_HASH"
+        usage
+        exit 1
+    fi
+    # Verify rev is for a commit and unify hashes into a complete SHA1.
+    readonly SHA="$( git rev-parse --quiet --verify "${commit}^{commit}" )"
+    if [ -z "$SHA" ]
+    then
+        printf "invalid commit hash: %s\n" "$commit"
+        exit 2
+    fi
+    if [ "$( git merge-base "$SHA" HEAD )" != "$SHA" ]
+    then
+        printf "commit '%s' not found on this branch\n" "$commit"
+        exit 2
+    fi
+}
+
+package_dirs() {
+    # Return a list of package directories in the form:
+    #
+    #  package/directory/a
+    #  package/directory/b
+    #  deeper/package/directory/a
+    #  ...
+    #
+    # Making sure to exclude any packages in the EXCLUDE_PACKAGES regexp.
+    find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; \
+        | grep -E -v "$EXCLUDE_PACKAGES" \
+        | sed 's/^\.\///' \
+        | sort
+}
+
+git_tag() {
+    local tag="$1"
+    local commit="$2"
+
+    git tag -a "$tag" -s -m "Version $tag" "$commit"
+}
+
+previous_version() {
+    local current="$1"
+
+    # Requires git > 2.0
+    git tag -l --sort=v:refname \
+        | grep -E "^${SEMVER_REGEX}$" \
+        | grep -v "$current" \
+        | tail -1
+}
+
+print_changes() {
+    local tag="$1"
+    local previous
+
+    previous="$( previous_version "$tag" )"
+    if [ -n "$previous" ]
+    then
+        printf "\nRaw changes made between %s and %s\n" "$previous" "$tag"
+        printf "======================================\n"
+        git --no-pager log --pretty=oneline "${previous}..$tag"
+    fi
+}
+
+main() {
+    local dir
+
+    cmdline "$@"
+
+    cd "$PROGDIR" || exit 3
+
+    # Create tag for root package.
+    git_tag "$TAG" "$SHA"
+    printf "created tag: %s\n" "$TAG"
+
+    # Create tag for all sub-packages.
+    for dir in $( package_dirs )
+    do
+        git_tag "${dir}/$TAG" "$SHA"
+        printf "created tag: %s\n" "${dir}/$TAG"
+    done
+
+    print_changes "$TAG"
+}
+main "$@"
diff --git a/vendor/go.opentelemetry.io/otel/trace.go b/vendor/go.opentelemetry.io/otel/trace.go
new file mode 100644
index 0000000000..bd6b6818cf
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/trace.go
@@ -0,0 +1,44 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 otel // import "go.opentelemetry.io/otel"
+
+import (
+	"go.opentelemetry.io/otel/internal/global"
+	"go.opentelemetry.io/otel/trace"
+)
+
+// Tracer creates a named tracer that implements Tracer interface.
+// If the name is an empty string then provider uses default name.
+//
+// This is short for TracerProvider().Tracer(name)
+func Tracer(name string) trace.Tracer {
+	return GetTracerProvider().Tracer(name)
+}
+
+// GetTracerProvider returns the registered global trace provider.
+// If none is registered then an instance of NoopTracerProvider is returned.
+//
+// Use the trace provider to create a named tracer. E.g.
+//     tracer := global.GetTracerProvider().Tracer("example.com/foo")
+// or
+//     tracer := global.Tracer("example.com/foo")
+func GetTracerProvider() trace.TracerProvider {
+	return global.TracerProvider()
+}
+
+// SetTracerProvider registers `tp` as the global trace provider.
+func SetTracerProvider(tp trace.TracerProvider) {
+	global.SetTracerProvider(tp)
+}
diff --git a/vendor/go.opentelemetry.io/otel/trace/config.go b/vendor/go.opentelemetry.io/otel/trace/config.go
new file mode 100644
index 0000000000..9a343633d5
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/trace/config.go
@@ -0,0 +1,196 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 trace
+
+import (
+	"time"
+
+	"go.opentelemetry.io/otel/label"
+)
+
+// TracerConfig is a group of options for a Tracer.
+type TracerConfig struct {
+	// InstrumentationVersion is the version of the library providing
+	// instrumentation.
+	InstrumentationVersion string
+}
+
+// NewTracerConfig applies all the options to a returned TracerConfig.
+func NewTracerConfig(options ...TracerOption) *TracerConfig {
+	config := new(TracerConfig)
+	for _, option := range options {
+		option.ApplyTracer(config)
+	}
+	return config
+}
+
+// TracerOption applies an option to a TracerConfig.
+type TracerOption interface {
+	ApplyTracer(*TracerConfig)
+}
+
+// SpanConfig is a group of options for a Span.
+type SpanConfig struct {
+	// Attributes describe the associated qualities of a Span.
+	Attributes []label.KeyValue
+	// Timestamp is a time in a Span life-cycle.
+	Timestamp time.Time
+	// Links are the associations a Span has with other Spans.
+	Links []Link
+	// Record is the recording state of a Span.
+	Record bool
+	// NewRoot identifies a Span as the root Span for a new trace. This is
+	// commonly used when an existing trace crosses trust boundaries and the
+	// remote parent span context should be ignored for security.
+	NewRoot bool
+	// SpanKind is the role a Span has in a trace.
+	SpanKind SpanKind
+}
+
+// NewSpanConfig applies all the options to a returned SpanConfig.
+// No validation is performed on the returned SpanConfig (e.g. no uniqueness
+// checking or bounding of data), it is left to the SDK to perform this
+// action.
+func NewSpanConfig(options ...SpanOption) *SpanConfig {
+	c := new(SpanConfig)
+	for _, option := range options {
+		option.ApplySpan(c)
+	}
+	return c
+}
+
+// SpanOption applies an option to a SpanConfig.
+type SpanOption interface {
+	ApplySpan(*SpanConfig)
+}
+
+// NewEventConfig applies all the EventOptions to a returned SpanConfig. If no
+// timestamp option is passed, the returned SpanConfig will have a Timestamp
+// set to the call time, otherwise no validation is performed on the returned
+// SpanConfig.
+func NewEventConfig(options ...EventOption) *SpanConfig {
+	c := new(SpanConfig)
+	for _, option := range options {
+		option.ApplyEvent(c)
+	}
+	if c.Timestamp.IsZero() {
+		c.Timestamp = time.Now()
+	}
+	return c
+}
+
+// EventOption applies span event options to a SpanConfig.
+type EventOption interface {
+	ApplyEvent(*SpanConfig)
+}
+
+// LifeCycleOption applies span life-cycle options to a SpanConfig. These
+// options set values releated to events in a spans life-cycle like starting,
+// ending, experiencing an error and other user defined notable events.
+type LifeCycleOption interface {
+	SpanOption
+	EventOption
+}
+
+type attributeSpanOption []label.KeyValue
+
+func (o attributeSpanOption) ApplySpan(c *SpanConfig)  { o.apply(c) }
+func (o attributeSpanOption) ApplyEvent(c *SpanConfig) { o.apply(c) }
+func (o attributeSpanOption) apply(c *SpanConfig) {
+	c.Attributes = append(c.Attributes, []label.KeyValue(o)...)
+}
+
+// WithAttributes adds the attributes related to a span life-cycle event.
+// These attributes are used to describe the work a Span represents when this
+// option is provided to a Span's start or end events. Otherwise, these
+// attributes provide additional information about the event being recorded
+// (e.g. error, state change, processing progress, system event).
+//
+// If multiple of these options are passed the attributes of each successive
+// option will extend the attributes instead of overwriting. There is no
+// guarantee of uniqueness in the resulting attributes.
+func WithAttributes(attributes ...label.KeyValue) LifeCycleOption {
+	return attributeSpanOption(attributes)
+}
+
+type timestampSpanOption time.Time
+
+func (o timestampSpanOption) ApplySpan(c *SpanConfig)  { o.apply(c) }
+func (o timestampSpanOption) ApplyEvent(c *SpanConfig) { o.apply(c) }
+func (o timestampSpanOption) apply(c *SpanConfig)      { c.Timestamp = time.Time(o) }
+
+// WithTimestamp sets the time of a Span life-cycle moment (e.g. started,
+// stopped, errored).
+func WithTimestamp(t time.Time) LifeCycleOption {
+	return timestampSpanOption(t)
+}
+
+type linksSpanOption []Link
+
+func (o linksSpanOption) ApplySpan(c *SpanConfig) { c.Links = append(c.Links, []Link(o)...) }
+
+// WithLinks adds links to a Span. The links are added to the existing Span
+// links, i.e. this does not overwrite.
+func WithLinks(links ...Link) SpanOption {
+	return linksSpanOption(links)
+}
+
+type recordSpanOption bool
+
+func (o recordSpanOption) ApplySpan(c *SpanConfig) { c.Record = bool(o) }
+
+// WithRecord specifies that the span should be recorded. It is important to
+// note that implementations may override this option, i.e. if the span is a
+// child of an un-sampled trace.
+func WithRecord() SpanOption {
+	return recordSpanOption(true)
+}
+
+type newRootSpanOption bool
+
+func (o newRootSpanOption) ApplySpan(c *SpanConfig) { c.NewRoot = bool(o) }
+
+// WithNewRoot specifies that the Span should be treated as a root Span. Any
+// existing parent span context will be ignored when defining the Span's trace
+// identifiers.
+func WithNewRoot() SpanOption {
+	return newRootSpanOption(true)
+}
+
+type spanKindSpanOption SpanKind
+
+func (o spanKindSpanOption) ApplySpan(c *SpanConfig) { c.SpanKind = SpanKind(o) }
+
+// WithSpanKind sets the SpanKind of a Span.
+func WithSpanKind(kind SpanKind) SpanOption {
+	return spanKindSpanOption(kind)
+}
+
+// InstrumentationOption is an interface for applying instrumentation specific
+// options.
+type InstrumentationOption interface {
+	TracerOption
+}
+
+// WithInstrumentationVersion sets the instrumentation version.
+func WithInstrumentationVersion(version string) InstrumentationOption {
+	return instrumentationVersionOption(version)
+}
+
+type instrumentationVersionOption string
+
+func (i instrumentationVersionOption) ApplyTracer(config *TracerConfig) {
+	config.InstrumentationVersion = string(i)
+}
diff --git a/vendor/go.opentelemetry.io/otel/trace/doc.go b/vendor/go.opentelemetry.io/otel/trace/doc.go
new file mode 100644
index 0000000000..c962f3bc62
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/trace/doc.go
@@ -0,0 +1,70 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 trace provides an implementation of the tracing part of the
+OpenTelemetry API.
+
+This package is currently in a pre-GA phase. Backwards incompatible changes
+may be introduced in subsequent minor version releases as we work to track the
+evolving OpenTelemetry specification and user feedback.
+
+To participate in distributed traces a Span needs to be created for the
+operation being performed as part of a traced workflow. It its simplest form:
+
+	var tracer trace.Tracer
+
+	func init() {
+		tracer = otel.Tracer("instrumentation/package/name")
+	}
+
+	func operation(ctx context.Context) {
+		var span trace.Span
+		ctx, span = tracer.Start(ctx, "operation")
+		defer span.End()
+		// ...
+	}
+
+A Tracer is unique to the instrumentation and is used to create Spans.
+Instrumentation should be designed to accept a TracerProvider from which it
+can create its own unique Tracer. Alternatively, the registered global
+TracerProvider from the go.opentelemetry.io/otel package can be used as
+a default.
+
+	const (
+		name    = "instrumentation/package/name"
+		version = "0.1.0"
+	)
+
+	type Instrumentation struct {
+		tracer trace.Tracer
+	}
+
+	func NewInstrumentation(tp trace.TracerProvider) *Instrumentation {
+		if tp == nil {
+			tp = otel.TracerProvider()
+		}
+		return &Instrumentation{
+			tracer: tp.Tracer(name, trace.WithInstrumentationVersion(version)),
+		}
+	}
+
+	func operation(ctx context.Context, inst *Instrumentation) {
+		var span trace.Span
+		ctx, span = inst.tracer.Start(ctx, "operation")
+		defer span.End()
+		// ...
+	}
+*/
+package trace // import "go.opentelemetry.io/otel/trace"
diff --git a/vendor/go.opentelemetry.io/otel/trace/trace.go b/vendor/go.opentelemetry.io/otel/trace/trace.go
new file mode 100644
index 0000000000..ced7f10ce0
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/trace/trace.go
@@ -0,0 +1,549 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 trace // import "go.opentelemetry.io/otel/trace"
+
+import (
+	"bytes"
+	"context"
+	"encoding/hex"
+	"encoding/json"
+	"regexp"
+	"strings"
+
+	"go.opentelemetry.io/otel/codes"
+	"go.opentelemetry.io/otel/label"
+)
+
+const (
+	// FlagsSampled is a bitmask with the sampled bit set. A SpanContext
+	// with the sampling bit set means the span is sampled.
+	FlagsSampled = byte(0x01)
+	// FlagsDeferred is a bitmask with the deferred bit set. A SpanContext
+	// with the deferred bit set means the sampling decision has been
+	// defered to the receiver.
+	FlagsDeferred = byte(0x02)
+	// FlagsDebug is a bitmask with the debug bit set.
+	FlagsDebug = byte(0x04)
+
+	errInvalidHexID errorConst = "trace-id and span-id can only contain [0-9a-f] characters, all lowercase"
+
+	errInvalidTraceIDLength errorConst = "hex encoded trace-id must have length equals to 32"
+	errNilTraceID           errorConst = "trace-id can't be all zero"
+
+	errInvalidSpanIDLength errorConst = "hex encoded span-id must have length equals to 16"
+	errNilSpanID           errorConst = "span-id can't be all zero"
+
+	// based on the W3C Trace Context specification, see https://www.w3.org/TR/trace-context-1/#tracestate-header
+	traceStateKeyFormat                      = `[a-z][_0-9a-z\-\*\/]{0,255}`
+	traceStateKeyFormatWithMultiTenantVendor = `[a-z][_0-9a-z\-\*\/]{0,240}@[a-z][_0-9a-z\-\*\/]{0,13}`
+	traceStateValueFormat                    = `[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]`
+
+	traceStateMaxListMembers = 32
+
+	errInvalidTraceStateKeyValue errorConst = "provided key or value is not valid according to the" +
+		" W3C Trace Context specification"
+	errInvalidTraceStateMembersNumber errorConst = "trace state would exceed the maximum limit of members (32)"
+	errInvalidTraceStateDuplicate     errorConst = "trace state key/value pairs with duplicate keys provided"
+)
+
+type errorConst string
+
+func (e errorConst) Error() string {
+	return string(e)
+}
+
+// TraceID is a unique identity of a trace.
+// nolint:golint
+type TraceID [16]byte
+
+var nilTraceID TraceID
+var _ json.Marshaler = nilTraceID
+
+// IsValid checks whether the trace TraceID is valid. A valid trace ID does
+// not consist of zeros only.
+func (t TraceID) IsValid() bool {
+	return !bytes.Equal(t[:], nilTraceID[:])
+}
+
+// MarshalJSON implements a custom marshal function to encode TraceID
+// as a hex string.
+func (t TraceID) MarshalJSON() ([]byte, error) {
+	return json.Marshal(t.String())
+}
+
+// String returns the hex string representation form of a TraceID
+func (t TraceID) String() string {
+	return hex.EncodeToString(t[:])
+}
+
+// SpanID is a unique identity of a span in a trace.
+type SpanID [8]byte
+
+var nilSpanID SpanID
+var _ json.Marshaler = nilSpanID
+
+// IsValid checks whether the SpanID is valid. A valid SpanID does not consist
+// of zeros only.
+func (s SpanID) IsValid() bool {
+	return !bytes.Equal(s[:], nilSpanID[:])
+}
+
+// MarshalJSON implements a custom marshal function to encode SpanID
+// as a hex string.
+func (s SpanID) MarshalJSON() ([]byte, error) {
+	return json.Marshal(s.String())
+}
+
+// String returns the hex string representation form of a SpanID
+func (s SpanID) String() string {
+	return hex.EncodeToString(s[:])
+}
+
+// TraceIDFromHex returns a TraceID from a hex string if it is compliant with
+// the W3C trace-context specification.  See more at
+// https://www.w3.org/TR/trace-context/#trace-id
+// nolint:golint
+func TraceIDFromHex(h string) (TraceID, error) {
+	t := TraceID{}
+	if len(h) != 32 {
+		return t, errInvalidTraceIDLength
+	}
+
+	if err := decodeHex(h, t[:]); err != nil {
+		return t, err
+	}
+
+	if !t.IsValid() {
+		return t, errNilTraceID
+	}
+	return t, nil
+}
+
+// SpanIDFromHex returns a SpanID from a hex string if it is compliant
+// with the w3c trace-context specification.
+// See more at https://www.w3.org/TR/trace-context/#parent-id
+func SpanIDFromHex(h string) (SpanID, error) {
+	s := SpanID{}
+	if len(h) != 16 {
+		return s, errInvalidSpanIDLength
+	}
+
+	if err := decodeHex(h, s[:]); err != nil {
+		return s, err
+	}
+
+	if !s.IsValid() {
+		return s, errNilSpanID
+	}
+	return s, nil
+}
+
+func decodeHex(h string, b []byte) error {
+	for _, r := range h {
+		switch {
+		case 'a' <= r && r <= 'f':
+			continue
+		case '0' <= r && r <= '9':
+			continue
+		default:
+			return errInvalidHexID
+		}
+	}
+
+	decoded, err := hex.DecodeString(h)
+	if err != nil {
+		return err
+	}
+
+	copy(b, decoded)
+	return nil
+}
+
+// TraceState provides additional vendor-specific trace identification information
+// across different distributed tracing systems. It represents an immutable list consisting
+// of key/value pairs. There can be a maximum of 32 entries in the list.
+//
+// Key and value of each list member must be valid according to the W3C Trace Context specification
+// (see https://www.w3.org/TR/trace-context-1/#key and https://www.w3.org/TR/trace-context-1/#value
+// respectively).
+//
+// Trace state must be valid according to the W3C Trace Context specification at all times. All
+// mutating operations validate their input and, in case of valid parameters, return a new TraceState.
+type TraceState struct { //nolint:golint
+	// TODO @matej-g: Consider implementing this as label.Set, see
+	// comment https://github.com/open-telemetry/opentelemetry-go/pull/1340#discussion_r540599226
+	kvs []label.KeyValue
+}
+
+var _ json.Marshaler = TraceState{}
+var keyFormatRegExp = regexp.MustCompile(
+	`^((` + traceStateKeyFormat + `)|(` + traceStateKeyFormatWithMultiTenantVendor + `))$`,
+)
+var valueFormatRegExp = regexp.MustCompile(`^(` + traceStateValueFormat + `)$`)
+
+// MarshalJSON implements a custom marshal function to encode trace state.
+func (ts TraceState) MarshalJSON() ([]byte, error) {
+	return json.Marshal(ts.kvs)
+}
+
+// String returns trace state as a string valid according to the
+// W3C Trace Context specification.
+func (ts TraceState) String() string {
+	var sb strings.Builder
+
+	for i, kv := range ts.kvs {
+		sb.WriteString((string)(kv.Key))
+		sb.WriteByte('=')
+		sb.WriteString(kv.Value.Emit())
+
+		if i != len(ts.kvs)-1 {
+			sb.WriteByte(',')
+		}
+	}
+
+	return sb.String()
+}
+
+// Get returns a value for given key from the trace state.
+// If no key is found or provided key is invalid, returns an empty value.
+func (ts TraceState) Get(key label.Key) label.Value {
+	if !isTraceStateKeyValid(key) {
+		return label.Value{}
+	}
+
+	for _, kv := range ts.kvs {
+		if kv.Key == key {
+			return kv.Value
+		}
+	}
+
+	return label.Value{}
+}
+
+// Insert adds a new key/value, if one doesn't exists; otherwise updates the existing entry.
+// The new or updated entry is always inserted at the beginning of the TraceState, i.e.
+// on the left side, as per the W3C Trace Context specification requirement.
+func (ts TraceState) Insert(entry label.KeyValue) (TraceState, error) {
+	if !isTraceStateKeyValueValid(entry) {
+		return ts, errInvalidTraceStateKeyValue
+	}
+
+	ckvs := ts.copyKVsAndDeleteEntry(entry.Key)
+	if len(ckvs)+1 > traceStateMaxListMembers {
+		return ts, errInvalidTraceStateMembersNumber
+	}
+
+	ckvs = append(ckvs, label.KeyValue{})
+	copy(ckvs[1:], ckvs)
+	ckvs[0] = entry
+
+	return TraceState{ckvs}, nil
+}
+
+// Delete removes specified entry from the trace state.
+func (ts TraceState) Delete(key label.Key) (TraceState, error) {
+	if !isTraceStateKeyValid(key) {
+		return ts, errInvalidTraceStateKeyValue
+	}
+
+	return TraceState{ts.copyKVsAndDeleteEntry(key)}, nil
+}
+
+// IsEmpty returns true if the TraceState does not contain any entries
+func (ts TraceState) IsEmpty() bool {
+	return len(ts.kvs) == 0
+}
+
+func (ts TraceState) copyKVsAndDeleteEntry(key label.Key) []label.KeyValue {
+	ckvs := make([]label.KeyValue, len(ts.kvs))
+	copy(ckvs, ts.kvs)
+	for i, kv := range ts.kvs {
+		if kv.Key == key {
+			ckvs = append(ckvs[:i], ckvs[i+1:]...)
+			break
+		}
+	}
+
+	return ckvs
+}
+
+// TraceStateFromKeyValues is a convenience method to create a new TraceState from
+// provided key/value pairs.
+func TraceStateFromKeyValues(kvs ...label.KeyValue) (TraceState, error) { //nolint:golint
+	if len(kvs) == 0 {
+		return TraceState{}, nil
+	}
+
+	if len(kvs) > traceStateMaxListMembers {
+		return TraceState{}, errInvalidTraceStateMembersNumber
+	}
+
+	km := make(map[label.Key]bool)
+	for _, kv := range kvs {
+		if !isTraceStateKeyValueValid(kv) {
+			return TraceState{}, errInvalidTraceStateKeyValue
+		}
+		_, ok := km[kv.Key]
+		if ok {
+			return TraceState{}, errInvalidTraceStateDuplicate
+		}
+		km[kv.Key] = true
+	}
+
+	ckvs := make([]label.KeyValue, len(kvs))
+	copy(ckvs, kvs)
+	return TraceState{ckvs}, nil
+}
+
+func isTraceStateKeyValid(key label.Key) bool {
+	return keyFormatRegExp.MatchString(string(key))
+}
+
+func isTraceStateKeyValueValid(kv label.KeyValue) bool {
+	return isTraceStateKeyValid(kv.Key) &&
+		valueFormatRegExp.MatchString(kv.Value.Emit())
+}
+
+// SpanContext contains identifying trace information about a Span.
+type SpanContext struct {
+	TraceID    TraceID
+	SpanID     SpanID
+	TraceFlags byte
+	TraceState TraceState
+}
+
+// IsValid returns if the SpanContext is valid. A valid span context has a
+// valid TraceID and SpanID.
+func (sc SpanContext) IsValid() bool {
+	return sc.HasTraceID() && sc.HasSpanID()
+}
+
+// HasTraceID checks if the SpanContext has a valid TraceID.
+func (sc SpanContext) HasTraceID() bool {
+	return sc.TraceID.IsValid()
+}
+
+// HasSpanID checks if the SpanContext has a valid SpanID.
+func (sc SpanContext) HasSpanID() bool {
+	return sc.SpanID.IsValid()
+}
+
+// IsDeferred returns if the deferred bit is set in the trace flags.
+func (sc SpanContext) IsDeferred() bool {
+	return sc.TraceFlags&FlagsDeferred == FlagsDeferred
+}
+
+// IsDebug returns if the debug bit is set in the trace flags.
+func (sc SpanContext) IsDebug() bool {
+	return sc.TraceFlags&FlagsDebug == FlagsDebug
+}
+
+// IsSampled returns if the sampling bit is set in the trace flags.
+func (sc SpanContext) IsSampled() bool {
+	return sc.TraceFlags&FlagsSampled == FlagsSampled
+}
+
+type traceContextKeyType int
+
+const (
+	currentSpanKey traceContextKeyType = iota
+	remoteContextKey
+)
+
+// ContextWithSpan returns a copy of parent with span set to current.
+func ContextWithSpan(parent context.Context, span Span) context.Context {
+	return context.WithValue(parent, currentSpanKey, span)
+}
+
+// SpanFromContext returns the current span from ctx, or noop span if none set.
+func SpanFromContext(ctx context.Context) Span {
+	if span, ok := ctx.Value(currentSpanKey).(Span); ok {
+		return span
+	}
+	return noopSpan{}
+}
+
+// SpanContextFromContext returns the current SpanContext from ctx, or an empty SpanContext if none set.
+func SpanContextFromContext(ctx context.Context) SpanContext {
+	if span := SpanFromContext(ctx); span != nil {
+		return span.SpanContext()
+	}
+	return SpanContext{}
+}
+
+// ContextWithRemoteSpanContext returns a copy of parent with a remote set as
+// the remote span context.
+func ContextWithRemoteSpanContext(parent context.Context, remote SpanContext) context.Context {
+	return context.WithValue(parent, remoteContextKey, remote)
+}
+
+// RemoteSpanContextFromContext returns the remote span context from ctx.
+func RemoteSpanContextFromContext(ctx context.Context) SpanContext {
+	if sc, ok := ctx.Value(remoteContextKey).(SpanContext); ok {
+		return sc
+	}
+	return SpanContext{}
+}
+
+// Span is the individual component of a trace. It represents a single named
+// and timed operation of a workflow that is traced. A Tracer is used to
+// create a Span and it is then up to the operation the Span represents to
+// properly end the Span when the operation itself ends.
+type Span interface {
+	// Tracer returns the Tracer that created the Span. Tracer MUST NOT be
+	// nil.
+	Tracer() Tracer
+
+	// End completes the Span. The Span is considered complete and ready to be
+	// delivered through the rest of the telemetry pipeline after this method
+	// is called. Therefore, updates to the Span are not allowed after this
+	// method has been called.
+	End(options ...SpanOption)
+
+	// AddEvent adds an event with the provided name and options.
+	AddEvent(name string, options ...EventOption)
+
+	// IsRecording returns the recording state of the Span. It will return
+	// true if the Span is active and events can be recorded.
+	IsRecording() bool
+
+	// RecordError records an error as a Span event.
+	RecordError(err error, options ...EventOption)
+
+	// SpanContext returns the SpanContext of the Span. The returned
+	// SpanContext is usable even after the End has been called for the Span.
+	SpanContext() SpanContext
+
+	// SetStatus sets the status of the Span in the form of a code and a
+	// message. SetStatus overrides the value of previous calls to SetStatus
+	// on the Span.
+	SetStatus(code codes.Code, msg string)
+
+	// SetName sets the Span name.
+	SetName(name string)
+
+	// SetAttributes sets kv as attributes of the Span. If a key from kv
+	// already exists for an attribute of the Span it will be overwritten with
+	// the value contained in kv.
+	SetAttributes(kv ...label.KeyValue)
+}
+
+// Link is the relationship between two Spans. The relationship can be within
+// the same Trace or across different Traces.
+//
+// For example, a Link is used in the following situations:
+//
+//   1. Batch Processing: A batch of operations may contain operations
+//      associated with one or more traces/spans. Since there can only be one
+//      parent SpanContext, a Link is used to keep reference to the
+//      SpanContext of all operations in the batch.
+//   2. Public Endpoint: A SpanContext for an in incoming client request on a
+//      public endpoint should be considered untrusted. In such a case, a new
+//      trace with its own identity and sampling decision needs to be created,
+//      but this new trace needs to be related to the original trace in some
+//      form. A Link is used to keep reference to the original SpanContext and
+//      track the relationship.
+type Link struct {
+	SpanContext
+	Attributes []label.KeyValue
+}
+
+// SpanKind is the role a Span plays in a Trace.
+type SpanKind int
+
+// As a convenience, these match the proto definition, see
+// https://github.com/open-telemetry/opentelemetry-proto/blob/30d237e1ff3ab7aa50e0922b5bebdd93505090af/opentelemetry/proto/trace/v1/trace.proto#L101-L129
+//
+// The unspecified value is not a valid `SpanKind`. Use `ValidateSpanKind()`
+// to coerce a span kind to a valid value.
+const (
+	// SpanKindUnspecified is an unspecified SpanKind and is not a valid
+	// SpanKind. SpanKindUnspecified should be replaced with SpanKindInternal
+	// if it is received.
+	SpanKindUnspecified SpanKind = 0
+	// SpanKindInternal is a SpanKind for a Span that represents an internal
+	// operation within an application.
+	SpanKindInternal SpanKind = 1
+	// SpanKindServer is a SpanKind for a Span that represents the operation
+	// of handling a request from a client.
+	SpanKindServer SpanKind = 2
+	// SpanKindClient is a SpanKind for a Span that represents the operation
+	// of client making a request to a server.
+	SpanKindClient SpanKind = 3
+	// SpanKindProducer is a SpanKind for a Span that represents the operation
+	// of a producer sending a message to a message broker. Unlike
+	// SpanKindClient and SpanKindServer, there is often no direct
+	// relationship between this kind of Span and a SpanKindConsumer kind. A
+	// SpanKindProducer Span will end once the message is accepted by the
+	// message broker which might not overlap with the processing of that
+	// message.
+	SpanKindProducer SpanKind = 4
+	// SpanKindConsumer is a SpanKind for a Span that represents the operation
+	// of a consumer receiving a message from a message broker. Like
+	// SpanKindProducer Spans, there is often no direct relationship between
+	// this Span and the Span that produced the message.
+	SpanKindConsumer SpanKind = 5
+)
+
+// ValidateSpanKind returns a valid span kind value.  This will coerce
+// invalid values into the default value, SpanKindInternal.
+func ValidateSpanKind(spanKind SpanKind) SpanKind {
+	switch spanKind {
+	case SpanKindInternal,
+		SpanKindServer,
+		SpanKindClient,
+		SpanKindProducer,
+		SpanKindConsumer:
+		// valid
+		return spanKind
+	default:
+		return SpanKindInternal
+	}
+}
+
+// String returns the specified name of the SpanKind in lower-case.
+func (sk SpanKind) String() string {
+	switch sk {
+	case SpanKindInternal:
+		return "internal"
+	case SpanKindServer:
+		return "server"
+	case SpanKindClient:
+		return "client"
+	case SpanKindProducer:
+		return "producer"
+	case SpanKindConsumer:
+		return "consumer"
+	default:
+		return "unspecified"
+	}
+}
+
+// Tracer is the creator of Spans.
+type Tracer interface {
+	// Start creates a span.
+	Start(ctx context.Context, spanName string, opts ...SpanOption) (context.Context, Span)
+}
+
+// TracerProvider provides access to instrumentation Tracers.
+type TracerProvider interface {
+	// Tracer creates an implementation of the Tracer interface.
+	// The instrumentationName must be the name of the library providing
+	// instrumentation. This name may be the same as the instrumented code
+	// only if that code provides built-in instrumentation. If the
+	// instrumentationName is empty, then a implementation defined default
+	// name will be used instead.
+	Tracer(instrumentationName string, opts ...TracerOption) Tracer
+}
diff --git a/vendor/go.opentelemetry.io/otel/trace/trace_noop.go b/vendor/go.opentelemetry.io/otel/trace/trace_noop.go
new file mode 100644
index 0000000000..6f18a94819
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/trace/trace_noop.go
@@ -0,0 +1,84 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 trace // import "go.opentelemetry.io/otel/trace"
+
+import (
+	"context"
+
+	"go.opentelemetry.io/otel/codes"
+	"go.opentelemetry.io/otel/label"
+)
+
+// NewNoopTracerProvider returns an implementation of TracerProvider that
+// performs no operations. The Tracer and Spans created from the returned
+// TracerProvider also perform no operations.
+func NewNoopTracerProvider() TracerProvider {
+	return noopTracerProvider{}
+}
+
+type noopTracerProvider struct{}
+
+var _ TracerProvider = noopTracerProvider{}
+
+// Tracer returns noop implementation of Tracer.
+func (p noopTracerProvider) Tracer(string, ...TracerOption) Tracer {
+	return noopTracer{}
+}
+
+// noopTracer is an implementation of Tracer that preforms no operations.
+type noopTracer struct{}
+
+var _ Tracer = noopTracer{}
+
+// Start starts a noop span.
+func (t noopTracer) Start(ctx context.Context, name string, _ ...SpanOption) (context.Context, Span) {
+	span := noopSpan{}
+	return ContextWithSpan(ctx, span), span
+}
+
+// noopSpan is an implementation of Span that preforms no operations.
+type noopSpan struct{}
+
+var _ noopSpan = noopSpan{}
+
+// SpanContext returns an empty span context.
+func (noopSpan) SpanContext() SpanContext { return SpanContext{} }
+
+// IsRecording always returns false.
+func (noopSpan) IsRecording() bool { return false }
+
+// SetStatus does nothing.
+func (noopSpan) SetStatus(codes.Code, string) {}
+
+// SetError does nothing.
+func (noopSpan) SetError(bool) {}
+
+// SetAttributes does nothing.
+func (noopSpan) SetAttributes(...label.KeyValue) {}
+
+// End does nothing.
+func (noopSpan) End(...SpanOption) {}
+
+// RecordError does nothing.
+func (noopSpan) RecordError(error, ...EventOption) {}
+
+// Tracer returns the Tracer that created this Span.
+func (noopSpan) Tracer() Tracer { return noopTracer{} }
+
+// AddEvent does nothing.
+func (noopSpan) AddEvent(string, ...EventOption) {}
+
+// SetName does nothing.
+func (noopSpan) SetName(string) {}
diff --git a/vendor/go.opentelemetry.io/otel/unit/doc.go b/vendor/go.opentelemetry.io/otel/unit/doc.go
new file mode 100644
index 0000000000..0d77a750cc
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/unit/doc.go
@@ -0,0 +1,20 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 unit provides units.
+//
+// This package is currently in a pre-GA phase. Backwards incompatible changes
+// may be introduced in subsequent minor version releases as we work to track
+// the evolving OpenTelemetry specification and user feedback.
+package unit // import "go.opentelemetry.io/otel/unit"
diff --git a/vendor/go.opentelemetry.io/otel/unit/unit.go b/vendor/go.opentelemetry.io/otel/unit/unit.go
new file mode 100644
index 0000000000..523bfe1d0a
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/unit/unit.go
@@ -0,0 +1,23 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 unit // import "go.opentelemetry.io/otel/unit"
+
+type Unit string
+
+const (
+	Dimensionless Unit = "1"
+	Bytes         Unit = "By"
+	Milliseconds  Unit = "ms"
+)
diff --git a/vendor/go.opentelemetry.io/otel/verify_examples.sh b/vendor/go.opentelemetry.io/otel/verify_examples.sh
new file mode 100644
index 0000000000..dbb61a4227
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/verify_examples.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+
+# Copyright The OpenTelemetry Authors
+#
+# 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.
+
+set -euo pipefail
+
+cd $(dirname $0)
+TOOLS_DIR=$(pwd)/.tools
+
+if [ -z "${GOPATH}" ] ; then
+	printf "GOPATH is not defined.\n"
+	exit -1
+fi
+
+if [ ! -d "${GOPATH}" ] ; then
+	printf "GOPATH ${GOPATH} is invalid \n"
+	exit -1
+fi
+
+# Pre-requisites
+if ! git diff --quiet; then \
+	git status
+	printf "\n\nError: working tree is not clean\n"
+	exit -1
+fi
+
+if [ "$(git tag --contains $(git log -1 --pretty=format:"%H"))" = "" ] ; then
+	printf "$(git log -1)"
+	printf "\n\nError: HEAD is not pointing to a tagged version"
+fi
+
+make ${TOOLS_DIR}/gojq
+
+DIR_TMP="${GOPATH}/src/oteltmp/"
+rm -rf $DIR_TMP
+mkdir -p $DIR_TMP
+
+printf "Copy examples to ${DIR_TMP}\n"
+cp -a ./example ${DIR_TMP}
+
+# Update go.mod files
+printf "Update go.mod: rename module and remove replace\n"
+
+PACKAGE_DIRS=$(find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; | egrep 'example' | sed 's/^\.\///' | sort)
+
+for dir in $PACKAGE_DIRS; do
+	printf "  Update go.mod for $dir\n"
+	(cd "${DIR_TMP}/${dir}" && \
+	 # replaces is ("mod1" "mod2" …)
+	 replaces=($(go mod edit -json | ${TOOLS_DIR}/gojq '.Replace[].Old.Path')) && \
+	 # strip double quotes
+	 replaces=("${replaces[@]%\"}") && \
+	 replaces=("${replaces[@]#\"}") && \
+	 # make an array (-dropreplace=mod1 -dropreplace=mod2 …)
+	 dropreplaces=("${replaces[@]/#/-dropreplace=}") && \
+	 go mod edit -module "oteltmp/${dir}" "${dropreplaces[@]}" && \
+	 go mod tidy)
+done
+printf "Update done:\n\n"
+
+# Build directories that contain main package. These directories are different than
+# directories that contain go.mod files.
+printf "Build examples:\n"
+EXAMPLES=$(./get_main_pkgs.sh ./example)
+for ex in $EXAMPLES; do
+	printf "  Build $ex in ${DIR_TMP}/${ex}\n"
+	(cd "${DIR_TMP}/${ex}" && \
+	 go build .)
+done
+
+# Cleanup
+printf "Remove copied files.\n"
+rm -rf $DIR_TMP
diff --git a/vendor/go.opentelemetry.io/otel/version.go b/vendor/go.opentelemetry.io/otel/version.go
new file mode 100644
index 0000000000..f9ffac7104
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/version.go
@@ -0,0 +1,20 @@
+// Copyright The OpenTelemetry Authors
+//
+// 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 otel // import "go.opentelemetry.io/otel"
+
+// Version is the current release version of OpenTelemetry in use.
+func Version() string {
+	return "0.16.0"
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 70ab9cce5c..6998fdb86e 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -195,6 +195,8 @@ github.com/denisenkom/go-mssqldb/internal/querytext
 # github.com/dgrijalva/jwt-go v3.2.0+incompatible
 ## explicit
 github.com/dgrijalva/jwt-go
+# github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f
+github.com/dgryski/go-rendezvous
 # github.com/dlclark/regexp2 v1.4.0
 ## explicit
 github.com/dlclark/regexp2
@@ -347,15 +349,16 @@ github.com/go-openapi/strfmt
 github.com/go-openapi/swag
 # github.com/go-openapi/validate v0.20.1
 github.com/go-openapi/validate
-# github.com/go-redis/redis/v7 v7.4.0
+# github.com/go-redis/redis/v8 v8.5.0
 ## explicit
-github.com/go-redis/redis/v7
-github.com/go-redis/redis/v7/internal
-github.com/go-redis/redis/v7/internal/consistenthash
-github.com/go-redis/redis/v7/internal/hashtag
-github.com/go-redis/redis/v7/internal/pool
-github.com/go-redis/redis/v7/internal/proto
-github.com/go-redis/redis/v7/internal/util
+github.com/go-redis/redis/v8
+github.com/go-redis/redis/v8/internal
+github.com/go-redis/redis/v8/internal/hashtag
+github.com/go-redis/redis/v8/internal/hscan
+github.com/go-redis/redis/v8/internal/pool
+github.com/go-redis/redis/v8/internal/proto
+github.com/go-redis/redis/v8/internal/rand
+github.com/go-redis/redis/v8/internal/util
 # github.com/go-sql-driver/mysql v1.5.0
 ## explicit
 github.com/go-sql-driver/mysql
@@ -795,6 +798,20 @@ go.mongodb.org/mongo-driver/bson/bsonrw
 go.mongodb.org/mongo-driver/bson/bsontype
 go.mongodb.org/mongo-driver/bson/primitive
 go.mongodb.org/mongo-driver/x/bsonx/bsoncore
+# go.opentelemetry.io/otel v0.16.0
+go.opentelemetry.io/otel
+go.opentelemetry.io/otel/codes
+go.opentelemetry.io/otel/internal
+go.opentelemetry.io/otel/internal/baggage
+go.opentelemetry.io/otel/internal/global
+go.opentelemetry.io/otel/internal/trace/noop
+go.opentelemetry.io/otel/label
+go.opentelemetry.io/otel/metric
+go.opentelemetry.io/otel/metric/number
+go.opentelemetry.io/otel/metric/registry
+go.opentelemetry.io/otel/propagation
+go.opentelemetry.io/otel/trace
+go.opentelemetry.io/otel/unit
 # go.uber.org/atomic v1.6.0
 go.uber.org/atomic
 # go.uber.org/multierr v1.5.0