Skip to content

Commit

Permalink
crud: support noreturn option
Browse files Browse the repository at this point in the history
`noreturn` option was introduced in crud 1.2.0 [1].

1. tarantool/crud@af0ce90
  • Loading branch information
DifferentialOrange authored and oleg-jukovec committed Oct 9, 2023
1 parent c3ba5b5 commit a300800
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
- Support password and password file to decrypt private SSL key file (#319)
- Support `operation_data` in `crud.Error` (#330)
- Support `fetch_latest_metadata` option for crud requests with metadata (#335)
- Support `noreturn` option for data change crud requests (#335)

### Changed

Expand Down
27 changes: 27 additions & 0 deletions crud/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,33 @@ func ExampleResult_many() {
// [[2010 45 bla] [2011 4 bla]]
}

// ExampleResult_noreturn demonstrates noreturn request: a data change
// request where you don't need to retrieve the result, just want to know
// whether it was successful or not.
func ExampleResult_noreturn() {
conn := exampleConnect()
req := crud.MakeReplaceManyRequest(exampleSpace).
Tuples([]crud.Tuple{
[]interface{}{uint(2010), nil, "bla"},
[]interface{}{uint(2011), nil, "bla"},
}).
Opts(crud.ReplaceManyOpts{
Noreturn: crud.MakeOptBool(true),
})

ret := crud.Result{}
if err := conn.Do(req).GetTyped(&ret); err != nil {
fmt.Printf("Failed to execute request: %s", err)
return
}

fmt.Println(ret.Metadata)
fmt.Println(ret.Rows)
// Output:
// []
// <nil>
}

// ExampleResult_error demonstrates how to use a helper type Result
// to handle a crud error.
func ExampleResult_error() {
Expand Down
35 changes: 27 additions & 8 deletions crud/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const (
afterOptName = "after"
batchSizeOptName = "batch_size"
fetchLatestMetadataOptName = "fetch_latest_metadata"
noreturnOptName = "noreturn"
)

// OptUint is an optional uint.
Expand Down Expand Up @@ -156,21 +157,26 @@ type SimpleOperationOpts struct {
// the latest migration of the data format. Performance overhead is up to 15%.
// Disabled by default.
FetchLatestMetadata OptBool
// Noreturn suppresses successfully processed data (first return value is `nil`).
// Disabled by default.
Noreturn OptBool
}

// EncodeMsgpack provides custom msgpack encoder.
func (opts SimpleOperationOpts) EncodeMsgpack(enc *msgpack.Encoder) error {
const optsCnt = 5
const optsCnt = 6

names := [optsCnt]string{timeoutOptName, vshardRouterOptName,
fieldsOptName, bucketIdOptName, fetchLatestMetadataOptName}
fieldsOptName, bucketIdOptName, fetchLatestMetadataOptName,
noreturnOptName}
values := [optsCnt]interface{}{}
exists := [optsCnt]bool{}
values[0], exists[0] = opts.Timeout.Get()
values[1], exists[1] = opts.VshardRouter.Get()
values[2], exists[2] = opts.Fields.Get()
values[3], exists[3] = opts.BucketId.Get()
values[4], exists[4] = opts.FetchLatestMetadata.Get()
values[5], exists[5] = opts.Noreturn.Get()

return encodeOptions(enc, names[:], values[:], exists[:])
}
Expand All @@ -196,15 +202,18 @@ type SimpleOperationObjectOpts struct {
// the latest migration of the data format. Performance overhead is up to 15%.
// Disabled by default.
FetchLatestMetadata OptBool
// Noreturn suppresses successfully processed data (first return value is `nil`).
// Disabled by default.
Noreturn OptBool
}

// EncodeMsgpack provides custom msgpack encoder.
func (opts SimpleOperationObjectOpts) EncodeMsgpack(enc *msgpack.Encoder) error {
const optsCnt = 6
const optsCnt = 7

names := [optsCnt]string{timeoutOptName, vshardRouterOptName,
fieldsOptName, bucketIdOptName, skipNullabilityCheckOnFlattenOptName,
fetchLatestMetadataOptName}
fetchLatestMetadataOptName, noreturnOptName}
values := [optsCnt]interface{}{}
exists := [optsCnt]bool{}
values[0], exists[0] = opts.Timeout.Get()
Expand All @@ -213,6 +222,7 @@ func (opts SimpleOperationObjectOpts) EncodeMsgpack(enc *msgpack.Encoder) error
values[3], exists[3] = opts.BucketId.Get()
values[4], exists[4] = opts.SkipNullabilityCheckOnFlatten.Get()
values[5], exists[5] = opts.FetchLatestMetadata.Get()
values[6], exists[6] = opts.Noreturn.Get()

return encodeOptions(enc, names[:], values[:], exists[:])
}
Expand Down Expand Up @@ -240,15 +250,18 @@ type OperationManyOpts struct {
// the latest migration of the data format. Performance overhead is up to 15%.
// Disabled by default.
FetchLatestMetadata OptBool
// Noreturn suppresses successfully processed data (first return value is `nil`).
// Disabled by default.
Noreturn OptBool
}

// EncodeMsgpack provides custom msgpack encoder.
func (opts OperationManyOpts) EncodeMsgpack(enc *msgpack.Encoder) error {
const optsCnt = 6
const optsCnt = 7

names := [optsCnt]string{timeoutOptName, vshardRouterOptName,
fieldsOptName, stopOnErrorOptName, rollbackOnErrorOptName,
fetchLatestMetadataOptName}
fetchLatestMetadataOptName, noreturnOptName}
values := [optsCnt]interface{}{}
exists := [optsCnt]bool{}
values[0], exists[0] = opts.Timeout.Get()
Expand All @@ -257,6 +270,7 @@ func (opts OperationManyOpts) EncodeMsgpack(enc *msgpack.Encoder) error {
values[3], exists[3] = opts.StopOnError.Get()
values[4], exists[4] = opts.RollbackOnError.Get()
values[5], exists[5] = opts.FetchLatestMetadata.Get()
values[6], exists[6] = opts.Noreturn.Get()

return encodeOptions(enc, names[:], values[:], exists[:])
}
Expand Down Expand Up @@ -287,15 +301,19 @@ type OperationObjectManyOpts struct {
// the latest migration of the data format. Performance overhead is up to 15%.
// Disabled by default.
FetchLatestMetadata OptBool
// Noreturn suppresses successfully processed data (first return value is `nil`).
// Disabled by default.
Noreturn OptBool
}

// EncodeMsgpack provides custom msgpack encoder.
func (opts OperationObjectManyOpts) EncodeMsgpack(enc *msgpack.Encoder) error {
const optsCnt = 7
const optsCnt = 8

names := [optsCnt]string{timeoutOptName, vshardRouterOptName,
fieldsOptName, stopOnErrorOptName, rollbackOnErrorOptName,
skipNullabilityCheckOnFlattenOptName, fetchLatestMetadataOptName}
skipNullabilityCheckOnFlattenOptName, fetchLatestMetadataOptName,
noreturnOptName}
values := [optsCnt]interface{}{}
exists := [optsCnt]bool{}
values[0], exists[0] = opts.Timeout.Get()
Expand All @@ -305,6 +323,7 @@ func (opts OperationObjectManyOpts) EncodeMsgpack(enc *msgpack.Encoder) error {
values[4], exists[4] = opts.RollbackOnError.Get()
values[5], exists[5] = opts.SkipNullabilityCheckOnFlatten.Get()
values[6], exists[6] = opts.FetchLatestMetadata.Get()
values[7], exists[7] = opts.Noreturn.Get()

return encodeOptions(enc, names[:], values[:], exists[:])
}
Expand Down
195 changes: 195 additions & 0 deletions crud/tarantool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,201 @@ func TestFetchLatestMetadataOption(t *testing.T) {
}
}

var testNoreturnCases = []struct {
name string
req tarantool.Request
}{
{
"Insert",
crud.MakeInsertRequest(spaceName).
Tuple(tuple).
Opts(crud.InsertOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"InsertObject",
crud.MakeInsertObjectRequest(spaceName).
Object(object).
Opts(crud.InsertObjectOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"InsertMany",
crud.MakeInsertManyRequest(spaceName).
Tuples(tuples).
Opts(crud.InsertManyOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"InsertObjectMany",
crud.MakeInsertObjectManyRequest(spaceName).
Objects(objects).
Opts(crud.InsertObjectManyOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"Replace",
crud.MakeReplaceRequest(spaceName).
Tuple(tuple).
Opts(crud.ReplaceOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"ReplaceObject",
crud.MakeReplaceObjectRequest(spaceName).
Object(object).
Opts(crud.ReplaceObjectOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"ReplaceMany",
crud.MakeReplaceManyRequest(spaceName).
Tuples(tuples).
Opts(crud.ReplaceManyOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"ReplaceObjectMany",
crud.MakeReplaceObjectManyRequest(spaceName).
Objects(objects).
Opts(crud.ReplaceObjectManyOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"Upsert",
crud.MakeUpsertRequest(spaceName).
Tuple(tuple).
Operations(operations).
Opts(crud.UpsertOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"UpsertObject",
crud.MakeUpsertObjectRequest(spaceName).
Object(object).
Operations(operations).
Opts(crud.UpsertObjectOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"UpsertMany",
crud.MakeUpsertManyRequest(spaceName).
TuplesOperationsData(tuplesOperationsData).
Opts(crud.UpsertManyOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"UpsertObjectMany",
crud.MakeUpsertObjectManyRequest(spaceName).
ObjectsOperationsData(objectsOperationData).
Opts(crud.UpsertObjectManyOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"Update",
crud.MakeUpdateRequest(spaceName).
Key(key).
Operations(operations).
Opts(crud.UpdateOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
{
"Delete",
crud.MakeDeleteRequest(spaceName).
Key(key).
Opts(crud.DeleteOpts{
Noreturn: crud.MakeOptBool(true),
}),
},
}

func TestNoreturnOption(t *testing.T) {
conn := connect(t)
defer conn.Close()

for _, testCase := range testNoreturnCases {
t.Run(testCase.name, func(t *testing.T) {
for i := 1010; i < 1020; i++ {
req := tarantool.NewDeleteRequest(spaceName).
Key([]interface{}{uint(i)})
conn.Do(req).Get()
}

resp, err := conn.Do(testCase.req).Get()
if err != nil {
t.Fatalf("Failed to Do CRUD request: %s", err)
}

if len(resp.Data) == 0 {
t.Fatalf("Expected explicit nil")
}

if resp.Data[0] != nil {
t.Fatalf("Expected nil result, got %v", resp.Data[0])
}

if len(resp.Data) >= 2 && resp.Data[1] != nil {
t.Fatalf("Expected no returned errors, got %v", resp.Data[1])
}

for i := 1010; i < 1020; i++ {
req := tarantool.NewDeleteRequest(spaceName).
Key([]interface{}{uint(i)})
conn.Do(req).Get()
}
})
}
}

func TestNoreturnOptionTyped(t *testing.T) {
conn := connect(t)
defer conn.Close()

for _, testCase := range testNoreturnCases {
t.Run(testCase.name, func(t *testing.T) {
for i := 1010; i < 1020; i++ {
req := tarantool.NewDeleteRequest(spaceName).
Key([]interface{}{uint(i)})
conn.Do(req).Get()
}

resp := crud.Result{}

err := conn.Do(testCase.req).GetTyped(&resp)
if err != nil {
t.Fatalf("Failed to Do CRUD request: %s", err)
}

if resp.Rows != nil {
t.Fatalf("Expected nil rows, got %v", resp.Rows)
}

if len(resp.Metadata) != 0 {
t.Fatalf("Expected no metadata")
}

for i := 1010; i < 1020; i++ {
req := tarantool.NewDeleteRequest(spaceName).
Key([]interface{}{uint(i)})
conn.Do(req).Get()
}
})
}
}

// runTestMain is a body of TestMain function
// (see https://pkg.go.dev/testing#hdr-Main).
// Using defer + os.Exit is not works so TestMain body
Expand Down

0 comments on commit a300800

Please sign in to comment.