From 5c70b18c08e176c77635659a300c180415ac6eed Mon Sep 17 00:00:00 2001 From: Nurzhan Saktaganov Date: Thu, 9 Jan 2025 16:16:16 +0300 Subject: [PATCH] api: implement VshardRouterCallResp.GetTransparent method (resolve #22) --- CHANGELOG.md | 12 ++++++++ api.go | 28 ++++++++++++++++++ tests/tnt/router_call_test.go | 53 ++++++++++++++++++++++++++++++++++- 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f93f6d6..b813173 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## Unreleased + +CHANGES: +* All PR, issue references in #XYZ format in commits older than 42f363775dfb9eaf7ec2a6ed7a999847752cec00 refer to https://github.com/KaymeKaydex/go-vshard-router. + +FEATURES: + +* Implement VshardRouterCallResp.GetTransparent method to reuse custom msgpack decoders of direct call (#22). + +TESTS: +* Tests for VshardRouterCallResp.GetTransparent. + ## v1.3.2 CHANGES: diff --git a/api.go b/api.go index 712d7c7..eeae65f 100644 --- a/api.go +++ b/api.go @@ -1,6 +1,7 @@ package vshard_router //nolint:revive import ( + "bytes" "context" "fmt" "time" @@ -228,6 +229,33 @@ func (r VshardRouterCallResp) GetTyped(result []interface{}) error { return nil } +// GetTransparent decodes a response from user defined function into result. +// The response has the same format as a direct call (without vshard router) to this function. +// If you have some custom decoder for some lua handler's response, you can reuse your decoder using this method. +// P.S. the maximum length of the response array is cut to 3 elements due to lua vshard storage implementation +// see https://github.com/tarantool/vshard/blob/dfa2cc8a2aff221d5f421298851a9a229b2e0434/vshard/storage/init.lua#L3130. +func (r VshardRouterCallResp) GetTransparent(result interface{}) error { + // Make a msgpack binary with desired array length. + enc := msgpack.GetEncoder() + defer msgpack.PutEncoder(enc) + + var buf bytes.Buffer + enc.Reset(&buf) + + err := enc.EncodeArrayLen(len(r.rawMessages)) + if err != nil { + return err + } + + data := buf.Bytes() + for _, rawMessage := range r.rawMessages { + data = append(data, rawMessage...) + } + + // Call users' custom decoder (if any). + return msgpack.Unmarshal(data, result) +} + // RouterCallImpl Perform shard operation function will restart operation // after wrong bucket response until timeout is reached // Deprecated: RouterCallImpl is deprecated. diff --git a/tests/tnt/router_call_test.go b/tests/tnt/router_call_test.go index 60c797d..4528788 100644 --- a/tests/tnt/router_call_test.go +++ b/tests/tnt/router_call_test.go @@ -11,7 +11,7 @@ import ( "github.com/tarantool/go-vshard-router/providers/static" ) -func TestRouterCallProto(t *testing.T) { +func TestRouterCallImplProto(t *testing.T) { skipOnInvalidRun(t) t.Parallel() @@ -111,3 +111,54 @@ func TestRouterCallProto(t *testing.T) { _, _, err = router.RouterCallImpl(ctx, bucketID, callOpts, "echo", args) require.Nil(t, err, "RouterCallImpl echo finished with no err even on dirty bucket map") } + +func TestRouterCallProto(t *testing.T) { + skipOnInvalidRun(t) + + t.Parallel() + + ctx := context.Background() + + cfg := getCfg() + + router, err := vshardrouter.NewRouter(ctx, vshardrouter.Config{ + TopologyProvider: static.NewProvider(cfg), + DiscoveryTimeout: 5 * time.Second, + DiscoveryMode: vshardrouter.DiscoveryModeOn, + TotalBucketCount: totalBucketCount, + User: defaultTntUser, + Password: defaultTntPassword, + }) + require.NoError(t, err, "NewRouter finished successfully") + + bucketID := randBucketID(totalBucketCount) + + rs, err := router.BucketResolve(ctx, bucketID) + require.NoError(t, err, "BucketResolve with no err") + + const maxRespLen = 3 + for argLen := 0; argLen <= maxRespLen; argLen++ { + args := []interface{}{} + + for i := 0; i < argLen; i++ { + args = append(args, "arg") + } + + var routerOpts vshardrouter.VshardRouterCallOptions + resp, err := router.CallRW(ctx, bucketID, "echo", args, routerOpts) + require.NoError(t, err, "router.CallRW with no err") + + var resViaVshard interface{} + var resDirect interface{} + + err = resp.GetTransparent(&resViaVshard) + require.NoError(t, err, "GetTransparent with no err") + + var rsOpts vshardrouter.ReplicasetCallOpts + + err = rs.CallAsync(ctx, rsOpts, "echo", args).GetTyped(&resDirect) + require.NoError(t, err, "rs.CallAsync.GetTyped with no error") + + require.Equalf(t, resDirect, resViaVshard, "resDirect != resViaVshard on argLen %d", argLen) + } +}