forked from TrueCloudLab/frostfs-sdk-go
Merge pull request #5 from masterSplinter01/feature/4-import-refactor-connection-pooling
Import refactor connection pooling
This commit is contained in:
commit
cbfc17a1a9
9 changed files with 623 additions and 0 deletions
3
go.mod
3
go.mod
|
@ -5,5 +5,8 @@ go 1.16
|
|||
require (
|
||||
github.com/alecthomas/participle v0.7.1
|
||||
github.com/nspcc-dev/neofs-api-go v1.26.1
|
||||
github.com/nspcc-dev/neofs-crypto v0.3.0
|
||||
github.com/stretchr/testify v1.6.1
|
||||
go.uber.org/zap v1.10.0
|
||||
google.golang.org/grpc v1.29.1
|
||||
)
|
||||
|
|
17
go.sum
17
go.sum
|
@ -23,6 +23,7 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
|
|||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
|
@ -86,10 +87,13 @@ github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp
|
|||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
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 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
|
@ -103,8 +107,10 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6
|
|||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
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/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
|
@ -119,6 +125,7 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mr-tron/base58 v1.1.2 h1:ZEw4I2EgPKDJ2iEw0cNmLB3ROrEmkOtXIkaG7wZg+78=
|
||||
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw=
|
||||
|
@ -131,14 +138,17 @@ github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y=
|
|||
github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU=
|
||||
github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg=
|
||||
github.com/nspcc-dev/neo-go v0.91.0/go.mod h1:G6HdOWvzQ6tlvFdvFSN/PgCzLPN/X/X4d5hTjFRUDcc=
|
||||
github.com/nspcc-dev/neo-go v0.95.0 h1:bttArYkIuhBJWSZsZ1xVW8MJsj5SvZwAhqVN3HZPNbo=
|
||||
github.com/nspcc-dev/neo-go v0.95.0/go.mod h1:bW07ge1WFXsBgqrcPpLUr6OcyQxHqM26MZNesWMdH0c=
|
||||
github.com/nspcc-dev/neofs-api-go v1.24.0/go.mod h1:G7dqincfdjBrAbL5nxVp82emF05fSVEqe59ICsoRDI8=
|
||||
github.com/nspcc-dev/neofs-api-go v1.26.1 h1:GMIuEB6Hv9IXP9SJd/1f8Df6gRriPkSplpmpJXgQ/1I=
|
||||
github.com/nspcc-dev/neofs-api-go v1.26.1/go.mod h1:SHuH1Ba3U/h3j+8HHbb3Cns1LfMlEb88guWog9Qi68Y=
|
||||
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
|
||||
github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
||||
github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnBg0L4ifM=
|
||||
github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
||||
github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
||||
github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE=
|
||||
github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
||||
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=
|
||||
|
@ -150,6 +160,7 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
|
|||
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
@ -192,13 +203,17 @@ github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBU
|
|||
github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
@ -245,6 +260,7 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
|
|||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
@ -266,6 +282,7 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||
gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
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/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
|
|
78
pkg/logger/grpc.go
Normal file
78
pkg/logger/grpc.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
)
|
||||
|
||||
type (
|
||||
zapLogger struct {
|
||||
zapcore.Core
|
||||
log *zap.SugaredLogger
|
||||
}
|
||||
|
||||
// Logger includes grpclog.LoggerV2 interface with an additional
|
||||
// Println method.
|
||||
Logger interface {
|
||||
grpclog.LoggerV2
|
||||
Println(v ...interface{})
|
||||
}
|
||||
)
|
||||
|
||||
// GRPC wraps given zap.Logger into grpclog.LoggerV2+ interface.
|
||||
func GRPC(l *zap.Logger) Logger {
|
||||
log := l.WithOptions(
|
||||
// skip gRPCLog + zapLogger in caller
|
||||
zap.AddCallerSkip(2))
|
||||
|
||||
return &zapLogger{
|
||||
Core: log.Core(),
|
||||
log: log.Sugar(),
|
||||
}
|
||||
}
|
||||
|
||||
// Info implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Info(args ...interface{}) { z.log.Info(args...) }
|
||||
|
||||
// Infoln implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Infoln(args ...interface{}) { z.log.Info(args...) }
|
||||
|
||||
// Infof implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Infof(format string, args ...interface{}) { z.log.Infof(format, args...) }
|
||||
|
||||
// Println allows to print a line with info severity.
|
||||
func (z *zapLogger) Println(args ...interface{}) { z.log.Info(args...) }
|
||||
|
||||
// Printf implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Printf(format string, args ...interface{}) { z.log.Infof(format, args...) }
|
||||
|
||||
// Warning implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Warning(args ...interface{}) { z.log.Warn(args...) }
|
||||
|
||||
// Warningln implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Warningln(args ...interface{}) { z.log.Warn(args...) }
|
||||
|
||||
// Warningf implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Warningf(format string, args ...interface{}) { z.log.Warnf(format, args...) }
|
||||
|
||||
// Error implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Error(args ...interface{}) { z.log.Error(args...) }
|
||||
|
||||
// Errorln implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Errorln(args ...interface{}) { z.log.Error(args...) }
|
||||
|
||||
// Errorf implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Errorf(format string, args ...interface{}) { z.log.Errorf(format, args...) }
|
||||
|
||||
// Fatal implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Fatal(args ...interface{}) { z.log.Fatal(args...) }
|
||||
|
||||
// Fatalln implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Fatalln(args ...interface{}) { z.log.Fatal(args...) }
|
||||
|
||||
// Fatalf implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) Fatalf(format string, args ...interface{}) { z.log.Fatalf(format, args...) }
|
||||
|
||||
// V implements grpclog.LoggerV2.
|
||||
func (z *zapLogger) V(int) bool { return z.Enabled(zapcore.DebugLevel) }
|
33
pkg/logger/option.go
Normal file
33
pkg/logger/option.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package logger
|
||||
|
||||
import "go.uber.org/zap"
|
||||
|
||||
// WithSamplingInitial returns Option that sets sampling initial parameter.
|
||||
func WithSamplingInitial(v int) Option { return func(o *options) { o.SamplingInitial = v } }
|
||||
|
||||
// WithSamplingThereafter returns Option that sets sampling thereafter parameter.
|
||||
func WithSamplingThereafter(v int) Option { return func(o *options) { o.SamplingThereafter = v } }
|
||||
|
||||
// WithFormat returns Option that sets format parameter.
|
||||
func WithFormat(v string) Option { return func(o *options) { o.Format = v } }
|
||||
|
||||
// WithLevel returns Option that sets Level parameter.
|
||||
func WithLevel(v string) Option { return func(o *options) { o.Level = v } }
|
||||
|
||||
// WithTraceLevel returns Option that sets trace level parameter.
|
||||
func WithTraceLevel(v string) Option { return func(o *options) { o.TraceLevel = v } }
|
||||
|
||||
// WithoutDisclaimer returns Option that disables disclaimer.
|
||||
func WithoutDisclaimer() Option { return func(o *options) { o.NoDisclaimer = true } }
|
||||
|
||||
// WithoutCaller returns Option that disables caller printing.
|
||||
func WithoutCaller() Option { return func(o *options) { o.NoCaller = true } }
|
||||
|
||||
// WithAppName returns Option that sets application name.
|
||||
func WithAppName(v string) Option { return func(o *options) { o.AppName = v } }
|
||||
|
||||
// WithAppVersion returns Option that sets application version.
|
||||
func WithAppVersion(v string) Option { return func(o *options) { o.AppVersion = v } }
|
||||
|
||||
// WithZapOptions returns Option that sets zap logger options.
|
||||
func WithZapOptions(opts ...zap.Option) Option { return func(o *options) { o.Options = opts } }
|
134
pkg/logger/zap.go
Normal file
134
pkg/logger/zap.go
Normal file
|
@ -0,0 +1,134 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type (
|
||||
// Option represents logger option setter.
|
||||
Option func(o *options)
|
||||
|
||||
options struct {
|
||||
Options []zap.Option
|
||||
|
||||
SamplingInitial int
|
||||
SamplingThereafter int
|
||||
|
||||
Format string
|
||||
Level string
|
||||
TraceLevel string
|
||||
|
||||
NoCaller bool
|
||||
NoDisclaimer bool
|
||||
|
||||
AppName string
|
||||
AppVersion string
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
formatJSON = "json"
|
||||
formatConsole = "console"
|
||||
|
||||
defaultSamplingInitial = 100
|
||||
defaultSamplingThereafter = 100
|
||||
|
||||
lvlInfo = "info"
|
||||
lvlWarn = "warn"
|
||||
lvlDebug = "debug"
|
||||
lvlError = "error"
|
||||
lvlFatal = "fatal"
|
||||
lvlPanic = "panic"
|
||||
)
|
||||
|
||||
func safeLevel(lvl string) zap.AtomicLevel {
|
||||
switch strings.ToLower(lvl) {
|
||||
case lvlDebug:
|
||||
return zap.NewAtomicLevelAt(zap.DebugLevel)
|
||||
case lvlWarn:
|
||||
return zap.NewAtomicLevelAt(zap.WarnLevel)
|
||||
case lvlError:
|
||||
return zap.NewAtomicLevelAt(zap.ErrorLevel)
|
||||
case lvlFatal:
|
||||
return zap.NewAtomicLevelAt(zap.FatalLevel)
|
||||
case lvlPanic:
|
||||
return zap.NewAtomicLevelAt(zap.PanicLevel)
|
||||
default:
|
||||
return zap.NewAtomicLevelAt(zap.InfoLevel)
|
||||
}
|
||||
}
|
||||
|
||||
func defaults() *options {
|
||||
return &options{
|
||||
SamplingInitial: defaultSamplingInitial,
|
||||
SamplingThereafter: defaultSamplingThereafter,
|
||||
|
||||
Format: formatConsole,
|
||||
Level: lvlDebug,
|
||||
TraceLevel: lvlInfo,
|
||||
|
||||
NoCaller: false,
|
||||
NoDisclaimer: false,
|
||||
|
||||
AppName: "",
|
||||
AppVersion: "",
|
||||
}
|
||||
}
|
||||
|
||||
// New returns new zap.Logger using all options specified and stdout used
|
||||
// for output.
|
||||
func New(opts ...Option) (*zap.Logger, error) {
|
||||
o := defaults()
|
||||
c := zap.NewProductionConfig()
|
||||
|
||||
c.OutputPaths = []string{"stdout"}
|
||||
c.ErrorOutputPaths = []string{"stdout"}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(o)
|
||||
}
|
||||
|
||||
// set sampling
|
||||
c.Sampling = &zap.SamplingConfig{
|
||||
Initial: o.SamplingInitial,
|
||||
Thereafter: o.SamplingThereafter,
|
||||
}
|
||||
|
||||
// logger level
|
||||
c.Level = safeLevel(o.Level)
|
||||
traceLvl := safeLevel(o.TraceLevel)
|
||||
|
||||
// logger format
|
||||
switch f := o.Format; strings.ToLower(f) {
|
||||
case formatConsole:
|
||||
c.Encoding = formatConsole
|
||||
default:
|
||||
c.Encoding = formatJSON
|
||||
}
|
||||
|
||||
// logger time
|
||||
c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
|
||||
|
||||
if o.NoCaller {
|
||||
c.EncoderConfig.EncodeCaller = nil
|
||||
}
|
||||
|
||||
// enable trace only for current log-level
|
||||
o.Options = append(o.Options, zap.AddStacktrace(traceLvl))
|
||||
|
||||
l, err := c.Build(o.Options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if o.NoDisclaimer {
|
||||
return l, nil
|
||||
}
|
||||
|
||||
return l.With(
|
||||
zap.String("app_name", o.AppName),
|
||||
zap.String("app_version", o.AppVersion)), nil
|
||||
}
|
39
pkg/neofs/client-plant.go
Normal file
39
pkg/neofs/client-plant.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package neofs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/pkg/client"
|
||||
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
|
||||
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
||||
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
|
||||
)
|
||||
|
||||
// ClientPlant provides connections to NeoFS nodes from pool and allows to
|
||||
// get local owner ID.
|
||||
type ClientPlant interface {
|
||||
ConnectionArtifacts() (client.Client, *token.SessionToken, error)
|
||||
OwnerID() *owner.ID
|
||||
}
|
||||
|
||||
type neofsClientPlant struct {
|
||||
key *ecdsa.PrivateKey
|
||||
ownerID *owner.ID
|
||||
pool pool.Pool
|
||||
}
|
||||
|
||||
// ConnectionArtifacts returns connection from pool.
|
||||
func (cp *neofsClientPlant) ConnectionArtifacts() (client.Client, *token.SessionToken, error) {
|
||||
return cp.pool.ConnectionArtifacts()
|
||||
}
|
||||
|
||||
// OwnerID returns plant's owner ID.
|
||||
func (cp *neofsClientPlant) OwnerID() *owner.ID {
|
||||
return cp.ownerID
|
||||
}
|
||||
|
||||
// NewClientPlant creates new ClientPlant from given context, pool and credentials.
|
||||
func NewClientPlant(ctx context.Context, pool pool.Pool, creds Credentials) (ClientPlant, error) {
|
||||
return &neofsClientPlant{key: creds.PrivateKey(), ownerID: creds.Owner(), pool: pool}, nil
|
||||
}
|
78
pkg/neofs/credentials.go
Normal file
78
pkg/neofs/credentials.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
package neofs
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
|
||||
crypto "github.com/nspcc-dev/neofs-crypto"
|
||||
)
|
||||
|
||||
type (
|
||||
// Credentials contains methods that needed to work with NeoFS.
|
||||
Credentials interface {
|
||||
Owner() *owner.ID
|
||||
PublicKey() *ecdsa.PublicKey
|
||||
PrivateKey() *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
credentials struct {
|
||||
key *ecdsa.PrivateKey
|
||||
ownerID *owner.ID
|
||||
}
|
||||
)
|
||||
|
||||
// NewCredentials creates an instance of Credentials through string
|
||||
// representation of secret. It allows passing WIF, path, hex-encoded and others.
|
||||
func NewCredentials(secret string) (Credentials, error) {
|
||||
key, err := crypto.LoadPrivateKey(secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return setFromPrivateKey(key)
|
||||
}
|
||||
|
||||
// NewEphemeralCredentials creates new private key and Credentials based on that
|
||||
// key.
|
||||
func NewEphemeralCredentials() (Credentials, error) {
|
||||
c := elliptic.P256()
|
||||
priv, x, y, err := elliptic.GenerateKey(c, rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := &ecdsa.PrivateKey{
|
||||
PublicKey: ecdsa.PublicKey{
|
||||
Curve: c,
|
||||
X: x,
|
||||
Y: y,
|
||||
},
|
||||
D: new(big.Int).SetBytes(priv),
|
||||
}
|
||||
return setFromPrivateKey(key)
|
||||
}
|
||||
|
||||
// PrivateKey returns ecdsa.PrivateKey.
|
||||
func (c *credentials) PrivateKey() *ecdsa.PrivateKey {
|
||||
return c.key
|
||||
}
|
||||
|
||||
// PublicKey returns ecdsa.PublicKey.
|
||||
func (c *credentials) PublicKey() *ecdsa.PublicKey {
|
||||
return &c.key.PublicKey
|
||||
}
|
||||
|
||||
// Owner returns owner.ID.
|
||||
func (c *credentials) Owner() *owner.ID {
|
||||
return c.ownerID
|
||||
}
|
||||
|
||||
func setFromPrivateKey(key *ecdsa.PrivateKey) (*credentials, error) {
|
||||
wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ownerID := owner.NewIDFromNeo3Wallet(wallet)
|
||||
return &credentials{key: key, ownerID: ownerID}, nil
|
||||
}
|
160
pkg/pool/pool.go
Normal file
160
pkg/pool/pool.go
Normal file
|
@ -0,0 +1,160 @@
|
|||
package pool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nspcc-dev/neofs-api-go/pkg/client"
|
||||
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
)
|
||||
|
||||
// BuilderOptions contains options used to build connection pool.
|
||||
type BuilderOptions struct {
|
||||
Key *ecdsa.PrivateKey
|
||||
NodeConnectionTimeout time.Duration
|
||||
NodeRequestTimeout time.Duration
|
||||
ClientRebalanceInterval time.Duration
|
||||
KeepaliveTime time.Duration
|
||||
KeepaliveTimeout time.Duration
|
||||
KeepalivePermitWoStream bool
|
||||
SessionExpirationEpoch uint64
|
||||
weights []float64
|
||||
connections []*grpc.ClientConn
|
||||
}
|
||||
|
||||
// Builder is an interim structure used to collect node addresses/weights and
|
||||
// build connection pool subsequently.
|
||||
type Builder struct {
|
||||
addresses []string
|
||||
weights []float64
|
||||
}
|
||||
|
||||
// AddNode adds address/weight pair to node PoolBuilder list.
|
||||
func (pb *Builder) AddNode(address string, weight float64) *Builder {
|
||||
pb.addresses = append(pb.addresses, address)
|
||||
pb.weights = append(pb.weights, weight)
|
||||
return pb
|
||||
}
|
||||
|
||||
// Build creates new pool based on current PoolBuilder state and options.
|
||||
func (pb *Builder) Build(ctx context.Context, options *BuilderOptions) (Pool, error) {
|
||||
if len(pb.addresses) == 0 {
|
||||
return nil, errors.New("no NeoFS peers configured")
|
||||
}
|
||||
totalWeight := 0.0
|
||||
for _, w := range pb.weights {
|
||||
totalWeight += w
|
||||
}
|
||||
for i, w := range pb.weights {
|
||||
pb.weights[i] = w / totalWeight
|
||||
}
|
||||
var cons = make([]*grpc.ClientConn, len(pb.addresses))
|
||||
for i, address := range pb.addresses {
|
||||
con, err := func() (*grpc.ClientConn, error) {
|
||||
toctx, c := context.WithTimeout(ctx, options.NodeConnectionTimeout)
|
||||
defer c()
|
||||
return grpc.DialContext(toctx, address,
|
||||
grpc.WithInsecure(),
|
||||
grpc.WithBlock(),
|
||||
grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||
Time: options.KeepaliveTime,
|
||||
Timeout: options.KeepaliveTimeout,
|
||||
PermitWithoutStream: options.KeepalivePermitWoStream,
|
||||
}),
|
||||
)
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cons[i] = con
|
||||
}
|
||||
options.weights = pb.weights
|
||||
options.connections = cons
|
||||
return new(ctx, options)
|
||||
}
|
||||
|
||||
// Pool is an interface providing connection artifacts on request.
|
||||
type Pool interface {
|
||||
ConnectionArtifacts() (client.Client, *token.SessionToken, error)
|
||||
}
|
||||
|
||||
type clientPack struct {
|
||||
client client.Client
|
||||
sessionToken *token.SessionToken
|
||||
healthy bool
|
||||
}
|
||||
|
||||
type pool struct {
|
||||
lock sync.RWMutex
|
||||
sampler *Sampler
|
||||
clientPacks []*clientPack
|
||||
}
|
||||
|
||||
func new(ctx context.Context, options *BuilderOptions) (Pool, error) {
|
||||
clientPacks := make([]*clientPack, len(options.weights))
|
||||
for i, con := range options.connections {
|
||||
c, err := client.New(client.WithDefaultPrivateKey(options.Key), client.WithGRPCConnection(con))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st, err := c.CreateSession(ctx, options.SessionExpirationEpoch)
|
||||
if err != nil {
|
||||
address := "unknown"
|
||||
if epi, err := c.EndpointInfo(ctx); err == nil {
|
||||
address = epi.NodeInfo().Address()
|
||||
}
|
||||
return nil, fmt.Errorf("failed to create neofs session token for client %s: %w", address, err)
|
||||
}
|
||||
clientPacks[i] = &clientPack{client: c, sessionToken: st, healthy: true}
|
||||
}
|
||||
source := rand.NewSource(time.Now().UnixNano())
|
||||
sampler := NewSampler(options.weights, source)
|
||||
pool := &pool{sampler: sampler, clientPacks: clientPacks}
|
||||
go func() {
|
||||
ticker := time.NewTimer(options.ClientRebalanceInterval)
|
||||
for range ticker.C {
|
||||
ok := true
|
||||
for i, clientPack := range pool.clientPacks {
|
||||
func() {
|
||||
tctx, c := context.WithTimeout(ctx, options.NodeRequestTimeout)
|
||||
defer c()
|
||||
if _, err := clientPack.client.EndpointInfo(tctx); err != nil {
|
||||
ok = false
|
||||
}
|
||||
pool.lock.Lock()
|
||||
pool.clientPacks[i].healthy = ok
|
||||
pool.lock.Unlock()
|
||||
}()
|
||||
}
|
||||
ticker.Reset(options.ClientRebalanceInterval)
|
||||
}
|
||||
}()
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
func (p *pool) ConnectionArtifacts() (client.Client, *token.SessionToken, error) {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
if len(p.clientPacks) == 1 {
|
||||
cp := p.clientPacks[0]
|
||||
if cp.healthy {
|
||||
return cp.client, cp.sessionToken, nil
|
||||
}
|
||||
return nil, nil, errors.New("no healthy client")
|
||||
}
|
||||
attempts := 3 * len(p.clientPacks)
|
||||
for k := 0; k < attempts; k++ {
|
||||
i := p.sampler.Next()
|
||||
if cp := p.clientPacks[i]; cp.healthy {
|
||||
return cp.client, cp.sessionToken, nil
|
||||
}
|
||||
}
|
||||
return nil, nil, errors.New("no healthy client")
|
||||
}
|
81
pkg/pool/sampler.go
Normal file
81
pkg/pool/sampler.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package pool
|
||||
|
||||
import "math/rand"
|
||||
|
||||
// Sampler implements weighted random number generation using Vose's Alias
|
||||
// Method (https://www.keithschwarz.com/darts-dice-coins/).
|
||||
type Sampler struct {
|
||||
randomGenerator *rand.Rand
|
||||
probabilities []float64
|
||||
alias []int
|
||||
}
|
||||
|
||||
// NewSampler creates new Sampler with a given set of probabilities using
|
||||
// given source of randomness. Created Sampler will produce numbers from
|
||||
// 0 to len(probabilities).
|
||||
func NewSampler(probabilities []float64, source rand.Source) *Sampler {
|
||||
sampler := &Sampler{}
|
||||
var (
|
||||
small workList
|
||||
large workList
|
||||
)
|
||||
n := len(probabilities)
|
||||
sampler.randomGenerator = rand.New(source)
|
||||
sampler.probabilities = make([]float64, n)
|
||||
sampler.alias = make([]int, n)
|
||||
// Compute scaled probabilities.
|
||||
p := make([]float64, n)
|
||||
for i := 0; i < n; i++ {
|
||||
p[i] = probabilities[i] * float64(n)
|
||||
}
|
||||
for i, pi := range p {
|
||||
if pi < 1 {
|
||||
small.add(i)
|
||||
} else {
|
||||
large.add(i)
|
||||
}
|
||||
}
|
||||
for len(small) > 0 && len(large) > 0 {
|
||||
l, g := small.remove(), large.remove()
|
||||
sampler.probabilities[l] = p[l]
|
||||
sampler.alias[l] = g
|
||||
p[g] = p[g] + p[l] - 1
|
||||
if p[g] < 1 {
|
||||
small.add(g)
|
||||
} else {
|
||||
large.add(g)
|
||||
}
|
||||
}
|
||||
for len(large) > 0 {
|
||||
g := large.remove()
|
||||
sampler.probabilities[g] = 1
|
||||
}
|
||||
for len(small) > 0 {
|
||||
l := small.remove()
|
||||
sampler.probabilities[l] = 1
|
||||
}
|
||||
return sampler
|
||||
}
|
||||
|
||||
// Next returns the next (not so) random number from Sampler.
|
||||
func (g *Sampler) Next() int {
|
||||
n := len(g.alias)
|
||||
i := g.randomGenerator.Intn(n)
|
||||
if g.randomGenerator.Float64() < g.probabilities[i] {
|
||||
return i
|
||||
}
|
||||
return g.alias[i]
|
||||
}
|
||||
|
||||
type workList []int
|
||||
|
||||
func (wl *workList) add(e int) {
|
||||
*wl = append(*wl, e)
|
||||
}
|
||||
|
||||
func (wl *workList) remove() int {
|
||||
l := len(*wl) - 1
|
||||
n := (*wl)[l]
|
||||
*wl = (*wl)[:l]
|
||||
return n
|
||||
}
|
Loading…
Reference in a new issue