sync/waitgroup: initial sync.WaitGroup wrapper with context support #319
@ -1,6 +1,5 @@
|
|||||||
run:
|
run:
|
||||||
concurrency: 4
|
concurrency: 4
|
||||||
deadline: 5m
|
|
||||||
issues-exit-code: 1
|
issues-exit-code: 1
|
||||||
tests: true
|
tests: true
|
||||||
|
|
||||||
@ -13,15 +12,13 @@ linters-settings:
|
|||||||
linters:
|
linters:
|
||||||
enable:
|
enable:
|
||||||
- govet
|
- govet
|
||||||
- deadcode
|
|
||||||
- errcheck
|
- errcheck
|
||||||
- govet
|
- govet
|
||||||
- ineffassign
|
- ineffassign
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- structcheck
|
|
||||||
- typecheck
|
- typecheck
|
||||||
- unused
|
- unused
|
||||||
- varcheck
|
- spancheck
|
||||||
- bodyclose
|
- bodyclose
|
||||||
- gci
|
- gci
|
||||||
- goconst
|
- goconst
|
||||||
@ -41,4 +38,5 @@ linters:
|
|||||||
- prealloc
|
- prealloc
|
||||||
- unconvert
|
- unconvert
|
||||||
- unparam
|
- unparam
|
||||||
|
- unused
|
||||||
disable-all: false
|
disable-all: false
|
||||||
|
@ -58,14 +58,14 @@ func NewOptions(opts ...options.Option) Options {
|
|||||||
type PublishOptions struct {
|
type PublishOptions struct {
|
||||||
// Context holds external options
|
// Context holds external options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
// BodyOnly flag says the message contains raw body bytes
|
|
||||||
BodyOnly bool
|
|
||||||
// Message metadata usually passed as message headers
|
// Message metadata usually passed as message headers
|
||||||
Metadata metadata.Metadata
|
Metadata metadata.Metadata
|
||||||
// Content-Type of message for marshal
|
// Content-Type of message for marshal
|
||||||
ContentType string
|
ContentType string
|
||||||
// Topic destination
|
// Topic destination
|
||||||
Topic string
|
Topic string
|
||||||
|
// BodyOnly flag says the message contains raw body bytes
|
||||||
|
BodyOnly bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPublishOptions creates PublishOptions struct
|
// NewPublishOptions creates PublishOptions struct
|
||||||
|
@ -19,8 +19,8 @@ var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
|||||||
|
|
||||||
// Is this an exported - upper case - name?
|
// Is this an exported - upper case - name?
|
||||||
func isExported(name string) bool {
|
func isExported(name string) bool {
|
||||||
rune, _ := utf8.DecodeRuneInString(name)
|
r, _ := utf8.DecodeRuneInString(name)
|
||||||
return unicode.IsUpper(rune)
|
return unicode.IsUpper(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is this type exported or a builtin?
|
// Is this type exported or a builtin?
|
||||||
|
122
database/dsn.go
122
database/dsn.go
@ -75,78 +75,80 @@ func ParseDSN(dsn string) (*Config, error) {
|
|||||||
// Find last '/' that goes before dbname
|
// Find last '/' that goes before dbname
|
||||||
foundSlash := false
|
foundSlash := false
|
||||||
for i := len(dsn) - 1; i >= 0; i-- {
|
for i := len(dsn) - 1; i >= 0; i-- {
|
||||||
if dsn[i] == '/' {
|
if dsn[i] != '/' {
|
||||||
foundSlash = true
|
continue
|
||||||
var j, k int
|
}
|
||||||
|
|
||||||
// left part is empty if i <= 0
|
foundSlash = true
|
||||||
if i > 0 {
|
var j, k int
|
||||||
// Find the first ':' in dsn
|
|
||||||
for j = i; j >= 0; j-- {
|
// left part is empty if i <= 0
|
||||||
if dsn[j] == ':' {
|
if i > 0 {
|
||||||
cfg.Scheme = dsn[0:j]
|
// Find the first ':' in dsn
|
||||||
}
|
for j = i; j >= 0; j-- {
|
||||||
|
if dsn[j] == ':' {
|
||||||
|
cfg.Scheme = dsn[0:j]
|
||||||
}
|
}
|
||||||
|
|
||||||
// [username[:password]@][host]
|
|
||||||
// Find the last '@' in dsn[:i]
|
|
||||||
for j = i; j >= 0; j-- {
|
|
||||||
if dsn[j] == '@' {
|
|
||||||
// username[:password]
|
|
||||||
// Find the second ':' in dsn[:j]
|
|
||||||
for k = 0; k < j; k++ {
|
|
||||||
if dsn[k] == ':' {
|
|
||||||
if cfg.Scheme == dsn[:k] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
cfg.Password, err = url.PathUnescape(dsn[k+1 : j])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cfg.Username = dsn[len(cfg.Scheme)+3 : k]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k = j + 1; k < i; k++ {
|
|
||||||
if dsn[k] == ':' {
|
|
||||||
cfg.Host = dsn[j+1 : k]
|
|
||||||
cfg.Port = dsn[k+1 : i]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// dbname[?param1=value1&...¶mN=valueN]
|
// [username[:password]@][host]
|
||||||
// Find the first '?' in dsn[i+1:]
|
// Find the last '@' in dsn[:i]
|
||||||
for j = i + 1; j < len(dsn); j++ {
|
for j = i; j >= 0; j-- {
|
||||||
if dsn[j] == '?' {
|
if dsn[j] == '@' {
|
||||||
parts := strings.Split(dsn[j+1:], "&")
|
// username[:password]
|
||||||
cfg.Params = make([]string, 0, len(parts)*2)
|
// Find the second ':' in dsn[:j]
|
||||||
for _, p := range parts {
|
for k = 0; k < j; k++ {
|
||||||
k, v, found := strings.Cut(p, "=")
|
if dsn[k] == ':' {
|
||||||
if !found {
|
if cfg.Scheme == dsn[:k] {
|
||||||
continue
|
continue
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
cfg.Password, err = url.PathUnescape(dsn[k+1 : j])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
cfg.Params = append(cfg.Params, k, v)
|
|
||||||
}
|
}
|
||||||
|
cfg.Username = dsn[len(cfg.Scheme)+3 : k]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var err error
|
|
||||||
dbname := dsn[i+1 : j]
|
for k = j + 1; k < i; k++ {
|
||||||
if cfg.Database, err = url.PathUnescape(dbname); err != nil {
|
if dsn[k] == ':' {
|
||||||
return nil, fmt.Errorf("invalid dbname %q: %w", dbname, err)
|
cfg.Host = dsn[j+1 : k]
|
||||||
|
cfg.Port = dsn[k+1 : i]
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dbname[?param1=value1&...¶mN=valueN]
|
||||||
|
// Find the first '?' in dsn[i+1:]
|
||||||
|
for j = i + 1; j < len(dsn); j++ {
|
||||||
|
if dsn[j] == '?' {
|
||||||
|
parts := strings.Split(dsn[j+1:], "&")
|
||||||
|
cfg.Params = make([]string, 0, len(parts)*2)
|
||||||
|
for _, p := range parts {
|
||||||
|
k, v, found := strings.Cut(p, "=")
|
||||||
|
if !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cfg.Params = append(cfg.Params, k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
dbname := dsn[i+1 : j]
|
||||||
|
if cfg.Database, err = url.PathUnescape(dbname); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid dbname %q: %w", dbname, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if !foundSlash && len(dsn) > 0 {
|
if !foundSlash && len(dsn) > 0 {
|
||||||
|
13
go.mod
13
go.mod
@ -5,17 +5,16 @@ go 1.20
|
|||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0
|
dario.cat/mergo v1.0.0
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||||
github.com/google/uuid v1.3.1
|
github.com/google/uuid v1.6.0
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5
|
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5
|
||||||
golang.org/x/sync v0.3.0
|
golang.org/x/sync v0.6.0
|
||||||
golang.org/x/sys v0.12.0
|
golang.org/x/sys v0.16.0
|
||||||
google.golang.org/grpc v1.58.2
|
google.golang.org/grpc v1.62.1
|
||||||
google.golang.org/protobuf v1.31.0
|
google.golang.org/protobuf v1.32.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
golang.org/x/net v0.15.0 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
|
||||||
)
|
)
|
||||||
|
31
go.sum
31
go.sum
@ -6,29 +6,28 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
|
|||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 h1:G/FZtUu7a6NTWl3KUHMV9jkLAh/Rvtf03NWMHaEDl+E=
|
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 h1:G/FZtUu7a6NTWl3KUHMV9jkLAh/Rvtf03NWMHaEDl+E=
|
||||||
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
||||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
|
||||||
google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
|
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||||
google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
@ -18,18 +18,12 @@ type Options struct {
|
|||||||
Out io.Writer
|
Out io.Writer
|
||||||
// Context holds exernal options
|
// Context holds exernal options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
// Attrs holds additional attributes
|
// TimeFunc used to obtain current time
|
||||||
Attrs []interface{}
|
TimeFunc func() time.Time
|
||||||
// Name holds the logger name
|
|
||||||
Name string
|
|
||||||
// The logging level the logger should log
|
|
||||||
Level Level
|
|
||||||
// CallerSkipCount number of frmaes to skip
|
|
||||||
CallerSkipCount int
|
|
||||||
// ContextAttrFuncs contains funcs that executed before log func on context
|
|
||||||
ContextAttrFuncs []ContextAttrFunc
|
|
||||||
// TimeKey is the key used for the time of the log call
|
// TimeKey is the key used for the time of the log call
|
||||||
TimeKey string
|
TimeKey string
|
||||||
|
// Name holds the logger name
|
||||||
|
Name string
|
||||||
// LevelKey is the key used for the level of the log call
|
// LevelKey is the key used for the level of the log call
|
||||||
LevelKey string
|
LevelKey string
|
||||||
// MessageKey is the key used for the message of the log call
|
// MessageKey is the key used for the message of the log call
|
||||||
@ -40,12 +34,18 @@ type Options struct {
|
|||||||
SourceKey string
|
SourceKey string
|
||||||
// StacktraceKey is the key used for the stacktrace
|
// StacktraceKey is the key used for the stacktrace
|
||||||
StacktraceKey string
|
StacktraceKey string
|
||||||
|
// Attrs holds additional attributes
|
||||||
|
Attrs []interface{}
|
||||||
|
// ContextAttrFuncs contains funcs that executed before log func on context
|
||||||
|
ContextAttrFuncs []ContextAttrFunc
|
||||||
|
// CallerSkipCount number of frmaes to skip
|
||||||
|
CallerSkipCount int
|
||||||
|
// The logging level the logger should log
|
||||||
|
Level Level
|
||||||
// AddStacktrace controls writing of stacktaces on error
|
// AddStacktrace controls writing of stacktaces on error
|
||||||
AddStacktrace bool
|
AddStacktrace bool
|
||||||
// AddSource enabled writing source file and position in log
|
// AddSource enabled writing source file and position in log
|
||||||
AddSource bool
|
AddSource bool
|
||||||
// TimeFunc used to obtain current time
|
|
||||||
TimeFunc func() time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions creates new options struct
|
// NewOptions creates new options struct
|
||||||
|
@ -56,9 +56,9 @@ type Wrapper struct {
|
|||||||
s fmt.State
|
s fmt.State
|
||||||
pointers map[uintptr]int
|
pointers map[uintptr]int
|
||||||
opts *Options
|
opts *Options
|
||||||
|
takeMap map[int]bool
|
||||||
depth int
|
depth int
|
||||||
ignoreNextType bool
|
ignoreNextType bool
|
||||||
takeMap map[int]bool
|
|
||||||
protoWrapperType bool
|
protoWrapperType bool
|
||||||
sqlWrapperType bool
|
sqlWrapperType bool
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,24 @@ func testOutgoingCtx(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPassing(t *testing.T) {
|
func TestIncoming(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
md1 := New(2)
|
||||||
|
md1.Set("Key1", "Val1")
|
||||||
|
md1.Set("Key2", "Val2")
|
||||||
|
|
||||||
|
ctx = NewIncomingContext(ctx, md1)
|
||||||
|
testIncomingCtx(ctx)
|
||||||
|
md, ok := FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("missing metadata from incoming context")
|
||||||
|
}
|
||||||
|
if v, ok := md.Get("Key1"); !ok || v != "Val1_new" {
|
||||||
|
t.Fatalf("invalid metadata value %#+v", md)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOutgoing(t *testing.T) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
md1 := New(2)
|
md1 := New(2)
|
||||||
md1.Set("Key1", "Val1")
|
md1.Set("Key1", "Val1")
|
||||||
|
36
micro.go
36
micro.go
@ -65,6 +65,8 @@ func As(b any, target any) bool {
|
|||||||
break
|
break
|
||||||
case targetType.Implements(routerType):
|
case targetType.Implements(routerType):
|
||||||
break
|
break
|
||||||
|
case targetType.Implements(tracerType):
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -76,19 +78,21 @@ func As(b any, target any) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var brokerType = reflect.TypeOf((*broker.Broker)(nil)).Elem()
|
var (
|
||||||
var loggerType = reflect.TypeOf((*logger.Logger)(nil)).Elem()
|
brokerType = reflect.TypeOf((*broker.Broker)(nil)).Elem()
|
||||||
var clientType = reflect.TypeOf((*client.Client)(nil)).Elem()
|
loggerType = reflect.TypeOf((*logger.Logger)(nil)).Elem()
|
||||||
var serverType = reflect.TypeOf((*server.Server)(nil)).Elem()
|
clientType = reflect.TypeOf((*client.Client)(nil)).Elem()
|
||||||
var codecType = reflect.TypeOf((*codec.Codec)(nil)).Elem()
|
serverType = reflect.TypeOf((*server.Server)(nil)).Elem()
|
||||||
var flowType = reflect.TypeOf((*flow.Flow)(nil)).Elem()
|
codecType = reflect.TypeOf((*codec.Codec)(nil)).Elem()
|
||||||
var fsmType = reflect.TypeOf((*fsm.FSM)(nil)).Elem()
|
flowType = reflect.TypeOf((*flow.Flow)(nil)).Elem()
|
||||||
var meterType = reflect.TypeOf((*meter.Meter)(nil)).Elem()
|
fsmType = reflect.TypeOf((*fsm.FSM)(nil)).Elem()
|
||||||
var registerType = reflect.TypeOf((*register.Register)(nil)).Elem()
|
meterType = reflect.TypeOf((*meter.Meter)(nil)).Elem()
|
||||||
var resolverType = reflect.TypeOf((*resolver.Resolver)(nil)).Elem()
|
registerType = reflect.TypeOf((*register.Register)(nil)).Elem()
|
||||||
var routerType = reflect.TypeOf((*router.Router)(nil)).Elem()
|
resolverType = reflect.TypeOf((*resolver.Resolver)(nil)).Elem()
|
||||||
var selectorType = reflect.TypeOf((*selector.Selector)(nil)).Elem()
|
routerType = reflect.TypeOf((*router.Router)(nil)).Elem()
|
||||||
var storeType = reflect.TypeOf((*store.Store)(nil)).Elem()
|
selectorType = reflect.TypeOf((*selector.Selector)(nil)).Elem()
|
||||||
var syncType = reflect.TypeOf((*sync.Sync)(nil)).Elem()
|
storeType = reflect.TypeOf((*store.Store)(nil)).Elem()
|
||||||
var tracerType = reflect.TypeOf((*tracer.Tracer)(nil)).Elem()
|
syncType = reflect.TypeOf((*sync.Sync)(nil)).Elem()
|
||||||
var serviceType = reflect.TypeOf((*Service)(nil)).Elem()
|
tracerType = reflect.TypeOf((*tracer.Tracer)(nil)).Elem()
|
||||||
|
serviceType = reflect.TypeOf((*Service)(nil)).Elem()
|
||||||
|
)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package options_test
|
package options_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"go.unistack.org/micro/v4/codec"
|
"go.unistack.org/micro/v4/codec"
|
||||||
@ -132,7 +131,6 @@ func TestMetadataAny(t *testing.T) {
|
|||||||
var opts []options.Option
|
var opts []options.Option
|
||||||
switch valData := tt.Data.(type) {
|
switch valData := tt.Data.(type) {
|
||||||
case []any:
|
case []any:
|
||||||
fmt.Printf("%s any %#+v\n", tt.Name, valData)
|
|
||||||
opts = append(opts, options.Metadata(valData...))
|
opts = append(opts, options.Metadata(valData...))
|
||||||
case map[string]string, map[string][]string, metadata.Metadata:
|
case map[string]string, map[string][]string, metadata.Metadata:
|
||||||
opts = append(opts, options.Metadata(valData))
|
opts = append(opts, options.Metadata(valData))
|
||||||
|
@ -32,10 +32,10 @@ type record struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type memory struct {
|
type memory struct {
|
||||||
sync.RWMutex
|
|
||||||
records map[string]services
|
records map[string]services
|
||||||
watchers map[string]*watcher
|
watchers map[string]*watcher
|
||||||
opts register.Options
|
opts register.Options
|
||||||
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// services is a KV map with service name as the key and a map of records as the value
|
// services is a KV map with service name as the key and a map of records as the value
|
||||||
@ -102,10 +102,20 @@ func (m *memory) sendEvent(r *register.Result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *memory) Connect(ctx context.Context) error {
|
func (m *memory) Connect(ctx context.Context) error {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memory) Disconnect(ctx context.Context) error {
|
func (m *memory) Disconnect(ctx context.Context) error {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +136,11 @@ func (m *memory) Options() register.Options {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *memory) Register(ctx context.Context, s *register.Service, opts ...register.RegisterOption) error {
|
func (m *memory) Register(ctx context.Context, s *register.Service, opts ...register.RegisterOption) error {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
@ -467,9 +482,7 @@ func serviceToRecord(s *register.Service, ttl time.Duration) *record {
|
|||||||
}
|
}
|
||||||
|
|
||||||
endpoints := make([]*register.Endpoint, len(s.Endpoints))
|
endpoints := make([]*register.Endpoint, len(s.Endpoints))
|
||||||
for i, e := range s.Endpoints { // TODO: vtolstov use copy
|
copy(endpoints, s.Endpoints)
|
||||||
endpoints[i] = e
|
|
||||||
}
|
|
||||||
|
|
||||||
return &record{
|
return &record{
|
||||||
Name: s.Name,
|
Name: s.Name,
|
||||||
|
@ -3,12 +3,13 @@ package memory
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go.unistack.org/micro/v4"
|
|
||||||
"go.unistack.org/micro/v4/register"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v4"
|
||||||
|
"go.unistack.org/micro/v4/register"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testData = map[string][]*register.Service{
|
var testData = map[string][]*register.Service{
|
||||||
@ -209,9 +210,9 @@ func TestMemoryRegistryTTLConcurrent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
|
// if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
|
||||||
// t.Logf("test will wait %v, then check TTL timeouts", waitTime)
|
// t.Logf("test will wait %v, then check TTL timeouts", waitTime)
|
||||||
//}
|
// }
|
||||||
|
|
||||||
errChan := make(chan error, concurrency)
|
errChan := make(chan error, concurrency)
|
||||||
syncChan := make(chan struct{})
|
syncChan := make(chan struct{})
|
||||||
@ -252,6 +253,13 @@ func TestMemoryWildcard(t *testing.T) {
|
|||||||
m := NewRegister()
|
m := NewRegister()
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
if err := m.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.Connect(ctx); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
testSrv := ®ister.Service{Name: "foo", Version: "1.0.0"}
|
testSrv := ®ister.Service{Name: "foo", Version: "1.0.0"}
|
||||||
|
|
||||||
if err := m.Register(ctx, testSrv, register.RegisterDomain("one")); err != nil {
|
if err := m.Register(ctx, testSrv, register.RegisterDomain("one")); err != nil {
|
||||||
@ -291,8 +299,12 @@ func TestWatcher(t *testing.T) {
|
|||||||
|
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
m := NewRegister()
|
m := NewRegister()
|
||||||
m.Init()
|
if err := m.Init(); err != nil {
|
||||||
m.Connect(ctx)
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := m.Connect(ctx); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
wc, err := m.Watch(ctx)
|
wc, err := m.Watch(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("cant watch: %v", err)
|
t.Fatalf("cant watch: %v", err)
|
||||||
|
69
sync/waitgroup.go
Normal file
69
sync/waitgroup.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WaitGroup struct {
|
||||||
|
wg *sync.WaitGroup
|
||||||
|
c int
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func WrapWaitGroup(wg *sync.WaitGroup) *WaitGroup {
|
||||||
|
g := &WaitGroup{
|
||||||
|
wg: wg,
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWaitGroup() *WaitGroup {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
return WrapWaitGroup(&wg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *WaitGroup) Add(n int) {
|
||||||
|
g.mu.Lock()
|
||||||
|
g.c += n
|
||||||
|
g.wg.Add(n)
|
||||||
|
g.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *WaitGroup) Done() {
|
||||||
|
g.mu.Lock()
|
||||||
|
g.c += -1
|
||||||
|
g.wg.Add(-1)
|
||||||
|
g.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *WaitGroup) Wait() {
|
||||||
|
g.wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *WaitGroup) WaitContext(ctx context.Context) {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
g.wg.Wait()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
g.mu.Lock()
|
||||||
|
g.wg.Add(-g.c)
|
||||||
|
<-done
|
||||||
|
g.wg.Add(g.c)
|
||||||
|
g.mu.Unlock()
|
||||||
|
return
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *WaitGroup) Waiters() int {
|
||||||
|
g.mu.Lock()
|
||||||
|
c := g.c
|
||||||
|
g.mu.Unlock()
|
||||||
|
return c
|
||||||
|
}
|
37
sync/waitgroup_test.go
Normal file
37
sync/waitgroup_test.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWaitGroupContext(t *testing.T) {
|
||||||
|
wg := NewWaitGroup()
|
||||||
|
_ = t
|
||||||
|
wg.Add(1)
|
||||||
|
ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
wg.WaitContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWaitGroupReuse(t *testing.T) {
|
||||||
|
wg := NewWaitGroup()
|
||||||
|
defer func() {
|
||||||
|
if wg.Waiters() != 0 {
|
||||||
|
t.Fatal("lost goroutines")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
defer wg.Done()
|
||||||
|
ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
wg.WaitContext(ctx)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
defer wg.Done()
|
||||||
|
ctx, cancel = context.WithTimeout(context.TODO(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
wg.WaitContext(ctx)
|
||||||
|
}
|
@ -109,12 +109,11 @@ func Merge(olist []*register.Service, nlist []*register.Service) []*register.Ser
|
|||||||
seen = true
|
seen = true
|
||||||
srv = append(srv, sp)
|
srv = append(srv, sp)
|
||||||
break
|
break
|
||||||
} else {
|
|
||||||
sp := ®ister.Service{}
|
|
||||||
// make copy
|
|
||||||
*sp = *o
|
|
||||||
srv = append(srv, sp)
|
|
||||||
}
|
}
|
||||||
|
sp := ®ister.Service{}
|
||||||
|
// make copy
|
||||||
|
*sp = *o
|
||||||
|
srv = append(srv, sp)
|
||||||
}
|
}
|
||||||
if !seen {
|
if !seen {
|
||||||
srv = append(srv, Copy([]*register.Service{n})...)
|
srv = append(srv, Copy([]*register.Service{n})...)
|
||||||
|
@ -12,7 +12,7 @@ type EC2Metadata struct {
|
|||||||
InstanceType string `json:"instance-type"`
|
InstanceType string `json:"instance-type"`
|
||||||
LocalHostname string `json:"local-hostname"`
|
LocalHostname string `json:"local-hostname"`
|
||||||
LocalIPv4 string `json:"local-ipv4"`
|
LocalIPv4 string `json:"local-ipv4"`
|
||||||
kernelID int `json:"kernel-id"`
|
KernelID int `json:"kernel-id"`
|
||||||
Placement string `json:"placement"`
|
Placement string `json:"placement"`
|
||||||
AvailabilityZone string `json:"availability-zone"`
|
AvailabilityZone string `json:"availability-zone"`
|
||||||
ProductCodes string `json:"product-codes"`
|
ProductCodes string `json:"product-codes"`
|
||||||
|
@ -67,9 +67,9 @@ func (fi *fileInfo) Name() string {
|
|||||||
|
|
||||||
func (fi *fileInfo) Mode() os.FileMode {
|
func (fi *fileInfo) Mode() os.FileMode {
|
||||||
if strings.HasSuffix(fi.name, "/") {
|
if strings.HasSuffix(fi.name, "/") {
|
||||||
return os.FileMode(0755) | os.ModeDir
|
return os.FileMode(0o755) | os.ModeDir
|
||||||
}
|
}
|
||||||
return os.FileMode(0644)
|
return os.FileMode(0o644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fi *fileInfo) IsDir() bool {
|
func (fi *fileInfo) IsDir() bool {
|
||||||
@ -112,15 +112,14 @@ func (f *file) Readdir(count int) ([]os.FileInfo, error) {
|
|||||||
func (f *file) Seek(offset int64, whence int) (int64, error) {
|
func (f *file) Seek(offset int64, whence int) (int64, error) {
|
||||||
// log.Printf("seek %d %d %s\n", offset, whence, f.name)
|
// log.Printf("seek %d %d %s\n", offset, whence, f.name)
|
||||||
switch whence {
|
switch whence {
|
||||||
case os.SEEK_SET:
|
case io.SeekStart:
|
||||||
f.offset = offset
|
f.offset = offset
|
||||||
case os.SEEK_CUR:
|
case io.SeekCurrent:
|
||||||
f.offset += offset
|
f.offset += offset
|
||||||
case os.SEEK_END:
|
case io.SeekEnd:
|
||||||
f.offset = int64(len(f.data)) + offset
|
f.offset = int64(len(f.data)) + offset
|
||||||
}
|
}
|
||||||
return f.offset, nil
|
return f.offset, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *file) Stat() (os.FileInfo, error) {
|
func (f *file) Stat() (os.FileInfo, error) {
|
||||||
|
@ -2,7 +2,7 @@ package structfs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
@ -82,7 +82,7 @@ func get(path string) ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
return ioutil.ReadAll(res.Body)
|
return io.ReadAll(res.Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAll(t *testing.T) {
|
func TestAll(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user