From 55e64f9684df86841684348518104c4187602894 Mon Sep 17 00:00:00 2001 From: lohuza Date: Tue, 27 Dec 2022 04:50:43 +0400 Subject: [PATCH 01/12] added nats --- go.mod | 14 +++- go.sum | 30 ++++++-- nats/common.go | 25 +++++++ nats/handler.go | 183 ++++++++++++++++++++++++++++++++++++++++++++++ nats/publisher.go | 143 ++++++++++++++++++++++++++++++++++++ 5 files changed, 386 insertions(+), 9 deletions(-) create mode 100644 nats/common.go create mode 100644 nats/handler.go create mode 100644 nats/publisher.go diff --git a/go.mod b/go.mod index d151a0b..33a7ba1 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/gomarkdown/markdown v0.0.0-20220830015526-01a3c37d6f50 github.com/hashicorp/vault/api v1.7.2 github.com/iancoleman/strcase v0.2.0 + github.com/nats-io/nats.go v1.22.1 github.com/openware/pkg/ika v0.1.1 github.com/openware/pkg/mngapi v0.1.1 github.com/stretchr/testify v1.8.0 @@ -66,10 +67,12 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.15.11 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-colorable v0.1.6 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-sqlite3 v1.14.12 // indirect + github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.0.0 // indirect @@ -77,6 +80,10 @@ require ( github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nats-io/jwt/v2 v2.3.0 // indirect + github.com/nats-io/nats-server/v2 v2.9.10 // indirect + github.com/nats-io/nkeys v0.3.0 // indirect + github.com/nats-io/nuid v1.0.1 // indirect github.com/oklog/run v1.0.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.19.0 // indirect @@ -88,11 +95,12 @@ require ( github.com/ugorji/go/codec v1.2.7 // indirect github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect + go.uber.org/automaxprocs v1.5.1 // indirect + golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect + golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect + golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0 // indirect google.golang.org/grpc v1.41.0 // indirect google.golang.org/protobuf v1.28.0 // indirect diff --git a/go.sum b/go.sum index 62f1efd..5ed09f8 100644 --- a/go.sum +++ b/go.sum @@ -242,6 +242,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -277,6 +279,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0= github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= @@ -299,6 +303,16 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI= +github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/nats-server/v2 v2.9.10 h1:LMC46Oi9E6BUx/xBsaCVZgofliAqKQzRPU6eKWkN8jE= +github.com/nats-io/nats-server/v2 v2.9.10/go.mod h1:AB6hAnGZDlYfqb7CTAm66ZKMZy9DpfierY1/PbpvI2g= +github.com/nats-io/nats.go v1.22.1 h1:XzfqDspY0RNufzdrB8c4hFR+R3dahkxlpWe5+IWJzbE= +github.com/nats-io/nats.go v1.22.1/go.mod h1:tLqubohF7t4z3du1QDPYJIQQyhb4wl6DhjxEajSI7UA= +github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -390,6 +404,8 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= +go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -405,11 +421,12 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -450,6 +467,7 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -474,8 +492,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -486,8 +504,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/nats/common.go b/nats/common.go new file mode 100644 index 0000000..d376aa7 --- /dev/null +++ b/nats/common.go @@ -0,0 +1,25 @@ +package nats + +import ( + "github.com/nats-io/nats-server/v2/server" + "github.com/nats-io/nats.go" +) + +func InitNats(connectionString string) (*nats.Conn, error) { + nc, err := nats.Connect(connectionString) + + return nc, err +} + +func InitEmbededNats() (*nats.Conn, error) { + opts := &server.Options{} + ns, err := server.NewServer(opts) + if err != nil { + panic("failed to initialize nats mock server") + } + + ns.Start() + nc, err := nats.Connect(ns.ClientURL()) + + return nc, err +} diff --git a/nats/handler.go b/nats/handler.go new file mode 100644 index 0000000..2cff5b2 --- /dev/null +++ b/nats/handler.go @@ -0,0 +1,183 @@ +package nats + +import ( + "os" + "sync" + + "github.com/nats-io/nats.go" +) + +type EventHandler interface { + SubscribeToQueueUsingChannel(subject string, group string, msgChannel chan *nats.Msg) error + SubscribeToQueue(subject string, group string, cb nats.MsgHandler) error + Subscribe(subject string, cb nats.MsgHandler) error +} + +type eventHandlerBase struct { + subs []*nats.Subscription + mutex sync.Mutex + termination <-chan os.Signal +} + +type handlerConfig struct { + autoUnsubscribeOnShutdown bool +} + +type natsEventHandler struct { + eventHandlerBase + nc *nats.Conn + config *handlerConfig +} + +type jsEventHandler struct { + eventHandlerBase + js nats.JetStreamContext + config *handlerConfig +} + +// For Checking compatibility +var ( + _ EventHandler = (*natsEventHandler)(nil) + _ EventHandler = (*jsEventHandler)(nil) +) + +func newHandlerBase(termination <-chan os.Signal) eventHandlerBase { + return eventHandlerBase{ + termination: termination, + subs: make([]*nats.Subscription, 0), + mutex: sync.Mutex{}, + } +} + +func NewNatsHandler(nc *nats.Conn, termination <-chan os.Signal, config *handlerConfig) *natsEventHandler { + if config == nil { + config = NewHandlerDefaultConfig() + } + + handler := natsEventHandler{ + nc: nc, + eventHandlerBase: newHandlerBase(termination), + config: config, + } + + go handler.handleShutdown(config.autoUnsubscribeOnShutdown) + + return &handler +} + +func NewJsHandler(nc *nats.Conn, termination <-chan os.Signal, config *handlerConfig) (*jsEventHandler, error) { + if config == nil { + config = NewHandlerDefaultConfig() + } + + js, err := nc.JetStream() + if err != nil { + return nil, err + } + + handler := &jsEventHandler{ + js: js, + eventHandlerBase: newHandlerBase(termination), + config: config, + } + + go handler.handleShutdown(config.autoUnsubscribeOnShutdown) + + return handler, nil +} + +func NewHandlerDefaultConfig() *handlerConfig { + return &handlerConfig{ + autoUnsubscribeOnShutdown: true, + } +} + +func (h *eventHandlerBase) handleShutdown(unsubOnShutdown bool) []error { + if unsubOnShutdown { + for _ = range h.termination { + var errors []error + for _, sub := range h.subs { + err := sub.Unsubscribe() + if err != nil { + errors = append(errors, err) + } + } + + return errors + } + } + + return nil +} + +func (h *eventHandlerBase) GetSubscriptions() []*nats.Subscription { + return h.subs +} + +func (h *eventHandlerBase) pushSub(sub *nats.Subscription) { + h.mutex.Lock() + defer h.mutex.Unlock() + + h.subs = append(h.subs, sub) +} + +func (j *jsEventHandler) SubscribeToQueueUsingChannel(subject string, group string, msgChannel chan *nats.Msg) error { + sub, err := j.js.ChanQueueSubscribe(subject, group, msgChannel, nats.AckExplicit()) + if err != nil { + return err + } + + j.pushSub(sub) + return nil +} + +func (j *jsEventHandler) SubscribeToQueue(subject string, group string, cb nats.MsgHandler) error { + sub, err := j.js.QueueSubscribe(subject, group, cb, nats.AckExplicit()) + if err != nil { + return err + } + + j.pushSub(sub) + return nil +} + +func (j *jsEventHandler) Subscribe(subject string, cb nats.MsgHandler) error { + sub, err := j.js.Subscribe(subject, cb, nats.AckExplicit()) + if err != nil { + return err + } + + j.pushSub(sub) + return nil +} + +func (n *natsEventHandler) SubscribeToQueueUsingChannel(subject string, group string, msgChannel chan *nats.Msg) error { + sub, err := n.nc.ChanQueueSubscribe(subject, group, msgChannel) + if err != nil { + return err + } + + n.pushSub(sub) + return nil +} + +func (n *natsEventHandler) SubscribeToQueue(subject string, group string, cb nats.MsgHandler) error { + sub, err := n.nc.QueueSubscribe(subject, group, cb) + if err != nil { + return err + } + + n.pushSub(sub) + return nil +} + +func (n *natsEventHandler) Subscribe(subject string, cb nats.MsgHandler) error { + sub, err := n.nc.Subscribe(subject, cb) + + if err != nil { + return err + } + + n.pushSub(sub) + return nil +} diff --git a/nats/publisher.go b/nats/publisher.go new file mode 100644 index 0000000..ace0767 --- /dev/null +++ b/nats/publisher.go @@ -0,0 +1,143 @@ +package nats + +import ( + "sync" + "time" + + "github.com/nats-io/nats.go" +) + +type eventPublisherBase interface { + Request(subj string, data []byte) (*nats.Msg, error) + RequestWithTimeout(subject string, data []byte, timeout time.Duration) (*nats.Msg, error) + Publish(topic string, payload []byte) error + publishMultiple(topics []string, payload []byte) []error +} + +type EventPublisher interface { + eventPublisherBase +} + +type JsEventPublisher interface { + eventPublisherBase + CreateNewEventStream(string, []string) error + DeleteEventStream(streamName string) error +} + +type publisherBase struct { + nc *nats.Conn +} + +var _ eventPublisherBase = (*publisherBase)(nil) + +type natsEventPublisher struct { + publisherBase +} + +var _ EventPublisher = (*natsEventPublisher)(nil) + +type jsEventPublisher struct { + publisherBase + js nats.JetStreamContext +} + +var _ JsEventPublisher = (*jsEventPublisher)(nil) + +func NewNatsEventPublisher(nc *nats.Conn) *natsEventPublisher { + dispatcher := natsEventPublisher{ + publisherBase{nc: nc}, + } + + return &dispatcher +} + +func NewJsEventPublisher(nc *nats.Conn) (*jsEventPublisher, error) { + js, err := nc.JetStream() + if err != nil { + return nil, err + } + + dispatcher := jsEventPublisher{ + publisherBase: publisherBase{nc}, + js: js, + } + + return &dispatcher, nil +} + +func (p *publisherBase) Request(subject string, data []byte) (*nats.Msg, error) { + // TODO: maybe modify to something else + return p.RequestWithTimeout(subject, data, time.Second*3) +} + +func (p *publisherBase) RequestWithTimeout(subject string, data []byte, timeout time.Duration) (*nats.Msg, error) { + return p.nc.Request(subject, data, timeout) +} + +func (p *publisherBase) Publish(topic string, payload []byte) error { + return p.nc.Publish(topic, payload) +} + +func (p *publisherBase) publishMultiple(topics []string, payload []byte) []error { + wg := sync.WaitGroup{} + var errors []error + for _, topic := range topics { + wg.Add(1) + go func() { + defer wg.Done() + err := p.Publish(topic, payload) + if err != nil { + errors = append(errors, err) + } + }() + } + wg.Wait() + + return errors +} + +func (j *jsEventPublisher) CreateNewEventStream(streamName string, subjects []string) error { + stream, err := j.js.StreamInfo(streamName) + if err != nil && err != nats.ErrStreamNotFound { + return err + } + + if stream == nil { + _, err := j.js.AddStream(&nats.StreamConfig{ + Name: streamName, + Subjects: subjects, + }) + if err != nil { + return err + } + } + + return nil +} + +func (j *jsEventPublisher) DeleteEventStream(streamName string) error { + return j.js.DeleteStream(streamName) +} + +func (j *jsEventPublisher) Publish(topic string, payload []byte) error { + _, err := j.js.Publish(topic, payload) + return err +} + +func (j *jsEventPublisher) publishMultiple(topics []string, payload []byte) []error { + wg := sync.WaitGroup{} + var errors []error + for _, topic := range topics { + wg.Add(1) + go func() { + defer wg.Done() + _, err := j.js.Publish(topic, payload) + if err != nil { + errors = append(errors, err) + } + }() + } + wg.Wait() + + return errors +} From adf3b5442d5a70b0b3e1638123637d41ad610691 Mon Sep 17 00:00:00 2001 From: lohuza Date: Wed, 28 Dec 2022 04:14:43 +0400 Subject: [PATCH 02/12] nats testing and separate mod --- .idea/.gitignore | 8 +++++ nats/go.mod | 22 ++++++++++++++ nats/go.sum | 47 ++++++++++++++++++++++++++++++ nats/handler.go | 28 +++++++++--------- nats/nats_test.go | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 165 insertions(+), 14 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 nats/go.mod create mode 100644 nats/go.sum create mode 100644 nats/nats_test.go diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/nats/go.mod b/nats/go.mod new file mode 100644 index 0000000..806de52 --- /dev/null +++ b/nats/go.mod @@ -0,0 +1,22 @@ +module github.com/openware/pkg/nats + +go 1.18 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/klauspost/compress v1.15.11 // indirect + github.com/minio/highwayhash v1.0.2 // indirect + github.com/nats-io/jwt/v2 v2.3.0 // indirect + github.com/nats-io/nats-server/v2 v2.9.10 // indirect + github.com/nats-io/nats.go v1.22.1 // indirect + github.com/nats-io/nkeys v0.3.0 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect + github.com/stretchr/testify v1.8.1 // indirect + go.uber.org/automaxprocs v1.5.1 // indirect + golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect + golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect + golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/nats/go.sum b/nats/go.sum new file mode 100644 index 0000000..dcb1557 --- /dev/null +++ b/nats/go.sum @@ -0,0 +1,47 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI= +github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/nats-server/v2 v2.9.10 h1:LMC46Oi9E6BUx/xBsaCVZgofliAqKQzRPU6eKWkN8jE= +github.com/nats-io/nats-server/v2 v2.9.10/go.mod h1:AB6hAnGZDlYfqb7CTAm66ZKMZy9DpfierY1/PbpvI2g= +github.com/nats-io/nats.go v1.22.1 h1:XzfqDspY0RNufzdrB8c4hFR+R3dahkxlpWe5+IWJzbE= +github.com/nats-io/nats.go v1.22.1/go.mod h1:tLqubohF7t4z3du1QDPYJIQQyhb4wl6DhjxEajSI7UA= +github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= +go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/nats/handler.go b/nats/handler.go index 2cff5b2..0389b31 100644 --- a/nats/handler.go +++ b/nats/handler.go @@ -23,13 +23,13 @@ type handlerConfig struct { autoUnsubscribeOnShutdown bool } -type natsEventHandler struct { +type NatsEventHandler struct { eventHandlerBase nc *nats.Conn config *handlerConfig } -type jsEventHandler struct { +type JsEventHandler struct { eventHandlerBase js nats.JetStreamContext config *handlerConfig @@ -37,8 +37,8 @@ type jsEventHandler struct { // For Checking compatibility var ( - _ EventHandler = (*natsEventHandler)(nil) - _ EventHandler = (*jsEventHandler)(nil) + _ EventHandler = (*NatsEventHandler)(nil) + _ EventHandler = (*JsEventHandler)(nil) ) func newHandlerBase(termination <-chan os.Signal) eventHandlerBase { @@ -49,12 +49,12 @@ func newHandlerBase(termination <-chan os.Signal) eventHandlerBase { } } -func NewNatsHandler(nc *nats.Conn, termination <-chan os.Signal, config *handlerConfig) *natsEventHandler { +func NewNatsHandler(nc *nats.Conn, termination <-chan os.Signal, config *handlerConfig) *NatsEventHandler { if config == nil { config = NewHandlerDefaultConfig() } - handler := natsEventHandler{ + handler := NatsEventHandler{ nc: nc, eventHandlerBase: newHandlerBase(termination), config: config, @@ -65,7 +65,7 @@ func NewNatsHandler(nc *nats.Conn, termination <-chan os.Signal, config *handler return &handler } -func NewJsHandler(nc *nats.Conn, termination <-chan os.Signal, config *handlerConfig) (*jsEventHandler, error) { +func NewJsHandler(nc *nats.Conn, termination <-chan os.Signal, config *handlerConfig) (*JsEventHandler, error) { if config == nil { config = NewHandlerDefaultConfig() } @@ -75,7 +75,7 @@ func NewJsHandler(nc *nats.Conn, termination <-chan os.Signal, config *handlerCo return nil, err } - handler := &jsEventHandler{ + handler := &JsEventHandler{ js: js, eventHandlerBase: newHandlerBase(termination), config: config, @@ -121,7 +121,7 @@ func (h *eventHandlerBase) pushSub(sub *nats.Subscription) { h.subs = append(h.subs, sub) } -func (j *jsEventHandler) SubscribeToQueueUsingChannel(subject string, group string, msgChannel chan *nats.Msg) error { +func (j *JsEventHandler) SubscribeToQueueUsingChannel(subject string, group string, msgChannel chan *nats.Msg) error { sub, err := j.js.ChanQueueSubscribe(subject, group, msgChannel, nats.AckExplicit()) if err != nil { return err @@ -131,7 +131,7 @@ func (j *jsEventHandler) SubscribeToQueueUsingChannel(subject string, group stri return nil } -func (j *jsEventHandler) SubscribeToQueue(subject string, group string, cb nats.MsgHandler) error { +func (j *JsEventHandler) SubscribeToQueue(subject string, group string, cb nats.MsgHandler) error { sub, err := j.js.QueueSubscribe(subject, group, cb, nats.AckExplicit()) if err != nil { return err @@ -141,7 +141,7 @@ func (j *jsEventHandler) SubscribeToQueue(subject string, group string, cb nats. return nil } -func (j *jsEventHandler) Subscribe(subject string, cb nats.MsgHandler) error { +func (j *JsEventHandler) Subscribe(subject string, cb nats.MsgHandler) error { sub, err := j.js.Subscribe(subject, cb, nats.AckExplicit()) if err != nil { return err @@ -151,7 +151,7 @@ func (j *jsEventHandler) Subscribe(subject string, cb nats.MsgHandler) error { return nil } -func (n *natsEventHandler) SubscribeToQueueUsingChannel(subject string, group string, msgChannel chan *nats.Msg) error { +func (n *NatsEventHandler) SubscribeToQueueUsingChannel(subject string, group string, msgChannel chan *nats.Msg) error { sub, err := n.nc.ChanQueueSubscribe(subject, group, msgChannel) if err != nil { return err @@ -161,7 +161,7 @@ func (n *natsEventHandler) SubscribeToQueueUsingChannel(subject string, group st return nil } -func (n *natsEventHandler) SubscribeToQueue(subject string, group string, cb nats.MsgHandler) error { +func (n *NatsEventHandler) SubscribeToQueue(subject string, group string, cb nats.MsgHandler) error { sub, err := n.nc.QueueSubscribe(subject, group, cb) if err != nil { return err @@ -171,7 +171,7 @@ func (n *natsEventHandler) SubscribeToQueue(subject string, group string, cb nat return nil } -func (n *natsEventHandler) Subscribe(subject string, cb nats.MsgHandler) error { +func (n *NatsEventHandler) Subscribe(subject string, cb nats.MsgHandler) error { sub, err := n.nc.Subscribe(subject, cb) if err != nil { diff --git a/nats/nats_test.go b/nats/nats_test.go new file mode 100644 index 0000000..36e4432 --- /dev/null +++ b/nats/nats_test.go @@ -0,0 +1,74 @@ +package nats + +import ( + "sync" + "testing" + + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" +) + +func initNatsHandlers() (*natsEventPublisher, *NatsEventHandler) { + nc, _ := InitEmbededNats() + publisher := NewNatsEventPublisher(nc) + handler := NewNatsHandler(nc, nil, nil) + + return publisher, handler +} + +func TestNatsPubSub(t *testing.T) { + pub, handler := initNatsHandlers() + handler.Subscribe("test.*", func(msg *nats.Msg) { + res := string(msg.Data) + assert.Equal(t, "test", res) + }) + + handler.Subscribe("test.*", func(msg *nats.Msg) { + res := string(msg.Data) + assert.Equal(t, "test", res) + }) + + pub.Publish("test.all", []byte("test")) +} + +func TestNatsQueue(t *testing.T) { + pub, handler := initNatsHandlers() + wg := sync.WaitGroup{} + subCount := 0 + handler.SubscribeToQueue("test.*", "grp", func(msg *nats.Msg) { + subCount++ + wg.Done() + }) + + handler.SubscribeToQueue("test.*", "grp", func(msg *nats.Msg) { + subCount++ + wg.Done() + }) + + handler.SubscribeToQueue("test.*", "grp2", func(msg *nats.Msg) { + subCount++ + wg.Done() + }) + + pub.Publish("test.all", []byte("test")) + wg.Add(2) + wg.Wait() + assert.Equal(t, 2, subCount) +} + +func TestNatsChannelQueue(t *testing.T) { + pub, handler := initNatsHandlers() + msgChan := make(chan *nats.Msg) + wg := sync.WaitGroup{} + go func() { + for msg := range msgChan { + assert.Equal(t, "test", string(msg.Data)) + wg.Done() + } + }() + + handler.SubscribeToQueueUsingChannel("test.*", "grp", msgChan) + pub.Publish("test.topic", []byte("test")) + wg.Add(1) + wg.Wait() +} From df00b9b78564eb4f3d84e180112419796ff77cc5 Mon Sep 17 00:00:00 2001 From: lohuza Date: Thu, 29 Dec 2022 12:59:22 +0400 Subject: [PATCH 03/12] make publish multiple public --- nats/publisher.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nats/publisher.go b/nats/publisher.go index ace0767..06647ea 100644 --- a/nats/publisher.go +++ b/nats/publisher.go @@ -11,7 +11,7 @@ type eventPublisherBase interface { Request(subj string, data []byte) (*nats.Msg, error) RequestWithTimeout(subject string, data []byte, timeout time.Duration) (*nats.Msg, error) Publish(topic string, payload []byte) error - publishMultiple(topics []string, payload []byte) []error + PublishMultiple(topics []string, payload []byte) []error } type EventPublisher interface { @@ -78,7 +78,7 @@ func (p *publisherBase) Publish(topic string, payload []byte) error { return p.nc.Publish(topic, payload) } -func (p *publisherBase) publishMultiple(topics []string, payload []byte) []error { +func (p *publisherBase) PublishMultiple(topics []string, payload []byte) []error { wg := sync.WaitGroup{} var errors []error for _, topic := range topics { @@ -124,7 +124,7 @@ func (j *jsEventPublisher) Publish(topic string, payload []byte) error { return err } -func (j *jsEventPublisher) publishMultiple(topics []string, payload []byte) []error { +func (j *jsEventPublisher) PublishMultiple(topics []string, payload []byte) []error { wg := sync.WaitGroup{} var errors []error for _, topic := range topics { From cd20424c2f8c6c6279f629f5438041a9cac5e77d Mon Sep 17 00:00:00 2001 From: lohuza Date: Mon, 9 Jan 2023 04:46:39 +0400 Subject: [PATCH 04/12] include godoc --- nats/README.md | 4 ++++ nats/common.go | 3 +++ nats/handler.go | 14 ++++++++++++++ nats/publisher.go | 13 +++++++++++++ 4 files changed, 34 insertions(+) create mode 100644 nats/README.md diff --git a/nats/README.md b/nats/README.md new file mode 100644 index 0000000..890c5c5 --- /dev/null +++ b/nats/README.md @@ -0,0 +1,4 @@ +# Openware Nats + +openware common nats package. + diff --git a/nats/common.go b/nats/common.go index d376aa7..6f78c35 100644 --- a/nats/common.go +++ b/nats/common.go @@ -1,3 +1,4 @@ +// Package nats is used for handling nats (or nats jetstream) pub/sub package nats import ( @@ -5,12 +6,14 @@ import ( "github.com/nats-io/nats.go" ) +// InitNats initialize nats using connectionSting func InitNats(connectionString string) (*nats.Conn, error) { nc, err := nats.Connect(connectionString) return nc, err } +// InitEmbededNats initialize nats in memory func InitEmbededNats() (*nats.Conn, error) { opts := &server.Options{} ns, err := server.NewServer(opts) diff --git a/nats/handler.go b/nats/handler.go index 0389b31..67bca08 100644 --- a/nats/handler.go +++ b/nats/handler.go @@ -7,6 +7,7 @@ import ( "github.com/nats-io/nats.go" ) +// EventHandler handles subscribing and queue subscribing on nats and jetstream type EventHandler interface { SubscribeToQueueUsingChannel(subject string, group string, msgChannel chan *nats.Msg) error SubscribeToQueue(subject string, group string, cb nats.MsgHandler) error @@ -19,16 +20,19 @@ type eventHandlerBase struct { termination <-chan os.Signal } +// handlerConfig config for nats and jetstream type handlerConfig struct { autoUnsubscribeOnShutdown bool } +// NatsEventHandler nats event handler structure which implements EventHandler interface type NatsEventHandler struct { eventHandlerBase nc *nats.Conn config *handlerConfig } +// JsEventHandler jetstream event handler structure which implements EventHandler interface type JsEventHandler struct { eventHandlerBase js nats.JetStreamContext @@ -49,6 +53,7 @@ func newHandlerBase(termination <-chan os.Signal) eventHandlerBase { } } +// NewNatsHandler initializes new nats handler. func NewNatsHandler(nc *nats.Conn, termination <-chan os.Signal, config *handlerConfig) *NatsEventHandler { if config == nil { config = NewHandlerDefaultConfig() @@ -65,6 +70,7 @@ func NewNatsHandler(nc *nats.Conn, termination <-chan os.Signal, config *handler return &handler } +// NewJsHandler initializes new jetstream handler. func NewJsHandler(nc *nats.Conn, termination <-chan os.Signal, config *handlerConfig) (*JsEventHandler, error) { if config == nil { config = NewHandlerDefaultConfig() @@ -86,6 +92,7 @@ func NewJsHandler(nc *nats.Conn, termination <-chan os.Signal, config *handlerCo return handler, nil } +// NewHandlerDefaultConfig initialize default config for event handlers func NewHandlerDefaultConfig() *handlerConfig { return &handlerConfig{ autoUnsubscribeOnShutdown: true, @@ -110,6 +117,7 @@ func (h *eventHandlerBase) handleShutdown(unsubOnShutdown bool) []error { return nil } +// GetSubscriptions get list of subscriptions func (h *eventHandlerBase) GetSubscriptions() []*nats.Subscription { return h.subs } @@ -121,6 +129,7 @@ func (h *eventHandlerBase) pushSub(sub *nats.Subscription) { h.subs = append(h.subs, sub) } +// SubscribeToQueueUsingChannel subscribe to queue with channel. you'll receive all the new events into the channel func (j *JsEventHandler) SubscribeToQueueUsingChannel(subject string, group string, msgChannel chan *nats.Msg) error { sub, err := j.js.ChanQueueSubscribe(subject, group, msgChannel, nats.AckExplicit()) if err != nil { @@ -131,6 +140,7 @@ func (j *JsEventHandler) SubscribeToQueueUsingChannel(subject string, group stri return nil } +// SubscribeToQueue subscribe to queue using a callback. func (j *JsEventHandler) SubscribeToQueue(subject string, group string, cb nats.MsgHandler) error { sub, err := j.js.QueueSubscribe(subject, group, cb, nats.AckExplicit()) if err != nil { @@ -141,6 +151,7 @@ func (j *JsEventHandler) SubscribeToQueue(subject string, group string, cb nats. return nil } +// Subscribe subscribe using a callback. func (j *JsEventHandler) Subscribe(subject string, cb nats.MsgHandler) error { sub, err := j.js.Subscribe(subject, cb, nats.AckExplicit()) if err != nil { @@ -151,6 +162,7 @@ func (j *JsEventHandler) Subscribe(subject string, cb nats.MsgHandler) error { return nil } +// SubscribeToQueueUsingChannel subscribe to queue with channel. you'll receive all the new events into the channel func (n *NatsEventHandler) SubscribeToQueueUsingChannel(subject string, group string, msgChannel chan *nats.Msg) error { sub, err := n.nc.ChanQueueSubscribe(subject, group, msgChannel) if err != nil { @@ -161,6 +173,7 @@ func (n *NatsEventHandler) SubscribeToQueueUsingChannel(subject string, group st return nil } +// SubscribeToQueue subscribe to queue using a callback. func (n *NatsEventHandler) SubscribeToQueue(subject string, group string, cb nats.MsgHandler) error { sub, err := n.nc.QueueSubscribe(subject, group, cb) if err != nil { @@ -171,6 +184,7 @@ func (n *NatsEventHandler) SubscribeToQueue(subject string, group string, cb nat return nil } +// Subscribe subscribe using a callback. func (n *NatsEventHandler) Subscribe(subject string, cb nats.MsgHandler) error { sub, err := n.nc.Subscribe(subject, cb) diff --git a/nats/publisher.go b/nats/publisher.go index 06647ea..5eaac47 100644 --- a/nats/publisher.go +++ b/nats/publisher.go @@ -14,16 +14,19 @@ type eventPublisherBase interface { PublishMultiple(topics []string, payload []byte) []error } +// EventPublisher nats event publisher interface type EventPublisher interface { eventPublisherBase } +// JsEventPublisher jetstream event publisher and event stream manager type JsEventPublisher interface { eventPublisherBase CreateNewEventStream(string, []string) error DeleteEventStream(streamName string) error } +// publisherBase Base publisher stuct. it has base implementation for publishing events type publisherBase struct { nc *nats.Conn } @@ -43,6 +46,7 @@ type jsEventPublisher struct { var _ JsEventPublisher = (*jsEventPublisher)(nil) +// NewNatsEventPublisher initialize new nats event publisher func NewNatsEventPublisher(nc *nats.Conn) *natsEventPublisher { dispatcher := natsEventPublisher{ publisherBase{nc: nc}, @@ -51,6 +55,7 @@ func NewNatsEventPublisher(nc *nats.Conn) *natsEventPublisher { return &dispatcher } +// NewJsEventPublisher initialize new jetstream event publisher func NewJsEventPublisher(nc *nats.Conn) (*jsEventPublisher, error) { js, err := nc.JetStream() if err != nil { @@ -65,19 +70,23 @@ func NewJsEventPublisher(nc *nats.Conn) (*jsEventPublisher, error) { return &dispatcher, nil } +// Request make a request request to specific subject. (default timeout is set on 3 seconds) func (p *publisherBase) Request(subject string, data []byte) (*nats.Msg, error) { // TODO: maybe modify to something else return p.RequestWithTimeout(subject, data, time.Second*3) } +// RequestWithTimeout make a request to specific subject and specify timeout func (p *publisherBase) RequestWithTimeout(subject string, data []byte, timeout time.Duration) (*nats.Msg, error) { return p.nc.Request(subject, data, timeout) } +// Publish publish an event for specific topic func (p *publisherBase) Publish(topic string, payload []byte) error { return p.nc.Publish(topic, payload) } +// PublishMultiple publish an event for multiple payload. because each publish may result with error we return error array func (p *publisherBase) PublishMultiple(topics []string, payload []byte) []error { wg := sync.WaitGroup{} var errors []error @@ -96,6 +105,7 @@ func (p *publisherBase) PublishMultiple(topics []string, payload []byte) []error return errors } +// CreateNewEventStream create stream for specific subject for jetstream. if stream already exists nothing happens. func (j *jsEventPublisher) CreateNewEventStream(streamName string, subjects []string) error { stream, err := j.js.StreamInfo(streamName) if err != nil && err != nats.ErrStreamNotFound { @@ -115,15 +125,18 @@ func (j *jsEventPublisher) CreateNewEventStream(streamName string, subjects []st return nil } +// DeleteEventStream delete a stream in jetstream func (j *jsEventPublisher) DeleteEventStream(streamName string) error { return j.js.DeleteStream(streamName) } +// Publish publish a new event func (j *jsEventPublisher) Publish(topic string, payload []byte) error { _, err := j.js.Publish(topic, payload) return err } +// PublishMultiple publish an event for multiple payload. because each publish may result with error we return error array func (j *jsEventPublisher) PublishMultiple(topics []string, payload []byte) []error { wg := sync.WaitGroup{} var errors []error From 5a9590c5fb3a37b5166f00d4f4c767cfc62de81c Mon Sep 17 00:00:00 2001 From: lohuza Date: Mon, 9 Jan 2023 05:55:10 +0400 Subject: [PATCH 05/12] readme --- nats/README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/nats/README.md b/nats/README.md index 890c5c5..2552c64 100644 --- a/nats/README.md +++ b/nats/README.md @@ -2,3 +2,45 @@ openware common nats package. +first we need to initialize nats connection + +``` +connectionString = "localhost:4222" +nc, err := nats.InitNats(connectionString) +``` +we need nc for initializing handlers and subscribers + + +### Publisher + +we have publisher for publishing event using either nats or Jetstream + +to intialize nats and publish an event: +``` +pub, err := nats.NewNatsEventPublisher(nc) +// handle error +pub.Publish("foo.bar", []byte("baz")) +``` + +to initialize jetstream and publish an event: +``` +js, err := nats.NewJsEventPublisher(nc) +// handle error +js.Publish("foo.bar", []byte("baz")) +``` +keep in mind that before publishing an event you'll also need to have a stream of the topic. to create a stream: +``` +err := js.CreateNewEventStream("foo", []string{"foo.baz", "foo.bar"}) +// handle error +``` + +### Subscriber +With subscribers we can subscribe to different topics. If we subscribe using queue, subscribers with the same group name will receive an event once. + +to intiialize nats and subscribe to a topic. +we can pass ```<-chan os.Signal``` for subscribing to shutdown event. so at the end it can cleanup +``` +handler := nats.NewNatsHandler(nc, terminationChannel, nats.NewHandlerDefaultConfig()) +msgChan := make(chan *nats.Msg) +handler.SubscribeToQueueUsingChannel("foo.baz", "bar", msgChannel) +``` \ No newline at end of file From d6c22a852be85337fd0a586f673ee0aa2f674aa2 Mon Sep 17 00:00:00 2001 From: lohuza Date: Mon, 9 Jan 2023 13:32:47 +0400 Subject: [PATCH 06/12] moved interface compatibility checks into tests. --- nats/handler.go | 6 ------ nats/nats_test.go | 10 ++++++++++ nats/publisher.go | 6 ------ 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/nats/handler.go b/nats/handler.go index 67bca08..e207d97 100644 --- a/nats/handler.go +++ b/nats/handler.go @@ -39,12 +39,6 @@ type JsEventHandler struct { config *handlerConfig } -// For Checking compatibility -var ( - _ EventHandler = (*NatsEventHandler)(nil) - _ EventHandler = (*JsEventHandler)(nil) -) - func newHandlerBase(termination <-chan os.Signal) eventHandlerBase { return eventHandlerBase{ termination: termination, diff --git a/nats/nats_test.go b/nats/nats_test.go index 36e4432..84b3191 100644 --- a/nats/nats_test.go +++ b/nats/nats_test.go @@ -8,6 +8,16 @@ import ( "github.com/stretchr/testify/assert" ) +// For Checking compatibility +var ( + _ EventHandler = (*NatsEventHandler)(nil) + _ EventHandler = (*JsEventHandler)(nil) + + _ eventPublisherBase = (*publisherBase)(nil) + _ EventPublisher = (*natsEventPublisher)(nil) + _ JsEventPublisher = (*jsEventPublisher)(nil) +) + func initNatsHandlers() (*natsEventPublisher, *NatsEventHandler) { nc, _ := InitEmbededNats() publisher := NewNatsEventPublisher(nc) diff --git a/nats/publisher.go b/nats/publisher.go index 5eaac47..a811c7c 100644 --- a/nats/publisher.go +++ b/nats/publisher.go @@ -31,21 +31,15 @@ type publisherBase struct { nc *nats.Conn } -var _ eventPublisherBase = (*publisherBase)(nil) - type natsEventPublisher struct { publisherBase } -var _ EventPublisher = (*natsEventPublisher)(nil) - type jsEventPublisher struct { publisherBase js nats.JetStreamContext } -var _ JsEventPublisher = (*jsEventPublisher)(nil) - // NewNatsEventPublisher initialize new nats event publisher func NewNatsEventPublisher(nc *nats.Conn) *natsEventPublisher { dispatcher := natsEventPublisher{ From d0579d9f5cb655adbd8ef11bf037584397b900bb Mon Sep 17 00:00:00 2001 From: lohuza Date: Mon, 16 Jan 2023 04:33:24 +0400 Subject: [PATCH 07/12] added new protocols, health check and ms commands --- nats/command/command.go | 61 ++ nats/command/listener.go | 1 + nats/go.mod | 6 +- nats/go.sum | 32 + nats/health/service.go | 19 + nats/health/service_test.go | 1 + nats/protocol/message.go | 67 ++ nats/protocol/message_gen.go | 1012 +++++++++++++++++++++++++++++ nats/protocol/message_gen_test.go | 462 +++++++++++++ 9 files changed, 1660 insertions(+), 1 deletion(-) create mode 100644 nats/command/command.go create mode 100644 nats/command/listener.go create mode 100644 nats/health/service.go create mode 100644 nats/health/service_test.go create mode 100644 nats/protocol/message.go create mode 100644 nats/protocol/message_gen.go create mode 100644 nats/protocol/message_gen_test.go diff --git a/nats/command/command.go b/nats/command/command.go new file mode 100644 index 0000000..786fa70 --- /dev/null +++ b/nats/command/command.go @@ -0,0 +1,61 @@ +package command + +import ( + "errors" + + "github.com/openware/pkg/nats/protocol" +) + +const ( + RESTART_SERVICE_COMMAND = "service_restart" + LOAD_CONFIG_COMMAND = "load_config" + SET_CONFIG_VALUE_COMMAND = "set_config" +) + +var ( + InvalidParamError = errors.New("invalid param in payload") +) + +type ( + ServiceRestartCommand protocol.RequestMessage + LoadConfigCommand protocol.RequestMessage + SetConfigValueCommand protocol.RequestMessage +) + +func NewServiceRestartCommand(msgId uint32) *ServiceRestartCommand { + return (*ServiceRestartCommand)(protocol.NewRequestMessage(msgId, RESTART_SERVICE_COMMAND, nil)) +} + +func NewLoadConfigCommand(msgId uint32, param string) *LoadConfigCommand { + return (*LoadConfigCommand)(protocol.NewRequestMessage(msgId, LOAD_CONFIG_COMMAND, []interface{}{param})) +} + +func NewSetConfigValueCommand(msgId uint32, config [2]string) *SetConfigValueCommand { + return (*SetConfigValueCommand)(protocol.NewRequestMessage(msgId, SET_CONFIG_VALUE_COMMAND, []interface{}{config})) +} + +func (command *LoadConfigCommand) ReadParam() (string, error) { + if len(command.Params) != 1 { + return "", InvalidParamError + } + + val, ok := command.Params[0].(string) + if !ok { + return "", InvalidParamError + } + + return val, nil +} + +func (command *SetConfigValueCommand) ReadConfig() ([2]string, error) { + if len(command.Params) != 1 { + return [2]string{}, InvalidParamError + } + + val, ok := command.Params[0].([2]string) + if !ok { + return [2]string{}, InvalidParamError + } + + return val, nil +} diff --git a/nats/command/listener.go b/nats/command/listener.go new file mode 100644 index 0000000..d47dcf0 --- /dev/null +++ b/nats/command/listener.go @@ -0,0 +1 @@ +package command diff --git a/nats/go.mod b/nats/go.mod index 806de52..3724a4d 100644 --- a/nats/go.mod +++ b/nats/go.mod @@ -11,12 +11,16 @@ require ( github.com/nats-io/nats.go v1.22.1 // indirect github.com/nats-io/nkeys v0.3.0 // indirect github.com/nats-io/nuid v1.0.1 // indirect + github.com/philhofer/fwd v1.1.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/stretchr/testify v1.8.1 // indirect + github.com/tinylib/msgp v1.1.8 // indirect go.uber.org/automaxprocs v1.5.1 // indirect golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect - golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect + golang.org/x/mod v0.7.0 // indirect + golang.org/x/sys v0.3.0 // indirect golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect + golang.org/x/tools v0.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/nats/go.sum b/nats/go.sum index dcb1557..07ed1cf 100644 --- a/nats/go.sum +++ b/nats/go.sum @@ -15,6 +15,8 @@ github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -25,22 +27,52 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= +github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI= golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/nats/health/service.go b/nats/health/service.go new file mode 100644 index 0000000..f59080d --- /dev/null +++ b/nats/health/service.go @@ -0,0 +1,19 @@ +package health + +import ( + "time" + + "github.com/openware/pkg/nats" +) + +func RunHealthService(publisher nats.EventPublisher, serviceName string, period time.Duration, cb func() string) { + subjectName := serviceName + ".metric" + go publishHealthReport(publisher, subjectName, period, cb) +} + +func publishHealthReport(publisher nats.EventPublisher, subject string, period time.Duration, cb func() string) { + for range time.Tick(period) { + result := cb() + publisher.Publish(subject, []byte(result)) + } +} diff --git a/nats/health/service_test.go b/nats/health/service_test.go new file mode 100644 index 0000000..b2c91c4 --- /dev/null +++ b/nats/health/service_test.go @@ -0,0 +1 @@ +package health diff --git a/nats/protocol/message.go b/nats/protocol/message.go new file mode 100644 index 0000000..dff7c96 --- /dev/null +++ b/nats/protocol/message.go @@ -0,0 +1,67 @@ +package protocol + +type MessageType int32 + +const ( + Request MessageType = iota + Response + Event +) + +//go:generate msgp + +type MessageBase struct { + Type MessageType `msg:"message_type"` + MsgId uint32 `msg:"msg_id"` +} + +type RequestMessage struct { + MessageBase + Method string `msg:"method"` + Params []interface{} `msg:"params"` +} + +type ResponseMessage struct { + MessageBase + Error interface{} `msg:"method"` + Result interface{} `msg:"result"` +} + +type EventMessage struct { + MessageBase + Method string `msg:"method"` + Params []interface{} `msg:"params"` +} + +func NewRequestMessage(msgId uint32, method string, params []interface{}) *RequestMessage { + return &RequestMessage{ + MessageBase: MessageBase{ + Type: Request, + MsgId: msgId, + }, + Method: method, + Params: params, + } +} + +func NewResponseMessage(msgId uint32, error any, result interface{}) *ResponseMessage { + return &ResponseMessage{ + MessageBase: MessageBase{ + Type: Response, + MsgId: msgId, + }, + Error: nil, + Result: nil, + } +} + +func NewEventMessage(msgId uint32, method string, params []interface{}) *EventMessage { + return &EventMessage{ + MessageBase: MessageBase{ + Type: Event, + MsgId: msgId, + }, + Method: method, + Params: params, + } +} diff --git a/nats/protocol/message_gen.go b/nats/protocol/message_gen.go new file mode 100644 index 0000000..11f067b --- /dev/null +++ b/nats/protocol/message_gen.go @@ -0,0 +1,1012 @@ +package protocol + +// Code generated by github.com/tinylib/msgp DO NOT EDIT. + +import ( + "github.com/tinylib/msgp/msgp" +) + +// DecodeMsg implements msgp.Decodable +func (z *EventMessage) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "MessageBase": + var zb0002 uint32 + zb0002, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + for zb0002 > 0 { + zb0002-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + switch msgp.UnsafeString(field) { + case "message_type": + { + var zb0003 int32 + zb0003, err = dc.ReadInt32() + if err != nil { + err = msgp.WrapError(err, "MessageBase", "Type") + return + } + z.MessageBase.Type = MessageType(zb0003) + } + case "msg_id": + z.MessageBase.MsgId, err = dc.ReadUint32() + if err != nil { + err = msgp.WrapError(err, "MessageBase", "MsgId") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + } + } + case "method": + z.Method, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Method") + return + } + case "params": + var zb0004 uint32 + zb0004, err = dc.ReadArrayHeader() + if err != nil { + err = msgp.WrapError(err, "Params") + return + } + if cap(z.Params) >= int(zb0004) { + z.Params = (z.Params)[:zb0004] + } else { + z.Params = make([]interface{}, zb0004) + } + for za0001 := range z.Params { + z.Params[za0001], err = dc.ReadIntf() + if err != nil { + err = msgp.WrapError(err, "Params", za0001) + return + } + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z *EventMessage) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 3 + // write "MessageBase" + err = en.Append(0x83, 0xab, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65) + if err != nil { + return + } + // map header, size 2 + // write "message_type" + err = en.Append(0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) + if err != nil { + return + } + err = en.WriteInt32(int32(z.MessageBase.Type)) + if err != nil { + err = msgp.WrapError(err, "MessageBase", "Type") + return + } + // write "msg_id" + err = en.Append(0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteUint32(z.MessageBase.MsgId) + if err != nil { + err = msgp.WrapError(err, "MessageBase", "MsgId") + return + } + // write "method" + err = en.Append(0xa6, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64) + if err != nil { + return + } + err = en.WriteString(z.Method) + if err != nil { + err = msgp.WrapError(err, "Method") + return + } + // write "params" + err = en.Append(0xa6, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73) + if err != nil { + return + } + err = en.WriteArrayHeader(uint32(len(z.Params))) + if err != nil { + err = msgp.WrapError(err, "Params") + return + } + for za0001 := range z.Params { + err = en.WriteIntf(z.Params[za0001]) + if err != nil { + err = msgp.WrapError(err, "Params", za0001) + return + } + } + return +} + +// MarshalMsg implements msgp.Marshaler +func (z *EventMessage) MarshalMsg(b []byte) (o []byte, err error) { + o = msgp.Require(b, z.Msgsize()) + // map header, size 3 + // string "MessageBase" + o = append(o, 0x83, 0xab, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65) + // map header, size 2 + // string "message_type" + o = append(o, 0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) + o = msgp.AppendInt32(o, int32(z.MessageBase.Type)) + // string "msg_id" + o = append(o, 0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) + o = msgp.AppendUint32(o, z.MessageBase.MsgId) + // string "method" + o = append(o, 0xa6, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64) + o = msgp.AppendString(o, z.Method) + // string "params" + o = append(o, 0xa6, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73) + o = msgp.AppendArrayHeader(o, uint32(len(z.Params))) + for za0001 := range z.Params { + o, err = msgp.AppendIntf(o, z.Params[za0001]) + if err != nil { + err = msgp.WrapError(err, "Params", za0001) + return + } + } + return +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *EventMessage) UnmarshalMsg(bts []byte) (o []byte, err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "MessageBase": + var zb0002 uint32 + zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + for zb0002 > 0 { + zb0002-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + switch msgp.UnsafeString(field) { + case "message_type": + { + var zb0003 int32 + zb0003, bts, err = msgp.ReadInt32Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase", "Type") + return + } + z.MessageBase.Type = MessageType(zb0003) + } + case "msg_id": + z.MessageBase.MsgId, bts, err = msgp.ReadUint32Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase", "MsgId") + return + } + default: + bts, err = msgp.Skip(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + } + } + case "method": + z.Method, bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Method") + return + } + case "params": + var zb0004 uint32 + zb0004, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Params") + return + } + if cap(z.Params) >= int(zb0004) { + z.Params = (z.Params)[:zb0004] + } else { + z.Params = make([]interface{}, zb0004) + } + for za0001 := range z.Params { + z.Params[za0001], bts, err = msgp.ReadIntfBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Params", za0001) + return + } + } + default: + bts, err = msgp.Skip(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + o = bts + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z *EventMessage) Msgsize() (s int) { + s = 1 + 12 + 1 + 13 + msgp.Int32Size + 7 + msgp.Uint32Size + 7 + msgp.StringPrefixSize + len(z.Method) + 7 + msgp.ArrayHeaderSize + for za0001 := range z.Params { + s += msgp.GuessSize(z.Params[za0001]) + } + return +} + +// DecodeMsg implements msgp.Decodable +func (z *MessageBase) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "message_type": + { + var zb0002 int32 + zb0002, err = dc.ReadInt32() + if err != nil { + err = msgp.WrapError(err, "Type") + return + } + z.Type = MessageType(zb0002) + } + case "msg_id": + z.MsgId, err = dc.ReadUint32() + if err != nil { + err = msgp.WrapError(err, "MsgId") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z MessageBase) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 2 + // write "message_type" + err = en.Append(0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) + if err != nil { + return + } + err = en.WriteInt32(int32(z.Type)) + if err != nil { + err = msgp.WrapError(err, "Type") + return + } + // write "msg_id" + err = en.Append(0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteUint32(z.MsgId) + if err != nil { + err = msgp.WrapError(err, "MsgId") + return + } + return +} + +// MarshalMsg implements msgp.Marshaler +func (z MessageBase) MarshalMsg(b []byte) (o []byte, err error) { + o = msgp.Require(b, z.Msgsize()) + // map header, size 2 + // string "message_type" + o = append(o, 0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) + o = msgp.AppendInt32(o, int32(z.Type)) + // string "msg_id" + o = append(o, 0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) + o = msgp.AppendUint32(o, z.MsgId) + return +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *MessageBase) UnmarshalMsg(bts []byte) (o []byte, err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "message_type": + { + var zb0002 int32 + zb0002, bts, err = msgp.ReadInt32Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "Type") + return + } + z.Type = MessageType(zb0002) + } + case "msg_id": + z.MsgId, bts, err = msgp.ReadUint32Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "MsgId") + return + } + default: + bts, err = msgp.Skip(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + o = bts + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z MessageBase) Msgsize() (s int) { + s = 1 + 13 + msgp.Int32Size + 7 + msgp.Uint32Size + return +} + +// DecodeMsg implements msgp.Decodable +func (z *MessageType) DecodeMsg(dc *msgp.Reader) (err error) { + { + var zb0001 int32 + zb0001, err = dc.ReadInt32() + if err != nil { + err = msgp.WrapError(err) + return + } + (*z) = MessageType(zb0001) + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z MessageType) EncodeMsg(en *msgp.Writer) (err error) { + err = en.WriteInt32(int32(z)) + if err != nil { + err = msgp.WrapError(err) + return + } + return +} + +// MarshalMsg implements msgp.Marshaler +func (z MessageType) MarshalMsg(b []byte) (o []byte, err error) { + o = msgp.Require(b, z.Msgsize()) + o = msgp.AppendInt32(o, int32(z)) + return +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *MessageType) UnmarshalMsg(bts []byte) (o []byte, err error) { + { + var zb0001 int32 + zb0001, bts, err = msgp.ReadInt32Bytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + (*z) = MessageType(zb0001) + } + o = bts + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z MessageType) Msgsize() (s int) { + s = msgp.Int32Size + return +} + +// DecodeMsg implements msgp.Decodable +func (z *RequestMessage) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "MessageBase": + var zb0002 uint32 + zb0002, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + for zb0002 > 0 { + zb0002-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + switch msgp.UnsafeString(field) { + case "message_type": + { + var zb0003 int32 + zb0003, err = dc.ReadInt32() + if err != nil { + err = msgp.WrapError(err, "MessageBase", "Type") + return + } + z.MessageBase.Type = MessageType(zb0003) + } + case "msg_id": + z.MessageBase.MsgId, err = dc.ReadUint32() + if err != nil { + err = msgp.WrapError(err, "MessageBase", "MsgId") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + } + } + case "method": + z.Method, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Method") + return + } + case "params": + var zb0004 uint32 + zb0004, err = dc.ReadArrayHeader() + if err != nil { + err = msgp.WrapError(err, "Params") + return + } + if cap(z.Params) >= int(zb0004) { + z.Params = (z.Params)[:zb0004] + } else { + z.Params = make([]interface{}, zb0004) + } + for za0001 := range z.Params { + z.Params[za0001], err = dc.ReadIntf() + if err != nil { + err = msgp.WrapError(err, "Params", za0001) + return + } + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z *RequestMessage) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 3 + // write "MessageBase" + err = en.Append(0x83, 0xab, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65) + if err != nil { + return + } + // map header, size 2 + // write "message_type" + err = en.Append(0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) + if err != nil { + return + } + err = en.WriteInt32(int32(z.MessageBase.Type)) + if err != nil { + err = msgp.WrapError(err, "MessageBase", "Type") + return + } + // write "msg_id" + err = en.Append(0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteUint32(z.MessageBase.MsgId) + if err != nil { + err = msgp.WrapError(err, "MessageBase", "MsgId") + return + } + // write "method" + err = en.Append(0xa6, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64) + if err != nil { + return + } + err = en.WriteString(z.Method) + if err != nil { + err = msgp.WrapError(err, "Method") + return + } + // write "params" + err = en.Append(0xa6, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73) + if err != nil { + return + } + err = en.WriteArrayHeader(uint32(len(z.Params))) + if err != nil { + err = msgp.WrapError(err, "Params") + return + } + for za0001 := range z.Params { + err = en.WriteIntf(z.Params[za0001]) + if err != nil { + err = msgp.WrapError(err, "Params", za0001) + return + } + } + return +} + +// MarshalMsg implements msgp.Marshaler +func (z *RequestMessage) MarshalMsg(b []byte) (o []byte, err error) { + o = msgp.Require(b, z.Msgsize()) + // map header, size 3 + // string "MessageBase" + o = append(o, 0x83, 0xab, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65) + // map header, size 2 + // string "message_type" + o = append(o, 0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) + o = msgp.AppendInt32(o, int32(z.MessageBase.Type)) + // string "msg_id" + o = append(o, 0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) + o = msgp.AppendUint32(o, z.MessageBase.MsgId) + // string "method" + o = append(o, 0xa6, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64) + o = msgp.AppendString(o, z.Method) + // string "params" + o = append(o, 0xa6, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73) + o = msgp.AppendArrayHeader(o, uint32(len(z.Params))) + for za0001 := range z.Params { + o, err = msgp.AppendIntf(o, z.Params[za0001]) + if err != nil { + err = msgp.WrapError(err, "Params", za0001) + return + } + } + return +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *RequestMessage) UnmarshalMsg(bts []byte) (o []byte, err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "MessageBase": + var zb0002 uint32 + zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + for zb0002 > 0 { + zb0002-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + switch msgp.UnsafeString(field) { + case "message_type": + { + var zb0003 int32 + zb0003, bts, err = msgp.ReadInt32Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase", "Type") + return + } + z.MessageBase.Type = MessageType(zb0003) + } + case "msg_id": + z.MessageBase.MsgId, bts, err = msgp.ReadUint32Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase", "MsgId") + return + } + default: + bts, err = msgp.Skip(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + } + } + case "method": + z.Method, bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Method") + return + } + case "params": + var zb0004 uint32 + zb0004, bts, err = msgp.ReadArrayHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Params") + return + } + if cap(z.Params) >= int(zb0004) { + z.Params = (z.Params)[:zb0004] + } else { + z.Params = make([]interface{}, zb0004) + } + for za0001 := range z.Params { + z.Params[za0001], bts, err = msgp.ReadIntfBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Params", za0001) + return + } + } + default: + bts, err = msgp.Skip(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + o = bts + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z *RequestMessage) Msgsize() (s int) { + s = 1 + 12 + 1 + 13 + msgp.Int32Size + 7 + msgp.Uint32Size + 7 + msgp.StringPrefixSize + len(z.Method) + 7 + msgp.ArrayHeaderSize + for za0001 := range z.Params { + s += msgp.GuessSize(z.Params[za0001]) + } + return +} + +// DecodeMsg implements msgp.Decodable +func (z *ResponseMessage) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "MessageBase": + var zb0002 uint32 + zb0002, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + for zb0002 > 0 { + zb0002-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + switch msgp.UnsafeString(field) { + case "message_type": + { + var zb0003 int32 + zb0003, err = dc.ReadInt32() + if err != nil { + err = msgp.WrapError(err, "MessageBase", "Type") + return + } + z.MessageBase.Type = MessageType(zb0003) + } + case "msg_id": + z.MessageBase.MsgId, err = dc.ReadUint32() + if err != nil { + err = msgp.WrapError(err, "MessageBase", "MsgId") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + } + } + case "method": + z.Error, err = dc.ReadIntf() + if err != nil { + err = msgp.WrapError(err, "Error") + return + } + case "result": + z.Result, err = dc.ReadIntf() + if err != nil { + err = msgp.WrapError(err, "Result") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z *ResponseMessage) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 3 + // write "MessageBase" + err = en.Append(0x83, 0xab, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65) + if err != nil { + return + } + // map header, size 2 + // write "message_type" + err = en.Append(0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) + if err != nil { + return + } + err = en.WriteInt32(int32(z.MessageBase.Type)) + if err != nil { + err = msgp.WrapError(err, "MessageBase", "Type") + return + } + // write "msg_id" + err = en.Append(0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteUint32(z.MessageBase.MsgId) + if err != nil { + err = msgp.WrapError(err, "MessageBase", "MsgId") + return + } + // write "method" + err = en.Append(0xa6, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64) + if err != nil { + return + } + err = en.WriteIntf(z.Error) + if err != nil { + err = msgp.WrapError(err, "Error") + return + } + // write "result" + err = en.Append(0xa6, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74) + if err != nil { + return + } + err = en.WriteIntf(z.Result) + if err != nil { + err = msgp.WrapError(err, "Result") + return + } + return +} + +// MarshalMsg implements msgp.Marshaler +func (z *ResponseMessage) MarshalMsg(b []byte) (o []byte, err error) { + o = msgp.Require(b, z.Msgsize()) + // map header, size 3 + // string "MessageBase" + o = append(o, 0x83, 0xab, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65) + // map header, size 2 + // string "message_type" + o = append(o, 0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) + o = msgp.AppendInt32(o, int32(z.MessageBase.Type)) + // string "msg_id" + o = append(o, 0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) + o = msgp.AppendUint32(o, z.MessageBase.MsgId) + // string "method" + o = append(o, 0xa6, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64) + o, err = msgp.AppendIntf(o, z.Error) + if err != nil { + err = msgp.WrapError(err, "Error") + return + } + // string "result" + o = append(o, 0xa6, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74) + o, err = msgp.AppendIntf(o, z.Result) + if err != nil { + err = msgp.WrapError(err, "Result") + return + } + return +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *ResponseMessage) UnmarshalMsg(bts []byte) (o []byte, err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "MessageBase": + var zb0002 uint32 + zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + for zb0002 > 0 { + zb0002-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + switch msgp.UnsafeString(field) { + case "message_type": + { + var zb0003 int32 + zb0003, bts, err = msgp.ReadInt32Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase", "Type") + return + } + z.MessageBase.Type = MessageType(zb0003) + } + case "msg_id": + z.MessageBase.MsgId, bts, err = msgp.ReadUint32Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase", "MsgId") + return + } + default: + bts, err = msgp.Skip(bts) + if err != nil { + err = msgp.WrapError(err, "MessageBase") + return + } + } + } + case "method": + z.Error, bts, err = msgp.ReadIntfBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Error") + return + } + case "result": + z.Result, bts, err = msgp.ReadIntfBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Result") + return + } + default: + bts, err = msgp.Skip(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + o = bts + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z *ResponseMessage) Msgsize() (s int) { + s = 1 + 12 + 1 + 13 + msgp.Int32Size + 7 + msgp.Uint32Size + 7 + msgp.GuessSize(z.Error) + 7 + msgp.GuessSize(z.Result) + return +} diff --git a/nats/protocol/message_gen_test.go b/nats/protocol/message_gen_test.go new file mode 100644 index 0000000..d0906e0 --- /dev/null +++ b/nats/protocol/message_gen_test.go @@ -0,0 +1,462 @@ +package protocol + +// Code generated by github.com/tinylib/msgp DO NOT EDIT. + +import ( + "bytes" + "testing" + + "github.com/tinylib/msgp/msgp" +) + +func TestMarshalUnmarshalEventMessage(t *testing.T) { + v := EventMessage{} + bts, err := v.MarshalMsg(nil) + if err != nil { + t.Fatal(err) + } + left, err := v.UnmarshalMsg(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) + } + + left, err = msgp.Skip(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after Skip(): %q", len(left), left) + } +} + +func BenchmarkMarshalMsgEventMessage(b *testing.B) { + v := EventMessage{} + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.MarshalMsg(nil) + } +} + +func BenchmarkAppendMsgEventMessage(b *testing.B) { + v := EventMessage{} + bts := make([]byte, 0, v.Msgsize()) + bts, _ = v.MarshalMsg(bts[0:0]) + b.SetBytes(int64(len(bts))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + bts, _ = v.MarshalMsg(bts[0:0]) + } +} + +func BenchmarkUnmarshalEventMessage(b *testing.B) { + v := EventMessage{} + bts, _ := v.MarshalMsg(nil) + b.ReportAllocs() + b.SetBytes(int64(len(bts))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := v.UnmarshalMsg(bts) + if err != nil { + b.Fatal(err) + } + } +} + +func TestEncodeDecodeEventMessage(t *testing.T) { + v := EventMessage{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + + m := v.Msgsize() + if buf.Len() > m { + t.Log("WARNING: TestEncodeDecodeEventMessage Msgsize() is inaccurate") + } + + vn := EventMessage{} + err := msgp.Decode(&buf, &vn) + if err != nil { + t.Error(err) + } + + buf.Reset() + msgp.Encode(&buf, &v) + err = msgp.NewReader(&buf).Skip() + if err != nil { + t.Error(err) + } +} + +func BenchmarkEncodeEventMessage(b *testing.B) { + v := EventMessage{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + en := msgp.NewWriter(msgp.Nowhere) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.EncodeMsg(en) + } + en.Flush() +} + +func BenchmarkDecodeEventMessage(b *testing.B) { + v := EventMessage{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + rd := msgp.NewEndlessReader(buf.Bytes(), b) + dc := msgp.NewReader(rd) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := v.DecodeMsg(dc) + if err != nil { + b.Fatal(err) + } + } +} + +func TestMarshalUnmarshalMessageBase(t *testing.T) { + v := MessageBase{} + bts, err := v.MarshalMsg(nil) + if err != nil { + t.Fatal(err) + } + left, err := v.UnmarshalMsg(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) + } + + left, err = msgp.Skip(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after Skip(): %q", len(left), left) + } +} + +func BenchmarkMarshalMsgMessageBase(b *testing.B) { + v := MessageBase{} + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.MarshalMsg(nil) + } +} + +func BenchmarkAppendMsgMessageBase(b *testing.B) { + v := MessageBase{} + bts := make([]byte, 0, v.Msgsize()) + bts, _ = v.MarshalMsg(bts[0:0]) + b.SetBytes(int64(len(bts))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + bts, _ = v.MarshalMsg(bts[0:0]) + } +} + +func BenchmarkUnmarshalMessageBase(b *testing.B) { + v := MessageBase{} + bts, _ := v.MarshalMsg(nil) + b.ReportAllocs() + b.SetBytes(int64(len(bts))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := v.UnmarshalMsg(bts) + if err != nil { + b.Fatal(err) + } + } +} + +func TestEncodeDecodeMessageBase(t *testing.T) { + v := MessageBase{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + + m := v.Msgsize() + if buf.Len() > m { + t.Log("WARNING: TestEncodeDecodeMessageBase Msgsize() is inaccurate") + } + + vn := MessageBase{} + err := msgp.Decode(&buf, &vn) + if err != nil { + t.Error(err) + } + + buf.Reset() + msgp.Encode(&buf, &v) + err = msgp.NewReader(&buf).Skip() + if err != nil { + t.Error(err) + } +} + +func BenchmarkEncodeMessageBase(b *testing.B) { + v := MessageBase{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + en := msgp.NewWriter(msgp.Nowhere) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.EncodeMsg(en) + } + en.Flush() +} + +func BenchmarkDecodeMessageBase(b *testing.B) { + v := MessageBase{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + rd := msgp.NewEndlessReader(buf.Bytes(), b) + dc := msgp.NewReader(rd) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := v.DecodeMsg(dc) + if err != nil { + b.Fatal(err) + } + } +} + +func TestMarshalUnmarshalRequestMessage(t *testing.T) { + v := RequestMessage{} + bts, err := v.MarshalMsg(nil) + if err != nil { + t.Fatal(err) + } + left, err := v.UnmarshalMsg(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) + } + + left, err = msgp.Skip(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after Skip(): %q", len(left), left) + } +} + +func BenchmarkMarshalMsgRequestMessage(b *testing.B) { + v := RequestMessage{} + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.MarshalMsg(nil) + } +} + +func BenchmarkAppendMsgRequestMessage(b *testing.B) { + v := RequestMessage{} + bts := make([]byte, 0, v.Msgsize()) + bts, _ = v.MarshalMsg(bts[0:0]) + b.SetBytes(int64(len(bts))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + bts, _ = v.MarshalMsg(bts[0:0]) + } +} + +func BenchmarkUnmarshalRequestMessage(b *testing.B) { + v := RequestMessage{} + bts, _ := v.MarshalMsg(nil) + b.ReportAllocs() + b.SetBytes(int64(len(bts))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := v.UnmarshalMsg(bts) + if err != nil { + b.Fatal(err) + } + } +} + +func TestEncodeDecodeRequestMessage(t *testing.T) { + v := RequestMessage{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + + m := v.Msgsize() + if buf.Len() > m { + t.Log("WARNING: TestEncodeDecodeRequestMessage Msgsize() is inaccurate") + } + + vn := RequestMessage{} + err := msgp.Decode(&buf, &vn) + if err != nil { + t.Error(err) + } + + buf.Reset() + msgp.Encode(&buf, &v) + err = msgp.NewReader(&buf).Skip() + if err != nil { + t.Error(err) + } +} + +func BenchmarkEncodeRequestMessage(b *testing.B) { + v := RequestMessage{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + en := msgp.NewWriter(msgp.Nowhere) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.EncodeMsg(en) + } + en.Flush() +} + +func BenchmarkDecodeRequestMessage(b *testing.B) { + v := RequestMessage{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + rd := msgp.NewEndlessReader(buf.Bytes(), b) + dc := msgp.NewReader(rd) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := v.DecodeMsg(dc) + if err != nil { + b.Fatal(err) + } + } +} + +func TestMarshalUnmarshalResponseMessage(t *testing.T) { + v := ResponseMessage{} + bts, err := v.MarshalMsg(nil) + if err != nil { + t.Fatal(err) + } + left, err := v.UnmarshalMsg(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) + } + + left, err = msgp.Skip(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after Skip(): %q", len(left), left) + } +} + +func BenchmarkMarshalMsgResponseMessage(b *testing.B) { + v := ResponseMessage{} + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.MarshalMsg(nil) + } +} + +func BenchmarkAppendMsgResponseMessage(b *testing.B) { + v := ResponseMessage{} + bts := make([]byte, 0, v.Msgsize()) + bts, _ = v.MarshalMsg(bts[0:0]) + b.SetBytes(int64(len(bts))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + bts, _ = v.MarshalMsg(bts[0:0]) + } +} + +func BenchmarkUnmarshalResponseMessage(b *testing.B) { + v := ResponseMessage{} + bts, _ := v.MarshalMsg(nil) + b.ReportAllocs() + b.SetBytes(int64(len(bts))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := v.UnmarshalMsg(bts) + if err != nil { + b.Fatal(err) + } + } +} + +func TestEncodeDecodeResponseMessage(t *testing.T) { + v := ResponseMessage{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + + m := v.Msgsize() + if buf.Len() > m { + t.Log("WARNING: TestEncodeDecodeResponseMessage Msgsize() is inaccurate") + } + + vn := ResponseMessage{} + err := msgp.Decode(&buf, &vn) + if err != nil { + t.Error(err) + } + + buf.Reset() + msgp.Encode(&buf, &v) + err = msgp.NewReader(&buf).Skip() + if err != nil { + t.Error(err) + } +} + +func BenchmarkEncodeResponseMessage(b *testing.B) { + v := ResponseMessage{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + en := msgp.NewWriter(msgp.Nowhere) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.EncodeMsg(en) + } + en.Flush() +} + +func BenchmarkDecodeResponseMessage(b *testing.B) { + v := ResponseMessage{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + rd := msgp.NewEndlessReader(buf.Bytes(), b) + dc := msgp.NewReader(rd) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := v.DecodeMsg(dc) + if err != nil { + b.Fatal(err) + } + } +} From 3b2a5071b39ea1ef2262c54e62d180385c3ac5e6 Mon Sep 17 00:00:00 2001 From: lohuza Date: Mon, 16 Jan 2023 06:36:55 +0400 Subject: [PATCH 08/12] command listener --- nats/command/listener.go | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/nats/command/listener.go b/nats/command/listener.go index d47dcf0..bc43b6b 100644 --- a/nats/command/listener.go +++ b/nats/command/listener.go @@ -1 +1,79 @@ package command + +import ( + "time" + + natspkg "github.com/nats-io/nats.go" + "github.com/openware/pkg/nats" + "github.com/openware/pkg/nats/protocol" +) + +type CommandListener interface { + ListenToCommands() + SetServiceRestartCb(cb func() *protocol.ResponseMessage) + SetConfigLoadCb(cb func(string) *protocol.ResponseMessage) + SetConfigValueCb(cb func(string, string) *protocol.ResponseMessage) +} + +type commandListener struct { + subjectName string + handler nats.EventHandler + restartService func() *protocol.ResponseMessage + loadConfig func(string) *protocol.ResponseMessage + setConfigValue func(string, string) *protocol.ResponseMessage +} + +func NewCommandListener(serviceName string, handler nats.EventHandler) *commandListener { + return &commandListener{ + subjectName: serviceName + ".ctrl", + handler: handler, + } + +} + +func (c *commandListener) ListenToCommands() { + go c.listenToCommands(3) +} + +func (c *commandListener) listenToCommands(tries int32) { + err := c.handler.Subscribe(c.subjectName, func(msg *natspkg.Msg) { + request := &protocol.RequestMessage{} + request.UnmarshalMsg(msg.Data) + + switch request.Method { + case RESTART_SERVICE_COMMAND: + c.restartService() + break + case LOAD_CONFIG_COMMAND: + command := (*LoadConfigCommand)(request) + param, _ := command.ReadParam() + c.loadConfig(param) + break + case SET_CONFIG_VALUE_COMMAND: + command := (*SetConfigValueCommand)(request) + param, _ := command.ReadConfig() + c.setConfigValue(param[0], param[1]) + break + default: + // TODO: log + } + }) + + if err != nil && tries > 0 { + time.Sleep(100 * time.Millisecond) + } else if err != nil { + panic("couldn't listen to commands after 3 tries") + } +} + +func (c *commandListener) SetServiceRestartCb(cb func() *protocol.ResponseMessage) { + c.restartService = cb +} + +func (c *commandListener) SetConfigLoadCb(cb func(string) *protocol.ResponseMessage) { + c.loadConfig = cb +} + +func (c *commandListener) ListenToSetConfigValue(cb func(string, string) *protocol.ResponseMessage) { + c.setConfigValue = cb +} From bbf4630aa554a367805a23e7fe5390dbdf6b1d23 Mon Sep 17 00:00:00 2001 From: lohuza Date: Tue, 17 Jan 2023 04:59:22 +0400 Subject: [PATCH 09/12] buckets and message changes --- nats/command/command_test.go | 1 + nats/command/listener.go | 13 +- nats/command/listener_test.go | 1 + nats/protocol/message.go | 46 +- nats/protocol/message_gen.go | 1012 ----------------------------- nats/protocol/message_gen_test.go | 462 ------------- nats/protocol/message_test.go | 37 ++ nats/store.go | 132 ++++ nats/store_test.go | 1 + 9 files changed, 207 insertions(+), 1498 deletions(-) create mode 100644 nats/command/command_test.go create mode 100644 nats/command/listener_test.go delete mode 100644 nats/protocol/message_gen.go delete mode 100644 nats/protocol/message_gen_test.go create mode 100644 nats/protocol/message_test.go create mode 100644 nats/store.go create mode 100644 nats/store_test.go diff --git a/nats/command/command_test.go b/nats/command/command_test.go new file mode 100644 index 0000000..d47dcf0 --- /dev/null +++ b/nats/command/command_test.go @@ -0,0 +1 @@ +package command diff --git a/nats/command/listener.go b/nats/command/listener.go index bc43b6b..e1b5c12 100644 --- a/nats/command/listener.go +++ b/nats/command/listener.go @@ -28,7 +28,6 @@ func NewCommandListener(serviceName string, handler nats.EventHandler) *commandL subjectName: serviceName + ".ctrl", handler: handler, } - } func (c *commandListener) ListenToCommands() { @@ -37,20 +36,22 @@ func (c *commandListener) ListenToCommands() { func (c *commandListener) listenToCommands(tries int32) { err := c.handler.Subscribe(c.subjectName, func(msg *natspkg.Msg) { - request := &protocol.RequestMessage{} - request.UnmarshalMsg(msg.Data) - + request, err := protocol.UnmarshalMessage[protocol.RequestMessage](msg.Data) + // TODO: handle error + if err != nil { + return + } switch request.Method { case RESTART_SERVICE_COMMAND: c.restartService() break case LOAD_CONFIG_COMMAND: - command := (*LoadConfigCommand)(request) + command := (LoadConfigCommand)(*request) param, _ := command.ReadParam() c.loadConfig(param) break case SET_CONFIG_VALUE_COMMAND: - command := (*SetConfigValueCommand)(request) + command := (SetConfigValueCommand)(*request) param, _ := command.ReadConfig() c.setConfigValue(param[0], param[1]) break diff --git a/nats/command/listener_test.go b/nats/command/listener_test.go new file mode 100644 index 0000000..d47dcf0 --- /dev/null +++ b/nats/command/listener_test.go @@ -0,0 +1 @@ +package command diff --git a/nats/protocol/message.go b/nats/protocol/message.go index dff7c96..bf14eb8 100644 --- a/nats/protocol/message.go +++ b/nats/protocol/message.go @@ -1,6 +1,8 @@ package protocol -type MessageType int32 +import "encoding/json" + +type MessageType uint8 const ( Request MessageType = iota @@ -8,34 +10,32 @@ const ( Event ) -//go:generate msgp - -type MessageBase struct { - Type MessageType `msg:"message_type"` - MsgId uint32 `msg:"msg_id"` +type messageBase struct { + Type MessageType `json:"message_type"` + MsgId uint32 `json:"msg_id"` } type RequestMessage struct { - MessageBase - Method string `msg:"method"` - Params []interface{} `msg:"params"` + messageBase + Method string `json:"method"` + Params []interface{} `json:"params"` } type ResponseMessage struct { - MessageBase - Error interface{} `msg:"method"` - Result interface{} `msg:"result"` + messageBase + Error interface{} `json:"method"` + Result interface{} `json:"result"` } type EventMessage struct { - MessageBase - Method string `msg:"method"` - Params []interface{} `msg:"params"` + messageBase + Method string `json:"method"` + Params []interface{} `json:"params"` } func NewRequestMessage(msgId uint32, method string, params []interface{}) *RequestMessage { return &RequestMessage{ - MessageBase: MessageBase{ + messageBase: messageBase{ Type: Request, MsgId: msgId, }, @@ -46,7 +46,7 @@ func NewRequestMessage(msgId uint32, method string, params []interface{}) *Reque func NewResponseMessage(msgId uint32, error any, result interface{}) *ResponseMessage { return &ResponseMessage{ - MessageBase: MessageBase{ + messageBase: messageBase{ Type: Response, MsgId: msgId, }, @@ -57,7 +57,7 @@ func NewResponseMessage(msgId uint32, error any, result interface{}) *ResponseMe func NewEventMessage(msgId uint32, method string, params []interface{}) *EventMessage { return &EventMessage{ - MessageBase: MessageBase{ + messageBase: messageBase{ Type: Event, MsgId: msgId, }, @@ -65,3 +65,13 @@ func NewEventMessage(msgId uint32, method string, params []interface{}) *EventMe Params: params, } } + +func MarshalMessage[T RequestMessage | ResponseMessage | EventMessage](message *T) ([]byte, error) { + return json.Marshal(message) +} + +func UnmarshalMessage[T RequestMessage | ResponseMessage | EventMessage](data []byte) (*T, error) { + message := new(T) + err := json.Unmarshal(data, message) + return message, err +} diff --git a/nats/protocol/message_gen.go b/nats/protocol/message_gen.go deleted file mode 100644 index 11f067b..0000000 --- a/nats/protocol/message_gen.go +++ /dev/null @@ -1,1012 +0,0 @@ -package protocol - -// Code generated by github.com/tinylib/msgp DO NOT EDIT. - -import ( - "github.com/tinylib/msgp/msgp" -) - -// DecodeMsg implements msgp.Decodable -func (z *EventMessage) DecodeMsg(dc *msgp.Reader) (err error) { - var field []byte - _ = field - var zb0001 uint32 - zb0001, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0001 > 0 { - zb0001-- - field, err = dc.ReadMapKeyPtr() - if err != nil { - err = msgp.WrapError(err) - return - } - switch msgp.UnsafeString(field) { - case "MessageBase": - var zb0002 uint32 - zb0002, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - for zb0002 > 0 { - zb0002-- - field, err = dc.ReadMapKeyPtr() - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - switch msgp.UnsafeString(field) { - case "message_type": - { - var zb0003 int32 - zb0003, err = dc.ReadInt32() - if err != nil { - err = msgp.WrapError(err, "MessageBase", "Type") - return - } - z.MessageBase.Type = MessageType(zb0003) - } - case "msg_id": - z.MessageBase.MsgId, err = dc.ReadUint32() - if err != nil { - err = msgp.WrapError(err, "MessageBase", "MsgId") - return - } - default: - err = dc.Skip() - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - } - } - case "method": - z.Method, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Method") - return - } - case "params": - var zb0004 uint32 - zb0004, err = dc.ReadArrayHeader() - if err != nil { - err = msgp.WrapError(err, "Params") - return - } - if cap(z.Params) >= int(zb0004) { - z.Params = (z.Params)[:zb0004] - } else { - z.Params = make([]interface{}, zb0004) - } - for za0001 := range z.Params { - z.Params[za0001], err = dc.ReadIntf() - if err != nil { - err = msgp.WrapError(err, "Params", za0001) - return - } - } - default: - err = dc.Skip() - if err != nil { - err = msgp.WrapError(err) - return - } - } - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z *EventMessage) EncodeMsg(en *msgp.Writer) (err error) { - // map header, size 3 - // write "MessageBase" - err = en.Append(0x83, 0xab, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65) - if err != nil { - return - } - // map header, size 2 - // write "message_type" - err = en.Append(0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) - if err != nil { - return - } - err = en.WriteInt32(int32(z.MessageBase.Type)) - if err != nil { - err = msgp.WrapError(err, "MessageBase", "Type") - return - } - // write "msg_id" - err = en.Append(0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) - if err != nil { - return - } - err = en.WriteUint32(z.MessageBase.MsgId) - if err != nil { - err = msgp.WrapError(err, "MessageBase", "MsgId") - return - } - // write "method" - err = en.Append(0xa6, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64) - if err != nil { - return - } - err = en.WriteString(z.Method) - if err != nil { - err = msgp.WrapError(err, "Method") - return - } - // write "params" - err = en.Append(0xa6, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73) - if err != nil { - return - } - err = en.WriteArrayHeader(uint32(len(z.Params))) - if err != nil { - err = msgp.WrapError(err, "Params") - return - } - for za0001 := range z.Params { - err = en.WriteIntf(z.Params[za0001]) - if err != nil { - err = msgp.WrapError(err, "Params", za0001) - return - } - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z *EventMessage) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - // map header, size 3 - // string "MessageBase" - o = append(o, 0x83, 0xab, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65) - // map header, size 2 - // string "message_type" - o = append(o, 0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) - o = msgp.AppendInt32(o, int32(z.MessageBase.Type)) - // string "msg_id" - o = append(o, 0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) - o = msgp.AppendUint32(o, z.MessageBase.MsgId) - // string "method" - o = append(o, 0xa6, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64) - o = msgp.AppendString(o, z.Method) - // string "params" - o = append(o, 0xa6, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73) - o = msgp.AppendArrayHeader(o, uint32(len(z.Params))) - for za0001 := range z.Params { - o, err = msgp.AppendIntf(o, z.Params[za0001]) - if err != nil { - err = msgp.WrapError(err, "Params", za0001) - return - } - } - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *EventMessage) UnmarshalMsg(bts []byte) (o []byte, err error) { - var field []byte - _ = field - var zb0001 uint32 - zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0001 > 0 { - zb0001-- - field, bts, err = msgp.ReadMapKeyZC(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - switch msgp.UnsafeString(field) { - case "MessageBase": - var zb0002 uint32 - zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - for zb0002 > 0 { - zb0002-- - field, bts, err = msgp.ReadMapKeyZC(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - switch msgp.UnsafeString(field) { - case "message_type": - { - var zb0003 int32 - zb0003, bts, err = msgp.ReadInt32Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase", "Type") - return - } - z.MessageBase.Type = MessageType(zb0003) - } - case "msg_id": - z.MessageBase.MsgId, bts, err = msgp.ReadUint32Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase", "MsgId") - return - } - default: - bts, err = msgp.Skip(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - } - } - case "method": - z.Method, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Method") - return - } - case "params": - var zb0004 uint32 - zb0004, bts, err = msgp.ReadArrayHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Params") - return - } - if cap(z.Params) >= int(zb0004) { - z.Params = (z.Params)[:zb0004] - } else { - z.Params = make([]interface{}, zb0004) - } - for za0001 := range z.Params { - z.Params[za0001], bts, err = msgp.ReadIntfBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Params", za0001) - return - } - } - default: - bts, err = msgp.Skip(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - } - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z *EventMessage) Msgsize() (s int) { - s = 1 + 12 + 1 + 13 + msgp.Int32Size + 7 + msgp.Uint32Size + 7 + msgp.StringPrefixSize + len(z.Method) + 7 + msgp.ArrayHeaderSize - for za0001 := range z.Params { - s += msgp.GuessSize(z.Params[za0001]) - } - return -} - -// DecodeMsg implements msgp.Decodable -func (z *MessageBase) DecodeMsg(dc *msgp.Reader) (err error) { - var field []byte - _ = field - var zb0001 uint32 - zb0001, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0001 > 0 { - zb0001-- - field, err = dc.ReadMapKeyPtr() - if err != nil { - err = msgp.WrapError(err) - return - } - switch msgp.UnsafeString(field) { - case "message_type": - { - var zb0002 int32 - zb0002, err = dc.ReadInt32() - if err != nil { - err = msgp.WrapError(err, "Type") - return - } - z.Type = MessageType(zb0002) - } - case "msg_id": - z.MsgId, err = dc.ReadUint32() - if err != nil { - err = msgp.WrapError(err, "MsgId") - return - } - default: - err = dc.Skip() - if err != nil { - err = msgp.WrapError(err) - return - } - } - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z MessageBase) EncodeMsg(en *msgp.Writer) (err error) { - // map header, size 2 - // write "message_type" - err = en.Append(0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) - if err != nil { - return - } - err = en.WriteInt32(int32(z.Type)) - if err != nil { - err = msgp.WrapError(err, "Type") - return - } - // write "msg_id" - err = en.Append(0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) - if err != nil { - return - } - err = en.WriteUint32(z.MsgId) - if err != nil { - err = msgp.WrapError(err, "MsgId") - return - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z MessageBase) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - // map header, size 2 - // string "message_type" - o = append(o, 0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) - o = msgp.AppendInt32(o, int32(z.Type)) - // string "msg_id" - o = append(o, 0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) - o = msgp.AppendUint32(o, z.MsgId) - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *MessageBase) UnmarshalMsg(bts []byte) (o []byte, err error) { - var field []byte - _ = field - var zb0001 uint32 - zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0001 > 0 { - zb0001-- - field, bts, err = msgp.ReadMapKeyZC(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - switch msgp.UnsafeString(field) { - case "message_type": - { - var zb0002 int32 - zb0002, bts, err = msgp.ReadInt32Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "Type") - return - } - z.Type = MessageType(zb0002) - } - case "msg_id": - z.MsgId, bts, err = msgp.ReadUint32Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "MsgId") - return - } - default: - bts, err = msgp.Skip(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - } - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z MessageBase) Msgsize() (s int) { - s = 1 + 13 + msgp.Int32Size + 7 + msgp.Uint32Size - return -} - -// DecodeMsg implements msgp.Decodable -func (z *MessageType) DecodeMsg(dc *msgp.Reader) (err error) { - { - var zb0001 int32 - zb0001, err = dc.ReadInt32() - if err != nil { - err = msgp.WrapError(err) - return - } - (*z) = MessageType(zb0001) - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z MessageType) EncodeMsg(en *msgp.Writer) (err error) { - err = en.WriteInt32(int32(z)) - if err != nil { - err = msgp.WrapError(err) - return - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z MessageType) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - o = msgp.AppendInt32(o, int32(z)) - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *MessageType) UnmarshalMsg(bts []byte) (o []byte, err error) { - { - var zb0001 int32 - zb0001, bts, err = msgp.ReadInt32Bytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - (*z) = MessageType(zb0001) - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z MessageType) Msgsize() (s int) { - s = msgp.Int32Size - return -} - -// DecodeMsg implements msgp.Decodable -func (z *RequestMessage) DecodeMsg(dc *msgp.Reader) (err error) { - var field []byte - _ = field - var zb0001 uint32 - zb0001, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0001 > 0 { - zb0001-- - field, err = dc.ReadMapKeyPtr() - if err != nil { - err = msgp.WrapError(err) - return - } - switch msgp.UnsafeString(field) { - case "MessageBase": - var zb0002 uint32 - zb0002, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - for zb0002 > 0 { - zb0002-- - field, err = dc.ReadMapKeyPtr() - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - switch msgp.UnsafeString(field) { - case "message_type": - { - var zb0003 int32 - zb0003, err = dc.ReadInt32() - if err != nil { - err = msgp.WrapError(err, "MessageBase", "Type") - return - } - z.MessageBase.Type = MessageType(zb0003) - } - case "msg_id": - z.MessageBase.MsgId, err = dc.ReadUint32() - if err != nil { - err = msgp.WrapError(err, "MessageBase", "MsgId") - return - } - default: - err = dc.Skip() - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - } - } - case "method": - z.Method, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Method") - return - } - case "params": - var zb0004 uint32 - zb0004, err = dc.ReadArrayHeader() - if err != nil { - err = msgp.WrapError(err, "Params") - return - } - if cap(z.Params) >= int(zb0004) { - z.Params = (z.Params)[:zb0004] - } else { - z.Params = make([]interface{}, zb0004) - } - for za0001 := range z.Params { - z.Params[za0001], err = dc.ReadIntf() - if err != nil { - err = msgp.WrapError(err, "Params", za0001) - return - } - } - default: - err = dc.Skip() - if err != nil { - err = msgp.WrapError(err) - return - } - } - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z *RequestMessage) EncodeMsg(en *msgp.Writer) (err error) { - // map header, size 3 - // write "MessageBase" - err = en.Append(0x83, 0xab, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65) - if err != nil { - return - } - // map header, size 2 - // write "message_type" - err = en.Append(0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) - if err != nil { - return - } - err = en.WriteInt32(int32(z.MessageBase.Type)) - if err != nil { - err = msgp.WrapError(err, "MessageBase", "Type") - return - } - // write "msg_id" - err = en.Append(0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) - if err != nil { - return - } - err = en.WriteUint32(z.MessageBase.MsgId) - if err != nil { - err = msgp.WrapError(err, "MessageBase", "MsgId") - return - } - // write "method" - err = en.Append(0xa6, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64) - if err != nil { - return - } - err = en.WriteString(z.Method) - if err != nil { - err = msgp.WrapError(err, "Method") - return - } - // write "params" - err = en.Append(0xa6, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73) - if err != nil { - return - } - err = en.WriteArrayHeader(uint32(len(z.Params))) - if err != nil { - err = msgp.WrapError(err, "Params") - return - } - for za0001 := range z.Params { - err = en.WriteIntf(z.Params[za0001]) - if err != nil { - err = msgp.WrapError(err, "Params", za0001) - return - } - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z *RequestMessage) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - // map header, size 3 - // string "MessageBase" - o = append(o, 0x83, 0xab, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65) - // map header, size 2 - // string "message_type" - o = append(o, 0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) - o = msgp.AppendInt32(o, int32(z.MessageBase.Type)) - // string "msg_id" - o = append(o, 0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) - o = msgp.AppendUint32(o, z.MessageBase.MsgId) - // string "method" - o = append(o, 0xa6, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64) - o = msgp.AppendString(o, z.Method) - // string "params" - o = append(o, 0xa6, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73) - o = msgp.AppendArrayHeader(o, uint32(len(z.Params))) - for za0001 := range z.Params { - o, err = msgp.AppendIntf(o, z.Params[za0001]) - if err != nil { - err = msgp.WrapError(err, "Params", za0001) - return - } - } - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *RequestMessage) UnmarshalMsg(bts []byte) (o []byte, err error) { - var field []byte - _ = field - var zb0001 uint32 - zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0001 > 0 { - zb0001-- - field, bts, err = msgp.ReadMapKeyZC(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - switch msgp.UnsafeString(field) { - case "MessageBase": - var zb0002 uint32 - zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - for zb0002 > 0 { - zb0002-- - field, bts, err = msgp.ReadMapKeyZC(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - switch msgp.UnsafeString(field) { - case "message_type": - { - var zb0003 int32 - zb0003, bts, err = msgp.ReadInt32Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase", "Type") - return - } - z.MessageBase.Type = MessageType(zb0003) - } - case "msg_id": - z.MessageBase.MsgId, bts, err = msgp.ReadUint32Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase", "MsgId") - return - } - default: - bts, err = msgp.Skip(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - } - } - case "method": - z.Method, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Method") - return - } - case "params": - var zb0004 uint32 - zb0004, bts, err = msgp.ReadArrayHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Params") - return - } - if cap(z.Params) >= int(zb0004) { - z.Params = (z.Params)[:zb0004] - } else { - z.Params = make([]interface{}, zb0004) - } - for za0001 := range z.Params { - z.Params[za0001], bts, err = msgp.ReadIntfBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Params", za0001) - return - } - } - default: - bts, err = msgp.Skip(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - } - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z *RequestMessage) Msgsize() (s int) { - s = 1 + 12 + 1 + 13 + msgp.Int32Size + 7 + msgp.Uint32Size + 7 + msgp.StringPrefixSize + len(z.Method) + 7 + msgp.ArrayHeaderSize - for za0001 := range z.Params { - s += msgp.GuessSize(z.Params[za0001]) - } - return -} - -// DecodeMsg implements msgp.Decodable -func (z *ResponseMessage) DecodeMsg(dc *msgp.Reader) (err error) { - var field []byte - _ = field - var zb0001 uint32 - zb0001, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0001 > 0 { - zb0001-- - field, err = dc.ReadMapKeyPtr() - if err != nil { - err = msgp.WrapError(err) - return - } - switch msgp.UnsafeString(field) { - case "MessageBase": - var zb0002 uint32 - zb0002, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - for zb0002 > 0 { - zb0002-- - field, err = dc.ReadMapKeyPtr() - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - switch msgp.UnsafeString(field) { - case "message_type": - { - var zb0003 int32 - zb0003, err = dc.ReadInt32() - if err != nil { - err = msgp.WrapError(err, "MessageBase", "Type") - return - } - z.MessageBase.Type = MessageType(zb0003) - } - case "msg_id": - z.MessageBase.MsgId, err = dc.ReadUint32() - if err != nil { - err = msgp.WrapError(err, "MessageBase", "MsgId") - return - } - default: - err = dc.Skip() - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - } - } - case "method": - z.Error, err = dc.ReadIntf() - if err != nil { - err = msgp.WrapError(err, "Error") - return - } - case "result": - z.Result, err = dc.ReadIntf() - if err != nil { - err = msgp.WrapError(err, "Result") - return - } - default: - err = dc.Skip() - if err != nil { - err = msgp.WrapError(err) - return - } - } - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z *ResponseMessage) EncodeMsg(en *msgp.Writer) (err error) { - // map header, size 3 - // write "MessageBase" - err = en.Append(0x83, 0xab, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65) - if err != nil { - return - } - // map header, size 2 - // write "message_type" - err = en.Append(0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) - if err != nil { - return - } - err = en.WriteInt32(int32(z.MessageBase.Type)) - if err != nil { - err = msgp.WrapError(err, "MessageBase", "Type") - return - } - // write "msg_id" - err = en.Append(0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) - if err != nil { - return - } - err = en.WriteUint32(z.MessageBase.MsgId) - if err != nil { - err = msgp.WrapError(err, "MessageBase", "MsgId") - return - } - // write "method" - err = en.Append(0xa6, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64) - if err != nil { - return - } - err = en.WriteIntf(z.Error) - if err != nil { - err = msgp.WrapError(err, "Error") - return - } - // write "result" - err = en.Append(0xa6, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74) - if err != nil { - return - } - err = en.WriteIntf(z.Result) - if err != nil { - err = msgp.WrapError(err, "Result") - return - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z *ResponseMessage) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - // map header, size 3 - // string "MessageBase" - o = append(o, 0x83, 0xab, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x61, 0x73, 0x65) - // map header, size 2 - // string "message_type" - o = append(o, 0x82, 0xac, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65) - o = msgp.AppendInt32(o, int32(z.MessageBase.Type)) - // string "msg_id" - o = append(o, 0xa6, 0x6d, 0x73, 0x67, 0x5f, 0x69, 0x64) - o = msgp.AppendUint32(o, z.MessageBase.MsgId) - // string "method" - o = append(o, 0xa6, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64) - o, err = msgp.AppendIntf(o, z.Error) - if err != nil { - err = msgp.WrapError(err, "Error") - return - } - // string "result" - o = append(o, 0xa6, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74) - o, err = msgp.AppendIntf(o, z.Result) - if err != nil { - err = msgp.WrapError(err, "Result") - return - } - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *ResponseMessage) UnmarshalMsg(bts []byte) (o []byte, err error) { - var field []byte - _ = field - var zb0001 uint32 - zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0001 > 0 { - zb0001-- - field, bts, err = msgp.ReadMapKeyZC(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - switch msgp.UnsafeString(field) { - case "MessageBase": - var zb0002 uint32 - zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - for zb0002 > 0 { - zb0002-- - field, bts, err = msgp.ReadMapKeyZC(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - switch msgp.UnsafeString(field) { - case "message_type": - { - var zb0003 int32 - zb0003, bts, err = msgp.ReadInt32Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase", "Type") - return - } - z.MessageBase.Type = MessageType(zb0003) - } - case "msg_id": - z.MessageBase.MsgId, bts, err = msgp.ReadUint32Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase", "MsgId") - return - } - default: - bts, err = msgp.Skip(bts) - if err != nil { - err = msgp.WrapError(err, "MessageBase") - return - } - } - } - case "method": - z.Error, bts, err = msgp.ReadIntfBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Error") - return - } - case "result": - z.Result, bts, err = msgp.ReadIntfBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Result") - return - } - default: - bts, err = msgp.Skip(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - } - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z *ResponseMessage) Msgsize() (s int) { - s = 1 + 12 + 1 + 13 + msgp.Int32Size + 7 + msgp.Uint32Size + 7 + msgp.GuessSize(z.Error) + 7 + msgp.GuessSize(z.Result) - return -} diff --git a/nats/protocol/message_gen_test.go b/nats/protocol/message_gen_test.go deleted file mode 100644 index d0906e0..0000000 --- a/nats/protocol/message_gen_test.go +++ /dev/null @@ -1,462 +0,0 @@ -package protocol - -// Code generated by github.com/tinylib/msgp DO NOT EDIT. - -import ( - "bytes" - "testing" - - "github.com/tinylib/msgp/msgp" -) - -func TestMarshalUnmarshalEventMessage(t *testing.T) { - v := EventMessage{} - bts, err := v.MarshalMsg(nil) - if err != nil { - t.Fatal(err) - } - left, err := v.UnmarshalMsg(bts) - if err != nil { - t.Fatal(err) - } - if len(left) > 0 { - t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) - } - - left, err = msgp.Skip(bts) - if err != nil { - t.Fatal(err) - } - if len(left) > 0 { - t.Errorf("%d bytes left over after Skip(): %q", len(left), left) - } -} - -func BenchmarkMarshalMsgEventMessage(b *testing.B) { - v := EventMessage{} - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - v.MarshalMsg(nil) - } -} - -func BenchmarkAppendMsgEventMessage(b *testing.B) { - v := EventMessage{} - bts := make([]byte, 0, v.Msgsize()) - bts, _ = v.MarshalMsg(bts[0:0]) - b.SetBytes(int64(len(bts))) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - bts, _ = v.MarshalMsg(bts[0:0]) - } -} - -func BenchmarkUnmarshalEventMessage(b *testing.B) { - v := EventMessage{} - bts, _ := v.MarshalMsg(nil) - b.ReportAllocs() - b.SetBytes(int64(len(bts))) - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := v.UnmarshalMsg(bts) - if err != nil { - b.Fatal(err) - } - } -} - -func TestEncodeDecodeEventMessage(t *testing.T) { - v := EventMessage{} - var buf bytes.Buffer - msgp.Encode(&buf, &v) - - m := v.Msgsize() - if buf.Len() > m { - t.Log("WARNING: TestEncodeDecodeEventMessage Msgsize() is inaccurate") - } - - vn := EventMessage{} - err := msgp.Decode(&buf, &vn) - if err != nil { - t.Error(err) - } - - buf.Reset() - msgp.Encode(&buf, &v) - err = msgp.NewReader(&buf).Skip() - if err != nil { - t.Error(err) - } -} - -func BenchmarkEncodeEventMessage(b *testing.B) { - v := EventMessage{} - var buf bytes.Buffer - msgp.Encode(&buf, &v) - b.SetBytes(int64(buf.Len())) - en := msgp.NewWriter(msgp.Nowhere) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - v.EncodeMsg(en) - } - en.Flush() -} - -func BenchmarkDecodeEventMessage(b *testing.B) { - v := EventMessage{} - var buf bytes.Buffer - msgp.Encode(&buf, &v) - b.SetBytes(int64(buf.Len())) - rd := msgp.NewEndlessReader(buf.Bytes(), b) - dc := msgp.NewReader(rd) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := v.DecodeMsg(dc) - if err != nil { - b.Fatal(err) - } - } -} - -func TestMarshalUnmarshalMessageBase(t *testing.T) { - v := MessageBase{} - bts, err := v.MarshalMsg(nil) - if err != nil { - t.Fatal(err) - } - left, err := v.UnmarshalMsg(bts) - if err != nil { - t.Fatal(err) - } - if len(left) > 0 { - t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) - } - - left, err = msgp.Skip(bts) - if err != nil { - t.Fatal(err) - } - if len(left) > 0 { - t.Errorf("%d bytes left over after Skip(): %q", len(left), left) - } -} - -func BenchmarkMarshalMsgMessageBase(b *testing.B) { - v := MessageBase{} - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - v.MarshalMsg(nil) - } -} - -func BenchmarkAppendMsgMessageBase(b *testing.B) { - v := MessageBase{} - bts := make([]byte, 0, v.Msgsize()) - bts, _ = v.MarshalMsg(bts[0:0]) - b.SetBytes(int64(len(bts))) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - bts, _ = v.MarshalMsg(bts[0:0]) - } -} - -func BenchmarkUnmarshalMessageBase(b *testing.B) { - v := MessageBase{} - bts, _ := v.MarshalMsg(nil) - b.ReportAllocs() - b.SetBytes(int64(len(bts))) - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := v.UnmarshalMsg(bts) - if err != nil { - b.Fatal(err) - } - } -} - -func TestEncodeDecodeMessageBase(t *testing.T) { - v := MessageBase{} - var buf bytes.Buffer - msgp.Encode(&buf, &v) - - m := v.Msgsize() - if buf.Len() > m { - t.Log("WARNING: TestEncodeDecodeMessageBase Msgsize() is inaccurate") - } - - vn := MessageBase{} - err := msgp.Decode(&buf, &vn) - if err != nil { - t.Error(err) - } - - buf.Reset() - msgp.Encode(&buf, &v) - err = msgp.NewReader(&buf).Skip() - if err != nil { - t.Error(err) - } -} - -func BenchmarkEncodeMessageBase(b *testing.B) { - v := MessageBase{} - var buf bytes.Buffer - msgp.Encode(&buf, &v) - b.SetBytes(int64(buf.Len())) - en := msgp.NewWriter(msgp.Nowhere) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - v.EncodeMsg(en) - } - en.Flush() -} - -func BenchmarkDecodeMessageBase(b *testing.B) { - v := MessageBase{} - var buf bytes.Buffer - msgp.Encode(&buf, &v) - b.SetBytes(int64(buf.Len())) - rd := msgp.NewEndlessReader(buf.Bytes(), b) - dc := msgp.NewReader(rd) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := v.DecodeMsg(dc) - if err != nil { - b.Fatal(err) - } - } -} - -func TestMarshalUnmarshalRequestMessage(t *testing.T) { - v := RequestMessage{} - bts, err := v.MarshalMsg(nil) - if err != nil { - t.Fatal(err) - } - left, err := v.UnmarshalMsg(bts) - if err != nil { - t.Fatal(err) - } - if len(left) > 0 { - t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) - } - - left, err = msgp.Skip(bts) - if err != nil { - t.Fatal(err) - } - if len(left) > 0 { - t.Errorf("%d bytes left over after Skip(): %q", len(left), left) - } -} - -func BenchmarkMarshalMsgRequestMessage(b *testing.B) { - v := RequestMessage{} - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - v.MarshalMsg(nil) - } -} - -func BenchmarkAppendMsgRequestMessage(b *testing.B) { - v := RequestMessage{} - bts := make([]byte, 0, v.Msgsize()) - bts, _ = v.MarshalMsg(bts[0:0]) - b.SetBytes(int64(len(bts))) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - bts, _ = v.MarshalMsg(bts[0:0]) - } -} - -func BenchmarkUnmarshalRequestMessage(b *testing.B) { - v := RequestMessage{} - bts, _ := v.MarshalMsg(nil) - b.ReportAllocs() - b.SetBytes(int64(len(bts))) - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := v.UnmarshalMsg(bts) - if err != nil { - b.Fatal(err) - } - } -} - -func TestEncodeDecodeRequestMessage(t *testing.T) { - v := RequestMessage{} - var buf bytes.Buffer - msgp.Encode(&buf, &v) - - m := v.Msgsize() - if buf.Len() > m { - t.Log("WARNING: TestEncodeDecodeRequestMessage Msgsize() is inaccurate") - } - - vn := RequestMessage{} - err := msgp.Decode(&buf, &vn) - if err != nil { - t.Error(err) - } - - buf.Reset() - msgp.Encode(&buf, &v) - err = msgp.NewReader(&buf).Skip() - if err != nil { - t.Error(err) - } -} - -func BenchmarkEncodeRequestMessage(b *testing.B) { - v := RequestMessage{} - var buf bytes.Buffer - msgp.Encode(&buf, &v) - b.SetBytes(int64(buf.Len())) - en := msgp.NewWriter(msgp.Nowhere) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - v.EncodeMsg(en) - } - en.Flush() -} - -func BenchmarkDecodeRequestMessage(b *testing.B) { - v := RequestMessage{} - var buf bytes.Buffer - msgp.Encode(&buf, &v) - b.SetBytes(int64(buf.Len())) - rd := msgp.NewEndlessReader(buf.Bytes(), b) - dc := msgp.NewReader(rd) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := v.DecodeMsg(dc) - if err != nil { - b.Fatal(err) - } - } -} - -func TestMarshalUnmarshalResponseMessage(t *testing.T) { - v := ResponseMessage{} - bts, err := v.MarshalMsg(nil) - if err != nil { - t.Fatal(err) - } - left, err := v.UnmarshalMsg(bts) - if err != nil { - t.Fatal(err) - } - if len(left) > 0 { - t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) - } - - left, err = msgp.Skip(bts) - if err != nil { - t.Fatal(err) - } - if len(left) > 0 { - t.Errorf("%d bytes left over after Skip(): %q", len(left), left) - } -} - -func BenchmarkMarshalMsgResponseMessage(b *testing.B) { - v := ResponseMessage{} - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - v.MarshalMsg(nil) - } -} - -func BenchmarkAppendMsgResponseMessage(b *testing.B) { - v := ResponseMessage{} - bts := make([]byte, 0, v.Msgsize()) - bts, _ = v.MarshalMsg(bts[0:0]) - b.SetBytes(int64(len(bts))) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - bts, _ = v.MarshalMsg(bts[0:0]) - } -} - -func BenchmarkUnmarshalResponseMessage(b *testing.B) { - v := ResponseMessage{} - bts, _ := v.MarshalMsg(nil) - b.ReportAllocs() - b.SetBytes(int64(len(bts))) - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := v.UnmarshalMsg(bts) - if err != nil { - b.Fatal(err) - } - } -} - -func TestEncodeDecodeResponseMessage(t *testing.T) { - v := ResponseMessage{} - var buf bytes.Buffer - msgp.Encode(&buf, &v) - - m := v.Msgsize() - if buf.Len() > m { - t.Log("WARNING: TestEncodeDecodeResponseMessage Msgsize() is inaccurate") - } - - vn := ResponseMessage{} - err := msgp.Decode(&buf, &vn) - if err != nil { - t.Error(err) - } - - buf.Reset() - msgp.Encode(&buf, &v) - err = msgp.NewReader(&buf).Skip() - if err != nil { - t.Error(err) - } -} - -func BenchmarkEncodeResponseMessage(b *testing.B) { - v := ResponseMessage{} - var buf bytes.Buffer - msgp.Encode(&buf, &v) - b.SetBytes(int64(buf.Len())) - en := msgp.NewWriter(msgp.Nowhere) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - v.EncodeMsg(en) - } - en.Flush() -} - -func BenchmarkDecodeResponseMessage(b *testing.B) { - v := ResponseMessage{} - var buf bytes.Buffer - msgp.Encode(&buf, &v) - b.SetBytes(int64(buf.Len())) - rd := msgp.NewEndlessReader(buf.Bytes(), b) - dc := msgp.NewReader(rd) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - err := v.DecodeMsg(dc) - if err != nil { - b.Fatal(err) - } - } -} diff --git a/nats/protocol/message_test.go b/nats/protocol/message_test.go new file mode 100644 index 0000000..3cfa65c --- /dev/null +++ b/nats/protocol/message_test.go @@ -0,0 +1,37 @@ +package protocol + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMarshalUnmarshal(t *testing.T) { + msg1 := NewRequestMessage(uint32(123), "test", []interface{}{ + []int32{1, 2, 3}, struct { + Param string + }{ + Param: "test-param", + }, + "string", + }) + + bytes, err := MarshalMessage[RequestMessage](msg1) + assert.NoError(t, err) + + msg1_1, err := UnmarshalMessage[RequestMessage](bytes) + assert.Equal(t, &RequestMessage{ + messageBase: messageBase{ + Type: Request, + MsgId: 123, + }, + Method: "test", + Params: []interface{}{ + []interface{}{1, 2, 3}, + map[string]interface{}{ + "Param": "test-param", + }, + "string", + }, + }, msg1_1) +} diff --git a/nats/store.go b/nats/store.go new file mode 100644 index 0000000..88e295c --- /dev/null +++ b/nats/store.go @@ -0,0 +1,132 @@ +package nats + +import ( + "errors" + "time" + + "github.com/nats-io/nats.go" +) + +type BucketManager interface { + CreateBucketOrFindExisting(name string) (*bucket, error) + DeleteBucket(name string) error +} + +type Bucket interface { + GetValue(key string) ([]byte, error) + GetKeyCreateDate(key string) (time.Time, error) + GetAllTheKeys() ([]string, error) + GetHistoryOfTheKey(key string) ([]KeyVal, error) + AddPair(key string, val []byte) error + Delete(key string) error +} + +type bucketManager struct { + js nats.JetStreamContext +} + +type bucket struct { + items nats.KeyValue +} + +type KeyVal struct { + Key string + Value []byte + CreateDate time.Time +} + +func NewBucketManager(nc *nats.Conn) (*bucketManager, error) { + js, err := nc.JetStream() + if err != nil { + return nil, err + } + + return &bucketManager{ + js: js, + }, nil +} + +func (s *bucketManager) CreateBucketOrFindExisting(name string) (*bucket, error) { + keyVal, err := s.js.KeyValue(name) + if errors.Is(err, nats.ErrBucketNotFound) { + keyVal, err = s.js.CreateKeyValue(&nats.KeyValueConfig{ + Bucket: name, + Replicas: 3, + }) + } else { + return nil, err + } + + if keyVal != nil { + bucket := &bucket{ + items: keyVal, + } + + return bucket, nil + } + + return nil, err +} + +func (s *bucketManager) DeleteBucket(name string) error { + return s.js.DeleteKeyValue(name) +} + +func (b *bucket) GetValue(key string) ([]byte, error) { + val, err := b.items.Get(key) + if err != nil { + return nil, err + } + return val.Value(), nil +} + +func (b *bucket) GetKeyCreateDate(key string) (time.Time, error) { + val, err := b.items.Get(key) + if err != nil { + return time.Time{}, err + } + return val.Created(), err +} + +func (b *bucket) GetAllTheKeys() ([]string, error) { + return b.items.Keys() +} + +func (b *bucket) GetHistoryOfTheKey(key string) ([]KeyVal, error) { + keyHistory, err := b.items.History(key) + if err != nil { + return nil, err + } + + history := make([]KeyVal, len(keyHistory)) + for i := 0; i < len(keyHistory); i++ { + history[i] = KeyVal{ + Key: keyHistory[i].Key(), + Value: keyHistory[i].Value(), + CreateDate: keyHistory[i].Created(), + } + } + + return history, nil +} + +func (b *bucket) AddPair(key string, val []byte) error { + _, err := b.items.Get(key) + if err != nil && !errors.Is(err, nats.ErrKeyNotFound) { + return err + } else if err != nil { + return err + } + + if errors.Is(err, nats.ErrKeyNotFound) { + _, err = b.items.Create(key, val) + return nil + } + + _, err = b.items.Put(key, val) + return err +} + +func (b *bucket) Delete(key string) error { + return b.items.Delete(key) +} diff --git a/nats/store_test.go b/nats/store_test.go new file mode 100644 index 0000000..40b4928 --- /dev/null +++ b/nats/store_test.go @@ -0,0 +1 @@ +package nats From 7a19dbeec94850009a75fa903c8140baa6932bc2 Mon Sep 17 00:00:00 2001 From: lohuza Date: Tue, 17 Jan 2023 07:02:20 +0400 Subject: [PATCH 10/12] bugfixing and testing --- nats/command/command_test.go | 30 ++++++++++++++++++ nats/store.go | 28 +++++++++++------ nats/store_test.go | 60 ++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 9 deletions(-) diff --git a/nats/command/command_test.go b/nats/command/command_test.go index d47dcf0..34f1702 100644 --- a/nats/command/command_test.go +++ b/nats/command/command_test.go @@ -1 +1,31 @@ package command + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReadParam(t *testing.T) { + msg := &LoadConfigCommand{ + Method: "test", + Params: []interface{}{"my-command"}, + } + + param, err := msg.ReadParam() + assert.NoError(t, err) + assert.Equal(t, "my-command", param) +} + +func TestReadConfig(t *testing.T) { + msg := &SetConfigValueCommand{ + Method: "test", + Params: []interface{}{ + [2]string{"foo", "baz"}, + }, + } + + params, err := msg.ReadConfig() + assert.NoError(t, err) + assert.Equal(t, [2]string{"foo", "baz"}, params) +} diff --git a/nats/store.go b/nats/store.go index 88e295c..289bd81 100644 --- a/nats/store.go +++ b/nats/store.go @@ -9,6 +9,7 @@ import ( type BucketManager interface { CreateBucketOrFindExisting(name string) (*bucket, error) + BucketExists(name string) (bool, error) DeleteBucket(name string) error } @@ -46,14 +47,14 @@ func NewBucketManager(nc *nats.Conn) (*bucketManager, error) { }, nil } -func (s *bucketManager) CreateBucketOrFindExisting(name string) (*bucket, error) { +func (s *bucketManager) CreateBucketOrFindExisting(name string, replicas int) (*bucket, error) { keyVal, err := s.js.KeyValue(name) if errors.Is(err, nats.ErrBucketNotFound) { keyVal, err = s.js.CreateKeyValue(&nats.KeyValueConfig{ Bucket: name, - Replicas: 3, + Replicas: replicas, }) - } else { + } else if err != nil { return nil, err } @@ -68,6 +69,18 @@ func (s *bucketManager) CreateBucketOrFindExisting(name string) (*bucket, error) return nil, err } +func (s *bucketManager) BucketExists(name string) (bool, error) { + _, err := s.js.KeyValue(name) + if errors.Is(err, nats.ErrBucketNotFound) { + return false, nil + } + if err != nil { + return false, err + } + + return true, nil +} + func (s *bucketManager) DeleteBucket(name string) error { return s.js.DeleteKeyValue(name) } @@ -112,15 +125,12 @@ func (b *bucket) GetHistoryOfTheKey(key string) ([]KeyVal, error) { func (b *bucket) AddPair(key string, val []byte) error { _, err := b.items.Get(key) - if err != nil && !errors.Is(err, nats.ErrKeyNotFound) { - return err - } else if err != nil { - return err - } if errors.Is(err, nats.ErrKeyNotFound) { _, err = b.items.Create(key, val) - return nil + return err + } else if err != nil { + return err } _, err = b.items.Put(key, val) diff --git a/nats/store_test.go b/nats/store_test.go index 40b4928..e7f5d29 100644 --- a/nats/store_test.go +++ b/nats/store_test.go @@ -1 +1,61 @@ package nats + +import ( + "testing" + + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" +) + +func initBucketManager() *bucketManager { + //nc, _ := InitNats("localhost:4222") + nc, _ := InitEmbededNats() + bm, _ := NewBucketManager(nc) + + return bm +} + +func TestCreateBucketOrFindExisting(t *testing.T) { + bm := initBucketManager() + + exist, err := bm.BucketExists("foo") + assert.NoError(t, err) + assert.Equal(t, false, exist) + + bucket, err := bm.CreateBucketOrFindExisting("foo", 1) + assert.NoError(t, err) + assert.NotNil(t, bucket) + + exist, err = bm.BucketExists("foo") + assert.NoError(t, err) + assert.Equal(t, true, exist) +} + +func TestDeleteBucket(t *testing.T) { + bm := initBucketManager() + bucket, _ := bm.CreateBucketOrFindExisting("foo", 1) + assert.NotNil(t, bucket) + + bm.DeleteBucket("foo") + exist, _ := bm.BucketExists("foo") + assert.Equal(t, false, exist) +} + +func TestGetSetValue(t *testing.T) { + bm := initBucketManager() + bucket, _ := bm.CreateBucketOrFindExisting("foo", 1) + + data, err := bucket.GetValue("baz") + assert.ErrorIs(t, err, nats.ErrKeyNotFound) + assert.Nil(t, data) + + err = bucket.AddPair("baz", []byte("bar")) + assert.NoError(t, err) + + data, _ = bucket.GetValue("baz") + assert.Equal(t, []byte("bar"), data) +} + +func TestGetAllTheKeys(t *testing.T) { + +} From 347df5d331786dcaa5b61dedf2bb638e91f04189 Mon Sep 17 00:00:00 2001 From: lohuza Date: Tue, 17 Jan 2023 09:32:26 +0400 Subject: [PATCH 11/12] tests and bugfixes --- nats/command/command.go | 26 +++++++++----- nats/command/command_test.go | 18 ++++------ nats/command/listener.go | 18 +++++----- nats/command/listener_test.go | 67 +++++++++++++++++++++++++++++++++++ nats/protocol/message.go | 4 +-- 5 files changed, 103 insertions(+), 30 deletions(-) diff --git a/nats/command/command.go b/nats/command/command.go index 786fa70..bce124e 100644 --- a/nats/command/command.go +++ b/nats/command/command.go @@ -2,6 +2,7 @@ package command import ( "errors" + "fmt" "github.com/openware/pkg/nats/protocol" ) @@ -30,7 +31,7 @@ func NewLoadConfigCommand(msgId uint32, param string) *LoadConfigCommand { return (*LoadConfigCommand)(protocol.NewRequestMessage(msgId, LOAD_CONFIG_COMMAND, []interface{}{param})) } -func NewSetConfigValueCommand(msgId uint32, config [2]string) *SetConfigValueCommand { +func NewSetConfigValueCommand(msgId uint32, config []string) *SetConfigValueCommand { return (*SetConfigValueCommand)(protocol.NewRequestMessage(msgId, SET_CONFIG_VALUE_COMMAND, []interface{}{config})) } @@ -47,15 +48,24 @@ func (command *LoadConfigCommand) ReadParam() (string, error) { return val, nil } -func (command *SetConfigValueCommand) ReadConfig() ([2]string, error) { +func (command *SetConfigValueCommand) ReadConfig() ([]string, error) { if len(command.Params) != 1 { - return [2]string{}, InvalidParamError + return nil, InvalidParamError } - val, ok := command.Params[0].([2]string) - if !ok { - return [2]string{}, InvalidParamError - } + switch command.Params[0].(type) { + case []string: + return command.Params[0].([]string), nil + case []interface{}: + val := command.Params[0].([]interface{}) + params := make([]string, 2) + for i, param := range val { + params[i] = param.(string) + } - return val, nil + return params, nil + + default: + return nil, fmt.Errorf("invalid type") + } } diff --git a/nats/command/command_test.go b/nats/command/command_test.go index 34f1702..5426728 100644 --- a/nats/command/command_test.go +++ b/nats/command/command_test.go @@ -7,25 +7,21 @@ import ( ) func TestReadParam(t *testing.T) { - msg := &LoadConfigCommand{ - Method: "test", - Params: []interface{}{"my-command"}, - } + msg := NewLoadConfigCommand(123, "my-command") param, err := msg.ReadParam() assert.NoError(t, err) + assert.Equal(t, LOAD_CONFIG_COMMAND, msg.Method) + assert.Equal(t, uint32(123), msg.MsgId) assert.Equal(t, "my-command", param) } func TestReadConfig(t *testing.T) { - msg := &SetConfigValueCommand{ - Method: "test", - Params: []interface{}{ - [2]string{"foo", "baz"}, - }, - } + msg := NewSetConfigValueCommand(234, []string{"foo", "baz"}) + assert.Equal(t, SET_CONFIG_VALUE_COMMAND, msg.Method) + assert.Equal(t, uint32(234), msg.MsgId) params, err := msg.ReadConfig() assert.NoError(t, err) - assert.Equal(t, [2]string{"foo", "baz"}, params) + assert.Equal(t, []string{"foo", "baz"}, params) } diff --git a/nats/command/listener.go b/nats/command/listener.go index e1b5c12..db2fcd0 100644 --- a/nats/command/listener.go +++ b/nats/command/listener.go @@ -10,17 +10,17 @@ import ( type CommandListener interface { ListenToCommands() - SetServiceRestartCb(cb func() *protocol.ResponseMessage) - SetConfigLoadCb(cb func(string) *protocol.ResponseMessage) - SetConfigValueCb(cb func(string, string) *protocol.ResponseMessage) + SetServiceRestartCb(cb func()) + SetConfigLoadCb(cb func(string)) + SetConfigValueCb(cb func(string, string)) } type commandListener struct { subjectName string handler nats.EventHandler - restartService func() *protocol.ResponseMessage - loadConfig func(string) *protocol.ResponseMessage - setConfigValue func(string, string) *protocol.ResponseMessage + restartService func() + loadConfig func(string) + setConfigValue func(string, string) } func NewCommandListener(serviceName string, handler nats.EventHandler) *commandListener { @@ -67,14 +67,14 @@ func (c *commandListener) listenToCommands(tries int32) { } } -func (c *commandListener) SetServiceRestartCb(cb func() *protocol.ResponseMessage) { +func (c *commandListener) SetServiceRestartCb(cb func()) { c.restartService = cb } -func (c *commandListener) SetConfigLoadCb(cb func(string) *protocol.ResponseMessage) { +func (c *commandListener) SetConfigLoadCb(cb func(string)) { c.loadConfig = cb } -func (c *commandListener) ListenToSetConfigValue(cb func(string, string) *protocol.ResponseMessage) { +func (c *commandListener) ListenToSetConfigValue(cb func(string, string)) { c.setConfigValue = cb } diff --git a/nats/command/listener_test.go b/nats/command/listener_test.go index d47dcf0..7a5c819 100644 --- a/nats/command/listener_test.go +++ b/nats/command/listener_test.go @@ -1 +1,68 @@ package command + +import ( + "sync" + "testing" + + "github.com/openware/pkg/nats" + "github.com/openware/pkg/nats/protocol" + "github.com/stretchr/testify/assert" +) + +func setupCommandListener() *commandListener { + nc, _ := nats.InitEmbededNats() + handler := nats.NewNatsHandler(nc, nil, nil) + + return NewCommandListener("foo", handler) +} + +func TestNewCommandListener(t *testing.T) { + nc, _ := nats.InitEmbededNats() + handler := nats.NewNatsHandler(nc, nil, nil) + listener := NewCommandListener("foo", handler) + + assert.Equal(t, "foo.ctrl", listener.subjectName) +} + +func TestListenToCommands(t *testing.T) { + comListener := setupCommandListener() + wg := sync.WaitGroup{} + shouldRestart := false + configLoadout := "" + var configValues []string + + comListener.SetServiceRestartCb(func() { + shouldRestart = true + wg.Done() + }) + comListener.SetConfigLoadCb(func(s string) { + configLoadout = s + wg.Done() + }) + comListener.ListenToSetConfigValue(func(key string, val string) { + configValues = []string{key, val} + wg.Done() + }) + comListener.ListenToCommands() + + nc, _ := nats.InitEmbededNats() + + command1 := NewServiceRestartCommand(123) + data, _ := protocol.MarshalMessage[protocol.RequestMessage]((*protocol.RequestMessage)(command1)) + nc.Publish("foo.ctrl", data) + + command2 := NewLoadConfigCommand(234, "test-param") + data, _ = protocol.MarshalMessage[protocol.RequestMessage]((*protocol.RequestMessage)(command2)) + nc.Publish("foo.ctrl", data) + + command3 := NewSetConfigValueCommand(234, []string{"key", "value"}) + data, _ = protocol.MarshalMessage[protocol.RequestMessage]((*protocol.RequestMessage)(command3)) + nc.Publish("foo.ctrl", data) + + wg.Add(3) + wg.Wait() + + assert.Equal(t, true, shouldRestart) + assert.Equal(t, "test-param", configLoadout) + assert.Equal(t, []string{"key", "value"}, configValues) +} diff --git a/nats/protocol/message.go b/nats/protocol/message.go index bf14eb8..acab391 100644 --- a/nats/protocol/message.go +++ b/nats/protocol/message.go @@ -50,8 +50,8 @@ func NewResponseMessage(msgId uint32, error any, result interface{}) *ResponseMe Type: Response, MsgId: msgId, }, - Error: nil, - Result: nil, + Error: error, + Result: result, } } From c259812096316717e2dc2625ba4cfcc40a5d2952 Mon Sep 17 00:00:00 2001 From: lohuza Date: Mon, 23 Jan 2023 09:05:41 +0400 Subject: [PATCH 12/12] make files public --- nats/go.mod | 7 +++-- nats/store.go | 64 +++++++++++++++++++++++----------------------- nats/store_test.go | 2 +- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/nats/go.mod b/nats/go.mod index 3724a4d..4f3437f 100644 --- a/nats/go.mod +++ b/nats/go.mod @@ -2,13 +2,16 @@ module github.com/openware/pkg/nats go 1.18 +require ( + github.com/nats-io/nats-server/v2 v2.9.10 + github.com/nats-io/nats.go v1.22.1 +) + require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/klauspost/compress v1.15.11 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/nats-io/jwt/v2 v2.3.0 // indirect - github.com/nats-io/nats-server/v2 v2.9.10 // indirect - github.com/nats-io/nats.go v1.22.1 // indirect github.com/nats-io/nkeys v0.3.0 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/philhofer/fwd v1.1.2 // indirect diff --git a/nats/store.go b/nats/store.go index 289bd81..3412ba1 100644 --- a/nats/store.go +++ b/nats/store.go @@ -8,12 +8,12 @@ import ( ) type BucketManager interface { - CreateBucketOrFindExisting(name string) (*bucket, error) + CreateBucketOrFindExisting(name string, replicas int) (*Bucket, error) BucketExists(name string) (bool, error) DeleteBucket(name string) error } -type Bucket interface { +type BucketInterface interface { GetValue(key string) ([]byte, error) GetKeyCreateDate(key string) (time.Time, error) GetAllTheKeys() ([]string, error) @@ -22,12 +22,12 @@ type Bucket interface { Delete(key string) error } -type bucketManager struct { - js nats.JetStreamContext +type Manager struct { + Js nats.JetStreamContext } -type bucket struct { - items nats.KeyValue +type Bucket struct { + Items nats.KeyValue } type KeyVal struct { @@ -36,21 +36,21 @@ type KeyVal struct { CreateDate time.Time } -func NewBucketManager(nc *nats.Conn) (*bucketManager, error) { +func NewBucketManager(nc *nats.Conn) (*Manager, error) { js, err := nc.JetStream() if err != nil { return nil, err } - return &bucketManager{ - js: js, + return &Manager{ + Js: js, }, nil } -func (s *bucketManager) CreateBucketOrFindExisting(name string, replicas int) (*bucket, error) { - keyVal, err := s.js.KeyValue(name) +func (s *Manager) CreateBucketOrFindExisting(name string, replicas int) (*Bucket, error) { + keyVal, err := s.Js.KeyValue(name) if errors.Is(err, nats.ErrBucketNotFound) { - keyVal, err = s.js.CreateKeyValue(&nats.KeyValueConfig{ + keyVal, err = s.Js.CreateKeyValue(&nats.KeyValueConfig{ Bucket: name, Replicas: replicas, }) @@ -59,8 +59,8 @@ func (s *bucketManager) CreateBucketOrFindExisting(name string, replicas int) (* } if keyVal != nil { - bucket := &bucket{ - items: keyVal, + bucket := &Bucket{ + Items: keyVal, } return bucket, nil @@ -69,8 +69,8 @@ func (s *bucketManager) CreateBucketOrFindExisting(name string, replicas int) (* return nil, err } -func (s *bucketManager) BucketExists(name string) (bool, error) { - _, err := s.js.KeyValue(name) +func (s *Manager) BucketExists(name string) (bool, error) { + _, err := s.Js.KeyValue(name) if errors.Is(err, nats.ErrBucketNotFound) { return false, nil } @@ -81,32 +81,32 @@ func (s *bucketManager) BucketExists(name string) (bool, error) { return true, nil } -func (s *bucketManager) DeleteBucket(name string) error { - return s.js.DeleteKeyValue(name) +func (s *Manager) DeleteBucket(name string) error { + return s.Js.DeleteKeyValue(name) } -func (b *bucket) GetValue(key string) ([]byte, error) { - val, err := b.items.Get(key) +func (b *Bucket) GetValue(key string) ([]byte, error) { + val, err := b.Items.Get(key) if err != nil { return nil, err } return val.Value(), nil } -func (b *bucket) GetKeyCreateDate(key string) (time.Time, error) { - val, err := b.items.Get(key) +func (b *Bucket) GetKeyCreateDate(key string) (time.Time, error) { + val, err := b.Items.Get(key) if err != nil { return time.Time{}, err } return val.Created(), err } -func (b *bucket) GetAllTheKeys() ([]string, error) { - return b.items.Keys() +func (b *Bucket) GetAllTheKeys() ([]string, error) { + return b.Items.Keys() } -func (b *bucket) GetHistoryOfTheKey(key string) ([]KeyVal, error) { - keyHistory, err := b.items.History(key) +func (b *Bucket) GetHistoryOfTheKey(key string) ([]KeyVal, error) { + keyHistory, err := b.Items.History(key) if err != nil { return nil, err } @@ -123,20 +123,20 @@ func (b *bucket) GetHistoryOfTheKey(key string) ([]KeyVal, error) { return history, nil } -func (b *bucket) AddPair(key string, val []byte) error { - _, err := b.items.Get(key) +func (b *Bucket) AddPair(key string, val []byte) error { + _, err := b.Items.Get(key) if errors.Is(err, nats.ErrKeyNotFound) { - _, err = b.items.Create(key, val) + _, err = b.Items.Create(key, val) return err } else if err != nil { return err } - _, err = b.items.Put(key, val) + _, err = b.Items.Put(key, val) return err } -func (b *bucket) Delete(key string) error { - return b.items.Delete(key) +func (b *Bucket) Delete(key string) error { + return b.Items.Delete(key) } diff --git a/nats/store_test.go b/nats/store_test.go index e7f5d29..d2809b2 100644 --- a/nats/store_test.go +++ b/nats/store_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" ) -func initBucketManager() *bucketManager { +func initBucketManager() *Manager { //nc, _ := InitNats("localhost:4222") nc, _ := InitEmbededNats() bm, _ := NewBucketManager(nc)