From f460444e8a0a48e0f945272b327d0781877ad4f9 Mon Sep 17 00:00:00 2001 From: Maksim Konovalov Date: Mon, 9 Sep 2024 13:50:43 +0300 Subject: [PATCH 1/8] feature/suguar: implement go-tarantool like faces for sharded tarantool request --- CHANGELOG.md | 1 + suguar.go | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++ vshard.go | 10 ++++- 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 suguar.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d73e36..5298616 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ FEATURES: * Support new Sprintf-like logging interface (#48) +* Added call interfaces backwards compatible with go-tarantool (#31). REFACTOR: diff --git a/suguar.go b/suguar.go new file mode 100644 index 0000000..b407f06 --- /dev/null +++ b/suguar.go @@ -0,0 +1,104 @@ +package vshard_router //nolint:revive + +import ( + "context" + "fmt" + + "github.com/tarantool/go-tarantool/v2/pool" +) + +// CallRequest helps you to create a call request object for execution +// by a Connection. +type CallRequest struct { + ctx context.Context + fnc string + args interface{} + bucketID uint64 +} + +// CallResponse is a backwards-compatible structure with go-tarantool for easier replacement. +type CallResponse struct { + rawResp interface{} + getTypedFnc StorageResultTypedFunc + err error +} + +// NewCallRequest returns a new empty CallRequest. +func (r *Router) NewCallRequest(function string) *CallRequest { + req := new(CallRequest) + req.fnc = function + return req +} + +// Do perform a request asynchronously on the connection. +func (r *Router) Do(req *CallRequest, userMode pool.Mode) *CallResponse { + ctx := req.ctx + bucketID := req.bucketID + resp := new(CallResponse) + + if req.bucketID == 0 { + if r.cfg.BucketGetter == nil { + resp.err = fmt.Errorf("bucket id for request is not set") + return resp + } + + bucketID = r.cfg.BucketGetter(ctx) + } + + vshardMode := ReadMode + + if userMode == pool.RW { + vshardMode = WriteMode + } + + resp.rawResp, resp.getTypedFnc, resp.err = r.RouterCallImpl(ctx, + bucketID, + CallOpts{ + Timeout: r.cfg.RequestTimeout, + PoolMode: userMode, + VshardMode: vshardMode, + }, + req.fnc, + req.args) + + return resp +} + +// Args sets the args for the eval request. +// Note: default value is empty. +func (req *CallRequest) Args(args interface{}) *CallRequest { + req.args = args + return req +} + +// Context sets a passed context to the request. +func (req *CallRequest) Context(ctx context.Context) *CallRequest { + req.ctx = ctx + return req +} + +// BucketID method that sets the bucketID for your request. +// You can ignore this parameter if you have a bucketGetter. +// However, this method has a higher priority. +func (req *CallRequest) BucketID(bucketID uint64) *CallRequest { + req.bucketID = bucketID + return req +} + +// GetTyped waits for Future and calls msgpack.Decoder.Decode(result) if no error happens. +func (resp *CallResponse) GetTyped(result interface{}) error { + if resp.err != nil { + return resp.err + } + + return resp.getTypedFnc(result) +} + +// Get waits for Future to be filled and returns the data of the Response and error. +func (resp *CallResponse) Get() ([]interface{}, error) { + if resp.err != nil { + return nil, resp.err + } + + return []interface{}{resp.rawResp}, nil +} diff --git a/vshard.go b/vshard.go index a1f1a0f..b36a440 100644 --- a/vshard.go +++ b/vshard.go @@ -92,13 +92,21 @@ type Config struct { TopologyProvider TopologyProvider // TopologyProvider is required provider // Discovery - DiscoveryTimeout time.Duration // DiscoveryTimeout is timeout between cron discovery job; by default there is no timeout + // DiscoveryTimeout is timeout between cron discovery job; by default there is no timeout. + DiscoveryTimeout time.Duration DiscoveryMode DiscoveryMode TotalBucketCount uint64 User string Password string PoolOpts tarantool.Opts + + // BucketGetter is an optional argument. + // You can specify a function that will receive the bucket id from the context. + // This is useful if you use middleware that inserts the calculated bucket id into the request context. + BucketGetter func(ctx context.Context) uint64 + // RequestTimeout timeout for requests to Tarantool. + RequestTimeout time.Duration } type BucketStatInfo struct { From ef102b33cedab6ed8bf566b65b731748af03053b Mon Sep 17 00:00:00 2001 From: Maksim Konovalov Date: Tue, 17 Sep 2024 10:22:32 +0300 Subject: [PATCH 2/8] rename --- suguar.go => sugar.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename suguar.go => sugar.go (100%) diff --git a/suguar.go b/sugar.go similarity index 100% rename from suguar.go rename to sugar.go From 09e3e271efbe1100bcd827664c18dce662485233 Mon Sep 17 00:00:00 2001 From: Maksim Konovalov Date: Fri, 20 Sep 2024 12:12:57 +0300 Subject: [PATCH 3/8] fix Do function comment --- sugar.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sugar.go b/sugar.go index b407f06..5ec172d 100644 --- a/sugar.go +++ b/sugar.go @@ -30,7 +30,8 @@ func (r *Router) NewCallRequest(function string) *CallRequest { return req } -// Do perform a request asynchronously on the connection. +// Do perform a request synchronously on the connection. +// It is important that the logic of this method is different from go-tarantool. func (r *Router) Do(req *CallRequest, userMode pool.Mode) *CallResponse { ctx := req.ctx bucketID := req.bucketID From 1896201005383fa37013f8d6a9db7823247ad175 Mon Sep 17 00:00:00 2001 From: Maksim Konovalov Date: Fri, 20 Sep 2024 12:23:35 +0300 Subject: [PATCH 4/8] add more comments about request timeout --- vshard.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vshard.go b/vshard.go index b36a440..1fbee7c 100644 --- a/vshard.go +++ b/vshard.go @@ -106,6 +106,8 @@ type Config struct { // This is useful if you use middleware that inserts the calculated bucket id into the request context. BucketGetter func(ctx context.Context) uint64 // RequestTimeout timeout for requests to Tarantool. + // Don't rely on using this timeout. + // Currently, it only works for sugar implementations and works as a retry timeout. RequestTimeout time.Duration } From cb44cd4263691a71020c9794d629e203c916b564 Mon Sep 17 00:00:00 2001 From: Maksim Konovalov Date: Fri, 20 Sep 2024 12:26:34 +0300 Subject: [PATCH 5/8] fixed GetTyped --- sugar.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sugar.go b/sugar.go index 5ec172d..a4e2af7 100644 --- a/sugar.go +++ b/sugar.go @@ -86,7 +86,7 @@ func (req *CallRequest) BucketID(bucketID uint64) *CallRequest { return req } -// GetTyped waits for Future and calls msgpack.Decoder.Decode(result) if no error happens. +// GetTyped waits synchronously for response and calls msgpack.Decoder.Decode(result) if no error happens. func (resp *CallResponse) GetTyped(result interface{}) error { if resp.err != nil { return resp.err @@ -95,7 +95,8 @@ func (resp *CallResponse) GetTyped(result interface{}) error { return resp.getTypedFnc(result) } -// Get waits for Future to be filled and returns the data of the Response and error. +// Get implementation now works synchronously for response. +// The interface was created purely for convenient migration to go-vshard-router from go-tarantool. func (resp *CallResponse) Get() ([]interface{}, error) { if resp.err != nil { return nil, resp.err From a7257f246a0f74d1a9f3dd637e79b43d8fb3c5d5 Mon Sep 17 00:00:00 2001 From: Maksim Konovalov Date: Fri, 20 Sep 2024 12:29:40 +0300 Subject: [PATCH 6/8] add Do validation --- sugar.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sugar.go b/sugar.go index a4e2af7..545283a 100644 --- a/sugar.go +++ b/sugar.go @@ -37,6 +37,16 @@ func (r *Router) Do(req *CallRequest, userMode pool.Mode) *CallResponse { bucketID := req.bucketID resp := new(CallResponse) + if req.fnc == "" { + resp.err = fmt.Errorf("func name is empty") + return resp + } + + if req.args == nil { + resp.err = fmt.Errorf("no request args") + return resp + } + if req.bucketID == 0 { if r.cfg.BucketGetter == nil { resp.err = fmt.Errorf("bucket id for request is not set") From 1101ea39bc9f6d42ec83c8e88868dd796f6e7934 Mon Sep 17 00:00:00 2001 From: Maksim Konovalov Date: Mon, 23 Sep 2024 12:47:11 +0300 Subject: [PATCH 7/8] fix timeout comment --- vshard.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vshard.go b/vshard.go index abfc284..673cee5 100644 --- a/vshard.go +++ b/vshard.go @@ -107,7 +107,9 @@ type Config struct { BucketGetter func(ctx context.Context) uint64 // RequestTimeout timeout for requests to Tarantool. // Don't rely on using this timeout. - // Currently, it only works for sugar implementations and works as a retry timeout. + // This is the difference between the timeout of the library itself + // that is, our retry timeout if the buckets, for example, move. + // Currently, it only works for sugar implementations . RequestTimeout time.Duration } From b5693647b2889a89ee752e167ae5d59f449ff9c3 Mon Sep 17 00:00:00 2001 From: Maksim Konovalov Date: Mon, 23 Sep 2024 13:10:40 +0300 Subject: [PATCH 8/8] add user mode comment --- sugar.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sugar.go b/sugar.go index 545283a..b1603d6 100644 --- a/sugar.go +++ b/sugar.go @@ -58,6 +58,8 @@ func (r *Router) Do(req *CallRequest, userMode pool.Mode) *CallResponse { vshardMode := ReadMode + // If the user says he prefers to do it on the master, + // then he agrees that it will go to the replica, which means he will not write. if userMode == pool.RW { vshardMode = WriteMode }