Compare commits

...

11 Commits

Author SHA1 Message Date
9ee31fb5a6 fixup compile
Some checks failed
coverage / build (push) Has been cancelled
test / test (push) Has been cancelled
sync / sync (push) Successful in 7s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-29 12:46:23 +03:00
ed5d30a58e store/noop: fixup Exists
Some checks failed
coverage / build (push) Has been cancelled
test / test (push) Has been cancelled
sync / sync (push) Has been cancelled
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-29 12:43:39 +03:00
vtolstov
b4b67a8b41 Apply Code Coverage Badge 2025-05-25 02:41:23 +00:00
13f90ff716 changed embedded mutex to private field (#217)
Some checks failed
sync / sync (push) Failing after 16m12s
test / test (push) Failing after 17m28s
coverage / build (push) Failing after 17m40s
2025-05-25 01:15:03 +03:00
0f8f12aee0 add tracer enabled status
Some checks failed
coverage / build (push) Successful in 2m52s
test / test (push) Failing after 18m53s
sync / sync (push) Successful in 26s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-19 09:33:01 +03:00
8b406cf963 util/buffer: add Reset() method
Some checks failed
coverage / build (push) Failing after 1m36s
test / test (push) Successful in 3m35s
sync / sync (push) Successful in 7s
closes #402

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-12 19:18:45 +03:00
029a434a2b broker: pass broker content type if message options not pass it
All checks were successful
coverage / build (push) Successful in 1m44s
test / test (push) Successful in 3m5s
sync / sync (push) Successful in 7s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-09 13:51:35 +03:00
vtolstov
847259bc39 Apply Code Coverage Badge 2025-05-09 09:36:02 +00:00
a1ee8728ad broker: add Content-Type and DefaultContentType
All checks were successful
coverage / build (push) Successful in 1m58s
sync / sync (push) Successful in 1m37s
test / test (push) Successful in 3m47s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-09 12:34:46 +03:00
88a5875cfb switch yaml package to maintained one
All checks were successful
coverage / build (push) Successful in 2m16s
test / test (push) Successful in 4m37s
sync / sync (push) Successful in 8s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-09 12:18:49 +03:00
03ee33040c util/id: switch to default uuid package
All checks were successful
coverage / build (push) Successful in 2m21s
test / test (push) Successful in 4m50s
sync / sync (push) Successful in 6s
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2025-05-08 19:07:00 +03:00
27 changed files with 227 additions and 188 deletions

View File

@@ -22,8 +22,8 @@ type Broker struct {
subscribers map[string][]*Subscriber subscribers map[string][]*Subscriber
addr string addr string
opts broker.Options opts broker.Options
sync.RWMutex mu sync.RWMutex
connected bool connected bool
} }
type memoryMessage struct { type memoryMessage struct {
@@ -72,9 +72,9 @@ func (b *Broker) newCodec(ct string) (codec.Codec, error) {
if idx := strings.IndexRune(ct, ';'); idx >= 0 { if idx := strings.IndexRune(ct, ';'); idx >= 0 {
ct = ct[:idx] ct = ct[:idx]
} }
b.RLock() b.mu.RLock()
c, ok := b.opts.Codecs[ct] c, ok := b.opts.Codecs[ct]
b.RUnlock() b.mu.RUnlock()
if ok { if ok {
return c, nil return c, nil
} }
@@ -96,8 +96,8 @@ func (b *Broker) Connect(ctx context.Context) error {
default: default:
} }
b.Lock() b.mu.Lock()
defer b.Unlock() defer b.mu.Unlock()
if b.connected { if b.connected {
return nil return nil
@@ -126,8 +126,8 @@ func (b *Broker) Disconnect(ctx context.Context) error {
default: default:
} }
b.Lock() b.mu.Lock()
defer b.Unlock() defer b.mu.Unlock()
if !b.connected { if !b.connected {
return nil return nil
@@ -159,6 +159,9 @@ func (b *Broker) Init(opts ...broker.Option) error {
func (b *Broker) NewMessage(ctx context.Context, hdr metadata.Metadata, body interface{}, opts ...broker.PublishOption) (broker.Message, error) { func (b *Broker) NewMessage(ctx context.Context, hdr metadata.Metadata, body interface{}, opts ...broker.PublishOption) (broker.Message, error) {
options := broker.NewPublishOptions(opts...) options := broker.NewPublishOptions(opts...)
if options.ContentType == "" {
options.ContentType = b.opts.ContentType
}
m := &memoryMessage{ctx: ctx, hdr: hdr, opts: options} m := &memoryMessage{ctx: ctx, hdr: hdr, opts: options}
c, err := b.newCodec(m.opts.ContentType) c, err := b.newCodec(m.opts.ContentType)
if err == nil { if err == nil {
@@ -180,12 +183,12 @@ func (b *Broker) fnPublish(ctx context.Context, topic string, messages ...broker
} }
func (b *Broker) publish(ctx context.Context, topic string, messages ...broker.Message) error { func (b *Broker) publish(ctx context.Context, topic string, messages ...broker.Message) error {
b.RLock() b.mu.RLock()
if !b.connected { if !b.connected {
b.RUnlock() b.mu.RUnlock()
return broker.ErrNotConnected return broker.ErrNotConnected
} }
b.RUnlock() b.mu.RUnlock()
select { select {
case <-ctx.Done(): case <-ctx.Done():
@@ -193,9 +196,9 @@ func (b *Broker) publish(ctx context.Context, topic string, messages ...broker.M
default: default:
} }
b.RLock() b.mu.RLock()
subs, ok := b.subscribers[topic] subs, ok := b.subscribers[topic]
b.RUnlock() b.mu.RUnlock()
if !ok { if !ok {
return nil return nil
} }
@@ -252,12 +255,12 @@ func (b *Broker) fnSubscribe(ctx context.Context, topic string, handler interfac
return nil, err return nil, err
} }
b.RLock() b.mu.RLock()
if !b.connected { if !b.connected {
b.RUnlock() b.mu.RUnlock()
return nil, broker.ErrNotConnected return nil, broker.ErrNotConnected
} }
b.RUnlock() b.mu.RUnlock()
sid, err := id.New() sid, err := id.New()
if err != nil { if err != nil {
@@ -275,13 +278,13 @@ func (b *Broker) fnSubscribe(ctx context.Context, topic string, handler interfac
ctx: ctx, ctx: ctx,
} }
b.Lock() b.mu.Lock()
b.subscribers[topic] = append(b.subscribers[topic], sub) b.subscribers[topic] = append(b.subscribers[topic], sub)
b.Unlock() b.mu.Unlock()
go func() { go func() {
<-sub.exit <-sub.exit
b.Lock() b.mu.Lock()
newSubscribers := make([]*Subscriber, 0, len(b.subscribers)-1) newSubscribers := make([]*Subscriber, 0, len(b.subscribers)-1)
for _, sb := range b.subscribers[topic] { for _, sb := range b.subscribers[topic] {
if sb.id == sub.id { if sb.id == sub.id {
@@ -290,7 +293,7 @@ func (b *Broker) fnSubscribe(ctx context.Context, topic string, handler interfac
newSubscribers = append(newSubscribers, sb) newSubscribers = append(newSubscribers, sb)
} }
b.subscribers[topic] = newSubscribers b.subscribers[topic] = newSubscribers
b.Unlock() b.mu.Unlock()
}() }()
return sub, nil return sub, nil

View File

@@ -14,16 +14,16 @@ type NoopBroker struct {
funcPublish FuncPublish funcPublish FuncPublish
funcSubscribe FuncSubscribe funcSubscribe FuncSubscribe
opts Options opts Options
sync.RWMutex mu sync.RWMutex
} }
func (b *NoopBroker) newCodec(ct string) (codec.Codec, error) { func (b *NoopBroker) newCodec(ct string) (codec.Codec, error) {
if idx := strings.IndexRune(ct, ';'); idx >= 0 { if idx := strings.IndexRune(ct, ';'); idx >= 0 {
ct = ct[:idx] ct = ct[:idx]
} }
b.RLock() b.mu.RLock()
c, ok := b.opts.Codecs[ct] c, ok := b.opts.Codecs[ct]
b.RUnlock() b.mu.RUnlock()
if ok { if ok {
return c, nil return c, nil
} }
@@ -128,6 +128,9 @@ func (m *noopMessage) Unmarshal(dst interface{}, opts ...codec.Option) error {
func (b *NoopBroker) NewMessage(ctx context.Context, hdr metadata.Metadata, body interface{}, opts ...PublishOption) (Message, error) { func (b *NoopBroker) NewMessage(ctx context.Context, hdr metadata.Metadata, body interface{}, opts ...PublishOption) (Message, error) {
options := NewPublishOptions(opts...) options := NewPublishOptions(opts...)
if options.ContentType == "" {
options.ContentType = b.opts.ContentType
}
m := &noopMessage{ctx: ctx, hdr: hdr, opts: options} m := &noopMessage{ctx: ctx, hdr: hdr, opts: options}
c, err := b.newCodec(m.opts.ContentType) c, err := b.newCodec(m.opts.ContentType)
if err == nil { if err == nil {

View File

@@ -45,6 +45,9 @@ type Options struct {
// GracefulTimeout contains time to wait to finish in flight requests // GracefulTimeout contains time to wait to finish in flight requests
GracefulTimeout time.Duration GracefulTimeout time.Duration
// ContentType will be used if no content-type set when creating message
ContentType string
} }
// NewOptions create new Options // NewOptions create new Options
@@ -57,14 +60,19 @@ func NewOptions(opts ...Option) Options {
Codecs: make(map[string]codec.Codec), Codecs: make(map[string]codec.Codec),
Tracer: tracer.DefaultTracer, Tracer: tracer.DefaultTracer,
GracefulTimeout: DefaultGracefulTimeout, GracefulTimeout: DefaultGracefulTimeout,
ContentType: DefaultContentType,
} }
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
} }
return options return options
} }
// DefaultContentType is the default content-type if not specified
var DefaultContentType = ""
// Context sets the context option // Context sets the context option
func Context(ctx context.Context) Option { func Context(ctx context.Context) Option {
return func(o *Options) { return func(o *Options) {
@@ -72,6 +80,13 @@ func Context(ctx context.Context) Option {
} }
} }
// ContentType used by default if not specified
func ContentType(ct string) Option {
return func(o *Options) {
o.ContentType = ct
}
}
// PublishOptions struct // PublishOptions struct
type PublishOptions struct { type PublishOptions struct {
// ContentType for message body // ContentType for message body

View File

@@ -3,8 +3,6 @@ package codec
import ( import (
"errors" "errors"
"gopkg.in/yaml.v3"
) )
var ( var (
@@ -68,10 +66,10 @@ func (m *RawMessage) MarshalYAML() ([]byte, error) {
} }
// UnmarshalYAML sets *m to a copy of data. // UnmarshalYAML sets *m to a copy of data.
func (m *RawMessage) UnmarshalYAML(n *yaml.Node) error { func (m *RawMessage) UnmarshalYAML(data []byte) error {
if m == nil { if m == nil {
return errors.New("RawMessage UnmarshalYAML on nil pointer") return errors.New("RawMessage UnmarshalYAML on nil pointer")
} }
*m = append((*m)[0:0], []byte(n.Value)...) *m = append((*m)[0:0], data...)
return nil return nil
} }

View File

@@ -1,7 +1,5 @@
package codec package codec
import "gopkg.in/yaml.v3"
// Frame gives us the ability to define raw data to send over the pipes // Frame gives us the ability to define raw data to send over the pipes
type Frame struct { type Frame struct {
Data []byte Data []byte
@@ -28,8 +26,8 @@ func (m *Frame) MarshalYAML() ([]byte, error) {
} }
// UnmarshalYAML set frame data // UnmarshalYAML set frame data
func (m *Frame) UnmarshalYAML(n *yaml.Node) error { func (m *Frame) UnmarshalYAML(data []byte) error {
m.Data = []byte(n.Value) m.Data = append((m.Data)[0:0], data...)
return nil return nil
} }

4
go.mod
View File

@@ -6,7 +6,7 @@ require (
dario.cat/mergo v1.0.1 dario.cat/mergo v1.0.1
github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/KimMachineGun/automemlimit v0.7.0 github.com/KimMachineGun/automemlimit v0.7.0
github.com/ash3in/uuidv8 v1.2.0 github.com/goccy/go-yaml v1.17.1
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/matoous/go-nanoid v1.5.1 github.com/matoous/go-nanoid v1.5.1
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
@@ -19,7 +19,6 @@ require (
golang.org/x/sync v0.10.0 golang.org/x/sync v0.10.0
google.golang.org/grpc v1.69.4 google.golang.org/grpc v1.69.4
google.golang.org/protobuf v1.36.3 google.golang.org/protobuf v1.36.3
gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
@@ -31,4 +30,5 @@ require (
golang.org/x/sys v0.29.0 // indirect golang.org/x/sys v0.29.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

4
go.sum
View File

@@ -4,12 +4,12 @@ github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7Oputl
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/KimMachineGun/automemlimit v0.7.0 h1:7G06p/dMSf7G8E6oq+f2uOPuVncFyIlDI/pBWK49u88= github.com/KimMachineGun/automemlimit v0.7.0 h1:7G06p/dMSf7G8E6oq+f2uOPuVncFyIlDI/pBWK49u88=
github.com/KimMachineGun/automemlimit v0.7.0/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM= github.com/KimMachineGun/automemlimit v0.7.0/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
github.com/ash3in/uuidv8 v1.2.0 h1:2oogGdtCPwaVtyvPPGin4TfZLtOGE5F+W++E880G6SI=
github.com/ash3in/uuidv8 v1.2.0/go.mod h1:BnU0wJBxnzdEKmVg4xckBkD+VZuecTFTUP3M0dWgyY4=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY=
github.com/goccy/go-yaml v1.17.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=

View File

@@ -11,8 +11,8 @@ import (
) )
type httpProfile struct { type httpProfile struct {
server *http.Server server *http.Server
sync.Mutex mu sync.Mutex
running bool running bool
} }
@@ -21,8 +21,8 @@ var DefaultAddress = ":6060"
// Start the profiler // Start the profiler
func (h *httpProfile) Start() error { func (h *httpProfile) Start() error {
h.Lock() h.mu.Lock()
defer h.Unlock() defer h.mu.Unlock()
if h.running { if h.running {
return nil return nil
@@ -30,9 +30,9 @@ func (h *httpProfile) Start() error {
go func() { go func() {
if err := h.server.ListenAndServe(); err != nil { if err := h.server.ListenAndServe(); err != nil {
h.Lock() h.mu.Lock()
h.running = false h.running = false
h.Unlock() h.mu.Unlock()
} }
}() }()
@@ -43,8 +43,8 @@ func (h *httpProfile) Start() error {
// Stop the profiler // Stop the profiler
func (h *httpProfile) Stop() error { func (h *httpProfile) Stop() error {
h.Lock() h.mu.Lock()
defer h.Unlock() defer h.mu.Unlock()
if !h.running { if !h.running {
return nil return nil

View File

@@ -17,7 +17,7 @@ type profiler struct {
cpuFile *os.File cpuFile *os.File
memFile *os.File memFile *os.File
opts profile.Options opts profile.Options
sync.Mutex mu sync.Mutex
running bool running bool
} }
@@ -39,8 +39,8 @@ func (p *profiler) writeHeap(f *os.File) {
} }
func (p *profiler) Start() error { func (p *profiler) Start() error {
p.Lock() p.mu.Lock()
defer p.Unlock() defer p.mu.Unlock()
if p.running { if p.running {
return nil return nil
@@ -86,8 +86,8 @@ func (p *profiler) Start() error {
} }
func (p *profiler) Stop() error { func (p *profiler) Stop() error {
p.Lock() p.mu.Lock()
defer p.Unlock() defer p.mu.Unlock()
select { select {
case <-p.exit: case <-p.exit:

View File

@@ -33,7 +33,7 @@ type memory struct {
records map[string]services records map[string]services
watchers map[string]*watcher watchers map[string]*watcher
opts register.Options opts register.Options
sync.RWMutex mu 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
@@ -57,7 +57,7 @@ func (m *memory) ttlPrune() {
defer prune.Stop() defer prune.Stop()
for range prune.C { for range prune.C {
m.Lock() m.mu.Lock()
for namespace, services := range m.records { for namespace, services := range m.records {
for service, versions := range services { for service, versions := range services {
for version, record := range versions { for version, record := range versions {
@@ -72,24 +72,24 @@ func (m *memory) ttlPrune() {
} }
} }
} }
m.Unlock() m.mu.Unlock()
} }
} }
func (m *memory) sendEvent(r *register.Result) { func (m *memory) sendEvent(r *register.Result) {
m.RLock() m.mu.RLock()
watchers := make([]*watcher, 0, len(m.watchers)) watchers := make([]*watcher, 0, len(m.watchers))
for _, w := range m.watchers { for _, w := range m.watchers {
watchers = append(watchers, w) watchers = append(watchers, w)
} }
m.RUnlock() m.mu.RUnlock()
for _, w := range watchers { for _, w := range watchers {
select { select {
case <-w.exit: case <-w.exit:
m.Lock() m.mu.Lock()
delete(m.watchers, w.id) delete(m.watchers, w.id)
m.Unlock() m.mu.Unlock()
default: default:
select { select {
case w.res <- r: case w.res <- r:
@@ -113,8 +113,8 @@ func (m *memory) Init(opts ...register.Option) error {
} }
// add services // add services
m.Lock() m.mu.Lock()
defer m.Unlock() defer m.mu.Unlock()
return nil return nil
} }
@@ -124,8 +124,8 @@ func (m *memory) Options() register.Options {
} }
func (m *memory) Register(_ context.Context, s *register.Service, opts ...register.RegisterOption) error { func (m *memory) Register(_ context.Context, s *register.Service, opts ...register.RegisterOption) error {
m.Lock() m.mu.Lock()
defer m.Unlock() defer m.mu.Unlock()
options := register.NewRegisterOptions(opts...) options := register.NewRegisterOptions(opts...)
@@ -197,8 +197,8 @@ func (m *memory) Register(_ context.Context, s *register.Service, opts ...regist
} }
func (m *memory) Deregister(ctx context.Context, s *register.Service, opts ...register.DeregisterOption) error { func (m *memory) Deregister(ctx context.Context, s *register.Service, opts ...register.DeregisterOption) error {
m.Lock() m.mu.Lock()
defer m.Unlock() defer m.mu.Unlock()
options := register.NewDeregisterOptions(opts...) options := register.NewDeregisterOptions(opts...)
@@ -264,9 +264,9 @@ func (m *memory) LookupService(ctx context.Context, name string, opts ...registe
// if it's a wildcard domain, return from all domains // if it's a wildcard domain, return from all domains
if options.Namespace == register.WildcardNamespace { if options.Namespace == register.WildcardNamespace {
m.RLock() m.mu.RLock()
recs := m.records recs := m.records
m.RUnlock() m.mu.RUnlock()
var services []*register.Service var services []*register.Service
@@ -286,8 +286,8 @@ func (m *memory) LookupService(ctx context.Context, name string, opts ...registe
return services, nil return services, nil
} }
m.RLock() m.mu.RLock()
defer m.RUnlock() defer m.mu.RUnlock()
// check the domain exists // check the domain exists
services, ok := m.records[options.Namespace] services, ok := m.records[options.Namespace]
@@ -319,9 +319,9 @@ func (m *memory) ListServices(ctx context.Context, opts ...register.ListOption)
// if it's a wildcard domain, list from all domains // if it's a wildcard domain, list from all domains
if options.Namespace == register.WildcardNamespace { if options.Namespace == register.WildcardNamespace {
m.RLock() m.mu.RLock()
recs := m.records recs := m.records
m.RUnlock() m.mu.RUnlock()
var services []*register.Service var services []*register.Service
@@ -336,8 +336,8 @@ func (m *memory) ListServices(ctx context.Context, opts ...register.ListOption)
return services, nil return services, nil
} }
m.RLock() m.mu.RLock()
defer m.RUnlock() defer m.mu.RUnlock()
// ensure the domain exists // ensure the domain exists
services, ok := m.records[options.Namespace] services, ok := m.records[options.Namespace]
@@ -371,9 +371,9 @@ func (m *memory) Watch(ctx context.Context, opts ...register.WatchOption) (regis
wo: wo, wo: wo,
} }
m.Lock() m.mu.Lock()
m.watchers[w.id] = w m.watchers[w.id] = w
m.Unlock() m.mu.Unlock()
return w, nil return w, nil
} }

View File

@@ -51,13 +51,13 @@ func (r *rpcHandler) Options() HandlerOptions {
} }
type noopServer struct { type noopServer struct {
h Handler h Handler
wg *sync.WaitGroup wg *sync.WaitGroup
rsvc *register.Service rsvc *register.Service
handlers map[string]Handler handlers map[string]Handler
exit chan chan error exit chan chan error
opts Options opts Options
sync.RWMutex mu sync.RWMutex
registered bool registered bool
started bool started bool
} }
@@ -125,10 +125,10 @@ func (n *noopServer) String() string {
//nolint:gocyclo //nolint:gocyclo
func (n *noopServer) Register() error { func (n *noopServer) Register() error {
n.RLock() n.mu.RLock()
rsvc := n.rsvc rsvc := n.rsvc
config := n.opts config := n.opts
n.RUnlock() n.mu.RUnlock()
// if service already filled, reuse it and return early // if service already filled, reuse it and return early
if rsvc != nil { if rsvc != nil {
@@ -144,9 +144,9 @@ func (n *noopServer) Register() error {
return err return err
} }
n.RLock() n.mu.RLock()
registered := n.registered registered := n.registered
n.RUnlock() n.mu.RUnlock()
if !registered { if !registered {
if config.Logger.V(logger.InfoLevel) { if config.Logger.V(logger.InfoLevel) {
@@ -164,8 +164,8 @@ func (n *noopServer) Register() error {
return nil return nil
} }
n.Lock() n.mu.Lock()
defer n.Unlock() defer n.mu.Unlock()
n.registered = true n.registered = true
if cacheService { if cacheService {
@@ -178,9 +178,9 @@ func (n *noopServer) Register() error {
func (n *noopServer) Deregister() error { func (n *noopServer) Deregister() error {
var err error var err error
n.RLock() n.mu.RLock()
config := n.opts config := n.opts
n.RUnlock() n.mu.RUnlock()
service, err := NewRegisterService(n) service, err := NewRegisterService(n)
if err != nil { if err != nil {
@@ -195,29 +195,29 @@ func (n *noopServer) Deregister() error {
return err return err
} }
n.Lock() n.mu.Lock()
n.rsvc = nil n.rsvc = nil
if !n.registered { if !n.registered {
n.Unlock() n.mu.Unlock()
return nil return nil
} }
n.registered = false n.registered = false
n.Unlock() n.mu.Unlock()
return nil return nil
} }
//nolint:gocyclo //nolint:gocyclo
func (n *noopServer) Start() error { func (n *noopServer) Start() error {
n.RLock() n.mu.RLock()
if n.started { if n.started {
n.RUnlock() n.mu.RUnlock()
return nil return nil
} }
config := n.Options() config := n.Options()
n.RUnlock() n.mu.RUnlock()
// use 127.0.0.1 to avoid scan of all network interfaces // use 127.0.0.1 to avoid scan of all network interfaces
addr, err := maddr.Extract("127.0.0.1") addr, err := maddr.Extract("127.0.0.1")
@@ -235,11 +235,11 @@ func (n *noopServer) Start() error {
config.Logger.Info(n.opts.Context, "server [noop] Listening on "+config.Address) config.Logger.Info(n.opts.Context, "server [noop] Listening on "+config.Address)
} }
n.Lock() n.mu.Lock()
if len(config.Advertise) == 0 { if len(config.Advertise) == 0 {
config.Advertise = config.Address config.Advertise = config.Address
} }
n.Unlock() n.mu.Unlock()
// use RegisterCheck func before register // use RegisterCheck func before register
// nolint: nestif // nolint: nestif
@@ -273,9 +273,9 @@ func (n *noopServer) Start() error {
select { select {
// register self on interval // register self on interval
case <-t.C: case <-t.C:
n.RLock() n.mu.RLock()
registered := n.registered registered := n.registered
n.RUnlock() n.mu.RUnlock()
rerr := config.RegisterCheck(config.Context) rerr := config.RegisterCheck(config.Context)
// nolint: nestif // nolint: nestif
if rerr != nil && registered { if rerr != nil && registered {
@@ -332,29 +332,29 @@ func (n *noopServer) Start() error {
}() }()
// mark the server as started // mark the server as started
n.Lock() n.mu.Lock()
n.started = true n.started = true
n.Unlock() n.mu.Unlock()
return nil return nil
} }
func (n *noopServer) Stop() error { func (n *noopServer) Stop() error {
n.RLock() n.mu.RLock()
if !n.started { if !n.started {
n.RUnlock() n.mu.RUnlock()
return nil return nil
} }
n.RUnlock() n.mu.RUnlock()
ch := make(chan error) ch := make(chan error)
n.exit <- ch n.exit <- ch
err := <-ch err := <-ch
n.Lock() n.mu.Lock()
n.rsvc = nil n.rsvc = nil
n.started = false n.started = false
n.Unlock() n.mu.Unlock()
return err return err
} }

View File

@@ -96,9 +96,9 @@ func RegisterHandler(s server.Server, h interface{}, opts ...server.HandlerOptio
} }
type service struct { type service struct {
done chan struct{} done chan struct{}
opts Options opts Options
sync.RWMutex mu sync.RWMutex
stopped bool stopped bool
} }
@@ -321,9 +321,9 @@ func (s *service) Health() bool {
func (s *service) Start() error { func (s *service) Start() error {
var err error var err error
s.RLock() s.mu.RLock()
config := s.opts config := s.opts
s.RUnlock() s.mu.RUnlock()
for _, cfg := range s.opts.Configs { for _, cfg := range s.opts.Configs {
if cfg.Options().Struct == nil { if cfg.Options().Struct == nil {
@@ -380,9 +380,9 @@ func (s *service) Start() error {
} }
func (s *service) Stop() error { func (s *service) Stop() error {
s.RLock() s.mu.RLock()
config := s.opts config := s.opts
s.RUnlock() s.mu.RUnlock()
if config.Loggers[0].V(logger.InfoLevel) { if config.Loggers[0].V(logger.InfoLevel) {
config.Loggers[0].Info(s.opts.Context, fmt.Sprintf("stoppping [service] %s", s.Name())) config.Loggers[0].Info(s.opts.Context, fmt.Sprintf("stoppping [service] %s", s.Name()))
@@ -457,13 +457,13 @@ func (s *service) Run() error {
// notifyShutdown marks the service as stopped and closes the done channel. // notifyShutdown marks the service as stopped and closes the done channel.
// It ensures the channel is closed only once, preventing multiple closures. // It ensures the channel is closed only once, preventing multiple closures.
func (s *service) notifyShutdown() { func (s *service) notifyShutdown() {
s.Lock() s.mu.Lock()
if s.stopped { if s.stopped {
s.Unlock() s.mu.Unlock()
return return
} }
s.stopped = true s.stopped = true
s.Unlock() s.mu.Unlock()
close(s.done) close(s.done)
} }

View File

@@ -139,7 +139,7 @@ func (n *noopStore) fnExists(ctx context.Context, _ string, _ ...ExistsOption) e
return ctx.Err() return ctx.Err()
default: default:
} }
return nil return ErrNotFound
} }
func (n *noopStore) Write(ctx context.Context, key string, val interface{}, opts ...WriteOption) error { func (n *noopStore) Write(ctx context.Context, key string, val interface{}, opts ...WriteOption) error {

View File

@@ -9,7 +9,7 @@ type memorySync struct {
locks map[string]*memoryLock locks map[string]*memoryLock
options Options options Options
mtx gosync.RWMutex mu gosync.RWMutex
} }
type memoryLock struct { type memoryLock struct {
@@ -74,7 +74,7 @@ func (m *memorySync) Options() Options {
func (m *memorySync) Lock(id string, opts ...LockOption) error { func (m *memorySync) Lock(id string, opts ...LockOption) error {
// lock our access // lock our access
m.mtx.Lock() m.mu.Lock()
var options LockOptions var options LockOptions
for _, o := range opts { for _, o := range opts {
@@ -90,11 +90,11 @@ func (m *memorySync) Lock(id string, opts ...LockOption) error {
release: make(chan bool), release: make(chan bool),
} }
// unlock // unlock
m.mtx.Unlock() m.mu.Unlock()
return nil return nil
} }
m.mtx.Unlock() m.mu.Unlock()
// set wait time // set wait time
var wait <-chan time.Time var wait <-chan time.Time
@@ -124,12 +124,12 @@ lockLoop:
// wait for the lock to be released // wait for the lock to be released
select { select {
case <-lk.release: case <-lk.release:
m.mtx.Lock() m.mu.Lock()
// someone locked before us // someone locked before us
lk, ok = m.locks[id] lk, ok = m.locks[id]
if ok { if ok {
m.mtx.Unlock() m.mu.Unlock()
continue continue
} }
@@ -141,7 +141,7 @@ lockLoop:
release: make(chan bool), release: make(chan bool),
} }
m.mtx.Unlock() m.mu.Unlock()
break lockLoop break lockLoop
case <-ttl: case <-ttl:
@@ -160,8 +160,8 @@ lockLoop:
} }
func (m *memorySync) Unlock(id string) error { func (m *memorySync) Unlock(id string) error {
m.mtx.Lock() m.mu.Lock()
defer m.mtx.Unlock() defer m.mu.Unlock()
lk, ok := m.locks[id] lk, ok := m.locks[id]
// no lock exists // no lock exists

View File

@@ -46,6 +46,10 @@ func (s memoryStringer) String() string {
return s.s return s.s
} }
func (t *Tracer) Enabled() bool {
return t.opts.Enabled
}
func (t *Tracer) Flush(_ context.Context) error { func (t *Tracer) Flush(_ context.Context) error {
return nil return nil
} }

View File

@@ -20,6 +20,10 @@ func (t *noopTracer) Spans() []Span {
var uuidNil = uuid.Nil.String() var uuidNil = uuid.Nil.String()
func (t *noopTracer) Enabled() bool {
return t.opts.Enabled
}
func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption) (context.Context, Span) { func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption) (context.Context, Span) {
options := NewSpanOptions(opts...) options := NewSpanOptions(opts...)
span := &noopSpan{ span := &noopSpan{

View File

@@ -142,6 +142,8 @@ type Options struct {
Name string Name string
// ContextAttrFuncs contains funcs that provides tracing // ContextAttrFuncs contains funcs that provides tracing
ContextAttrFuncs []ContextAttrFunc ContextAttrFuncs []ContextAttrFunc
// Enabled specify trace status
Enabled bool
} }
// Option func signature // Option func signature
@@ -181,6 +183,7 @@ func NewOptions(opts ...Option) Options {
Logger: logger.DefaultLogger, Logger: logger.DefaultLogger,
Context: context.Background(), Context: context.Background(),
ContextAttrFuncs: DefaultContextAttrFuncs, ContextAttrFuncs: DefaultContextAttrFuncs,
Enabled: true,
} }
for _, o := range opts { for _, o := range opts {
o(&options) o(&options)
@@ -194,3 +197,10 @@ func Name(n string) Option {
o.Name = n o.Name = n
} }
} }
// Disabled disable tracer
func Disabled(b bool) Option {
return func(o *Options) {
o.Enabled = !b
}
}

View File

@@ -51,6 +51,8 @@ type Tracer interface {
// Extract(ctx context.Context) // Extract(ctx context.Context)
// Flush flushes spans // Flush flushes spans
Flush(ctx context.Context) error Flush(ctx context.Context) error
// Enabled returns tracer status
Enabled() bool
} }
type Span interface { type Span interface {

View File

@@ -67,6 +67,12 @@ func (b *SeekerBuffer) Close() error {
return nil return nil
} }
// Reset clears all the data out of the buffer and sets the read position to 0.
func (b *SeekerBuffer) Reset() {
b.data = nil
b.pos = 0
}
// Len returns the length of data remaining to be read. // Len returns the length of data remaining to be read.
func (b *SeekerBuffer) Len() int { func (b *SeekerBuffer) Len() int {
return len(b.data[b.pos:]) return len(b.data[b.pos:])

View File

@@ -137,7 +137,7 @@ type cache struct {
opts Options opts Options
sync.RWMutex mu sync.RWMutex
} }
type cacheEntry struct { type cacheEntry struct {
@@ -171,7 +171,7 @@ func (c *cache) put(req string, res string) {
ttl = c.opts.MaxCacheTTL ttl = c.opts.MaxCacheTTL
} }
c.Lock() c.mu.Lock()
if c.entries == nil { if c.entries == nil {
c.entries = make(map[string]cacheEntry) c.entries = make(map[string]cacheEntry)
} }
@@ -207,7 +207,7 @@ func (c *cache) put(req string, res string) {
} }
c.opts.Meter.Counter(semconv.CacheItemsTotal, "type", "dns").Inc() c.opts.Meter.Counter(semconv.CacheItemsTotal, "type", "dns").Inc()
c.Unlock() c.mu.Unlock()
} }
func (c *cache) get(req string) (res string) { func (c *cache) get(req string) (res string) {
@@ -219,8 +219,8 @@ func (c *cache) get(req string) (res string) {
return "" return ""
} }
c.RLock() c.mu.RLock()
defer c.RUnlock() defer c.mu.RUnlock()
if c.entries == nil { if c.entries == nil {
return "" return ""

View File

@@ -20,7 +20,7 @@ type dnsConn struct {
ibuf bytes.Buffer ibuf bytes.Buffer
obuf bytes.Buffer obuf bytes.Buffer
sync.Mutex mu sync.Mutex
} }
type roundTripper func(ctx context.Context, req string) (res string, err error) type roundTripper func(ctx context.Context, req string) (res string, err error)
@@ -42,15 +42,15 @@ func (c *dnsConn) Read(b []byte) (n int, err error) {
} }
func (c *dnsConn) Write(b []byte) (n int, err error) { func (c *dnsConn) Write(b []byte) (n int, err error) {
c.Lock() c.mu.Lock()
defer c.Unlock() defer c.mu.Unlock()
return c.ibuf.Write(b) return c.ibuf.Write(b)
} }
func (c *dnsConn) Close() error { func (c *dnsConn) Close() error {
c.Lock() c.mu.Lock()
cancel := c.cancel cancel := c.cancel
c.Unlock() c.mu.Unlock()
if cancel != nil { if cancel != nil {
cancel() cancel()
@@ -78,9 +78,9 @@ func (c *dnsConn) SetDeadline(t time.Time) error {
} }
func (c *dnsConn) SetReadDeadline(t time.Time) error { func (c *dnsConn) SetReadDeadline(t time.Time) error {
c.Lock() c.mu.Lock()
c.deadline = t c.deadline = t
c.Unlock() c.mu.Unlock()
return nil return nil
} }
@@ -90,8 +90,8 @@ func (c *dnsConn) SetWriteDeadline(_ time.Time) error {
} }
func (c *dnsConn) drainBuffers(b []byte) (string, int, error) { func (c *dnsConn) drainBuffers(b []byte) (string, int, error) {
c.Lock() c.mu.Lock()
defer c.Unlock() defer c.mu.Unlock()
// drain the output buffer // drain the output buffer
if c.obuf.Len() > 0 { if c.obuf.Len() > 0 {
@@ -119,8 +119,8 @@ func (c *dnsConn) drainBuffers(b []byte) (string, int, error) {
} }
func (c *dnsConn) fillBuffer(b []byte, str string) (int, error) { func (c *dnsConn) fillBuffer(b []byte, str string) (int, error) {
c.Lock() c.mu.Lock()
defer c.Unlock() defer c.mu.Unlock()
c.obuf.WriteByte(byte(len(str) >> 8)) c.obuf.WriteByte(byte(len(str) >> 8))
c.obuf.WriteByte(byte(len(str))) c.obuf.WriteByte(byte(len(str)))
c.obuf.WriteString(str) c.obuf.WriteString(str)
@@ -128,8 +128,8 @@ func (c *dnsConn) fillBuffer(b []byte, str string) (int, error) {
} }
func (c *dnsConn) childContext() (context.Context, context.CancelFunc) { func (c *dnsConn) childContext() (context.Context, context.CancelFunc) {
c.Lock() c.mu.Lock()
defer c.Unlock() defer c.mu.Unlock()
if c.ctx == nil { if c.ctx == nil {
c.ctx, c.cancel = context.WithCancel(context.Background()) c.ctx, c.cancel = context.WithCancel(context.Background())
} }

View File

@@ -52,7 +52,7 @@ type clientTracer struct {
tr tracer.Tracer tr tracer.Tracer
activeHooks map[string]context.Context activeHooks map[string]context.Context
root tracer.Span root tracer.Span
mtx sync.Mutex mu sync.Mutex
} }
func NewClientTrace(ctx context.Context, tr tracer.Tracer) *httptrace.ClientTrace { func NewClientTrace(ctx context.Context, tr tracer.Tracer) *httptrace.ClientTrace {
@@ -83,8 +83,8 @@ func NewClientTrace(ctx context.Context, tr tracer.Tracer) *httptrace.ClientTrac
} }
func (ct *clientTracer) start(hook, spanName string, attrs ...interface{}) { func (ct *clientTracer) start(hook, spanName string, attrs ...interface{}) {
ct.mtx.Lock() ct.mu.Lock()
defer ct.mtx.Unlock() defer ct.mu.Unlock()
if hookCtx, found := ct.activeHooks[hook]; !found { if hookCtx, found := ct.activeHooks[hook]; !found {
var sp tracer.Span var sp tracer.Span
@@ -104,8 +104,8 @@ func (ct *clientTracer) start(hook, spanName string, attrs ...interface{}) {
} }
func (ct *clientTracer) end(hook string, err error, attrs ...interface{}) { func (ct *clientTracer) end(hook string, err error, attrs ...interface{}) {
ct.mtx.Lock() ct.mu.Lock()
defer ct.mtx.Unlock() defer ct.mu.Unlock()
if ctx, ok := ct.activeHooks[hook]; ok { // nolint:nestif if ctx, ok := ct.activeHooks[hook]; ok { // nolint:nestif
if span, ok := tracer.SpanFromContext(ctx); ok { if span, ok := tracer.SpanFromContext(ctx); ok {
if err != nil { if err != nil {
@@ -136,8 +136,8 @@ func (ct *clientTracer) getParentContext(hook string) context.Context {
} }
func (ct *clientTracer) span(hook string) (tracer.Span, bool) { func (ct *clientTracer) span(hook string) (tracer.Span, bool) {
ct.mtx.Lock() ct.mu.Lock()
defer ct.mtx.Unlock() defer ct.mu.Unlock()
if ctx, ok := ct.activeHooks[hook]; ok { if ctx, ok := ct.activeHooks[hook]; ok {
return tracer.SpanFromContext(ctx) return tracer.SpanFromContext(ctx)
} }

View File

@@ -2,12 +2,8 @@ package id
import ( import (
"crypto/rand" "crypto/rand"
"encoding/binary"
"errors" "errors"
"fmt"
"time"
uuidv8 "github.com/ash3in/uuidv8"
"github.com/google/uuid" "github.com/google/uuid"
nanoid "github.com/matoous/go-nanoid" nanoid "github.com/matoous/go-nanoid"
) )
@@ -25,6 +21,7 @@ type Type int
const ( const (
TypeUnspecified Type = iota TypeUnspecified Type = iota
TypeNanoid TypeNanoid
TypeUUIDv7
TypeUUIDv8 TypeUUIDv8
) )
@@ -58,14 +55,14 @@ func (g *Generator) New() (string, error) {
} }
return nanoid.Generate(g.opts.NanoidAlphabet, g.opts.NanoidSize) return nanoid.Generate(g.opts.NanoidAlphabet, g.opts.NanoidSize)
case TypeUUIDv8: case TypeUUIDv7:
timestamp := uint64(time.Now().UnixNano()) uid, err := uuid.NewV7()
clockSeq := make([]byte, 2) if err != nil {
if _, err := rand.Read(clockSeq); err != nil { return "", err
return "", fmt.Errorf("failed to generate random clock sequence: %w", err)
} }
clockSeqValue := binary.BigEndian.Uint16(clockSeq) & 0x0FFF // Mask to 12 bits return uid.String(), nil
return uuidv8.NewWithParams(timestamp, clockSeqValue, g.opts.UUIDNode[:], uuidv8.TimestampBits48) case TypeUUIDv8:
return "", errors.New("unsupported uuid version v8")
} }
return "", errors.New("invalid option, Type unspecified") return "", errors.New("invalid option, Type unspecified")
} }
@@ -82,16 +79,15 @@ func New(opts ...Option) (string, error) {
if options.NanoidSize <= 0 { if options.NanoidSize <= 0 {
return "", errors.New("invalid option, NanoidSize must be positive integer") return "", errors.New("invalid option, NanoidSize must be positive integer")
} }
return nanoid.Generate(options.NanoidAlphabet, options.NanoidSize) return nanoid.Generate(options.NanoidAlphabet, options.NanoidSize)
case TypeUUIDv8: case TypeUUIDv7:
timestamp := uint64(time.Now().UnixNano()) uid, err := uuid.NewV7()
clockSeq := make([]byte, 2) if err != nil {
if _, err := rand.Read(clockSeq); err != nil { return "", err
return "", fmt.Errorf("failed to generate random clock sequence: %w", err)
} }
clockSeqValue := binary.BigEndian.Uint16(clockSeq) & 0x0FFF // Mask to 12 bits return uid.String(), nil
return uuidv8.NewWithParams(timestamp, clockSeqValue, options.UUIDNode[:], uuidv8.TimestampBits48) case TypeUUIDv8:
return "", errors.New("unsupported uuid version v8")
} }
return "", errors.New("invalid option, Type unspecified") return "", errors.New("invalid option, Type unspecified")
@@ -145,7 +141,7 @@ func WithUUIDNode(node [6]byte) Option {
// NewOptions returns new Options struct filled by opts // NewOptions returns new Options struct filled by opts
func NewOptions(opts ...Option) Options { func NewOptions(opts ...Option) Options {
options := Options{ options := Options{
Type: TypeUUIDv8, Type: TypeUUIDv7,
NanoidAlphabet: DefaultNanoidAlphabet, NanoidAlphabet: DefaultNanoidAlphabet,
NanoidSize: DefaultNanoidSize, NanoidSize: DefaultNanoidSize,
UUIDNode: generatedNode, UUIDNode: generatedNode,

View File

@@ -14,7 +14,7 @@ type Buffer struct {
vals []*Entry vals []*Entry
size int size int
sync.RWMutex mu sync.RWMutex
} }
// Entry is ring buffer data entry // Entry is ring buffer data entry
@@ -35,8 +35,8 @@ type Stream struct {
// Put adds a new value to ring buffer // Put adds a new value to ring buffer
func (b *Buffer) Put(v interface{}) { func (b *Buffer) Put(v interface{}) {
b.Lock() b.mu.Lock()
defer b.Unlock() defer b.mu.Unlock()
// append to values // append to values
entry := &Entry{ entry := &Entry{
@@ -63,8 +63,8 @@ func (b *Buffer) Put(v interface{}) {
// Get returns the last n entries // Get returns the last n entries
func (b *Buffer) Get(n int) []*Entry { func (b *Buffer) Get(n int) []*Entry {
b.RLock() b.mu.RLock()
defer b.RUnlock() defer b.mu.RUnlock()
// reset any invalid values // reset any invalid values
if n > len(b.vals) || n < 0 { if n > len(b.vals) || n < 0 {
@@ -80,8 +80,8 @@ func (b *Buffer) Get(n int) []*Entry {
// Since returns the entries since a specific time // Since returns the entries since a specific time
func (b *Buffer) Since(t time.Time) []*Entry { func (b *Buffer) Since(t time.Time) []*Entry {
b.RLock() b.mu.RLock()
defer b.RUnlock() defer b.mu.RUnlock()
// return all the values // return all the values
if t.IsZero() { if t.IsZero() {
@@ -109,8 +109,8 @@ func (b *Buffer) Since(t time.Time) []*Entry {
// Stream logs from the buffer // Stream logs from the buffer
// Close the channel when you want to stop // Close the channel when you want to stop
func (b *Buffer) Stream() (<-chan *Entry, chan bool) { func (b *Buffer) Stream() (<-chan *Entry, chan bool) {
b.Lock() b.mu.Lock()
defer b.Unlock() defer b.mu.Unlock()
entries := make(chan *Entry, 128) entries := make(chan *Entry, 128)
id := id.MustNew() id := id.MustNew()

View File

@@ -24,7 +24,7 @@ type stream struct {
err error err error
request *request request *request
sync.RWMutex mu sync.RWMutex
} }
type request struct { type request struct {
@@ -57,9 +57,9 @@ func (s *stream) Request() server.Request {
func (s *stream) Send(v interface{}) error { func (s *stream) Send(v interface{}) error {
err := s.Stream.SendMsg(v) err := s.Stream.SendMsg(v)
if err != nil { if err != nil {
s.Lock() s.mu.Lock()
s.err = err s.err = err
s.Unlock() s.mu.Unlock()
} }
return err return err
} }
@@ -68,17 +68,17 @@ func (s *stream) Send(v interface{}) error {
func (s *stream) Recv(v interface{}) error { func (s *stream) Recv(v interface{}) error {
err := s.Stream.RecvMsg(v) err := s.Stream.RecvMsg(v)
if err != nil { if err != nil {
s.Lock() s.mu.Lock()
s.err = err s.err = err
s.Unlock() s.mu.Unlock()
} }
return err return err
} }
// Error returns error that stream holds // Error returns error that stream holds
func (s *stream) Error() error { func (s *stream) Error() error {
s.RLock() s.mu.RLock()
defer s.RUnlock() defer s.mu.RUnlock()
return s.err return s.err
} }

View File

@@ -6,7 +6,7 @@ import (
"strconv" "strconv"
"time" "time"
"gopkg.in/yaml.v3" "github.com/goccy/go-yaml"
) )
type Duration int64 type Duration int64
@@ -58,9 +58,9 @@ func (d Duration) MarshalYAML() (interface{}, error) {
return time.Duration(d).String(), nil return time.Duration(d).String(), nil
} }
func (d *Duration) UnmarshalYAML(n *yaml.Node) error { func (d *Duration) UnmarshalYAML(data []byte) error {
var v interface{} var v interface{}
if err := yaml.Unmarshal([]byte(n.Value), &v); err != nil { if err := yaml.Unmarshal(data, &v); err != nil {
return err return err
} }
switch value := v.(type) { switch value := v.(type) {

View File

@@ -6,7 +6,7 @@ import (
"testing" "testing"
"time" "time"
"gopkg.in/yaml.v3" "github.com/goccy/go-yaml"
) )
func TestMarshalYAML(t *testing.T) { func TestMarshalYAML(t *testing.T) {