mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	[Vendor] Update go-redis to v8.5.0 (#13749)
* Update go-redis to v8.4.0 * github.com/go-redis/redis/v8 v8.4.0 -> v8.5.0 * Apply suggestions from code review Co-authored-by: zeripath <art27@cantab.net> * TODO * Use the Queue termination channel as the default context for pushes Signed-off-by: Andrew Thornton <art27@cantab.net> * missed one Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: zeripath <art27@cantab.net>
This commit is contained in:
		
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										46
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								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=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								modules/cache/cache_redis.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								modules/cache/cache_redis.go
									
									
									
									
										vendored
									
									
								
							@@ -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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ import (
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-redis/redis/v7"
 | 
			
		||||
	"github.com/go-redis/redis/v8"
 | 
			
		||||
	"github.com/syndtr/goleveldb/leveldb"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-redis/redis/v7"
 | 
			
		||||
	"github.com/go-redis/redis/v8"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var replacer = strings.NewReplacer("_", "", "-", "")
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								vendor/github.com/dgryski/go-rendezvous/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/dgryski/go-rendezvous/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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.
 | 
			
		||||
							
								
								
									
										79
									
								
								vendor/github.com/dgryski/go-rendezvous/rdv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								vendor/github.com/dgryski/go-rendezvous/rdv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/go-redis/redis/v7/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/go-redis/redis/v7/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										46
									
								
								vendor/github.com/go-redis/redis/v7/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/go-redis/redis/v7/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										128
									
								
								vendor/github.com/go-redis/redis/v7/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										128
									
								
								vendor/github.com/go-redis/redis/v7/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,128 +0,0 @@
 | 
			
		||||
# Redis client for Golang
 | 
			
		||||
 | 
			
		||||
[](https://travis-ci.org/go-redis/redis)
 | 
			
		||||
[](https://godoc.org/github.com/go-redis/redis)
 | 
			
		||||
[](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)
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/go-redis/redis/v7/cluster_commands.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/go-redis/redis/v7/cluster_commands.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2643
									
								
								vendor/github.com/go-redis/redis/v7/commands.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2643
									
								
								vendor/github.com/go-redis/redis/v7/commands.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										15
									
								
								vendor/github.com/go-redis/redis/v7/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								vendor/github.com/go-redis/redis/v7/go.mod
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										47
									
								
								vendor/github.com/go-redis/redis/v7/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								vendor/github.com/go-redis/redis/v7/go.sum
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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=
 | 
			
		||||
							
								
								
									
										81
									
								
								vendor/github.com/go-redis/redis/v7/internal/consistenthash/consistenthash.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										81
									
								
								vendor/github.com/go-redis/redis/v7/internal/consistenthash/consistenthash.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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]]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								vendor/github.com/go-redis/redis/v7/internal/internal.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/go-redis/redis/v7/internal/internal.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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)))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								vendor/github.com/go-redis/redis/v7/internal/log.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/go-redis/redis/v7/internal/log.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,8 +0,0 @@
 | 
			
		||||
package internal
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var Logger = log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile)
 | 
			
		||||
							
								
								
									
										112
									
								
								vendor/github.com/go-redis/redis/v7/internal/pool/pool_sticky.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/go-redis/redis/v7/internal/pool/pool_sticky.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								vendor/github.com/go-redis/redis/v7/internal/util.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										56
									
								
								vendor/github.com/go-redis/redis/v7/internal/util.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										62
									
								
								vendor/github.com/go-redis/redis/v7/script.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/go-redis/redis/v7/script.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										509
									
								
								vendor/github.com/go-redis/redis/v7/sentinel.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										509
									
								
								vendor/github.com/go-redis/redis/v7/sentinel.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
@@ -1,2 +1,3 @@
 | 
			
		||||
*.rdb
 | 
			
		||||
testdata/*/
 | 
			
		||||
.idea/
 | 
			
		||||
@@ -7,9 +7,18 @@ linters:
 | 
			
		||||
  disable:
 | 
			
		||||
    - funlen
 | 
			
		||||
    - gochecknoglobals
 | 
			
		||||
    - gochecknoinits
 | 
			
		||||
    - gocognit
 | 
			
		||||
    - goconst
 | 
			
		||||
    - godox
 | 
			
		||||
    - gosec
 | 
			
		||||
    - maligned
 | 
			
		||||
    - wsl
 | 
			
		||||
    - gomnd
 | 
			
		||||
    - goerr113
 | 
			
		||||
    - exhaustive
 | 
			
		||||
    - nestif
 | 
			
		||||
    - nlreturn
 | 
			
		||||
    - exhaustivestruct
 | 
			
		||||
    - wrapcheck
 | 
			
		||||
    - errorlint
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/go-redis/redis/v8/.prettierrc
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/go-redis/redis/v8/.prettierrc
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
semi: false
 | 
			
		||||
singleQuote: true
 | 
			
		||||
proseWrap: always
 | 
			
		||||
printWidth: 100
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/go-redis/redis/v8/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/go-redis/redis/v8/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										5
									
								
								vendor/github.com/go-redis/redis/v8/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/go-redis/redis/v8/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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/
 | 
			
		||||
@@ -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)
 | 
			
		||||
							
								
								
									
										159
									
								
								vendor/github.com/go-redis/redis/v8/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								vendor/github.com/go-redis/redis/v8/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,159 @@
 | 
			
		||||
# Redis client for Golang
 | 
			
		||||
 | 
			
		||||
[](https://travis-ci.org/go-redis/redis)
 | 
			
		||||
[](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc)
 | 
			
		||||
[](https://redis.uptrace.dev/)
 | 
			
		||||
[](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)
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										25
									
								
								vendor/github.com/go-redis/redis/v8/cluster_commands.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/go-redis/redis/v8/cluster_commands.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2790
									
								
								vendor/github.com/go-redis/redis/v8/commands.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2790
									
								
								vendor/github.com/go-redis/redis/v8/commands.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/github.com/go-redis/redis/v8/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/go-redis/redis/v8/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										97
									
								
								vendor/github.com/go-redis/redis/v8/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								vendor/github.com/go-redis/redis/v8/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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=
 | 
			
		||||
							
								
								
									
										56
									
								
								vendor/github.com/go-redis/redis/v8/internal/arg.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								vendor/github.com/go-redis/redis/v8/internal/arg.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +1,9 @@
 | 
			
		||||
package hashtag
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-redis/redis/v8/internal/rand"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const slotNumber = 16384
 | 
			
		||||
							
								
								
									
										151
									
								
								vendor/github.com/go-redis/redis/v8/internal/hscan/hscan.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								vendor/github.com/go-redis/redis/v8/internal/hscan/hscan.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										87
									
								
								vendor/github.com/go-redis/redis/v8/internal/hscan/structmap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								vendor/github.com/go-redis/redis/v8/internal/hscan/structmap.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								vendor/github.com/go-redis/redis/v8/internal/instruments.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/go-redis/redis/v8/internal/instruments.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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"),
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								vendor/github.com/go-redis/redis/v8/internal/internal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/go-redis/redis/v8/internal/internal.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								vendor/github.com/go-redis/redis/v8/internal/log.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/go-redis/redis/v8/internal/log.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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),
 | 
			
		||||
}
 | 
			
		||||
@@ -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 {
 | 
			
		||||
@@ -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 {
 | 
			
		||||
							
								
								
									
										58
									
								
								vendor/github.com/go-redis/redis/v8/internal/pool/pool_single.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								vendor/github.com/go-redis/redis/v8/internal/pool/pool_single.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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{}
 | 
			
		||||
}
 | 
			
		||||
@@ -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{}
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -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')
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										45
									
								
								vendor/github.com/go-redis/redis/v8/internal/rand/rand.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								vendor/github.com/go-redis/redis/v8/internal/rand/rand.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/github.com/go-redis/redis/v8/internal/safe.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/go-redis/redis/v8/internal/safe.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
// +build appengine
 | 
			
		||||
 | 
			
		||||
package internal
 | 
			
		||||
 | 
			
		||||
func String(b []byte) string {
 | 
			
		||||
	return string(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Bytes(s string) []byte {
 | 
			
		||||
	return []byte(s)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								vendor/github.com/go-redis/redis/v8/internal/unsafe.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/go-redis/redis/v8/internal/unsafe.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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)},
 | 
			
		||||
	))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										73
									
								
								vendor/github.com/go-redis/redis/v8/internal/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								vendor/github.com/go-redis/redis/v8/internal/util.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
		}
 | 
			
		||||
@@ -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,
 | 
			
		||||
@@ -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 {
 | 
			
		||||
@@ -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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										336
									
								
								vendor/github.com/go-redis/redis/v7/redis.go → vendor/github.com/go-redis/redis/v8/redis.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										336
									
								
								vendor/github.com/go-redis/redis/v7/redis.go → vendor/github.com/go-redis/redis/v8/redis.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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.
 | 
			
		||||
@@ -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
 | 
			
		||||
							
								
								
									
										349
									
								
								vendor/github.com/go-redis/redis/v7/ring.go → vendor/github.com/go-redis/redis/v8/ring.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										349
									
								
								vendor/github.com/go-redis/redis/v7/ring.go → vendor/github.com/go-redis/redis/v8/ring.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								vendor/github.com/go-redis/redis/v8/script.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								vendor/github.com/go-redis/redis/v8/script.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										738
									
								
								vendor/github.com/go-redis/redis/v8/sentinel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										738
									
								
								vendor/github.com/go-redis/redis/v8/sentinel.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										51
									
								
								vendor/github.com/go-redis/redis/v7/tx.go → vendor/github.com/go-redis/redis/v8/tx.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										51
									
								
								vendor/github.com/go-redis/redis/v7/tx.go → vendor/github.com/go-redis/redis/v8/tx.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -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.
 | 
			
		||||
@@ -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:
 | 
			
		||||
							
								
								
									
										23
									
								
								vendor/go.opentelemetry.io/otel/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/go.opentelemetry.io/otel/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										3
									
								
								vendor/go.opentelemetry.io/otel/.gitmodules
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								vendor/go.opentelemetry.io/otel/.gitmodules
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
[submodule "opentelemetry-proto"]
 | 
			
		||||
	path = exporters/otlp/internal/opentelemetry-proto
 | 
			
		||||
	url = https://github.com/open-telemetry/opentelemetry-proto
 | 
			
		||||
							
								
								
									
										32
									
								
								vendor/go.opentelemetry.io/otel/.golangci.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								vendor/go.opentelemetry.io/otel/.golangci.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										1054
									
								
								vendor/go.opentelemetry.io/otel/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1054
									
								
								vendor/go.opentelemetry.io/otel/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										17
									
								
								vendor/go.opentelemetry.io/otel/CODEOWNERS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/go.opentelemetry.io/otel/CODEOWNERS
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										374
									
								
								vendor/go.opentelemetry.io/otel/CONTRIBUTING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								vendor/go.opentelemetry.io/otel/CONTRIBUTING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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).
 | 
			
		||||
							
								
								
									
										201
									
								
								vendor/go.opentelemetry.io/otel/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								vendor/go.opentelemetry.io/otel/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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.
 | 
			
		||||
							
								
								
									
										177
									
								
								vendor/go.opentelemetry.io/otel/Makefile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								vendor/go.opentelemetry.io/otel/Makefile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										129
									
								
								vendor/go.opentelemetry.io/otel/Makefile.proto
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								vendor/go.opentelemetry.io/otel/Makefile.proto
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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)
 | 
			
		||||
							
								
								
									
										71
									
								
								vendor/go.opentelemetry.io/otel/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/go.opentelemetry.io/otel/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
# OpenTelemetry-Go
 | 
			
		||||
 | 
			
		||||
[](https://github.com/open-telemetry/opentelemetry-go/actions?query=workflow%3Aci+branch%3Amaster)
 | 
			
		||||
[](https://pkg.go.dev/go.opentelemetry.io/otel)
 | 
			
		||||
[](https://goreportcard.com/report/go.opentelemetry.io/otel)
 | 
			
		||||
[](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).
 | 
			
		||||
							
								
								
									
										81
									
								
								vendor/go.opentelemetry.io/otel/RELEASING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								vendor/go.opentelemetry.io/otel/RELEASING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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.
 | 
			
		||||
							
								
								
									
										217
									
								
								vendor/go.opentelemetry.io/otel/VERSIONING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								vendor/go.opentelemetry.io/otel/VERSIONING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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`
 | 
			
		||||
							
								
								
									
										106
									
								
								vendor/go.opentelemetry.io/otel/codes/codes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								vendor/go.opentelemetry.io/otel/codes/codes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								vendor/go.opentelemetry.io/otel/codes/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/go.opentelemetry.io/otel/codes/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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"
 | 
			
		||||
							
								
								
									
										38
									
								
								vendor/go.opentelemetry.io/otel/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/go.opentelemetry.io/otel/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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"
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/go.opentelemetry.io/otel/error_handler.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/go.opentelemetry.io/otel/error_handler.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								vendor/go.opentelemetry.io/otel/get_main_pkgs.sh
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								vendor/go.opentelemetry.io/otel/get_main_pkgs.sh
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										8
									
								
								vendor/go.opentelemetry.io/otel/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/go.opentelemetry.io/otel/go.mod
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										15
									
								
								vendor/go.opentelemetry.io/otel/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								vendor/go.opentelemetry.io/otel/go.sum
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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=
 | 
			
		||||
							
								
								
									
										89
									
								
								vendor/go.opentelemetry.io/otel/handler.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								vendor/go.opentelemetry.io/otel/handler.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										338
									
								
								vendor/go.opentelemetry.io/otel/internal/baggage/baggage.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								vendor/go.opentelemetry.io/otel/internal/baggage/baggage.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										348
									
								
								vendor/go.opentelemetry.io/otel/internal/global/meter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										348
									
								
								vendor/go.opentelemetry.io/otel/internal/global/meter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										82
									
								
								vendor/go.opentelemetry.io/otel/internal/global/propagator.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								vendor/go.opentelemetry.io/otel/internal/global/propagator.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										143
									
								
								vendor/go.opentelemetry.io/otel/internal/global/state.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								vendor/go.opentelemetry.io/otel/internal/global/state.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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{}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										128
									
								
								vendor/go.opentelemetry.io/otel/internal/global/trace.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								vendor/go.opentelemetry.io/otel/internal/global/trace.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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...)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										91
									
								
								vendor/go.opentelemetry.io/otel/internal/rawhelpers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								vendor/go.opentelemetry.io/otel/internal/rawhelpers.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user