From df5bcf4727e39d5a748cc4c2d6cc9e0a3682e614 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Mon, 1 Jul 2024 17:46:48 +0000 Subject: [PATCH 01/15] [CLIENT-3022] Close() throws a nil point error on ProxyClient without Authentication --- proxy_client.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proxy_client.go b/proxy_client.go index a32fde85..3defe7a6 100644 --- a/proxy_client.go +++ b/proxy_client.go @@ -306,7 +306,9 @@ func (clnt *ProxyClient) createGrpcConn(noInterceptor bool) (*grpc.ClientConn, E // Close closes all Grpcclient connections to database server nodes. func (clnt *ProxyClient) Close() { clnt.active.Set(false) - clnt.authInterceptor.close() + if clnt.authInterceptor != nil { + clnt.authInterceptor.close() + } } // IsConnected determines if the Grpcclient is ready to talk to the database server cluster. From 8c8c1a6a3958936099e5dd5e11e9f9eb3dea96a8 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 3 Jul 2024 00:01:20 +0200 Subject: [PATCH 02/15] Update CHANGELOG --- CHANGELOG.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec7b7ebe..1286273f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,22 +1,28 @@ # Change History -## June 27 2024: v7.5.0 +## July 2 2024: v7.5.1 + + Hotfix release. + +- **Fixes** + - [CLIENT-3022] `Close()` throws a `nil` pointer error on `ProxyClient` without Authentication. + +## July 1 2024: v7.5.0 This a minor feature and fix release. - **New Features** - - [CLIENT-2968] Support new v71 proxy features: - - Info command. + - [CLIENT-2968] Support new v7.1 proxy server features: + - `Info` command. - `QueryPolicy.QueryDuration` - [CLIENT-3012] Support new server 7.1 info command error response strings. - **Improvements** - [CLIENT-2997] Scans should work in a mixed cluster of v5.7 and v6.4 server nodes. - - [CLIENT-3012] Support new server 7.1 info command error response strings. - - [CLIENT-3020] Change ReadModeSC doc from server to client perspective. + - [CLIENT-3020] Change `ReadModeSC` doc from server to client perspective. - **Fixes** - - [CLIENT-3019] Prevent Goroutine leak in AuthInterceptor for the Proxy Client. + - [CLIENT-3019] Prevent Goroutine leak in `AuthInterceptor` for the Proxy Client. ## May 20 2024: v7.4.0 From f2b691d4de5444efbeff3cee15ac70e100b600a3 Mon Sep 17 00:00:00 2001 From: "Eugene R." Date: Tue, 16 Jul 2024 20:19:43 +0300 Subject: [PATCH 03/15] [CLIENT-3044] Resolve memory leak caused by circular reference in client finalizer (#441) --- batch_command.go | 8 +++++++- batch_command_delete.go | 12 +++++++++--- batch_command_exists.go | 12 +++++++++--- batch_command_get.go | 13 +++++++++---- batch_command_operate.go | 17 +++++++++-------- batch_command_udf.go | 25 ++++++++++++++++--------- batch_index_command_get.go | 6 ++++-- client.go | 20 ++++++++------------ client_reflect.go | 2 +- cluster.go | 2 -- proxy_client.go | 7 ++++++- proxy_client_reflect.go | 3 +-- 12 files changed, 79 insertions(+), 48 deletions(-) diff --git a/batch_command.go b/batch_command.go index dd607791..0fcbf2a9 100644 --- a/batch_command.go +++ b/batch_command.go @@ -28,12 +28,18 @@ type batcher interface { generateBatchNodes(*Cluster) ([]*batchNode, Error) setSequence(int, int) - executeSingle(*Client) Error + executeSingle(clientIfc) Error +} + +type clientIfc interface { + ClientIfc + execute(policy *WritePolicy, key *Key, packageName string, functionName string, args ...Value) (*Record, Error) } type batchCommand struct { baseMultiCommand + client clientIfc batch *batchNode policy *BatchPolicy sequenceAP int diff --git a/batch_command_delete.go b/batch_command_delete.go index 0426662b..28bb0e87 100644 --- a/batch_command_delete.go +++ b/batch_command_delete.go @@ -29,7 +29,7 @@ type batchCommandDelete struct { } func newBatchCommandDelete( - node *Node, + client clientIfc, batch *batchNode, policy *BatchPolicy, batchDeletePolicy *BatchDeletePolicy, @@ -37,8 +37,14 @@ func newBatchCommandDelete( records []*BatchRecord, attr *batchAttr, ) *batchCommandDelete { + var node *Node + if batch != nil { + node = batch.Node + } + res := &batchCommandDelete{ batchCommand: batchCommand{ + client: client, baseMultiCommand: *newMultiCommand(node, nil, false), policy: policy, batch: batch, @@ -168,7 +174,7 @@ func (cmd *batchCommandDelete) transactionType() transactionType { return ttBatchWrite } -func (cmd *batchCommandDelete) executeSingle(client *Client) Error { +func (cmd *batchCommandDelete) executeSingle(client clientIfc) Error { policy := cmd.batchDeletePolicy.toWritePolicy(cmd.policy) for i, key := range cmd.keys { res, err := client.Operate(policy, key, DeleteOp()) @@ -197,7 +203,7 @@ func (cmd *batchCommandDelete) executeSingle(client *Client) Error { func (cmd *batchCommandDelete) Execute() Error { if len(cmd.keys) == 1 { - return cmd.executeSingle(cmd.node.cluster.client) + return cmd.executeSingle(cmd.client) } return cmd.execute(cmd) } diff --git a/batch_command_exists.go b/batch_command_exists.go index 18d532f0..faa7d957 100644 --- a/batch_command_exists.go +++ b/batch_command_exists.go @@ -27,14 +27,20 @@ type batchCommandExists struct { } func newBatchCommandExists( - node *Node, + client clientIfc, batch *batchNode, policy *BatchPolicy, keys []*Key, existsArray []bool, ) *batchCommandExists { + var node *Node + if batch != nil { + node = batch.Node + } + res := &batchCommandExists{ batchCommand: batchCommand{ + client: client, baseMultiCommand: *newMultiCommand(node, nil, false), policy: policy, batch: batch, @@ -110,7 +116,7 @@ func (cmd *batchCommandExists) transactionType() transactionType { return ttBatchRead } -func (cmd *batchCommandExists) executeSingle(client *Client) Error { +func (cmd *batchCommandExists) executeSingle(client clientIfc) Error { var err Error for _, offset := range cmd.batch.offsets { cmd.existsArray[offset], err = client.Exists(&cmd.policy.BasePolicy, cmd.keys[offset]) @@ -136,7 +142,7 @@ func (cmd *batchCommandExists) executeSingle(client *Client) Error { func (cmd *batchCommandExists) Execute() Error { if len(cmd.batch.offsets) == 1 { - return cmd.executeSingle(cmd.node.cluster.client) + return cmd.executeSingle(cmd.client) } return cmd.execute(cmd) } diff --git a/batch_command_get.go b/batch_command_get.go index 4ce85bcb..ea5c68ba 100644 --- a/batch_command_get.go +++ b/batch_command_get.go @@ -55,7 +55,7 @@ var batchObjectParser func( ) Error func newBatchCommandGet( - node *Node, + client clientIfc, batch *batchNode, policy *BatchPolicy, keys []*Key, @@ -65,8 +65,14 @@ func newBatchCommandGet( readAttr int, isOperation bool, ) *batchCommandGet { + var node *Node + if batch != nil { + node = batch.Node + } + res := &batchCommandGet{ batchCommand: batchCommand{ + client: client, baseMultiCommand: *newMultiCommand(node, nil, isOperation), policy: policy, batch: batch, @@ -158,7 +164,6 @@ func (cmd *batchCommandGet) parseRecordResults(ifc command, receiveSize int) (bo cmd.objectsFound[batchIndex] = true if err := batchObjectParser(cmd, batchIndex, opCount, fieldCount, generation, expiration); err != nil { return false, err - } } } @@ -216,7 +221,7 @@ func (cmd *batchCommandGet) transactionType() transactionType { return ttBatchRead } -func (cmd *batchCommandGet) executeSingle(client *Client) Error { +func (cmd *batchCommandGet) executeSingle(client clientIfc) Error { for _, offset := range cmd.batch.offsets { var err Error if len(cmd.ops) > 0 { @@ -254,7 +259,7 @@ func (cmd *batchCommandGet) executeSingle(client *Client) Error { func (cmd *batchCommandGet) Execute() Error { if cmd.objects == nil && len(cmd.batch.offsets) == 1 { - return cmd.executeSingle(cmd.node.cluster.client) + return cmd.executeSingle(cmd.client) } return cmd.execute(cmd) } diff --git a/batch_command_operate.go b/batch_command_operate.go index 2ff0bfbd..f0309263 100644 --- a/batch_command_operate.go +++ b/batch_command_operate.go @@ -25,7 +25,6 @@ import ( type batchCommandOperate struct { batchCommand - client ClientIfc attr *batchAttr records []BatchRecordIfc @@ -36,19 +35,23 @@ type batchCommandOperate struct { } func newBatchCommandOperate( - client ClientIfc, - node *Node, + client clientIfc, batch *batchNode, policy *BatchPolicy, records []BatchRecordIfc, ) *batchCommandOperate { + var node *Node + if batch != nil { + node = batch.Node + } + res := &batchCommandOperate{ batchCommand: batchCommand{ + client: client, baseMultiCommand: *newMultiCommand(node, nil, true), policy: policy, batch: batch, }, - client: client, records: records, } return res @@ -228,7 +231,7 @@ func (cmd *batchCommandOperate) parseRecord(key *Key, opCount int, generation, e return newRecord(cmd.node, key, bins, generation, expiration), nil } -func (cmd *batchCommandOperate) executeSingle(client *Client) Error { +func (cmd *batchCommandOperate) executeSingle(client clientIfc) Error { var res *Record var err Error for _, br := range cmd.records { @@ -250,11 +253,9 @@ func (cmd *batchCommandOperate) executeSingle(client *Client) Error { policy := cmd.client.getUsableBatchWritePolicy(br.Policy).toWritePolicy(cmd.policy) policy.RespondPerEachOp = true res, err = client.Operate(policy, br.Key, br.Ops...) - br.setRecord(res) case *BatchDelete: policy := cmd.client.getUsableBatchDeletePolicy(br.Policy).toWritePolicy(cmd.policy) res, err = client.Operate(policy, br.Key, DeleteOp()) - br.setRecord(res) case *BatchUDF: policy := cmd.client.getUsableBatchUDFPolicy(br.Policy).toWritePolicy(cmd.policy) policy.RespondPerEachOp = true @@ -287,7 +288,7 @@ func (cmd *batchCommandOperate) executeSingle(client *Client) Error { func (cmd *batchCommandOperate) Execute() Error { if cmd.objects == nil && len(cmd.records) == 1 { - return cmd.executeSingle(cmd.node.cluster.client) + return cmd.executeSingle(cmd.client) } return cmd.execute(cmd) } diff --git a/batch_command_udf.go b/batch_command_udf.go index d37a898d..b8b9c445 100644 --- a/batch_command_udf.go +++ b/batch_command_udf.go @@ -32,7 +32,7 @@ type batchCommandUDF struct { } func newBatchCommandUDF( - node *Node, + client clientIfc, batch *batchNode, policy *BatchPolicy, batchUDFPolicy *BatchUDFPolicy, @@ -43,18 +43,25 @@ func newBatchCommandUDF( records []*BatchRecord, attr *batchAttr, ) *batchCommandUDF { + var node *Node + if batch != nil { + node = batch.Node + } + res := &batchCommandUDF{ batchCommand: batchCommand{ + client: client, baseMultiCommand: *newMultiCommand(node, nil, false), policy: policy, batch: batch, }, - keys: keys, - records: records, - packageName: packageName, - functionName: functionName, - args: args, - attr: attr, + batchUDFPolicy: batchUDFPolicy, + keys: keys, + records: records, + packageName: packageName, + functionName: functionName, + args: args, + attr: attr, } return res } @@ -176,7 +183,7 @@ func (cmd *batchCommandUDF) isRead() bool { return !cmd.attr.hasWrite } -func (cmd *batchCommandUDF) executeSingle(client *Client) Error { +func (cmd *batchCommandUDF) executeSingle(client clientIfc) Error { for i, key := range cmd.keys { policy := cmd.batchUDFPolicy.toWritePolicy(cmd.policy) policy.RespondPerEachOp = true @@ -206,7 +213,7 @@ func (cmd *batchCommandUDF) executeSingle(client *Client) Error { func (cmd *batchCommandUDF) Execute() Error { if len(cmd.keys) == 1 { - return cmd.executeSingle(cmd.node.cluster.client) + return cmd.executeSingle(cmd.client) } return cmd.execute(cmd) } diff --git a/batch_index_command_get.go b/batch_index_command_get.go index e6cab565..4eeff58a 100644 --- a/batch_index_command_get.go +++ b/batch_index_command_get.go @@ -21,6 +21,7 @@ type batchIndexCommandGet struct { } func newBatchIndexCommandGet( + client clientIfc, batch *batchNode, policy *BatchPolicy, records []*BatchRead, @@ -34,6 +35,7 @@ func newBatchIndexCommandGet( res := &batchIndexCommandGet{ batchCommandGet{ batchCommand: batchCommand{ + client: client, baseMultiCommand: *newMultiCommand(node, nil, isOperation), policy: policy, batch: batch, @@ -59,12 +61,12 @@ func (cmd *batchIndexCommandGet) writeBuffer(ifc command) Error { func (cmd *batchIndexCommandGet) Execute() Error { if len(cmd.batch.offsets) == 1 { - return cmd.executeSingle(cmd.node.cluster.client) + return cmd.executeSingle(cmd.client) } return cmd.execute(cmd) } -func (cmd *batchIndexCommandGet) executeSingle(client *Client) Error { +func (cmd *batchIndexCommandGet) executeSingle(client clientIfc) Error { for i, br := range cmd.indexRecords { var ops []*Operation if br.headerOnly() { diff --git a/client.go b/client.go index a6841caf..83fccc90 100644 --- a/client.go +++ b/client.go @@ -111,12 +111,8 @@ func NewClientWithPolicyAndHost(policy *ClientPolicy, hosts ...*Host) (*Client, DefaultInfoPolicy: NewInfoPolicy(), } - // back reference especially used in batch commands - cluster.client = client - runtime.SetFinalizer(client, clientFinalizer) return client, err - } //------------------------------------------------------- @@ -453,7 +449,7 @@ func (clnt *Client) BatchExists(policy *BatchPolicy, keys []*Key) ([]bool, Error } // pass nil to make sure it will be cloned and prepared - cmd := newBatchCommandExists(nil, nil, policy, keys, existsArray) + cmd := newBatchCommandExists(clnt, nil, policy, keys, existsArray) filteredOut, err := clnt.batchExecute(policy, batchNodes, cmd) if filteredOut > 0 { err = chainErrors(ErrFilteredOut.err(), err) @@ -526,7 +522,7 @@ func (clnt *Client) BatchGet(policy *BatchPolicy, keys []*Key, binNames ...strin return nil, err } - cmd := newBatchCommandGet(nil, nil, policy, keys, binNames, nil, records, _INFO1_READ, false) + cmd := newBatchCommandGet(clnt, nil, policy, keys, binNames, nil, records, _INFO1_READ, false) filteredOut, err := clnt.batchExecute(policy, batchNodes, cmd) if err != nil && !policy.AllowPartialResults { return nil, err @@ -556,7 +552,7 @@ func (clnt *Client) BatchGetOperate(policy *BatchPolicy, keys []*Key, ops ...*Op return nil, err } - cmd := newBatchCommandGet(nil, nil, policy, keys, nil, ops, records, _INFO1_READ, true) + cmd := newBatchCommandGet(clnt, nil, policy, keys, nil, ops, records, _INFO1_READ, true) filteredOut, err := clnt.batchExecute(policy, batchNodes, cmd) if err != nil && !policy.AllowPartialResults { return nil, err @@ -578,7 +574,7 @@ func (clnt *Client) BatchGetOperate(policy *BatchPolicy, keys []*Key, ops ...*Op func (clnt *Client) BatchGetComplex(policy *BatchPolicy, records []*BatchRead) Error { policy = clnt.getUsableBatchPolicy(policy) - cmd := newBatchIndexCommandGet(nil, policy, records, true) + cmd := newBatchIndexCommandGet(clnt, nil, policy, records, true) batchNodes, err := newBatchIndexNodeList(clnt.cluster, policy, records) if err != nil { @@ -614,7 +610,7 @@ func (clnt *Client) BatchGetHeader(policy *BatchPolicy, keys []*Key) ([]*Record, return nil, err } - cmd := newBatchCommandGet(nil, nil, policy, keys, nil, nil, records, _INFO1_READ|_INFO1_NOBINDATA, false) + cmd := newBatchCommandGet(clnt, nil, policy, keys, nil, nil, records, _INFO1_READ|_INFO1_NOBINDATA, false) filteredOut, err := clnt.batchExecute(policy, batchNodes, cmd) if err != nil && !policy.AllowPartialResults { return nil, err @@ -650,7 +646,7 @@ func (clnt *Client) BatchDelete(policy *BatchPolicy, deletePolicy *BatchDeletePo return nil, err } - cmd := newBatchCommandDelete(nil, nil, policy, deletePolicy, keys, records, attr) + cmd := newBatchCommandDelete(clnt, nil, policy, deletePolicy, keys, records, attr) _, err = clnt.batchExecute(policy, batchNodes, cmd) return records, err } @@ -670,7 +666,7 @@ func (clnt *Client) BatchOperate(policy *BatchPolicy, records []BatchRecordIfc) return err } - cmd := newBatchCommandOperate(clnt, nil, nil, policy, records) + cmd := newBatchCommandOperate(clnt, nil, policy, records) _, err = clnt.batchExecute(policy, batchNodes, cmd) return err } @@ -701,7 +697,7 @@ func (clnt *Client) BatchExecute(policy *BatchPolicy, udfPolicy *BatchUDFPolicy, return nil, err } - cmd := newBatchCommandUDF(nil, nil, policy, udfPolicy, keys, packageName, functionName, args, records, attr) + cmd := newBatchCommandUDF(clnt, nil, policy, udfPolicy, keys, packageName, functionName, args, records, attr) _, err = clnt.batchExecute(policy, batchNodes, cmd) return records, err } diff --git a/client_reflect.go b/client_reflect.go index 2545dcb5..cf97dabe 100644 --- a/client_reflect.go +++ b/client_reflect.go @@ -128,7 +128,7 @@ func (clnt *Client) BatchGetObjects(policy *BatchPolicy, keys []*Key, objects [] } objectsFound := make([]bool, len(keys)) - cmd := newBatchCommandGet(nil, nil, policy, keys, binNames, nil, nil, _INFO1_READ, false) + cmd := newBatchCommandGet(clnt, nil, policy, keys, binNames, nil, nil, _INFO1_READ, false) cmd.objects = objectsVal cmd.objectsFound = objectsFound diff --git a/cluster.go b/cluster.go index 0152c621..6de03013 100644 --- a/cluster.go +++ b/cluster.go @@ -32,8 +32,6 @@ import ( // Cluster encapsulates the aerospike cluster nodes and manages // them. type Cluster struct { - client *Client - // Initial host nodes specified by user. seeds iatomic.SyncVal //[]*Host diff --git a/proxy_client.go b/proxy_client.go index 3defe7a6..2a5ee016 100644 --- a/proxy_client.go +++ b/proxy_client.go @@ -723,7 +723,7 @@ func (clnt *ProxyClient) batchOperate(policy *BatchPolicy, records []BatchRecord return 0, err } - cmd := newBatchCommandOperate(clnt, nil, batchNode, policy, records) + cmd := newBatchCommandOperate(clnt, batchNode, policy, records) return cmd.filteredOutCnt, cmd.ExecuteGRPC(clnt) } @@ -900,9 +900,14 @@ func (clnt *ProxyClient) Execute(policy *WritePolicy, key *Key, packageName stri if rec := command.GetRecord(); rec != nil && rec.Bins != nil { return rec.Bins["SUCCESS"], nil } + return nil, nil } +func (clnt *ProxyClient) execute(policy *WritePolicy, key *Key, packageName string, functionName string, args ...Value) (*Record, Error) { + return nil, newError(types.UNSUPPORTED_FEATURE) +} + //---------------------------------------------------------- // Query/Execute (Supported by Aerospike 3+ servers only) //---------------------------------------------------------- diff --git a/proxy_client_reflect.go b/proxy_client_reflect.go index 811039e1..a368b794 100644 --- a/proxy_client_reflect.go +++ b/proxy_client_reflect.go @@ -121,7 +121,7 @@ func (clnt *ProxyClient) BatchGetObjects(policy *BatchPolicy, keys []*Key, objec return nil, err } - cmd := newBatchCommandOperate(clnt, nil, batchNode, policy, batchRecordsIfc) + cmd := newBatchCommandOperate(clnt, batchNode, policy, batchRecordsIfc) objectsFound := make([]bool, len(keys)) cmd.objects = objectsVal @@ -133,7 +133,6 @@ func (clnt *ProxyClient) BatchGetObjects(policy *BatchPolicy, keys []*Key, objec // } return objectsFound, err - } // ScanPartitionObjects Reads records in specified namespace, set and partition filter. From 57c4f2bccff3478ef77b63ed9b7591cf27f35d39 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Tue, 16 Jul 2024 23:13:21 +0200 Subject: [PATCH 04/15] [CLIENT-3045] Move proxy client build behind a build flag This removes the GRPC compilation and potential namespace conflict from the default build and moves it behind the compiler build flag "as_proxy" --- .github/workflows/build.yml | 2 + README.md | 23 +- aerospike_bench_reflect_test.go | 1 - anonymous_fields_test.go | 1 - batch_command_get_reflect.go | 1 - batch_command_operate.go | 67 ---- batch_command_reflect.go | 1 - batch_policy.go | 12 - batch_test.go | 1 - cdt_map_test.go | 1 - client_appengine_exclusions.go | 12 - client_builder.go | 22 -- client_builder_native.go | 36 ++ client_builder_proxy.go | 39 ++ client_ifc.go | 1 - client_ifc_app_engine.go | 1 - client_ifc_app_engine_perf.go | 14 +- client_ifc_as_performance.go | 1 - client_object_test.go | 1 - client_reflect.go | 1 - client_reflect_test.go | 1 - client_test.go | 13 - commit_policy.go | 12 - delete_command.go | 50 --- error.go | 53 --- execute_command.go | 53 --- execute_task.go | 69 +--- execute_task_native.go | 22 ++ exists_command.go | 50 --- filter.go | 18 - generation_policy.go | 14 - index_collection_type.go | 20 - info_policy.go | 28 -- key_reflect_test.go | 1 - marshal.go | 1 - operate_command.go | 53 --- operation.go | 57 --- packer_reflect.go | 1 - partition_filter.go | 18 - partition_status.go | 14 - policy.go | 21 -- proxy_auth_interceptor.go | 2 + proxy_client.go | 3 + proxy_client_app_engine_exclusions.go | 28 ++ proxy_client_reflect.go | 3 +- proxy_client_test.go | 42 +++ proxy_commands.go | 517 ++++++++++++++++++++++++++ proxy_conv.go | 479 ++++++++++++++++++++++++ proxy_execute_task.go | 89 +++++ proxy_query_partition_command.go | 2 + proxy_scan_command.go | 2 + query_aggregate_command.go | 1 - query_aggregate_test.go | 1 - query_duration.go | 14 - query_policy.go | 35 -- read_command.go | 49 --- read_command_reflect.go | 1 - read_command_reflect_test.go | 1 - read_header_command.go | 50 --- read_mode_ap.go | 12 - read_mode_sc.go | 16 - record_exists_action.go | 18 - replica_policy.go | 18 - scan_policy.go | 29 -- server_command.go | 70 ---- statement.go | 39 -- touch_command.go | 49 --- udf_test.go | 1 - value_reflect.go | 1 - write_command.go | 50 --- write_policy.go | 54 --- 71 files changed, 1293 insertions(+), 1190 deletions(-) create mode 100644 client_builder_native.go create mode 100644 client_builder_proxy.go create mode 100644 execute_task_native.go create mode 100644 proxy_client_app_engine_exclusions.go create mode 100644 proxy_client_test.go create mode 100644 proxy_commands.go create mode 100644 proxy_conv.go create mode 100644 proxy_execute_task.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68f38f15..d1b7efc0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,6 +43,8 @@ jobs: run: go run github.com/onsi/ginkgo/v2/ginkgo build -tags="as_performance" . - name: Build for Google App Engine (unsafe package removed) run: go run github.com/onsi/ginkgo/v2/ginkgo build -tags="app_engine" . + - name: Build for DBAAS (proxy mode) + run: go run github.com/onsi/ginkgo/v2/ginkgo build -tags="as_proxy" . - name: Run the tests run: go run github.com/onsi/ginkgo/v2/ginkgo -coverprofile=./cover_native.out -covermode=atomic -coverpkg=./... -race -keep-going -succinct -randomize-suites -skip="HyperLogLog" - name: Combine Cover Profiles diff --git a/README.md b/README.md index 654b1075..36e82911 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ An Aerospike library for Go. -This library is compatible with Go 1.21+ and supports the following operating systems: Linux, Mac OS X (Windows builds are possible, but untested). +This library is compatible with Go 1.20+ and supports the following operating systems: Linux, Mac OS X (Windows builds are possible, but untested). Up-to-date documentation is available in the [![Godoc](https://godoc.org/github.com/aerospike/aerospike-client-go/v7?status.svg)](https://pkg.go.dev/github.com/aerospike/aerospike-client-go/v7). @@ -26,6 +26,7 @@ Please refer to [`CHANGELOG.md`](CHANGELOG.md) for release notes, or if you enco - [API Documentation](#api-documentation) - [Google App Engine](#google-app-engine) - [Reflection, and Object API](#reflection-and-object-api) + - [Proxy Client / DBAAS](#proxy-client--dbaas) - [License](#license) ## Usage @@ -87,7 +88,7 @@ Details about the API are available in the [`docs`](docs) directory. ## Prerequisites -[Go](http://golang.org) version v1.21+ is required. +[Go](http://golang.org) version v1.20+ is required. To install the latest stable version of Go, visit [http://golang.org/dl/](http://golang.org/dl/) @@ -138,40 +139,46 @@ To run all the test cases with race detection: $ ginkgo -r -race - + ## Examples A variety of example applications are provided in the [`examples`](examples) directory. - + ### Tools A variety of clones of original tools are provided in the [`tools`](tools) directory. They show how to use more advanced features of the library to re-implement the same functionality in a more concise way. - + ## Benchmarks Benchmark utility is provided in the [`tools/benchmark`](tools/benchmark) directory. See the [`tools/benchmark/README.md`](tools/benchmark/README.md) for details. - + ## API Documentation A simple API documentation is available in the [`docs`](docs/README.md) directory. The latest up-to-date docs can be found in [![Godoc](https://godoc.org/github.com/aerospike/aerospike-client-go?status.svg)](https://pkg.go.dev/github.com/aerospike/aerospike-client-go/v7). - + ## Google App Engine To build the library for App Engine, build it with the build tag `app_engine`. Aggregation functionality is not available in this build. - + ## Reflection, and Object API To make the library both flexible and fast, we had to integrate the reflection API (methods with `[Get/Put/...]Object` names) tightly in the library. In case you wanted to avoid mixing those API in your app inadvertently, you can use the build tag `as_performance` to remove those APIs from the build. + +## Proxy Client / DBAAS + +To compile the client for the proxy server in out database as a service environment, pass `-tags as_proxy` to the compiler on build. + + ## License The Aerospike Go Client is made available under the terms of the Apache License, Version 2, as stated in the file `LICENSE`. diff --git a/aerospike_bench_reflect_test.go b/aerospike_bench_reflect_test.go index 9f735e65..7e96d6a6 100644 --- a/aerospike_bench_reflect_test.go +++ b/aerospike_bench_reflect_test.go @@ -1,5 +1,4 @@ //go:build !as_performance -// +build !as_performance // Copyright 2014-2022 Aerospike, Inc. // diff --git a/anonymous_fields_test.go b/anonymous_fields_test.go index 51bdb03a..4b889bb4 100644 --- a/anonymous_fields_test.go +++ b/anonymous_fields_test.go @@ -1,5 +1,4 @@ //go:build !as_performance -// +build !as_performance // Copyright 2014-2022 Aerospike, Inc. // diff --git a/batch_command_get_reflect.go b/batch_command_get_reflect.go index 1e8cf458..cee4a09c 100644 --- a/batch_command_get_reflect.go +++ b/batch_command_get_reflect.go @@ -1,5 +1,4 @@ //go:build !as_performance -// +build !as_performance // Copyright 2014-2022 Aerospike, Inc. // diff --git a/batch_command_operate.go b/batch_command_operate.go index f0309263..d85e8f59 100644 --- a/batch_command_operate.go +++ b/batch_command_operate.go @@ -15,10 +15,8 @@ package aerospike import ( - "math/rand" "reflect" - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" "github.com/aerospike/aerospike-client-go/v7/types" Buffer "github.com/aerospike/aerospike-client-go/v7/utils/buffer" ) @@ -303,68 +301,3 @@ func (cmd *batchCommandOperate) transactionType() transactionType { func (cmd *batchCommandOperate) generateBatchNodes(cluster *Cluster) ([]*batchNode, Error) { return newBatchOperateNodeListIfcRetry(cluster, cmd.policy, cmd.records, cmd.sequenceAP, cmd.sequenceSC, cmd.batch) } - -func (cmd *batchCommandOperate) ExecuteGRPC(clnt *ProxyClient) Error { - defer cmd.grpcPutBufferBack() - - err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) - if err != nil { - return err - } - - req := kvs.AerospikeRequestPayload{ - Id: rand.Uint32(), - Iteration: 1, - Payload: cmd.dataBuffer[:cmd.dataOffset], - ReadPolicy: cmd.policy.grpc(), - WritePolicy: cmd.policy.grpc_write(), - } - - conn, err := clnt.grpcConn() - if err != nil { - return err - } - - client := kvs.NewKVSClient(conn) - - ctx, cancel := cmd.policy.grpcDeadlineContext() - defer cancel() - - streamRes, gerr := client.BatchOperate(ctx, &req) - if gerr != nil { - return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) - } - - cmd.commandWasSent = true - - readCallback := func() ([]byte, Error) { - if cmd.grpcEOS { - return nil, errGRPCStreamEnd - } - - res, gerr := streamRes.Recv() - if gerr != nil { - e := newGrpcError(!cmd.isRead(), gerr) - return nil, e - } - - if res.GetStatus() != 0 { - e := newGrpcStatusError(res) - return res.GetPayload(), e - } - - cmd.grpcEOS = !res.GetHasNext() - - return res.GetPayload(), nil - } - - cmd.conn = newGrpcFakeConnection(nil, readCallback) - err = cmd.parseResult(cmd, cmd.conn) - if err != nil && err != errGRPCStreamEnd { - return err - } - - clnt.returnGrpcConnToPool(conn) - - return nil -} diff --git a/batch_command_reflect.go b/batch_command_reflect.go index ba0675fb..9ce42a79 100644 --- a/batch_command_reflect.go +++ b/batch_command_reflect.go @@ -1,5 +1,4 @@ //go:build !as_performance -// +build !as_performance // Copyright 2014-2022 Aerospike, Inc. // diff --git a/batch_policy.go b/batch_policy.go index da49a30b..e07d8793 100644 --- a/batch_policy.go +++ b/batch_policy.go @@ -14,10 +14,6 @@ package aerospike -import ( - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" -) - // BatchPolicy encapsulates parameters for policy attributes used in write operations. // This object is passed into methods where database writes can occur. type BatchPolicy struct { @@ -124,11 +120,3 @@ func (p *BatchPolicy) toWritePolicy() *WritePolicy { } return wp } - -func (p *BatchPolicy) grpc_write() *kvs.WritePolicy { - return &kvs.WritePolicy{ - Replica: p.ReplicaPolicy.grpc(), - ReadModeSC: p.ReadModeSC.grpc(), - ReadModeAP: p.ReadModeAP.grpc(), - } -} diff --git a/batch_test.go b/batch_test.go index 1ded9f48..2071508f 100644 --- a/batch_test.go +++ b/batch_test.go @@ -1,5 +1,4 @@ //go:build !app_engine -// +build !app_engine // Copyright 2014-2022 Aerospike, Inc. // diff --git a/cdt_map_test.go b/cdt_map_test.go index 94fed022..5dd76084 100644 --- a/cdt_map_test.go +++ b/cdt_map_test.go @@ -1,5 +1,4 @@ //go:build !as_performance && !app_engine -// +build !as_performance,!app_engine // Copyright 2014-2022 Aerospike, Inc. // diff --git a/client_appengine_exclusions.go b/client_appengine_exclusions.go index 9da8f8cd..180445cf 100644 --- a/client_appengine_exclusions.go +++ b/client_appengine_exclusions.go @@ -1,5 +1,4 @@ //go:build !app_engine -// +build !app_engine // Copyright 2014-2022 Aerospike, Inc. // @@ -153,14 +152,3 @@ func (clnt *Client) QueryAggregate(policy *QueryPolicy, statement *Statement, pa return recSet, nil } - -// QueryAggregate executes a Map/Reduce query and returns the results. -// The query executor puts records on the channel from separate goroutines. -// The caller can concurrently pop records off the channel through the -// Recordset.Records channel. -// -// This method is only supported by Aerospike 3+ servers. -// If the policy is nil, the default relevant policy will be used. -func (clnt *ProxyClient) QueryAggregate(policy *QueryPolicy, statement *Statement, packageName, functionName string, functionArgs ...Value) (*Recordset, Error) { - panic("NOT SUPPORTED") -} diff --git a/client_builder.go b/client_builder.go index b3dafd66..6e34feb1 100644 --- a/client_builder.go +++ b/client_builder.go @@ -14,8 +14,6 @@ package aerospike -import "github.com/aerospike/aerospike-client-go/v7/types" - // ClientType determines the type of client to build. type ClientType int @@ -26,23 +24,3 @@ const ( // CTProxy means: Create a proxy client. CTProxy ) - -// CreateClientWithPolicyAndHost generates a new Client of the specified type -// with the specified ClientPolicy and sets up the cluster using the provided hosts. -// If the policy is nil, the default relevant policy will be used. -func CreateClientWithPolicyAndHost(typ ClientType, policy *ClientPolicy, hosts ...*Host) (ClientIfc, Error) { - if len(hosts) == 0 { - return nil, newError(types.SERVER_NOT_AVAILABLE, "No hosts were provided") - } - - switch typ { - case CTNative: - return NewClientWithPolicyAndHost(policy, hosts...) - case CTProxy: - if len(hosts) > 1 { - return nil, newError(types.GRPC_ERROR, "Only one proxy host is acceptable") - } - return NewProxyClientWithPolicyAndHost(policy, hosts[0]) - } - return nil, newError(types.SERVER_NOT_AVAILABLE, "Invalid client type") -} diff --git a/client_builder_native.go b/client_builder_native.go new file mode 100644 index 00000000..e3f54d14 --- /dev/null +++ b/client_builder_native.go @@ -0,0 +1,36 @@ +//go:build !as_proxy + +// Copyright 2014-2022 Aerospike, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package aerospike + +import "github.com/aerospike/aerospike-client-go/v7/types" + +// CreateClientWithPolicyAndHost generates a new Client of the specified type +// with the specified ClientPolicy and sets up the cluster using the provided hosts. +// If the policy is nil, the default relevant policy will be used. +func CreateClientWithPolicyAndHost(typ ClientType, policy *ClientPolicy, hosts ...*Host) (ClientIfc, Error) { + if len(hosts) == 0 { + return nil, newError(types.SERVER_NOT_AVAILABLE, "No hosts were provided") + } + + switch typ { + case CTNative: + return NewClientWithPolicyAndHost(policy, hosts...) + case CTProxy: + return nil, newError(types.GRPC_ERROR, "Proxy client mode not enabled. Pass -tags as_proxy during build") + } + return nil, newError(types.SERVER_NOT_AVAILABLE, "Invalid client type") +} diff --git a/client_builder_proxy.go b/client_builder_proxy.go new file mode 100644 index 00000000..de7f58bd --- /dev/null +++ b/client_builder_proxy.go @@ -0,0 +1,39 @@ +//go:build as_proxy + +// Copyright 2014-2022 Aerospike, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package aerospike + +import "github.com/aerospike/aerospike-client-go/v7/types" + +// CreateClientWithPolicyAndHost generates a new Client of the specified type +// with the specified ClientPolicy and sets up the cluster using the provided hosts. +// If the policy is nil, the default relevant policy will be used. +func CreateClientWithPolicyAndHost(typ ClientType, policy *ClientPolicy, hosts ...*Host) (ClientIfc, Error) { + if len(hosts) == 0 { + return nil, newError(types.SERVER_NOT_AVAILABLE, "No hosts were provided") + } + + switch typ { + case CTNative: + return NewClientWithPolicyAndHost(policy, hosts...) + case CTProxy: + if len(hosts) > 1 { + return nil, newError(types.GRPC_ERROR, "Only one proxy host is acceptable") + } + return NewProxyClientWithPolicyAndHost(policy, hosts[0]) + } + return nil, newError(types.SERVER_NOT_AVAILABLE, "Invalid client type") +} diff --git a/client_ifc.go b/client_ifc.go index d71e989d..7cfe2a1a 100644 --- a/client_ifc.go +++ b/client_ifc.go @@ -1,5 +1,4 @@ //go:build !as_performance && !app_engine -// +build !as_performance,!app_engine // Copyright 2014-2022 Aerospike, Inc. // diff --git a/client_ifc_app_engine.go b/client_ifc_app_engine.go index b817935e..310bb050 100644 --- a/client_ifc_app_engine.go +++ b/client_ifc_app_engine.go @@ -1,5 +1,4 @@ //go:build !as_performance && app_engine -// +build !as_performance,app_engine // Copyright 2014-2022 Aerospike, Inc. // diff --git a/client_ifc_app_engine_perf.go b/client_ifc_app_engine_perf.go index fd5431a2..e2ccaa8c 100644 --- a/client_ifc_app_engine_perf.go +++ b/client_ifc_app_engine_perf.go @@ -1,5 +1,4 @@ //go:build as_performance && app_engine -// +build as_performance,app_engine // Copyright 2014-2022 Aerospike, Inc. // @@ -103,6 +102,19 @@ type ClientIfc interface { // TODO: Synchronization here for the sake of dynamic config in the future + getUsablePolicy(*BasePolicy) *BasePolicy + getUsableWritePolicy(*WritePolicy) *WritePolicy + getUsableScanPolicy(*ScanPolicy) *ScanPolicy + getUsableQueryPolicy(*QueryPolicy) *QueryPolicy + getUsableAdminPolicy(*AdminPolicy) *AdminPolicy + getUsableInfoPolicy(*InfoPolicy) *InfoPolicy + + getUsableBatchPolicy(*BatchPolicy) *BatchPolicy + getUsableBatchReadPolicy(*BatchReadPolicy) *BatchReadPolicy + getUsableBatchWritePolicy(*BatchWritePolicy) *BatchWritePolicy + getUsableBatchDeletePolicy(*BatchDeletePolicy) *BatchDeletePolicy + getUsableBatchUDFPolicy(*BatchUDFPolicy) *BatchUDFPolicy + GetDefaultPolicy() *BasePolicy GetDefaultBatchPolicy() *BatchPolicy GetDefaultBatchWritePolicy() *BatchWritePolicy diff --git a/client_ifc_as_performance.go b/client_ifc_as_performance.go index f3504842..3e3376b6 100644 --- a/client_ifc_as_performance.go +++ b/client_ifc_as_performance.go @@ -1,5 +1,4 @@ //go:build as_performance && !app_engine -// +build as_performance,!app_engine // Copyright 2014-2022 Aerospike, Inc. // diff --git a/client_object_test.go b/client_object_test.go index b25f8431..3e71cef0 100644 --- a/client_object_test.go +++ b/client_object_test.go @@ -1,5 +1,4 @@ //go:build !as_performance && !app_engine -// +build !as_performance,!app_engine // Copyright 2014-2022 Aerospike, Inc. // diff --git a/client_reflect.go b/client_reflect.go index cf97dabe..995d27c3 100644 --- a/client_reflect.go +++ b/client_reflect.go @@ -1,5 +1,4 @@ //go:build !as_performance -// +build !as_performance // Copyright 2014-2022 Aerospike, Inc. // diff --git a/client_reflect_test.go b/client_reflect_test.go index c4a9fee6..6fac8087 100644 --- a/client_reflect_test.go +++ b/client_reflect_test.go @@ -1,5 +1,4 @@ //go:build !as_performance -// +build !as_performance // Copyright 2014-2022 Aerospike, Inc. // diff --git a/client_test.go b/client_test.go index db835bcf..5d0c96a2 100644 --- a/client_test.go +++ b/client_test.go @@ -263,19 +263,6 @@ var _ = gg.Describe("Aerospike", func() { }) }) - gg.Describe("Info operations on proxy client", func() { - gg.BeforeEach(func() { - if !*proxy { - gg.Skip("Only supported in grpc environment") - } - }) - - gg.It("must successfully call info command", func() { - _, err := client.(*as.ProxyClient).RequestInfo(nil) - gm.Expect(err).ToNot(gm.HaveOccurred()) - }) - }) - gg.Describe("Data operations on native types", func() { // connection data var err error diff --git a/commit_policy.go b/commit_policy.go index bf45ec0c..cf34300a 100644 --- a/commit_policy.go +++ b/commit_policy.go @@ -17,8 +17,6 @@ package aerospike -import kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" - // CommitLevel indicates the desired consistency guarantee when committing a transaction on the server. type CommitLevel int @@ -29,13 +27,3 @@ const ( // COMMIT_MASTER indicates the server should wait until successfully committing master only. COMMIT_MASTER ) - -func (cl CommitLevel) grpc() kvs.CommitLevel { - switch cl { - case COMMIT_ALL: - return kvs.CommitLevel_COMMIT_ALL - case COMMIT_MASTER: - return kvs.CommitLevel_COMMIT_MASTER - } - panic(unreachable) -} diff --git a/delete_command.go b/delete_command.go index a916a5e8..4f059d7c 100644 --- a/delete_command.go +++ b/delete_command.go @@ -15,9 +15,6 @@ package aerospike import ( - "math/rand" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" "github.com/aerospike/aerospike-client-go/v7/types" Buffer "github.com/aerospike/aerospike-client-go/v7/utils/buffer" @@ -116,50 +113,3 @@ func (cmd *deleteCommand) Execute() Error { func (cmd *deleteCommand) transactionType() transactionType { return ttDelete } - -func (cmd *deleteCommand) ExecuteGRPC(clnt *ProxyClient) Error { - defer cmd.grpcPutBufferBack() - - err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) - if err != nil { - return err - } - - req := kvs.AerospikeRequestPayload{ - Id: rand.Uint32(), - Iteration: 1, - Payload: cmd.dataBuffer[:cmd.dataOffset], - WritePolicy: cmd.policy.grpc(), - } - - conn, err := clnt.grpcConn() - if err != nil { - return err - } - - client := kvs.NewKVSClient(conn) - - ctx, cancel := cmd.policy.grpcDeadlineContext() - defer cancel() - - res, gerr := client.Delete(ctx, &req) - if gerr != nil { - return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) - } - - cmd.commandWasSent = true - - defer clnt.returnGrpcConnToPool(conn) - - if res.GetStatus() != 0 { - return newGrpcStatusError(res) - } - - cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) - err = cmd.parseResult(cmd, cmd.conn) - if err != nil { - return err - } - - return nil -} diff --git a/error.go b/error.go index 92f599cc..c1722a2f 100644 --- a/error.go +++ b/error.go @@ -22,7 +22,6 @@ import ( "strconv" "strings" - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" "github.com/aerospike/aerospike-client-go/v7/types" grpc "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -209,58 +208,6 @@ func newGrpcError(isWrite bool, e error, messages ...string) Error { return ne } -func newGrpcStatusError(res *kvs.AerospikeResponsePayload) Error { - if res.GetStatus() >= 0 { - return newError(types.ResultCode(res.GetStatus())).markInDoubt(res.GetInDoubt()) - } - - var resultCode = types.OK - switch res.GetStatus() { - case -16: - // BATCH_FAILED - resultCode = types.BATCH_FAILED - case -15: - // NO_RESPONSE - resultCode = types.NO_RESPONSE - case -12: - // MAX_ERROR_RATE - resultCode = types.MAX_ERROR_RATE - case -11: - // MAX_RETRIES_EXCEEDED - resultCode = types.MAX_RETRIES_EXCEEDED - case -10: - // SERIALIZE_ERROR - resultCode = types.SERIALIZE_ERROR - case -9: - // ASYNC_QUEUE_FULL - // resultCode = types.ASYNC_QUEUE_FULL - return newError(types.SERVER_ERROR, "Server ASYNC_QUEUE_FULL").markInDoubt(res.GetInDoubt()) - case -8: - // SERVER_NOT_AVAILABLE - resultCode = types.SERVER_NOT_AVAILABLE - case -7: - // NO_MORE_CONNECTIONS - resultCode = types.NO_AVAILABLE_CONNECTIONS_TO_NODE - case -5: - // QUERY_TERMINATED - resultCode = types.QUERY_TERMINATED - case -4: - // SCAN_TERMINATED - resultCode = types.SCAN_TERMINATED - case -3: - // INVALID_NODE_ERROR - resultCode = types.INVALID_NODE_ERROR - case -2: - // PARSE_ERROR - resultCode = types.PARSE_ERROR - case -1: - // CLIENT_ERROR - resultCode = types.COMMON_ERROR - } - - return newError(resultCode).markInDoubt(res.GetInDoubt()) -} - // SetInDoubt sets whether it is possible that the write transaction may have completed // even though this error was generated. This may be the case when a // client error occurs (like timeout) after the command was sent to the server. diff --git a/execute_command.go b/execute_command.go index 9c3d401a..9877a70c 100644 --- a/execute_command.go +++ b/execute_command.go @@ -14,12 +14,6 @@ package aerospike -import ( - "math/rand" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" -) - type executeCommand struct { readCommand @@ -85,50 +79,3 @@ func (cmd *executeCommand) Execute() Error { func (cmd *executeCommand) transactionType() transactionType { return ttUDF } - -func (cmd *executeCommand) ExecuteGRPC(clnt *ProxyClient) Error { - defer cmd.grpcPutBufferBack() - - err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) - if err != nil { - return err - } - - req := kvs.AerospikeRequestPayload{ - Id: rand.Uint32(), - Iteration: 1, - Payload: cmd.dataBuffer[:cmd.dataOffset], - WritePolicy: cmd.policy.grpc(), - } - - conn, err := clnt.grpcConn() - if err != nil { - return err - } - - client := kvs.NewKVSClient(conn) - - ctx, cancel := cmd.policy.grpcDeadlineContext() - defer cancel() - - res, gerr := client.Execute(ctx, &req) - if gerr != nil { - return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) - } - - cmd.commandWasSent = true - - defer clnt.returnGrpcConnToPool(conn) - - if res.GetStatus() != 0 { - return newGrpcStatusError(res) - } - - cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) - err = cmd.parseResult(cmd, cmd.conn) - if err != nil { - return err - } - - return nil -} diff --git a/execute_task.go b/execute_task.go index b9d39df9..9d441f24 100644 --- a/execute_task.go +++ b/execute_task.go @@ -15,13 +15,9 @@ package aerospike import ( - "context" - "math/rand" "strconv" "strings" - "time" - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" "github.com/aerospike/aerospike-client-go/v7/types" ) @@ -32,7 +28,7 @@ type ExecuteTask struct { taskID uint64 scan bool - clnt *ProxyClient + clnt ClientIfc // The following map keeps an account of what nodes were ever observed with the job registered on them. // If the job was ever observed, the task will return true for it is not found anymore (purged from task queue after completion) @@ -49,16 +45,6 @@ func NewExecuteTask(cluster *Cluster, statement *Statement) *ExecuteTask { } } -// newGRPCExecuteTask initializes task with fields needed to query server nodes. -func newGRPCExecuteTask(clnt *ProxyClient, statement *Statement) *ExecuteTask { - return &ExecuteTask{ - baseTask: newTask(nil), - taskID: statement.TaskId, - scan: statement.IsScan(), - clnt: clnt, - } -} - // TaskId returns the task id that was sent to the server. func (etsk *ExecuteTask) TaskId() uint64 { return etsk.taskID @@ -160,56 +146,3 @@ func (etsk *ExecuteTask) IsDone() (bool, Error) { func (etsk *ExecuteTask) OnComplete() chan Error { return etsk.onComplete(etsk) } - -func (etsk *ExecuteTask) grpcIsDone() (bool, Error) { - statusReq := &kvs.BackgroundTaskStatusRequest{ - TaskId: int64(etsk.taskID), - IsScan: etsk.scan, - } - - req := kvs.AerospikeRequestPayload{ - Id: rand.Uint32(), - Iteration: 1, - BackgroundTaskStatusRequest: statusReq, - } - - conn, err := etsk.clnt.grpcConn() - if err != nil { - return false, err - } - - client := kvs.NewQueryClient(conn) - - ctx, cancel := context.WithTimeout(context.Background(), NewInfoPolicy().Timeout) - defer cancel() - - streamRes, gerr := client.BackgroundTaskStatus(ctx, &req) - if gerr != nil { - return false, newGrpcError(true, gerr, gerr.Error()) - } - - for { - time.Sleep(time.Second) - - res, gerr := streamRes.Recv() - if gerr != nil { - e := newGrpcError(true, gerr) - return false, e - } - - if res.GetStatus() != 0 { - e := newGrpcStatusError(res) - etsk.clnt.returnGrpcConnToPool(conn) - return false, e - } - - switch res.GetBackgroundTaskStatus() { - case kvs.BackgroundTaskStatus_COMPLETE: - etsk.clnt.returnGrpcConnToPool(conn) - return true, nil - default: - etsk.clnt.returnGrpcConnToPool(conn) - return false, nil - } - } -} diff --git a/execute_task_native.go b/execute_task_native.go new file mode 100644 index 00000000..cbd62d7b --- /dev/null +++ b/execute_task_native.go @@ -0,0 +1,22 @@ +//go:build !as_proxy + +// Copyright 2014-2022 Aerospike, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package aerospike + +func (etsk *ExecuteTask) grpcIsDone() (bool, Error) { + // should not be called out of the grpc proxy server context + panic("UNREACHABLE") +} diff --git a/exists_command.go b/exists_command.go index c4d3c505..6eebbf9d 100644 --- a/exists_command.go +++ b/exists_command.go @@ -15,9 +15,6 @@ package aerospike import ( - "math/rand" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" "github.com/aerospike/aerospike-client-go/v7/types" Buffer "github.com/aerospike/aerospike-client-go/v7/utils/buffer" @@ -110,50 +107,3 @@ func (cmd *existsCommand) Execute() Error { func (cmd *existsCommand) transactionType() transactionType { return ttExists } - -func (cmd *existsCommand) ExecuteGRPC(clnt *ProxyClient) Error { - defer cmd.grpcPutBufferBack() - - err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) - if err != nil { - return err - } - - req := kvs.AerospikeRequestPayload{ - Id: rand.Uint32(), - Iteration: 1, - Payload: cmd.dataBuffer[:cmd.dataOffset], - ReadPolicy: cmd.policy.grpc(), - } - - conn, err := clnt.grpcConn() - if err != nil { - return err - } - - client := kvs.NewKVSClient(conn) - - ctx, cancel := cmd.policy.grpcDeadlineContext() - defer cancel() - - res, gerr := client.Exists(ctx, &req) - if gerr != nil { - return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) - } - - cmd.commandWasSent = true - - defer clnt.returnGrpcConnToPool(conn) - - if res.GetStatus() != 0 { - return newGrpcStatusError(res) - } - - cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) - err = cmd.parseResult(cmd, cmd.conn) - if err != nil { - return err - } - - return nil -} diff --git a/filter.go b/filter.go index 612e4b32..a07c0634 100644 --- a/filter.go +++ b/filter.go @@ -17,7 +17,6 @@ package aerospike import ( "fmt" - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" ParticleType "github.com/aerospike/aerospike-client-go/v7/types/particle_type" ) @@ -126,23 +125,6 @@ func (fltr *Filter) String() string { ) } -func (fltr *Filter) grpc() *kvs.Filter { - if fltr == nil { - return nil - } - - res := &kvs.Filter{ - Name: fltr.name, - ColType: fltr.idxType.grpc(), - PackedCtx: fltr.grpcPackCtxPayload(), - ValType: int32(fltr.valueParticleType), - Begin: grpcValuePacked(fltr.begin), - End: grpcValuePacked(fltr.end), - } - - return res -} - // IndexCollectionType returns filter's index type. func (fltr *Filter) IndexCollectionType() IndexCollectionType { return fltr.idxType diff --git a/generation_policy.go b/generation_policy.go index c8eb1961..94efbc8d 100644 --- a/generation_policy.go +++ b/generation_policy.go @@ -14,8 +14,6 @@ package aerospike -import kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" - // GenerationPolicy determines how to handle record writes based on record generation. type GenerationPolicy int @@ -30,15 +28,3 @@ const ( // This is useful for restore after backup. EXPECT_GEN_GT ) - -func (gp GenerationPolicy) grpc() kvs.GenerationPolicy { - switch gp { - case NONE: - return kvs.GenerationPolicy_NONE - case EXPECT_GEN_EQUAL: - return kvs.GenerationPolicy_EXPECT_GEN_EQUAL - case EXPECT_GEN_GT: - return kvs.GenerationPolicy_EXPECT_GEN_GT - } - panic(unreachable) -} diff --git a/index_collection_type.go b/index_collection_type.go index 445e875b..dc85253d 100644 --- a/index_collection_type.go +++ b/index_collection_type.go @@ -16,8 +16,6 @@ package aerospike import ( "fmt" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" ) // IndexCollectionType is the secondary index collection type. @@ -38,24 +36,6 @@ const ( ICT_MAPVALUES ) -func (ict IndexCollectionType) grpc() kvs.IndexCollectionType { - switch ict { - // Normal scalar index. - case ICT_DEFAULT: - return kvs.IndexCollectionType_DEFAULT - // Index list elements. - case ICT_LIST: - return kvs.IndexCollectionType_LIST - // Index map keys. - case ICT_MAPKEYS: - return kvs.IndexCollectionType_MAPKEYS - // Index map values. - case ICT_MAPVALUES: - return kvs.IndexCollectionType_MAPVALUES - } - panic(unreachable) -} - func (ict IndexCollectionType) String() string { switch ict { // Normal scalar index. diff --git a/info_policy.go b/info_policy.go index afa0267e..1442e010 100644 --- a/info_policy.go +++ b/info_policy.go @@ -15,10 +15,7 @@ package aerospike import ( - "context" "time" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" ) // InfoPolicy contains attributes used for info commands. @@ -52,28 +49,3 @@ func (p *InfoPolicy) timeout() time.Duration { return _DEFAULT_TIMEOUT } - -var simpleCancelFunc = func() {} - -func (p *InfoPolicy) grpcDeadlineContext() (context.Context, context.CancelFunc) { - timeout := p.timeout() - if timeout <= 0 { - return context.Background(), simpleCancelFunc - - } - ctx, cancel := context.WithTimeout(context.Background(), timeout) - return ctx, cancel -} - -func (p *InfoPolicy) grpc() *kvs.InfoPolicy { - if p == nil { - return nil - } - - Timeout := uint32(p.Timeout / time.Millisecond) - res := &kvs.InfoPolicy{ - Timeout: &Timeout, - } - - return res -} diff --git a/key_reflect_test.go b/key_reflect_test.go index 0d1a94f5..87e0d40f 100644 --- a/key_reflect_test.go +++ b/key_reflect_test.go @@ -1,5 +1,4 @@ //go:build !as_performance -// +build !as_performance // Copyright 2014-2022 Aerospike, Inc. // diff --git a/marshal.go b/marshal.go index 9668dd75..e7f23d11 100644 --- a/marshal.go +++ b/marshal.go @@ -1,5 +1,4 @@ //go:build !as_performance -// +build !as_performance // Copyright 2014-2022 Aerospike, Inc. // diff --git a/operate_command.go b/operate_command.go index f731f3e7..8b3e115f 100644 --- a/operate_command.go +++ b/operate_command.go @@ -14,12 +14,6 @@ package aerospike -import ( - "math/rand" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" -) - type operateCommand struct { readCommand @@ -73,50 +67,3 @@ func (cmd *operateCommand) Execute() Error { func (cmd *operateCommand) transactionType() transactionType { return ttOperate } - -func (cmd *operateCommand) ExecuteGRPC(clnt *ProxyClient) Error { - defer cmd.grpcPutBufferBack() - - err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) - if err != nil { - return err - } - - req := kvs.AerospikeRequestPayload{ - Id: rand.Uint32(), - Iteration: 1, - Payload: cmd.dataBuffer[:cmd.dataOffset], - WritePolicy: cmd.policy.grpc(), - } - - conn, err := clnt.grpcConn() - if err != nil { - return err - } - - client := kvs.NewKVSClient(conn) - - ctx, cancel := cmd.policy.grpcDeadlineContext() - defer cancel() - - res, gerr := client.Operate(ctx, &req) - if gerr != nil { - return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) - } - - cmd.commandWasSent = true - - defer clnt.returnGrpcConnToPool(conn) - - if res.GetStatus() != 0 { - return newGrpcStatusError(res) - } - - cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) - err = cmd.parseResult(cmd, cmd.conn) - if err != nil { - return err - } - - return nil -} diff --git a/operation.go b/operation.go index 97be025d..fc0221ed 100644 --- a/operation.go +++ b/operation.go @@ -14,10 +14,6 @@ package aerospike -import ( - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" -) - // OperationType determines operation type type OperationType struct { op byte @@ -50,50 +46,6 @@ var ( _HLL_MODIFY = OperationType{16, true, 17} ) -func (o *Operation) grpc_op_type() kvs.OperationType { - // case _READ: return kvs.OperationType_READ - switch o.opType { - case _READ: - return kvs.OperationType_READ - case _READ_HEADER: - return kvs.OperationType_READ_HEADER - case _WRITE: - return kvs.OperationType_WRITE - case _CDT_READ: - return kvs.OperationType_CDT_READ - case _CDT_MODIFY: - return kvs.OperationType_CDT_MODIFY - case _MAP_READ: - return kvs.OperationType_MAP_READ - case _MAP_MODIFY: - return kvs.OperationType_MAP_MODIFY - case _ADD: - return kvs.OperationType_ADD - case _EXP_READ: - return kvs.OperationType_EXP_READ - case _EXP_MODIFY: - return kvs.OperationType_EXP_MODIFY - case _APPEND: - return kvs.OperationType_APPEND - case _PREPEND: - return kvs.OperationType_PREPEND - case _TOUCH: - return kvs.OperationType_TOUCH - case _BIT_READ: - return kvs.OperationType_BIT_READ - case _BIT_MODIFY: - return kvs.OperationType_BIT_MODIFY - case _DELETE: - return kvs.OperationType_DELETE - case _HLL_READ: - return kvs.OperationType_HLL_READ - case _HLL_MODIFY: - return kvs.OperationType_HLL_MODIFY - } - - panic(unreachable) -} - // Operation contains operation definition. // This struct is used in client's operate() method. type Operation struct { @@ -139,15 +91,6 @@ func (op *Operation) size() (int, Error) { return size, nil } -func (op *Operation) grpc() *kvs.Operation { - BinName := op.binName - return &kvs.Operation{ - Type: op.grpc_op_type(), - BinName: &BinName, - Value: grpcValuePacked(op.binValue), - } -} - // GetBinOp creates read bin database operation. func GetBinOp(binName string) *Operation { return &Operation{opType: _READ, binName: binName, binValue: NewNullValue()} diff --git a/packer_reflect.go b/packer_reflect.go index dc3104dd..5898cad4 100644 --- a/packer_reflect.go +++ b/packer_reflect.go @@ -1,5 +1,4 @@ //go:build !as_performance -// +build !as_performance // Copyright 2014-2022 Aerospike, Inc. // diff --git a/partition_filter.go b/partition_filter.go index 971dcf55..5dd37755 100644 --- a/partition_filter.go +++ b/partition_filter.go @@ -19,7 +19,6 @@ import ( "bytes" "encoding/gob" - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" "github.com/aerospike/aerospike-client-go/v7/types" ) @@ -106,20 +105,3 @@ func (pf *PartitionFilter) DecodeCursor(b []byte) Error { pf.Partitions = parts return nil } - -func (pf *PartitionFilter) grpc() *kvs.PartitionFilter { - begin := uint32(pf.Begin) - ps := make([]*kvs.PartitionStatus, len(pf.Partitions)) - for i := range pf.Partitions { - ps[i] = pf.Partitions[i].grpc() - } - - return &kvs.PartitionFilter{ - Begin: &begin, - Count: uint32(pf.Count), - Digest: pf.Digest, - PartitionStatuses: ps, - Retry: true, - } - -} diff --git a/partition_status.go b/partition_status.go index a3490fca..7f541aa8 100644 --- a/partition_status.go +++ b/partition_status.go @@ -17,8 +17,6 @@ package aerospike import ( "fmt" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" ) // PartitionStatus encapsulates the pagination status in partitions. @@ -49,15 +47,3 @@ func (ps *PartitionStatus) String() string { } return fmt.Sprintf("%04d:%c", ps.Id, r) } - -func (ps *PartitionStatus) grpc() *kvs.PartitionStatus { - id := uint32(ps.Id) - bVal := ps.BVal - digest := ps.Digest - return &kvs.PartitionStatus{ - Id: &id, - BVal: &bVal, - Digest: digest, - Retry: ps.Retry, - } -} diff --git a/policy.go b/policy.go index 5e660c6c..cbfd4ff4 100644 --- a/policy.go +++ b/policy.go @@ -15,10 +15,7 @@ package aerospike import ( - "context" "time" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" ) // Policy Interface @@ -242,21 +239,3 @@ func (p *BasePolicy) deadline() time.Time { func (p *BasePolicy) compress() bool { return p.UseCompression } - -func (p *BasePolicy) grpc() *kvs.ReadPolicy { - return &kvs.ReadPolicy{ - Replica: p.ReplicaPolicy.grpc(), - ReadModeSC: p.ReadModeSC.grpc(), - ReadModeAP: p.ReadModeAP.grpc(), - } -} - -func (p *BasePolicy) grpcDeadlineContext() (context.Context, context.CancelFunc) { - timeout := p.timeout() - if timeout <= 0 { - return context.Background(), simpleCancelFunc - - } - ctx, cancel := context.WithTimeout(context.Background(), timeout) - return ctx, cancel -} diff --git a/proxy_auth_interceptor.go b/proxy_auth_interceptor.go index e6f8b46b..788d7386 100644 --- a/proxy_auth_interceptor.go +++ b/proxy_auth_interceptor.go @@ -1,3 +1,5 @@ +//go:build as_proxy + // Copyright 2014-2022 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/proxy_client.go b/proxy_client.go index 2a5ee016..9171b6bd 100644 --- a/proxy_client.go +++ b/proxy_client.go @@ -1,3 +1,5 @@ +//go:build as_proxy + // Copyright 2014-2022 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -83,6 +85,7 @@ func grpcClientFinalizer(f *ProxyClient) { // NewProxyClientWithPolicyAndHost generates a new ProxyClient with the specified ClientPolicy and // sets up the cluster using the provided hosts. +// You must pass the tag 'as_proxy' to the compiler during build. // If the policy is nil, the default relevant policy will be used. // Pass "dns:///
:" (note the 3 slashes) for dns load balancing, // automatically supported internally by grpc-go. diff --git a/proxy_client_app_engine_exclusions.go b/proxy_client_app_engine_exclusions.go new file mode 100644 index 00000000..f6484ef8 --- /dev/null +++ b/proxy_client_app_engine_exclusions.go @@ -0,0 +1,28 @@ +//go:build !app_engine && as_proxy + +// Copyright 2014-2022 Aerospike, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package aerospike + +// QueryAggregate executes a Map/Reduce query and returns the results. +// The query executor puts records on the channel from separate goroutines. +// The caller can concurrently pop records off the channel through the +// Recordset.Records channel. +// +// This method is only supported by Aerospike 3+ servers. +// If the policy is nil, the default relevant policy will be used. +func (clnt *ProxyClient) QueryAggregate(policy *QueryPolicy, statement *Statement, packageName, functionName string, functionArgs ...Value) (*Recordset, Error) { + panic("NOT SUPPORTED") +} diff --git a/proxy_client_reflect.go b/proxy_client_reflect.go index a368b794..8c656ff8 100644 --- a/proxy_client_reflect.go +++ b/proxy_client_reflect.go @@ -1,5 +1,4 @@ -//go:build !as_performance -// +build !as_performance +//go:build !as_performance && as_proxy // Copyright 2014-2022 Aerospike, Inc. // diff --git a/proxy_client_test.go b/proxy_client_test.go new file mode 100644 index 00000000..9a50ebff --- /dev/null +++ b/proxy_client_test.go @@ -0,0 +1,42 @@ +//go:build as_proxy + +// Copyright 2014-2022 Aerospike, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package aerospike_test + +import ( + as "github.com/aerospike/aerospike-client-go/v7" + + gg "github.com/onsi/ginkgo/v2" + gm "github.com/onsi/gomega" +) + +// ALL tests are isolated by SetName and Key, which are 50 random characters +var _ = gg.Describe("Aerospike Proxy Client", func() { + + gg.Describe("Info operations on proxy client", func() { + gg.BeforeEach(func() { + if !*proxy { + gg.Skip("Only supported in grpc environment") + } + }) + + gg.It("must successfully call info command", func() { + _, err := client.(*as.ProxyClient).RequestInfo(nil) + gm.Expect(err).ToNot(gm.HaveOccurred()) + }) + }) + +}) diff --git a/proxy_commands.go b/proxy_commands.go new file mode 100644 index 00000000..11cfcfae --- /dev/null +++ b/proxy_commands.go @@ -0,0 +1,517 @@ +//go:build as_proxy + +package aerospike + +import ( + "math/rand" + + kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" +) + +func (cmd *readCommand) ExecuteGRPC(clnt *ProxyClient) Error { + defer cmd.grpcPutBufferBack() + + err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) + if err != nil { + return err + } + + req := kvs.AerospikeRequestPayload{ + Id: rand.Uint32(), + Iteration: 1, + Payload: cmd.dataBuffer[:cmd.dataOffset], + ReadPolicy: cmd.policy.grpc(), + } + + conn, err := clnt.grpcConn() + if err != nil { + return err + } + + client := kvs.NewKVSClient(conn) + + ctx, cancel := cmd.policy.grpcDeadlineContext() + defer cancel() + + res, gerr := client.Read(ctx, &req) + if gerr != nil { + return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) + } + + cmd.commandWasSent = true + + defer clnt.returnGrpcConnToPool(conn) + + if res.GetStatus() != 0 { + return newGrpcStatusError(res) + } + + cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) + err = cmd.parseResult(cmd, cmd.conn) + if err != nil { + return err + } + + return nil +} + +func (cmd *batchCommandOperate) ExecuteGRPC(clnt *ProxyClient) Error { + defer cmd.grpcPutBufferBack() + + err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) + if err != nil { + return err + } + + req := kvs.AerospikeRequestPayload{ + Id: rand.Uint32(), + Iteration: 1, + Payload: cmd.dataBuffer[:cmd.dataOffset], + ReadPolicy: cmd.policy.grpc(), + WritePolicy: cmd.policy.grpc_write(), + } + + conn, err := clnt.grpcConn() + if err != nil { + return err + } + + client := kvs.NewKVSClient(conn) + + ctx, cancel := cmd.policy.grpcDeadlineContext() + defer cancel() + + streamRes, gerr := client.BatchOperate(ctx, &req) + if gerr != nil { + return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) + } + + cmd.commandWasSent = true + + readCallback := func() ([]byte, Error) { + if cmd.grpcEOS { + return nil, errGRPCStreamEnd + } + + res, gerr := streamRes.Recv() + if gerr != nil { + e := newGrpcError(!cmd.isRead(), gerr) + return nil, e + } + + if res.GetStatus() != 0 { + e := newGrpcStatusError(res) + return res.GetPayload(), e + } + + cmd.grpcEOS = !res.GetHasNext() + + return res.GetPayload(), nil + } + + cmd.conn = newGrpcFakeConnection(nil, readCallback) + err = cmd.parseResult(cmd, cmd.conn) + if err != nil && err != errGRPCStreamEnd { + return err + } + + clnt.returnGrpcConnToPool(conn) + + return nil +} + +func (cmd *deleteCommand) ExecuteGRPC(clnt *ProxyClient) Error { + defer cmd.grpcPutBufferBack() + + err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) + if err != nil { + return err + } + + req := kvs.AerospikeRequestPayload{ + Id: rand.Uint32(), + Iteration: 1, + Payload: cmd.dataBuffer[:cmd.dataOffset], + WritePolicy: cmd.policy.grpc(), + } + + conn, err := clnt.grpcConn() + if err != nil { + return err + } + + client := kvs.NewKVSClient(conn) + + ctx, cancel := cmd.policy.grpcDeadlineContext() + defer cancel() + + res, gerr := client.Delete(ctx, &req) + if gerr != nil { + return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) + } + + cmd.commandWasSent = true + + defer clnt.returnGrpcConnToPool(conn) + + if res.GetStatus() != 0 { + return newGrpcStatusError(res) + } + + cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) + err = cmd.parseResult(cmd, cmd.conn) + if err != nil { + return err + } + + return nil +} + +func (cmd *executeCommand) ExecuteGRPC(clnt *ProxyClient) Error { + defer cmd.grpcPutBufferBack() + + err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) + if err != nil { + return err + } + + req := kvs.AerospikeRequestPayload{ + Id: rand.Uint32(), + Iteration: 1, + Payload: cmd.dataBuffer[:cmd.dataOffset], + WritePolicy: cmd.policy.grpc(), + } + + conn, err := clnt.grpcConn() + if err != nil { + return err + } + + client := kvs.NewKVSClient(conn) + + ctx, cancel := cmd.policy.grpcDeadlineContext() + defer cancel() + + res, gerr := client.Execute(ctx, &req) + if gerr != nil { + return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) + } + + cmd.commandWasSent = true + + defer clnt.returnGrpcConnToPool(conn) + + if res.GetStatus() != 0 { + return newGrpcStatusError(res) + } + + cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) + err = cmd.parseResult(cmd, cmd.conn) + if err != nil { + return err + } + + return nil +} + +func (cmd *existsCommand) ExecuteGRPC(clnt *ProxyClient) Error { + defer cmd.grpcPutBufferBack() + + err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) + if err != nil { + return err + } + + req := kvs.AerospikeRequestPayload{ + Id: rand.Uint32(), + Iteration: 1, + Payload: cmd.dataBuffer[:cmd.dataOffset], + ReadPolicy: cmd.policy.grpc(), + } + + conn, err := clnt.grpcConn() + if err != nil { + return err + } + + client := kvs.NewKVSClient(conn) + + ctx, cancel := cmd.policy.grpcDeadlineContext() + defer cancel() + + res, gerr := client.Exists(ctx, &req) + if gerr != nil { + return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) + } + + cmd.commandWasSent = true + + defer clnt.returnGrpcConnToPool(conn) + + if res.GetStatus() != 0 { + return newGrpcStatusError(res) + } + + cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) + err = cmd.parseResult(cmd, cmd.conn) + if err != nil { + return err + } + + return nil +} + +func (cmd *operateCommand) ExecuteGRPC(clnt *ProxyClient) Error { + defer cmd.grpcPutBufferBack() + + err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) + if err != nil { + return err + } + + req := kvs.AerospikeRequestPayload{ + Id: rand.Uint32(), + Iteration: 1, + Payload: cmd.dataBuffer[:cmd.dataOffset], + WritePolicy: cmd.policy.grpc(), + } + + conn, err := clnt.grpcConn() + if err != nil { + return err + } + + client := kvs.NewKVSClient(conn) + + ctx, cancel := cmd.policy.grpcDeadlineContext() + defer cancel() + + res, gerr := client.Operate(ctx, &req) + if gerr != nil { + return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) + } + + cmd.commandWasSent = true + + defer clnt.returnGrpcConnToPool(conn) + + if res.GetStatus() != 0 { + return newGrpcStatusError(res) + } + + cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) + err = cmd.parseResult(cmd, cmd.conn) + if err != nil { + return err + } + + return nil +} + +func (cmd *readHeaderCommand) ExecuteGRPC(clnt *ProxyClient) Error { + defer cmd.grpcPutBufferBack() + + err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) + if err != nil { + return err + } + + req := kvs.AerospikeRequestPayload{ + Id: rand.Uint32(), + Iteration: 1, + Payload: cmd.dataBuffer[:cmd.dataOffset], + ReadPolicy: cmd.policy.grpc(), + } + + conn, err := clnt.grpcConn() + if err != nil { + return err + } + + client := kvs.NewKVSClient(conn) + + ctx, cancel := cmd.policy.grpcDeadlineContext() + defer cancel() + + res, gerr := client.GetHeader(ctx, &req) + if gerr != nil { + return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) + } + + cmd.commandWasSent = true + + defer clnt.returnGrpcConnToPool(conn) + + if res.GetStatus() != 0 { + return newGrpcStatusError(res) + } + + cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) + err = cmd.parseResult(cmd, cmd.conn) + if err != nil { + return err + } + + return nil +} + +func (cmd *serverCommand) ExecuteGRPC(clnt *ProxyClient) Error { + defer cmd.grpcPutBufferBack() + + err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) + if err != nil { + return err + } + + execReq := &kvs.BackgroundExecuteRequest{ + Statement: cmd.statement.grpc(cmd.policy, cmd.operations), + WritePolicy: cmd.writePolicy.grpc_exec(cmd.policy.FilterExpression), + } + + req := kvs.AerospikeRequestPayload{ + Id: rand.Uint32(), + Iteration: 1, + Payload: cmd.dataBuffer[:cmd.dataOffset], + BackgroundExecuteRequest: execReq, + } + + conn, err := clnt.grpcConn() + if err != nil { + return err + } + + client := kvs.NewQueryClient(conn) + + ctx, cancel := cmd.policy.grpcDeadlineContext() + defer cancel() + + streamRes, gerr := client.BackgroundExecute(ctx, &req) + if gerr != nil { + return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) + } + + cmd.commandWasSent = true + + readCallback := func() ([]byte, Error) { + res, gerr := streamRes.Recv() + if gerr != nil { + e := newGrpcError(!cmd.isRead(), gerr) + return nil, e + } + + if res.GetStatus() != 0 { + e := newGrpcStatusError(res) + return res.GetPayload(), e + } + + if !res.GetHasNext() { + return nil, errGRPCStreamEnd + } + + return res.GetPayload(), nil + } + + cmd.conn = newGrpcFakeConnection(nil, readCallback) + err = cmd.parseResult(cmd, cmd.conn) + if err != nil && err != errGRPCStreamEnd { + return err + } + + clnt.returnGrpcConnToPool(conn) + + return nil +} + +func (cmd *touchCommand) ExecuteGRPC(clnt *ProxyClient) Error { + defer cmd.grpcPutBufferBack() + + err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) + if err != nil { + return err + } + + req := kvs.AerospikeRequestPayload{ + Id: rand.Uint32(), + Iteration: 1, + Payload: cmd.dataBuffer[:cmd.dataOffset], + WritePolicy: cmd.policy.grpc(), + } + + conn, err := clnt.grpcConn() + if err != nil { + return err + } + + client := kvs.NewKVSClient(conn) + + ctx, cancel := cmd.policy.grpcDeadlineContext() + defer cancel() + + res, gerr := client.Touch(ctx, &req) + if gerr != nil { + return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) + } + + cmd.commandWasSent = true + + defer clnt.returnGrpcConnToPool(conn) + + if res.GetStatus() != 0 { + return newGrpcStatusError(res) + } + + cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) + err = cmd.parseResult(cmd, cmd.conn) + if err != nil { + return err + } + + return nil +} + +func (cmd *writeCommand) ExecuteGRPC(clnt *ProxyClient) Error { + defer cmd.grpcPutBufferBack() + + err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) + if err != nil { + return err + } + + req := kvs.AerospikeRequestPayload{ + Id: rand.Uint32(), + Iteration: 1, + Payload: cmd.dataBuffer[:cmd.dataOffset], + WritePolicy: cmd.policy.grpc(), + } + + conn, err := clnt.grpcConn() + if err != nil { + return err + } + + client := kvs.NewKVSClient(conn) + + ctx, cancel := cmd.policy.grpcDeadlineContext() + defer cancel() + + res, gerr := client.Write(ctx, &req) + if gerr != nil { + return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) + } + + cmd.commandWasSent = true + + defer clnt.returnGrpcConnToPool(conn) + + if res.GetStatus() != 0 { + return newGrpcStatusError(res) + } + + cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) + err = cmd.parseResult(cmd, cmd.conn) + if err != nil { + return err + } + + return nil +} diff --git a/proxy_conv.go b/proxy_conv.go new file mode 100644 index 00000000..ecd4fefc --- /dev/null +++ b/proxy_conv.go @@ -0,0 +1,479 @@ +//go:build as_proxy + +package aerospike + +import ( + "context" + "math/rand" + "time" + + kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" + "github.com/aerospike/aerospike-client-go/v7/types" +) + +func (fltr *Filter) grpc() *kvs.Filter { + if fltr == nil { + return nil + } + + res := &kvs.Filter{ + Name: fltr.name, + ColType: fltr.idxType.grpc(), + PackedCtx: fltr.grpcPackCtxPayload(), + ValType: int32(fltr.valueParticleType), + Begin: grpcValuePacked(fltr.begin), + End: grpcValuePacked(fltr.end), + } + + return res +} + +/////////////////////////////////////////////////////////////////// + +var simpleCancelFunc = func() {} + +func (p *InfoPolicy) grpcDeadlineContext() (context.Context, context.CancelFunc) { + timeout := p.timeout() + if timeout <= 0 { + return context.Background(), simpleCancelFunc + + } + ctx, cancel := context.WithTimeout(context.Background(), timeout) + return ctx, cancel +} + +func (p *InfoPolicy) grpc() *kvs.InfoPolicy { + if p == nil { + return nil + } + + Timeout := uint32(p.Timeout / time.Millisecond) + res := &kvs.InfoPolicy{ + Timeout: &Timeout, + } + + return res +} + +/////////////////////////////////////////////////////////////////// + +func (op *Operation) grpc() *kvs.Operation { + BinName := op.binName + return &kvs.Operation{ + Type: op.grpc_op_type(), + BinName: &BinName, + Value: grpcValuePacked(op.binValue), + } +} + +/////////////////////////////////////////////////////////////////// + +func (pf *PartitionFilter) grpc() *kvs.PartitionFilter { + begin := uint32(pf.Begin) + ps := make([]*kvs.PartitionStatus, len(pf.Partitions)) + for i := range pf.Partitions { + ps[i] = pf.Partitions[i].grpc() + } + + return &kvs.PartitionFilter{ + Begin: &begin, + Count: uint32(pf.Count), + Digest: pf.Digest, + PartitionStatuses: ps, + Retry: true, + } + +} + +/////////////////////////////////////////////////////////////////// + +func (ps *PartitionStatus) grpc() *kvs.PartitionStatus { + id := uint32(ps.Id) + bVal := ps.BVal + digest := ps.Digest + return &kvs.PartitionStatus{ + Id: &id, + BVal: &bVal, + Digest: digest, + Retry: ps.Retry, + } +} + +/////////////////////////////////////////////////////////////////// + +func (p *BasePolicy) grpc() *kvs.ReadPolicy { + return &kvs.ReadPolicy{ + Replica: p.ReplicaPolicy.grpc(), + ReadModeSC: p.ReadModeSC.grpc(), + ReadModeAP: p.ReadModeAP.grpc(), + } +} + +func (p *BasePolicy) grpcDeadlineContext() (context.Context, context.CancelFunc) { + timeout := p.timeout() + if timeout <= 0 { + return context.Background(), simpleCancelFunc + + } + ctx, cancel := context.WithTimeout(context.Background(), timeout) + return ctx, cancel +} + +/////////////////////////////////////////////////////////////////// + +func (qp *QueryPolicy) grpc() *kvs.QueryPolicy { + SendKey := qp.SendKey + TotalTimeout := uint32(qp.TotalTimeout / time.Millisecond) + RecordQueueSize := uint32(qp.RecordQueueSize) + MaxConcurrentNodes := uint32(qp.MaxConcurrentNodes) + IncludeBinData := qp.IncludeBinData + FailOnClusterChange := false //qp.FailOnClusterChange + ShortQuery := qp.ShortQuery || qp.ExpectedDuration == SHORT + InfoTimeout := uint32(qp.SocketTimeout / time.Millisecond) + ExpectedDuration := qp.ExpectedDuration.grpc() + + return &kvs.QueryPolicy{ + Replica: qp.ReplicaPolicy.grpc(), + ReadModeAP: qp.ReadModeAP.grpc(), + ReadModeSC: qp.ReadModeSC.grpc(), + SendKey: &SendKey, + Compress: qp.UseCompression, + Expression: qp.FilterExpression.grpc(), + TotalTimeout: &TotalTimeout, + MaxConcurrentNodes: &MaxConcurrentNodes, + RecordQueueSize: &RecordQueueSize, + IncludeBinData: &IncludeBinData, + FailOnClusterChange: &FailOnClusterChange, + ShortQuery: &ShortQuery, + InfoTimeout: &InfoTimeout, + ExpectedDuration: &ExpectedDuration, + } +} + +/////////////////////////////////////////////////////////////////// + +func (sp *ScanPolicy) grpc() *kvs.ScanPolicy { + TotalTimeout := uint32(sp.TotalTimeout / time.Millisecond) + MaxRecords := uint64(sp.MaxRecords) + RecordsPerSecond := uint32(sp.RecordsPerSecond) + MaxConcurrentNodes := uint32(sp.MaxConcurrentNodes) + IncludeBinData := sp.IncludeBinData + ConcurrentNodes := MaxConcurrentNodes > 1 + + return &kvs.ScanPolicy{ + Replica: sp.ReplicaPolicy.grpc(), + ReadModeAP: sp.ReadModeAP.grpc(), + ReadModeSC: sp.ReadModeSC.grpc(), + Compress: sp.UseCompression, + Expression: sp.FilterExpression.grpc(), + TotalTimeout: &TotalTimeout, + MaxRecords: &MaxRecords, + RecordsPerSecond: &RecordsPerSecond, + ConcurrentNodes: &ConcurrentNodes, + MaxConcurrentNodes: &MaxConcurrentNodes, + IncludeBinData: &IncludeBinData, + } +} + +/////////////////////////////////////////////////////////////////// + +func (p *WritePolicy) grpc() *kvs.WritePolicy { + return &kvs.WritePolicy{ + Replica: p.ReplicaPolicy.grpc(), + ReadModeSC: p.ReadModeSC.grpc(), + ReadModeAP: p.ReadModeAP.grpc(), + } +} + +func (p *WritePolicy) grpc_exec(expr *Expression) *kvs.BackgroundExecutePolicy { + if p == nil { + return nil + } + + SendKey := p.SendKey + TotalTimeout := uint32(p.TotalTimeout / time.Millisecond) + RecordExistsAction := p.RecordExistsAction.grpc() + GenerationPolicy := p.GenerationPolicy.grpc() + CommitLevel := p.CommitLevel.grpc() + Generation := p.Generation + Expiration := p.Expiration + RespondAllOps := p.RespondPerEachOp + DurableDelete := p.DurableDelete + + fe := expr + if fe == nil { + fe = p.FilterExpression + } + + res := &kvs.BackgroundExecutePolicy{ + Replica: p.ReplicaPolicy.grpc(), + ReadModeAP: p.ReadModeAP.grpc(), + ReadModeSC: p.ReadModeSC.grpc(), + SendKey: &SendKey, + Compress: p.UseCompression, + Expression: fe.grpc(), + TotalTimeout: &TotalTimeout, + + Xdr: nil, + + RecordExistsAction: &RecordExistsAction, + GenerationPolicy: &GenerationPolicy, + CommitLevel: &CommitLevel, + Generation: &Generation, + Expiration: &Expiration, + RespondAllOps: &RespondAllOps, + DurableDelete: &DurableDelete, + } + + return res +} + +func (p *BatchPolicy) grpc_write() *kvs.WritePolicy { + return &kvs.WritePolicy{ + Replica: p.ReplicaPolicy.grpc(), + ReadModeSC: p.ReadModeSC.grpc(), + ReadModeAP: p.ReadModeAP.grpc(), + } +} + +func (cl CommitLevel) grpc() kvs.CommitLevel { + switch cl { + case COMMIT_ALL: + return kvs.CommitLevel_COMMIT_ALL + case COMMIT_MASTER: + return kvs.CommitLevel_COMMIT_MASTER + } + panic(unreachable) +} + +func newGrpcStatusError(res *kvs.AerospikeResponsePayload) Error { + if res.GetStatus() >= 0 { + return newError(types.ResultCode(res.GetStatus())).markInDoubt(res.GetInDoubt()) + } + + var resultCode = types.OK + switch res.GetStatus() { + case -16: + // BATCH_FAILED + resultCode = types.BATCH_FAILED + case -15: + // NO_RESPONSE + resultCode = types.NO_RESPONSE + case -12: + // MAX_ERROR_RATE + resultCode = types.MAX_ERROR_RATE + case -11: + // MAX_RETRIES_EXCEEDED + resultCode = types.MAX_RETRIES_EXCEEDED + case -10: + // SERIALIZE_ERROR + resultCode = types.SERIALIZE_ERROR + case -9: + // ASYNC_QUEUE_FULL + // resultCode = types.ASYNC_QUEUE_FULL + return newError(types.SERVER_ERROR, "Server ASYNC_QUEUE_FULL").markInDoubt(res.GetInDoubt()) + case -8: + // SERVER_NOT_AVAILABLE + resultCode = types.SERVER_NOT_AVAILABLE + case -7: + // NO_MORE_CONNECTIONS + resultCode = types.NO_AVAILABLE_CONNECTIONS_TO_NODE + case -5: + // QUERY_TERMINATED + resultCode = types.QUERY_TERMINATED + case -4: + // SCAN_TERMINATED + resultCode = types.SCAN_TERMINATED + case -3: + // INVALID_NODE_ERROR + resultCode = types.INVALID_NODE_ERROR + case -2: + // PARSE_ERROR + resultCode = types.PARSE_ERROR + case -1: + // CLIENT_ERROR + resultCode = types.COMMON_ERROR + } + + return newError(resultCode).markInDoubt(res.GetInDoubt()) +} + +func (gp GenerationPolicy) grpc() kvs.GenerationPolicy { + switch gp { + case NONE: + return kvs.GenerationPolicy_NONE + case EXPECT_GEN_EQUAL: + return kvs.GenerationPolicy_EXPECT_GEN_EQUAL + case EXPECT_GEN_GT: + return kvs.GenerationPolicy_EXPECT_GEN_GT + } + panic(unreachable) +} + +func (ict IndexCollectionType) grpc() kvs.IndexCollectionType { + switch ict { + // Normal scalar index. + case ICT_DEFAULT: + return kvs.IndexCollectionType_DEFAULT + // Index list elements. + case ICT_LIST: + return kvs.IndexCollectionType_LIST + // Index map keys. + case ICT_MAPKEYS: + return kvs.IndexCollectionType_MAPKEYS + // Index map values. + case ICT_MAPVALUES: + return kvs.IndexCollectionType_MAPVALUES + } + panic(unreachable) +} + +func (o *Operation) grpc_op_type() kvs.OperationType { + // case _READ: return kvs.OperationType_READ + switch o.opType { + case _READ: + return kvs.OperationType_READ + case _READ_HEADER: + return kvs.OperationType_READ_HEADER + case _WRITE: + return kvs.OperationType_WRITE + case _CDT_READ: + return kvs.OperationType_CDT_READ + case _CDT_MODIFY: + return kvs.OperationType_CDT_MODIFY + case _MAP_READ: + return kvs.OperationType_MAP_READ + case _MAP_MODIFY: + return kvs.OperationType_MAP_MODIFY + case _ADD: + return kvs.OperationType_ADD + case _EXP_READ: + return kvs.OperationType_EXP_READ + case _EXP_MODIFY: + return kvs.OperationType_EXP_MODIFY + case _APPEND: + return kvs.OperationType_APPEND + case _PREPEND: + return kvs.OperationType_PREPEND + case _TOUCH: + return kvs.OperationType_TOUCH + case _BIT_READ: + return kvs.OperationType_BIT_READ + case _BIT_MODIFY: + return kvs.OperationType_BIT_MODIFY + case _DELETE: + return kvs.OperationType_DELETE + case _HLL_READ: + return kvs.OperationType_HLL_READ + case _HLL_MODIFY: + return kvs.OperationType_HLL_MODIFY + } + + panic(unreachable) +} + +func (stmt *Statement) grpc(policy *QueryPolicy, ops []*Operation) *kvs.Statement { + IndexName := stmt.IndexName + // reset taskID every time + TaskId := rand.Int63() + SetName := stmt.SetName + + MaxRecords := uint64(policy.MaxRecords) + RecordsPerSecond := uint32(policy.RecordsPerSecond) + + funcArgs := make([][]byte, 0, len(stmt.functionArgs)) + for i := range stmt.functionArgs { + funcArgs = append(funcArgs, grpcValuePacked(stmt.functionArgs[i])) + } + + return &kvs.Statement{ + Namespace: stmt.Namespace, + SetName: &SetName, + IndexName: &IndexName, + BinNames: stmt.BinNames, + Filter: stmt.Filter.grpc(), + PackageName: stmt.packageName, + FunctionName: stmt.functionName, + FunctionArgs: funcArgs, + Operations: grpcOperations(ops), + TaskId: &TaskId, + MaxRecords: &MaxRecords, + RecordsPerSecond: &RecordsPerSecond, + } +} + +func grpcOperations(ops []*Operation) []*kvs.Operation { + res := make([]*kvs.Operation, 0, len(ops)) + for i := range ops { + res = append(res, ops[i].grpc()) + } + return res +} + +func (qd QueryDuration) grpc() kvs.QueryDuration { + switch qd { + case LONG: + return kvs.QueryDuration(kvs.QueryDuration_LONG) + case SHORT: + return kvs.QueryDuration(kvs.QueryDuration_SHORT) + case LONG_RELAX_AP: + return kvs.QueryDuration(kvs.QueryDuration_LONG_RELAX_AP) + } + panic(unreachable) +} + +func (rm ReadModeAP) grpc() kvs.ReadModeAP { + switch rm { + case ReadModeAPOne: + return kvs.ReadModeAP_ONE + case ReadModeAPAll: + return kvs.ReadModeAP_ALL + } + panic(unreachable) +} + +func (rm ReadModeSC) grpc() kvs.ReadModeSC { + switch rm { + case ReadModeSCSession: + return kvs.ReadModeSC_SESSION + case ReadModeSCLinearize: + return kvs.ReadModeSC_LINEARIZE + case ReadModeSCAllowReplica: + return kvs.ReadModeSC_ALLOW_REPLICA + case ReadModeSCAllowUnavailable: + return kvs.ReadModeSC_ALLOW_UNAVAILABLE + } + panic(unreachable) +} + +func (rea RecordExistsAction) grpc() kvs.RecordExistsAction { + switch rea { + case UPDATE: + return kvs.RecordExistsAction_UPDATE + case UPDATE_ONLY: + return kvs.RecordExistsAction_UPDATE_ONLY + case REPLACE: + return kvs.RecordExistsAction_REPLACE + case REPLACE_ONLY: + return kvs.RecordExistsAction_REPLACE_ONLY + case CREATE_ONLY: + return kvs.RecordExistsAction_CREATE_ONLY + } + panic(unreachable) +} + +func (rp ReplicaPolicy) grpc() kvs.Replica { + switch rp { + case MASTER: + return kvs.Replica_MASTER + case MASTER_PROLES: + return kvs.Replica_MASTER_PROLES + case RANDOM: + return kvs.Replica_RANDOM + case SEQUENCE: + return kvs.Replica_SEQUENCE + case PREFER_RACK: + return kvs.Replica_PREFER_RACK + } + panic(unreachable) +} diff --git a/proxy_execute_task.go b/proxy_execute_task.go new file mode 100644 index 00000000..bc89b966 --- /dev/null +++ b/proxy_execute_task.go @@ -0,0 +1,89 @@ +//go:build as_proxy + +// Copyright 2014-2022 Aerospike, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package aerospike + +import ( + "context" + "math/rand" + "time" + + kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" +) + +// newGRPCExecuteTask initializes task with fields needed to query server nodes. +func newGRPCExecuteTask(clnt *ProxyClient, statement *Statement) *ExecuteTask { + return &ExecuteTask{ + baseTask: newTask(nil), + taskID: statement.TaskId, + scan: statement.IsScan(), + clnt: clnt, + } +} + +func (etsk *ExecuteTask) grpcIsDone() (bool, Error) { + statusReq := &kvs.BackgroundTaskStatusRequest{ + TaskId: int64(etsk.taskID), + IsScan: etsk.scan, + } + + req := kvs.AerospikeRequestPayload{ + Id: rand.Uint32(), + Iteration: 1, + BackgroundTaskStatusRequest: statusReq, + } + + clnt := etsk.clnt.(*ProxyClient) + conn, err := clnt.grpcConn() + if err != nil { + return false, err + } + + client := kvs.NewQueryClient(conn) + + ctx, cancel := context.WithTimeout(context.Background(), NewInfoPolicy().Timeout) + defer cancel() + + streamRes, gerr := client.BackgroundTaskStatus(ctx, &req) + if gerr != nil { + return false, newGrpcError(true, gerr, gerr.Error()) + } + + for { + time.Sleep(time.Second) + + res, gerr := streamRes.Recv() + if gerr != nil { + e := newGrpcError(true, gerr) + return false, e + } + + if res.GetStatus() != 0 { + e := newGrpcStatusError(res) + clnt.returnGrpcConnToPool(conn) + return false, e + } + + switch res.GetBackgroundTaskStatus() { + case kvs.BackgroundTaskStatus_COMPLETE: + clnt.returnGrpcConnToPool(conn) + return true, nil + default: + clnt.returnGrpcConnToPool(conn) + return false, nil + } + } +} diff --git a/proxy_query_partition_command.go b/proxy_query_partition_command.go index ed7be72d..de389261 100644 --- a/proxy_query_partition_command.go +++ b/proxy_query_partition_command.go @@ -1,3 +1,5 @@ +//go:build as_proxy + // Copyright 2014-2022 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/proxy_scan_command.go b/proxy_scan_command.go index 897241e1..7f0a06c7 100644 --- a/proxy_scan_command.go +++ b/proxy_scan_command.go @@ -1,3 +1,5 @@ +//go:build as_proxy + // Copyright 2014-2022 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/query_aggregate_command.go b/query_aggregate_command.go index 34606e7f..c4693478 100644 --- a/query_aggregate_command.go +++ b/query_aggregate_command.go @@ -1,5 +1,4 @@ //go:build !app_engine -// +build !app_engine // Copyright 2014-2022 Aerospike, Inc. // diff --git a/query_aggregate_test.go b/query_aggregate_test.go index dd26d1a9..adaaac9f 100644 --- a/query_aggregate_test.go +++ b/query_aggregate_test.go @@ -1,5 +1,4 @@ //go:build !app_engine -// +build !app_engine // Copyright 2014-2022 Aerospike, Inc. // diff --git a/query_duration.go b/query_duration.go index c06df6b7..0b4dc8a6 100644 --- a/query_duration.go +++ b/query_duration.go @@ -17,8 +17,6 @@ package aerospike -import kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" - // QueryDuration defines the expected query duration. The server treats the query in different ways depending on the expected duration. // This enum is ignored for aggregation queries, background queries and server versions < 6.0. type QueryDuration int @@ -48,15 +46,3 @@ const ( // This value is treated exactly like LONG for server versions < 7.1. LONG_RELAX_AP ) - -func (qd QueryDuration) grpc() kvs.QueryDuration { - switch qd { - case LONG: - return kvs.QueryDuration(kvs.QueryDuration_LONG) - case SHORT: - return kvs.QueryDuration(kvs.QueryDuration_SHORT) - case LONG_RELAX_AP: - return kvs.QueryDuration(kvs.QueryDuration_LONG_RELAX_AP) - } - panic(unreachable) -} diff --git a/query_policy.go b/query_policy.go index 40439a0d..747a06c2 100644 --- a/query_policy.go +++ b/query_policy.go @@ -14,12 +14,6 @@ package aerospike -import ( - "time" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" -) - // QueryPolicy encapsulates parameters for policy attributes used in query operations. type QueryPolicy struct { MultiPolicy @@ -60,32 +54,3 @@ func NewQueryPolicy() *QueryPolicy { MultiPolicy: *NewMultiPolicy(), } } - -func (qp *QueryPolicy) grpc() *kvs.QueryPolicy { - SendKey := qp.SendKey - TotalTimeout := uint32(qp.TotalTimeout / time.Millisecond) - RecordQueueSize := uint32(qp.RecordQueueSize) - MaxConcurrentNodes := uint32(qp.MaxConcurrentNodes) - IncludeBinData := qp.IncludeBinData - FailOnClusterChange := false //qp.FailOnClusterChange - ShortQuery := qp.ShortQuery || qp.ExpectedDuration == SHORT - InfoTimeout := uint32(qp.SocketTimeout / time.Millisecond) - ExpectedDuration := qp.ExpectedDuration.grpc() - - return &kvs.QueryPolicy{ - Replica: qp.ReplicaPolicy.grpc(), - ReadModeAP: qp.ReadModeAP.grpc(), - ReadModeSC: qp.ReadModeSC.grpc(), - SendKey: &SendKey, - Compress: qp.UseCompression, - Expression: qp.FilterExpression.grpc(), - TotalTimeout: &TotalTimeout, - MaxConcurrentNodes: &MaxConcurrentNodes, - RecordQueueSize: &RecordQueueSize, - IncludeBinData: &IncludeBinData, - FailOnClusterChange: &FailOnClusterChange, - ShortQuery: &ShortQuery, - InfoTimeout: &InfoTimeout, - ExpectedDuration: &ExpectedDuration, - } -} diff --git a/read_command.go b/read_command.go index 08949a01..ebe80dcb 100644 --- a/read_command.go +++ b/read_command.go @@ -16,11 +16,9 @@ package aerospike import ( "fmt" - "math/rand" "reflect" "github.com/aerospike/aerospike-client-go/v7/logger" - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" "github.com/aerospike/aerospike-client-go/v7/types" Buffer "github.com/aerospike/aerospike-client-go/v7/utils/buffer" @@ -271,50 +269,3 @@ func (cmd *readCommand) Execute() Error { func (cmd *readCommand) transactionType() transactionType { return ttGet } - -func (cmd *readCommand) ExecuteGRPC(clnt *ProxyClient) Error { - defer cmd.grpcPutBufferBack() - - err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) - if err != nil { - return err - } - - req := kvs.AerospikeRequestPayload{ - Id: rand.Uint32(), - Iteration: 1, - Payload: cmd.dataBuffer[:cmd.dataOffset], - ReadPolicy: cmd.policy.grpc(), - } - - conn, err := clnt.grpcConn() - if err != nil { - return err - } - - client := kvs.NewKVSClient(conn) - - ctx, cancel := cmd.policy.grpcDeadlineContext() - defer cancel() - - res, gerr := client.Read(ctx, &req) - if gerr != nil { - return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) - } - - cmd.commandWasSent = true - - defer clnt.returnGrpcConnToPool(conn) - - if res.GetStatus() != 0 { - return newGrpcStatusError(res) - } - - cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) - err = cmd.parseResult(cmd, cmd.conn) - if err != nil { - return err - } - - return nil -} diff --git a/read_command_reflect.go b/read_command_reflect.go index 210c11d1..ba9d91d5 100644 --- a/read_command_reflect.go +++ b/read_command_reflect.go @@ -1,5 +1,4 @@ //go:build !as_performance -// +build !as_performance // Copyright 2014-2022 Aerospike, Inc. // diff --git a/read_command_reflect_test.go b/read_command_reflect_test.go index 57891e63..48a65a4f 100644 --- a/read_command_reflect_test.go +++ b/read_command_reflect_test.go @@ -1,5 +1,4 @@ //go:build !as_performance -// +build !as_performance // Copyright 2014-2022 Aerospike, Inc. // diff --git a/read_header_command.go b/read_header_command.go index 832b0f2a..f21a09b4 100644 --- a/read_header_command.go +++ b/read_header_command.go @@ -15,9 +15,6 @@ package aerospike import ( - "math/rand" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" "github.com/aerospike/aerospike-client-go/v7/types" Buffer "github.com/aerospike/aerospike-client-go/v7/utils/buffer" @@ -107,50 +104,3 @@ func (cmd *readHeaderCommand) Execute() Error { func (cmd *readHeaderCommand) transactionType() transactionType { return ttGetHeader } - -func (cmd *readHeaderCommand) ExecuteGRPC(clnt *ProxyClient) Error { - defer cmd.grpcPutBufferBack() - - err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) - if err != nil { - return err - } - - req := kvs.AerospikeRequestPayload{ - Id: rand.Uint32(), - Iteration: 1, - Payload: cmd.dataBuffer[:cmd.dataOffset], - ReadPolicy: cmd.policy.grpc(), - } - - conn, err := clnt.grpcConn() - if err != nil { - return err - } - - client := kvs.NewKVSClient(conn) - - ctx, cancel := cmd.policy.grpcDeadlineContext() - defer cancel() - - res, gerr := client.GetHeader(ctx, &req) - if gerr != nil { - return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) - } - - cmd.commandWasSent = true - - defer clnt.returnGrpcConnToPool(conn) - - if res.GetStatus() != 0 { - return newGrpcStatusError(res) - } - - cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) - err = cmd.parseResult(cmd, cmd.conn) - if err != nil { - return err - } - - return nil -} diff --git a/read_mode_ap.go b/read_mode_ap.go index 5a421138..ca5af369 100644 --- a/read_mode_ap.go +++ b/read_mode_ap.go @@ -17,8 +17,6 @@ package aerospike -import kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" - // ReadModeAP is the read policy in AP (availability) mode namespaces. // It indicates how duplicates should be consulted in a read operation. // Only makes a difference during migrations and only applicable in AP mode. @@ -32,13 +30,3 @@ const ( // the read operation. ReadModeAPAll ) - -func (rm ReadModeAP) grpc() kvs.ReadModeAP { - switch rm { - case ReadModeAPOne: - return kvs.ReadModeAP_ONE - case ReadModeAPAll: - return kvs.ReadModeAP_ALL - } - panic(unreachable) -} diff --git a/read_mode_sc.go b/read_mode_sc.go index 6ac90309..38ea6c6e 100644 --- a/read_mode_sc.go +++ b/read_mode_sc.go @@ -17,8 +17,6 @@ package aerospike -import kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" - // ReadModeSC is the read policy in SC (strong consistency) mode namespaces. // Determines SC read consistency options. type ReadModeSC int @@ -40,17 +38,3 @@ const ( // partitions. Increasing sequence of record versions is not guaranteed. ReadModeSCAllowUnavailable ) - -func (rm ReadModeSC) grpc() kvs.ReadModeSC { - switch rm { - case ReadModeSCSession: - return kvs.ReadModeSC_SESSION - case ReadModeSCLinearize: - return kvs.ReadModeSC_LINEARIZE - case ReadModeSCAllowReplica: - return kvs.ReadModeSC_ALLOW_REPLICA - case ReadModeSCAllowUnavailable: - return kvs.ReadModeSC_ALLOW_UNAVAILABLE - } - panic(unreachable) -} diff --git a/record_exists_action.go b/record_exists_action.go index 88b43f7f..d3c81f11 100644 --- a/record_exists_action.go +++ b/record_exists_action.go @@ -14,8 +14,6 @@ package aerospike -import kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" - // RecordExistsAction determines how to handle writes when // the record already exists. type RecordExistsAction int @@ -45,19 +43,3 @@ const ( // CREATE_ONLY means: Create only. Fail if record exists. CREATE_ONLY ) - -func (rea RecordExistsAction) grpc() kvs.RecordExistsAction { - switch rea { - case UPDATE: - return kvs.RecordExistsAction_UPDATE - case UPDATE_ONLY: - return kvs.RecordExistsAction_UPDATE_ONLY - case REPLACE: - return kvs.RecordExistsAction_REPLACE - case REPLACE_ONLY: - return kvs.RecordExistsAction_REPLACE_ONLY - case CREATE_ONLY: - return kvs.RecordExistsAction_CREATE_ONLY - } - panic(unreachable) -} diff --git a/replica_policy.go b/replica_policy.go index 04f4652a..766d844c 100644 --- a/replica_policy.go +++ b/replica_policy.go @@ -17,8 +17,6 @@ package aerospike -import kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" - // ReplicaPolicy defines type of node partition targeted by read commands. type ReplicaPolicy int @@ -48,19 +46,3 @@ const ( // in order to function properly. PREFER_RACK ) - -func (rp ReplicaPolicy) grpc() kvs.Replica { - switch rp { - case MASTER: - return kvs.Replica_MASTER - case MASTER_PROLES: - return kvs.Replica_MASTER_PROLES - case RANDOM: - return kvs.Replica_RANDOM - case SEQUENCE: - return kvs.Replica_SEQUENCE - case PREFER_RACK: - return kvs.Replica_PREFER_RACK - } - panic(unreachable) -} diff --git a/scan_policy.go b/scan_policy.go index e5c2faf7..400850db 100644 --- a/scan_policy.go +++ b/scan_policy.go @@ -14,12 +14,6 @@ package aerospike -import ( - "time" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" -) - // ScanPolicy encapsulates parameters used in scan operations. type ScanPolicy struct { MultiPolicy @@ -46,26 +40,3 @@ func NewScanPolicy() *ScanPolicy { MultiPolicy: mp, } } - -func (sp *ScanPolicy) grpc() *kvs.ScanPolicy { - TotalTimeout := uint32(sp.TotalTimeout / time.Millisecond) - MaxRecords := uint64(sp.MaxRecords) - RecordsPerSecond := uint32(sp.RecordsPerSecond) - MaxConcurrentNodes := uint32(sp.MaxConcurrentNodes) - IncludeBinData := sp.IncludeBinData - ConcurrentNodes := MaxConcurrentNodes > 1 - - return &kvs.ScanPolicy{ - Replica: sp.ReplicaPolicy.grpc(), - ReadModeAP: sp.ReadModeAP.grpc(), - ReadModeSC: sp.ReadModeSC.grpc(), - Compress: sp.UseCompression, - Expression: sp.FilterExpression.grpc(), - TotalTimeout: &TotalTimeout, - MaxRecords: &MaxRecords, - RecordsPerSecond: &RecordsPerSecond, - ConcurrentNodes: &ConcurrentNodes, - MaxConcurrentNodes: &MaxConcurrentNodes, - IncludeBinData: &IncludeBinData, - } -} diff --git a/server_command.go b/server_command.go index 56fec97e..73f8371f 100644 --- a/server_command.go +++ b/server_command.go @@ -15,9 +15,6 @@ package aerospike import ( - "math/rand" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" "github.com/aerospike/aerospike-client-go/v7/types" Buffer "github.com/aerospike/aerospike-client-go/v7/utils/buffer" ) @@ -97,70 +94,3 @@ func (cmd *serverCommand) isRead() bool { func (cmd *serverCommand) Execute() Error { return cmd.execute(cmd) } - -func (cmd *serverCommand) ExecuteGRPC(clnt *ProxyClient) Error { - defer cmd.grpcPutBufferBack() - - err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) - if err != nil { - return err - } - - execReq := &kvs.BackgroundExecuteRequest{ - Statement: cmd.statement.grpc(cmd.policy, cmd.operations), - WritePolicy: cmd.writePolicy.grpc_exec(cmd.policy.FilterExpression), - } - - req := kvs.AerospikeRequestPayload{ - Id: rand.Uint32(), - Iteration: 1, - Payload: cmd.dataBuffer[:cmd.dataOffset], - BackgroundExecuteRequest: execReq, - } - - conn, err := clnt.grpcConn() - if err != nil { - return err - } - - client := kvs.NewQueryClient(conn) - - ctx, cancel := cmd.policy.grpcDeadlineContext() - defer cancel() - - streamRes, gerr := client.BackgroundExecute(ctx, &req) - if gerr != nil { - return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) - } - - cmd.commandWasSent = true - - readCallback := func() ([]byte, Error) { - res, gerr := streamRes.Recv() - if gerr != nil { - e := newGrpcError(!cmd.isRead(), gerr) - return nil, e - } - - if res.GetStatus() != 0 { - e := newGrpcStatusError(res) - return res.GetPayload(), e - } - - if !res.GetHasNext() { - return nil, errGRPCStreamEnd - } - - return res.GetPayload(), nil - } - - cmd.conn = newGrpcFakeConnection(nil, readCallback) - err = cmd.parseResult(cmd, cmd.conn) - if err != nil && err != errGRPCStreamEnd { - return err - } - - clnt.returnGrpcConnToPool(conn) - - return nil -} diff --git a/statement.go b/statement.go index 10c4346c..a9d3857d 100644 --- a/statement.go +++ b/statement.go @@ -18,7 +18,6 @@ import ( "fmt" "math/rand" - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" "github.com/aerospike/aerospike-client-go/v7/types" ) @@ -114,41 +113,3 @@ func (stmt *Statement) terminationError() types.ResultCode { func (stmt *Statement) prepare(returnData bool) { stmt.ReturnData = returnData } - -func (stmt *Statement) grpc(policy *QueryPolicy, ops []*Operation) *kvs.Statement { - IndexName := stmt.IndexName - // reset taskID every time - TaskId := rand.Int63() - SetName := stmt.SetName - - MaxRecords := uint64(policy.MaxRecords) - RecordsPerSecond := uint32(policy.RecordsPerSecond) - - funcArgs := make([][]byte, 0, len(stmt.functionArgs)) - for i := range stmt.functionArgs { - funcArgs = append(funcArgs, grpcValuePacked(stmt.functionArgs[i])) - } - - return &kvs.Statement{ - Namespace: stmt.Namespace, - SetName: &SetName, - IndexName: &IndexName, - BinNames: stmt.BinNames, - Filter: stmt.Filter.grpc(), - PackageName: stmt.packageName, - FunctionName: stmt.functionName, - FunctionArgs: funcArgs, - Operations: grpcOperations(ops), - TaskId: &TaskId, - MaxRecords: &MaxRecords, - RecordsPerSecond: &RecordsPerSecond, - } -} - -func grpcOperations(ops []*Operation) []*kvs.Operation { - res := make([]*kvs.Operation, 0, len(ops)) - for i := range ops { - res = append(res, ops[i].grpc()) - } - return res -} diff --git a/touch_command.go b/touch_command.go index f5f31483..fe0723ab 100644 --- a/touch_command.go +++ b/touch_command.go @@ -16,10 +16,8 @@ package aerospike import ( "fmt" - "math/rand" "github.com/aerospike/aerospike-client-go/v7/logger" - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" "github.com/aerospike/aerospike-client-go/v7/types" Buffer "github.com/aerospike/aerospike-client-go/v7/utils/buffer" @@ -143,50 +141,3 @@ func (cmd *touchCommand) Execute() Error { func (cmd *touchCommand) transactionType() transactionType { return ttPut } - -func (cmd *touchCommand) ExecuteGRPC(clnt *ProxyClient) Error { - defer cmd.grpcPutBufferBack() - - err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) - if err != nil { - return err - } - - req := kvs.AerospikeRequestPayload{ - Id: rand.Uint32(), - Iteration: 1, - Payload: cmd.dataBuffer[:cmd.dataOffset], - WritePolicy: cmd.policy.grpc(), - } - - conn, err := clnt.grpcConn() - if err != nil { - return err - } - - client := kvs.NewKVSClient(conn) - - ctx, cancel := cmd.policy.grpcDeadlineContext() - defer cancel() - - res, gerr := client.Touch(ctx, &req) - if gerr != nil { - return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) - } - - cmd.commandWasSent = true - - defer clnt.returnGrpcConnToPool(conn) - - if res.GetStatus() != 0 { - return newGrpcStatusError(res) - } - - cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) - err = cmd.parseResult(cmd, cmd.conn) - if err != nil { - return err - } - - return nil -} diff --git a/udf_test.go b/udf_test.go index 80d3f430..856b795a 100644 --- a/udf_test.go +++ b/udf_test.go @@ -1,5 +1,4 @@ //go:build !app_engine -// +build !app_engine // Copyright 2014-2022 Aerospike, Inc. // diff --git a/value_reflect.go b/value_reflect.go index f4b20df6..c2387b40 100644 --- a/value_reflect.go +++ b/value_reflect.go @@ -1,5 +1,4 @@ //go:build !as_performance -// +build !as_performance // Copyright 2014-2022 Aerospike, Inc. // diff --git a/write_command.go b/write_command.go index 67af862d..9d23432a 100644 --- a/write_command.go +++ b/write_command.go @@ -15,9 +15,6 @@ package aerospike import ( - "math/rand" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" "github.com/aerospike/aerospike-client-go/v7/types" Buffer "github.com/aerospike/aerospike-client-go/v7/utils/buffer" @@ -117,50 +114,3 @@ func (cmd *writeCommand) Execute() Error { func (cmd *writeCommand) transactionType() transactionType { return ttPut } - -func (cmd *writeCommand) ExecuteGRPC(clnt *ProxyClient) Error { - defer cmd.grpcPutBufferBack() - - err := cmd.prepareBuffer(cmd, cmd.policy.deadline()) - if err != nil { - return err - } - - req := kvs.AerospikeRequestPayload{ - Id: rand.Uint32(), - Iteration: 1, - Payload: cmd.dataBuffer[:cmd.dataOffset], - WritePolicy: cmd.policy.grpc(), - } - - conn, err := clnt.grpcConn() - if err != nil { - return err - } - - client := kvs.NewKVSClient(conn) - - ctx, cancel := cmd.policy.grpcDeadlineContext() - defer cancel() - - res, gerr := client.Write(ctx, &req) - if gerr != nil { - return newGrpcError(!cmd.isRead(), gerr, gerr.Error()) - } - - cmd.commandWasSent = true - - defer clnt.returnGrpcConnToPool(conn) - - if res.GetStatus() != 0 { - return newGrpcStatusError(res) - } - - cmd.conn = newGrpcFakeConnection(res.GetPayload(), nil) - err = cmd.parseResult(cmd, cmd.conn) - if err != nil { - return err - } - - return nil -} diff --git a/write_policy.go b/write_policy.go index 81d268c0..492fc384 100644 --- a/write_policy.go +++ b/write_policy.go @@ -16,9 +16,6 @@ package aerospike import ( "math" - "time" - - kvs "github.com/aerospike/aerospike-client-go/v7/proto/kvs" ) const ( @@ -96,54 +93,3 @@ func NewWritePolicy(generation, expiration uint32) *WritePolicy { return res } - -func (p *WritePolicy) grpc() *kvs.WritePolicy { - return &kvs.WritePolicy{ - Replica: p.ReplicaPolicy.grpc(), - ReadModeSC: p.ReadModeSC.grpc(), - ReadModeAP: p.ReadModeAP.grpc(), - } -} - -func (p *WritePolicy) grpc_exec(expr *Expression) *kvs.BackgroundExecutePolicy { - if p == nil { - return nil - } - - SendKey := p.SendKey - TotalTimeout := uint32(p.TotalTimeout / time.Millisecond) - RecordExistsAction := p.RecordExistsAction.grpc() - GenerationPolicy := p.GenerationPolicy.grpc() - CommitLevel := p.CommitLevel.grpc() - Generation := p.Generation - Expiration := p.Expiration - RespondAllOps := p.RespondPerEachOp - DurableDelete := p.DurableDelete - - fe := expr - if fe == nil { - fe = p.FilterExpression - } - - res := &kvs.BackgroundExecutePolicy{ - Replica: p.ReplicaPolicy.grpc(), - ReadModeAP: p.ReadModeAP.grpc(), - ReadModeSC: p.ReadModeSC.grpc(), - SendKey: &SendKey, - Compress: p.UseCompression, - Expression: fe.grpc(), - TotalTimeout: &TotalTimeout, - - Xdr: nil, - - RecordExistsAction: &RecordExistsAction, - GenerationPolicy: &GenerationPolicy, - CommitLevel: &CommitLevel, - Generation: &Generation, - Expiration: &Expiration, - RespondAllOps: &RespondAllOps, - DurableDelete: &DurableDelete, - } - - return res -} From 012cf106453b2f61ec5e192eaa9ae70e3558e0ec Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Tue, 16 Jul 2024 23:18:41 +0200 Subject: [PATCH 05/15] Update CHANGELOG --- CHANGELOG.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1286273f..bf259cf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,16 @@ # Change History -## July 2 2024: v7.5.1 +## July 17 2024: v7.6.0 - Hotfix release. + Minor fix release. + +- **Improvements** + - [CLIENT-3045] Move proxy client build behind a build flag. + This removes the GRPC compilation and potential namespace conflict from the default build and moves it behind the compiler build flag "as_proxy". - **Fixes** - [CLIENT-3022] `Close()` throws a `nil` pointer error on `ProxyClient` without Authentication. + - [CLIENT-3044] Circular reference in between `Client`<->`Cluster` causes memory leak when the client is not closed manually. ## July 1 2024: v7.5.0 From 35c64092dd50a66b11de09ecab8b1db045c1950c Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 17 Jul 2024 14:45:09 +0200 Subject: [PATCH 06/15] Update dependencies --- go.mod | 22 ++++++++++------------ go.sum | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 1a0b58a4..63df5f31 100644 --- a/go.mod +++ b/go.mod @@ -7,28 +7,26 @@ require ( github.com/onsi/gomega v1.32.0 github.com/yuin/gopher-lua v1.1.1 golang.org/x/sync v0.7.0 - google.golang.org/grpc v1.63.2 - google.golang.org/protobuf v1.33.0 + google.golang.org/grpc v1.63.3 + google.golang.org/protobuf v1.34.2 ) require ( - github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect + github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da // indirect github.com/kr/pretty v0.1.0 // indirect github.com/stretchr/testify v1.8.4 // indirect github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad // indirect - golang.org/x/net v0.24.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.19.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) -retract ( - v7.3.0 // `Client.BatchGetOperate` issue -) +retract v7.3.0 // `Client.BatchGetOperate` issue diff --git a/go.sum b/go.sum index 16b99f85..b2dbf46a 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ 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/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -11,6 +13,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da h1:xRmpO92tb8y+Z85iUOMOicpCfaYcv7o3Cg3wKrIpg8g= +github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -34,6 +38,8 @@ golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= @@ -42,20 +48,32 @@ golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d h1:JU0iKnSg02Gmb5ZdV8nYsKEKsP6o/FGVWTrw4i1DA9A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.63.3 h1:FGVegD7MHo/zhaGduk/R85WvSFJ+si70UQIJ0fg+BiU= +google.golang.org/grpc v1.63.3/go.mod h1:5FFeE/YiGPD2flWFCrCx8K3Ay7hALATnKiI8U3avIuw= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 90cbef57f71f2c0f69b6c05b7afaeae0b6836ee3 Mon Sep 17 00:00:00 2001 From: "Eugene R." Date: Wed, 17 Jul 2024 17:20:34 +0300 Subject: [PATCH 07/15] [CLIENT-3047] Fix pointer value assignment in baseMultiCommand.parseKey (#443) --- multi_command.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/multi_command.go b/multi_command.go index 6fb8ca7a..ae23b137 100644 --- a/multi_command.go +++ b/multi_command.go @@ -221,8 +221,10 @@ func (cmd *baseMultiCommand) parseKey(fieldCount int, bval *int64) (*Key, Error) return nil, err.setNode(cmd.node) } case BVAL_ARRAY: - v := Buffer.LittleBytesToInt64(cmd.dataBuffer, 1) - bval = &v + if bval != nil { + v := Buffer.LittleBytesToInt64(cmd.dataBuffer, 1) + *bval = v + } } } @@ -292,8 +294,8 @@ func (cmd *baseMultiCommand) parseRecordResults(ifc command, receiveSize int) (b fieldCount := int(Buffer.BytesToUint16(cmd.dataBuffer, 18)) opCount := int(Buffer.BytesToUint16(cmd.dataBuffer, 20)) - var bval *int64 - key, err := cmd.parseKey(fieldCount, bval) + var bval int64 + key, err := cmd.parseKey(fieldCount, &bval) if err != nil { err = newNodeError(cmd.node, err) return false, err @@ -366,7 +368,7 @@ func (cmd *baseMultiCommand) parseRecordResults(ifc command, receiveSize int) (b // block forever, or panic in case the channel is closed in the meantime. select { // send back the result on the async channel - case cmd.recordset.records <- &Result{Record: newRecord(cmd.node, key, bins, generation, expiration), Err: nil, BVal: bval}: + case cmd.recordset.records <- &Result{Record: newRecord(cmd.node, key, bins, generation, expiration), Err: nil, BVal: &bval}: case <-cmd.recordset.cancelled: switch cmd.terminationErrorType { case types.SCAN_TERMINATED: @@ -404,7 +406,7 @@ func (cmd *baseMultiCommand) parseRecordResults(ifc command, receiveSize int) (b if cmd.terminationErrorType == types.SCAN_TERMINATED { cmd.tracker.setDigest(cmd.nodePartitions, key) } else { - cmd.tracker.setLast(cmd.nodePartitions, key, bval) + cmd.tracker.setLast(cmd.nodePartitions, key, &bval) } } } From 04acf53effd0802703578a55853b8cb9961bde3b Mon Sep 17 00:00:00 2001 From: "Eugene R." Date: Wed, 17 Jul 2024 17:21:04 +0300 Subject: [PATCH 08/15] [CLIENT-3048] Use precomputed ops variable in batchIndexCommandGet.executeSingle (#442) --- batch_index_command_get.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/batch_index_command_get.go b/batch_index_command_get.go index 4eeff58a..2fc00d49 100644 --- a/batch_index_command_get.go +++ b/batch_index_command_get.go @@ -78,7 +78,7 @@ func (cmd *batchIndexCommandGet) executeSingle(client clientIfc) Error { } else { ops = br.Ops } - res, err := client.Operate(cmd.policy.toWritePolicy(), br.Key, br.Ops...) + res, err := client.Operate(cmd.policy.toWritePolicy(), br.Key, ops...) cmd.indexRecords[i].setRecord(res) if err != nil { cmd.indexRecords[i].setRawError(err) From 7b8b7fed4852a7934b3a7c8a9aaae43ffc2f7c1d Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 17 Jul 2024 16:22:53 +0200 Subject: [PATCH 09/15] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf259cf1..e1a6ad80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ - **Fixes** - [CLIENT-3022] `Close()` throws a `nil` pointer error on `ProxyClient` without Authentication. - [CLIENT-3044] Circular reference in between `Client`<->`Cluster` causes memory leak when the client is not closed manually. + - [CLIENT-3047] Fix pointer value assignment in baseMultiCommand.parseKey (#443). + - [CLIENT-3048] Use precomputed ops variable in batchIndexCommandGet.executeSingle (#442). ## July 1 2024: v7.5.0 From be575a35436eb89a4c059b520016d8204b28576e Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 17 Jul 2024 18:17:05 +0200 Subject: [PATCH 10/15] [CLIENT-3046] Wrong return type in Single Key Batch Operations with Multiple Ops per Bin --- CHANGELOG.md | 1 + batch_command.go | 2 + batch_command_get.go | 2 +- batch_command_operate.go | 6 +- batch_index_command_get.go | 6 +- batch_read.go | 2 +- batch_test.go | 2 +- client.go | 8 +- client_test.go | 632 +++++++++++++++++++------------------ operate_command.go | 14 +- proxy_client.go | 6 +- read_command.go | 12 +- 12 files changed, 358 insertions(+), 335 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1a6ad80..9caf2a4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - **Fixes** - [CLIENT-3022] `Close()` throws a `nil` pointer error on `ProxyClient` without Authentication. - [CLIENT-3044] Circular reference in between `Client`<->`Cluster` causes memory leak when the client is not closed manually. + - [CLIENT-3046] Wrong return type in Single Key Batch Operations with Multiple Ops per Bin. - [CLIENT-3047] Fix pointer value assignment in baseMultiCommand.parseKey (#443). - [CLIENT-3048] Use precomputed ops variable in batchIndexCommandGet.executeSingle (#442). diff --git a/batch_command.go b/batch_command.go index 0fcbf2a9..1d2c55f2 100644 --- a/batch_command.go +++ b/batch_command.go @@ -33,6 +33,8 @@ type batcher interface { type clientIfc interface { ClientIfc + + operate(*WritePolicy, *Key, bool, ...*Operation) (*Record, Error) execute(policy *WritePolicy, key *Key, packageName string, functionName string, args ...Value) (*Record, Error) } diff --git a/batch_command_get.go b/batch_command_get.go index ea5c68ba..1acd449c 100644 --- a/batch_command_get.go +++ b/batch_command_get.go @@ -231,7 +231,7 @@ func (cmd *batchCommandGet) executeSingle(client clientIfc) Error { return newError(types.PARAMETER_ERROR, "Write operations not allowed in batch read").setNode(cmd.node) } } - cmd.records[offset], err = client.Operate(cmd.policy.toWritePolicy(), cmd.keys[offset], cmd.ops...) + cmd.records[offset], err = client.operate(cmd.policy.toWritePolicy(), cmd.keys[offset], true, cmd.ops...) } else if (cmd.readAttr & _INFO1_NOBINDATA) == _INFO1_NOBINDATA { cmd.records[offset], err = client.GetHeader(&cmd.policy.BasePolicy, cmd.keys[offset]) } else { diff --git a/batch_command_operate.go b/batch_command_operate.go index d85e8f59..b0502787 100644 --- a/batch_command_operate.go +++ b/batch_command_operate.go @@ -246,14 +246,14 @@ func (cmd *batchCommandOperate) executeSingle(client clientIfc) Error { } else if len(ops) == 0 { ops = append(ops, GetOp()) } - res, err = client.Operate(cmd.client.getUsableBatchReadPolicy(br.Policy).toWritePolicy(cmd.policy), br.Key, ops...) + res, err = client.operate(cmd.client.getUsableBatchReadPolicy(br.Policy).toWritePolicy(cmd.policy), br.Key, true, ops...) case *BatchWrite: policy := cmd.client.getUsableBatchWritePolicy(br.Policy).toWritePolicy(cmd.policy) policy.RespondPerEachOp = true - res, err = client.Operate(policy, br.Key, br.Ops...) + res, err = client.operate(policy, br.Key, true, br.Ops...) case *BatchDelete: policy := cmd.client.getUsableBatchDeletePolicy(br.Policy).toWritePolicy(cmd.policy) - res, err = client.Operate(policy, br.Key, DeleteOp()) + res, err = client.operate(policy, br.Key, true, DeleteOp()) case *BatchUDF: policy := cmd.client.getUsableBatchUDFPolicy(br.Policy).toWritePolicy(cmd.policy) policy.RespondPerEachOp = true diff --git a/batch_index_command_get.go b/batch_index_command_get.go index 2fc00d49..f840b04a 100644 --- a/batch_index_command_get.go +++ b/batch_index_command_get.go @@ -14,7 +14,9 @@ package aerospike -import "github.com/aerospike/aerospike-client-go/v7/types" +import ( + "github.com/aerospike/aerospike-client-go/v7/types" +) type batchIndexCommandGet struct { batchCommandGet @@ -78,7 +80,7 @@ func (cmd *batchIndexCommandGet) executeSingle(client clientIfc) Error { } else { ops = br.Ops } - res, err := client.Operate(cmd.policy.toWritePolicy(), br.Key, ops...) + res, err := client.operate(cmd.policy.toWritePolicy(), br.Key, true, ops...) cmd.indexRecords[i].setRecord(res) if err != nil { cmd.indexRecords[i].setRawError(err) diff --git a/batch_read.go b/batch_read.go index 484163e7..aac921f7 100644 --- a/batch_read.go +++ b/batch_read.go @@ -155,5 +155,5 @@ func (br *BatchRead) String() string { } func (br *BatchRead) headerOnly() bool { - return len(br.BinNames) == 0 && !br.ReadAllBins + return len(br.Ops) == 0 && len(br.BinNames) == 0 && !br.ReadAllBins } diff --git a/batch_test.go b/batch_test.go index 2071508f..f03b726f 100644 --- a/batch_test.go +++ b/batch_test.go @@ -37,7 +37,7 @@ var _ = gg.Describe("Aerospike", func() { var set = randString(50) gg.It("must return the result with same ordering", func() { - for _, keyCount := range []int{256, 1} { + for _, keyCount := range []int{256, 5, 4, 3, 2, 1} { var keys []*as.Key for i := 0; i < keyCount; i++ { key, _ := as.NewKey(ns, set, i) diff --git a/client.go b/client.go index 83fccc90..54f4b6f0 100644 --- a/client.go +++ b/client.go @@ -712,12 +712,18 @@ func (clnt *Client) BatchExecute(policy *BatchPolicy, udfPolicy *BatchUDFPolicy, // // If the policy is nil, the default relevant policy will be used. func (clnt *Client) Operate(policy *WritePolicy, key *Key, operations ...*Operation) (*Record, Error) { + return clnt.operate(policy, key, false, operations...) +} + +// useOpResults is used in batch single nodes commands and should be true to return the right type for BatchOperate results +func (clnt *Client) operate(policy *WritePolicy, key *Key, useOpResults bool, operations ...*Operation) (*Record, Error) { + // TODO: Remove this method in the next major release. policy = clnt.getUsableWritePolicy(policy) args, err := newOperateArgs(clnt.cluster, policy, key, operations) if err != nil { return nil, err } - command, err := newOperateCommand(clnt.cluster, policy, key, args) + command, err := newOperateCommand(clnt.cluster, policy, key, args, useOpResults) if err != nil { return nil, err } diff --git a/client_test.go b/client_test.go index 5d0c96a2..93f4b6c5 100644 --- a/client_test.go +++ b/client_test.go @@ -1010,263 +1010,267 @@ var _ = gg.Describe("Aerospike", func() { gg.Context("Batch Exists operations", func() { bin := as.NewBin("Aerospike", rand.Intn(math.MaxInt16)) - const keyCount = 2048 + var keyCount = []int{1, 2048} gg.BeforeEach(func() { }) - for _, useInline := range []bool{true, false} { - gg.It(fmt.Sprintf("must return the result with same ordering. AllowInline: %v", useInline), func() { - var exists []bool - keys := []*as.Key{} + for _, keyCount := range keyCount { + for _, useInline := range []bool{true, false} { + gg.It(fmt.Sprintf("must return the result with same ordering. KeyCount: %d, AllowInline: %v", keyCount, useInline), func() { + var exists []bool + keys := []*as.Key{} - for i := 0; i < keyCount; i++ { - key, err := as.NewKey(ns, set, randString(50)) - gm.Expect(err).ToNot(gm.HaveOccurred()) - keys = append(keys, key) - - // if key shouldExist == true, put it in the DB - if i%2 == 0 { - err = client.PutBins(wpolicy, key, bin) - gm.Expect(err).ToNot(gm.HaveOccurred()) - - // make sure they exists in the DB - exists, err := client.Exists(rpolicy, key) + for i := 0; i < keyCount; i++ { + key, err := as.NewKey(ns, set, randString(50)) gm.Expect(err).ToNot(gm.HaveOccurred()) - gm.Expect(exists).To(gm.Equal(true)) + keys = append(keys, key) + + // if key shouldExist == true, put it in the DB + if i%2 == 0 { + err = client.PutBins(wpolicy, key, bin) + gm.Expect(err).ToNot(gm.HaveOccurred()) + + // make sure they exists in the DB + exists, err := client.Exists(rpolicy, key) + gm.Expect(err).ToNot(gm.HaveOccurred()) + gm.Expect(exists).To(gm.Equal(true)) + } } - } - bpolicy.AllowInline = useInline - exists, err = client.BatchExists(bpolicy, keys) - gm.Expect(err).ToNot(gm.HaveOccurred()) - gm.Expect(len(exists)).To(gm.Equal(len(keys))) - for idx, keyExists := range exists { - gm.Expect(keyExists).To(gm.Equal(idx%2 == 0)) - } - }) + bpolicy.AllowInline = useInline + exists, err = client.BatchExists(bpolicy, keys) + gm.Expect(err).ToNot(gm.HaveOccurred()) + gm.Expect(len(exists)).To(gm.Equal(len(keys))) + for idx, keyExists := range exists { + gm.Expect(keyExists).To(gm.Equal(idx%2 == 0)) + } + }) + } } }) // Batch Exists context gg.Context("Batch Get operations", func() { bin := as.NewBin("Aerospike", rand.Int()) - const keyCount = 2048 + var keyCount = []int{1, 2048} gg.BeforeEach(func() { }) - for _, useInline := range []bool{true, false} { - gg.It(fmt.Sprintf("must return the records with same ordering as keys. AllowInline: %v", useInline), func() { - binRedundant := as.NewBin("Redundant", "Redundant") - - var records []*as.Record - type existence struct { - key *as.Key - shouldExist bool // set randomly and checked against later - } + for _, keyCount := range keyCount { + for _, useInline := range []bool{true, false} { + gg.It(fmt.Sprintf("must return the records with same ordering as keys. KeyCount: %d, AllowInline: %v", keyCount, useInline), func() { + binRedundant := as.NewBin("Redundant", "Redundant") - exList := make([]existence, 0, keyCount) - keys := make([]*as.Key, 0, keyCount) - - for i := 0; i < keyCount; i++ { - key, err := as.NewKey(ns, set, randString(50)) - gm.Expect(err).ToNot(gm.HaveOccurred()) - e := existence{key: key, shouldExist: rand.Intn(100) > 50} - exList = append(exList, e) - keys = append(keys, key) + var records []*as.Record + type existence struct { + key *as.Key + shouldExist bool // set randomly and checked against later + } - // if key shouldExist == true, put it in the DB - if e.shouldExist { - err = client.PutBins(wpolicy, key, bin, binRedundant) - gm.Expect(err).ToNot(gm.HaveOccurred()) + exList := make([]existence, 0, keyCount) + keys := make([]*as.Key, 0, keyCount) - // make sure they exists in the DB - rec, err := client.Get(rpolicy, key) - gm.Expect(err).ToNot(gm.HaveOccurred()) - gm.Expect(rec.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) - gm.Expect(rec.Bins[binRedundant.Name]).To(gm.Equal(binRedundant.Value.GetObject())) - } else { - // make sure they exists in the DB - exists, err := client.Exists(rpolicy, key) + for i := 0; i < keyCount; i++ { + key, err := as.NewKey(ns, set, randString(50)) gm.Expect(err).ToNot(gm.HaveOccurred()) - gm.Expect(exists).To(gm.Equal(false)) + e := existence{key: key, shouldExist: rand.Intn(100) > 50} + exList = append(exList, e) + keys = append(keys, key) + + // if key shouldExist == true, put it in the DB + if e.shouldExist { + err = client.PutBins(wpolicy, key, bin, binRedundant) + gm.Expect(err).ToNot(gm.HaveOccurred()) + + // make sure they exists in the DB + rec, err := client.Get(rpolicy, key) + gm.Expect(err).ToNot(gm.HaveOccurred()) + gm.Expect(rec.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) + gm.Expect(rec.Bins[binRedundant.Name]).To(gm.Equal(binRedundant.Value.GetObject())) + } else { + // make sure they exists in the DB + exists, err := client.Exists(rpolicy, key) + gm.Expect(err).ToNot(gm.HaveOccurred()) + gm.Expect(exists).To(gm.Equal(false)) + } } - } - brecords := make([]*as.BatchRead, len(keys)) - for i := range keys { - brecords[i] = &as.BatchRead{ - BatchRecord: as.BatchRecord{ - Key: keys[i], - }, - ReadAllBins: true, + brecords := make([]*as.BatchRead, len(keys)) + for i := range keys { + brecords[i] = &as.BatchRead{ + BatchRecord: as.BatchRecord{ + Key: keys[i], + }, + ReadAllBins: true, + } } - } - bpolicy.AllowInline = useInline - err = client.BatchGetComplex(bpolicy, brecords) - gm.Expect(err).ToNot(gm.HaveOccurred()) - for idx, rec := range brecords { - if exList[idx].shouldExist { - gm.Expect(len(rec.Record.Bins)).To(gm.Equal(2)) - gm.Expect(rec.Record.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) - gm.Expect(rec.Record.Key).To(gm.Equal(keys[idx])) - } else { - gm.Expect(rec.Record).To(gm.BeNil()) + bpolicy.AllowInline = useInline + err = client.BatchGetComplex(bpolicy, brecords) + gm.Expect(err).ToNot(gm.HaveOccurred()) + for idx, rec := range brecords { + if exList[idx].shouldExist { + gm.Expect(len(rec.Record.Bins)).To(gm.Equal(2)) + gm.Expect(rec.Record.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) + gm.Expect(rec.Record.Key).To(gm.Equal(keys[idx])) + } else { + gm.Expect(rec.Record).To(gm.BeNil()) + } } - } - brecords = make([]*as.BatchRead, len(keys)) - for i := range keys { - brecords[i] = &as.BatchRead{ - BatchRecord: as.BatchRecord{ - Key: keys[i], - }, - ReadAllBins: false, - BinNames: []string{bin.Name}, + brecords = make([]*as.BatchRead, len(keys)) + for i := range keys { + brecords[i] = &as.BatchRead{ + BatchRecord: as.BatchRecord{ + Key: keys[i], + }, + ReadAllBins: false, + BinNames: []string{bin.Name}, + } } - } - err = client.BatchGetComplex(bpolicy, brecords) - gm.Expect(err).ToNot(gm.HaveOccurred()) - for idx, rec := range brecords { - if exList[idx].shouldExist { - gm.Expect(len(rec.Record.Bins)).To(gm.Equal(1)) - gm.Expect(rec.Record.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) - gm.Expect(rec.Record.Key).To(gm.Equal(keys[idx])) - } else { - gm.Expect(rec.Record).To(gm.BeNil()) + err = client.BatchGetComplex(bpolicy, brecords) + gm.Expect(err).ToNot(gm.HaveOccurred()) + for idx, rec := range brecords { + if exList[idx].shouldExist { + gm.Expect(len(rec.Record.Bins)).To(gm.Equal(1)) + gm.Expect(rec.Record.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) + gm.Expect(rec.Record.Key).To(gm.Equal(keys[idx])) + } else { + gm.Expect(rec.Record).To(gm.BeNil()) + } } - } - records, err = client.BatchGet(bpolicy, keys) - gm.Expect(err).ToNot(gm.HaveOccurred()) - gm.Expect(len(records)).To(gm.Equal(len(keys))) - for idx, rec := range records { - if exList[idx].shouldExist { - gm.Expect(rec.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) - gm.Expect(rec.Key).To(gm.Equal(keys[idx])) - } else { - gm.Expect(rec).To(gm.BeNil()) + records, err = client.BatchGet(bpolicy, keys) + gm.Expect(err).ToNot(gm.HaveOccurred()) + gm.Expect(len(records)).To(gm.Equal(len(keys))) + for idx, rec := range records { + if exList[idx].shouldExist { + gm.Expect(rec.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) + gm.Expect(rec.Key).To(gm.Equal(keys[idx])) + } else { + gm.Expect(rec).To(gm.BeNil()) + } } - } - records, err = client.BatchGet(bpolicy, keys, bin.Name) - gm.Expect(err).ToNot(gm.HaveOccurred()) - gm.Expect(len(records)).To(gm.Equal(len(keys))) - for idx, rec := range records { - if exList[idx].shouldExist { - // only bin1 has been requested - gm.Expect(rec.Bins[binRedundant.Name]).To(gm.BeNil()) - gm.Expect(rec.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) - gm.Expect(rec.Key).To(gm.Equal(keys[idx])) - } else { - gm.Expect(rec).To(gm.BeNil()) + records, err = client.BatchGet(bpolicy, keys, bin.Name) + gm.Expect(err).ToNot(gm.HaveOccurred()) + gm.Expect(len(records)).To(gm.Equal(len(keys))) + for idx, rec := range records { + if exList[idx].shouldExist { + // only bin1 has been requested + gm.Expect(rec.Bins[binRedundant.Name]).To(gm.BeNil()) + gm.Expect(rec.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) + gm.Expect(rec.Key).To(gm.Equal(keys[idx])) + } else { + gm.Expect(rec).To(gm.BeNil()) + } } - } - }) - - gg.It(fmt.Sprintf("must return the records with same ordering as keys via Batch Complex Protocol. AllowInline: %v", useInline), func() { - binRedundant := as.NewBin("Redundant", "Redundant") - - type existence struct { - key *as.Key - shouldExist bool // set randomly and checked against later - } + }) - exList := make([]existence, 0, keyCount) - keys := make([]*as.Key, 0, keyCount) + gg.It(fmt.Sprintf("must return the records with same ordering as keys via Batch Complex Protocol. keyCount: %d, AllowInline: %v", keyCount, useInline), func() { + binRedundant := as.NewBin("Redundant", "Redundant") - for i := 0; i < keyCount; i++ { - key, err := as.NewKey(ns, set, randString(50)) - gm.Expect(err).ToNot(gm.HaveOccurred()) - e := existence{key: key, shouldExist: rand.Intn(100) > 50} - exList = append(exList, e) - keys = append(keys, key) + type existence struct { + key *as.Key + shouldExist bool // set randomly and checked against later + } - // if key shouldExist == true, put it in the DB - if e.shouldExist { - err = client.PutBins(wpolicy, key, bin, binRedundant) - gm.Expect(err).ToNot(gm.HaveOccurred()) + exList := make([]existence, 0, keyCount) + keys := make([]*as.Key, 0, keyCount) - // make sure they exists in the DB - rec, err := client.Get(rpolicy, key) - gm.Expect(err).ToNot(gm.HaveOccurred()) - gm.Expect(rec.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) - gm.Expect(rec.Bins[binRedundant.Name]).To(gm.Equal(binRedundant.Value.GetObject())) - } else { - // make sure they exists in the DB - exists, err := client.Exists(rpolicy, key) + for i := 0; i < keyCount; i++ { + key, err := as.NewKey(ns, set, randString(50)) gm.Expect(err).ToNot(gm.HaveOccurred()) - gm.Expect(exists).To(gm.Equal(false)) + e := existence{key: key, shouldExist: rand.Intn(100) > 50} + exList = append(exList, e) + keys = append(keys, key) + + // if key shouldExist == true, put it in the DB + if e.shouldExist { + err = client.PutBins(wpolicy, key, bin, binRedundant) + gm.Expect(err).ToNot(gm.HaveOccurred()) + + // make sure they exists in the DB + rec, err := client.Get(rpolicy, key) + gm.Expect(err).ToNot(gm.HaveOccurred()) + gm.Expect(rec.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) + gm.Expect(rec.Bins[binRedundant.Name]).To(gm.Equal(binRedundant.Value.GetObject())) + } else { + // make sure they exists in the DB + exists, err := client.Exists(rpolicy, key) + gm.Expect(err).ToNot(gm.HaveOccurred()) + gm.Expect(exists).To(gm.Equal(false)) + } } - } - brecords := make([]*as.BatchRead, len(keys)) - for i := range keys { - brecords[i] = &as.BatchRead{ - BatchRecord: as.BatchRecord{ - Key: keys[i], - }, - ReadAllBins: true, + brecords := make([]*as.BatchRead, len(keys)) + for i := range keys { + brecords[i] = &as.BatchRead{ + BatchRecord: as.BatchRecord{ + Key: keys[i], + }, + ReadAllBins: true, + } } - } - bpolicy.AllowInline = useInline - err = client.BatchGetComplex(bpolicy, brecords) - gm.Expect(err).ToNot(gm.HaveOccurred()) - for idx, rec := range brecords { - if exList[idx].shouldExist { - gm.Expect(rec.Record.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) - gm.Expect(rec.Record.Key).To(gm.Equal(keys[idx])) - } else { - gm.Expect(rec.Record).To(gm.BeNil()) + bpolicy.AllowInline = useInline + err = client.BatchGetComplex(bpolicy, brecords) + gm.Expect(err).ToNot(gm.HaveOccurred()) + for idx, rec := range brecords { + if exList[idx].shouldExist { + gm.Expect(rec.Record.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) + gm.Expect(rec.Record.Key).To(gm.Equal(keys[idx])) + } else { + gm.Expect(rec.Record).To(gm.BeNil()) + } } - } - brecords = make([]*as.BatchRead, len(keys)) - for i := range keys { - brecords[i] = &as.BatchRead{ - BatchRecord: as.BatchRecord{ - Key: keys[i], - }, - ReadAllBins: false, - BinNames: []string{"Aerospike", "Redundant"}, + brecords = make([]*as.BatchRead, len(keys)) + for i := range keys { + brecords[i] = &as.BatchRead{ + BatchRecord: as.BatchRecord{ + Key: keys[i], + }, + ReadAllBins: false, + BinNames: []string{"Aerospike", "Redundant"}, + } } - } - bpolicy.AllowInline = useInline - err = client.BatchGetComplex(bpolicy, brecords) - gm.Expect(err).ToNot(gm.HaveOccurred()) - for idx, rec := range brecords { - if exList[idx].shouldExist { - gm.Expect(rec.Record.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) - gm.Expect(rec.Record.Key).To(gm.Equal(keys[idx])) - } else { - gm.Expect(rec.Record).To(gm.BeNil()) + bpolicy.AllowInline = useInline + err = client.BatchGetComplex(bpolicy, brecords) + gm.Expect(err).ToNot(gm.HaveOccurred()) + for idx, rec := range brecords { + if exList[idx].shouldExist { + gm.Expect(rec.Record.Bins[bin.Name]).To(gm.Equal(bin.Value.GetObject())) + gm.Expect(rec.Record.Key).To(gm.Equal(keys[idx])) + } else { + gm.Expect(rec.Record).To(gm.BeNil()) + } } - } - brecords = make([]*as.BatchRead, len(keys)) - for i := range keys { - brecords[i] = &as.BatchRead{ - BatchRecord: as.BatchRecord{ - Key: keys[i], - }, - ReadAllBins: false, - BinNames: nil, + brecords = make([]*as.BatchRead, len(keys)) + for i := range keys { + brecords[i] = &as.BatchRead{ + BatchRecord: as.BatchRecord{ + Key: keys[i], + }, + ReadAllBins: false, + BinNames: nil, + } } - } - bpolicy.AllowInline = useInline - err = client.BatchGetComplex(bpolicy, brecords) - gm.Expect(err).ToNot(gm.HaveOccurred()) - for idx, rec := range brecords { - if exList[idx].shouldExist { - gm.Expect(len(rec.Record.Bins)).To(gm.Equal(0)) - gm.Expect(rec.Record.Key).To(gm.Equal(keys[idx])) - } else { - gm.Expect(rec.Record).To(gm.BeNil()) + bpolicy.AllowInline = useInline + err = client.BatchGetComplex(bpolicy, brecords) + gm.Expect(err).ToNot(gm.HaveOccurred()) + for idx, rec := range brecords { + if exList[idx].shouldExist { + gm.Expect(len(rec.Record.Bins)).To(gm.Equal(0)) + gm.Expect(rec.Record.Key).To(gm.Equal(keys[idx])) + } else { + gm.Expect(rec.Record).To(gm.BeNil()) + } } - } - }) + }) + } } }) // Batch Get context @@ -1297,149 +1301,151 @@ var _ = gg.Describe("Aerospike", func() { gg.Context("BatchOperate", func() { gg.It("must execute BatchGetOperate with Operations", func() { - const keyCount = 10 const listSize = 10 const cdtBinName = "cdtBin" - keys := make([]*as.Key, 10) - for i := 0; i < keyCount; i++ { - keys[i], err = as.NewKey(ns, set, randString(10)) - gm.Expect(err).ToNot(gm.HaveOccurred()) - } + for keyCount := 1; keyCount < 10; keyCount++ { + keys := make([]*as.Key, keyCount) + for i := 0; i < keyCount; i++ { + keys[i], err = as.NewKey(ns, set, randString(10)) + gm.Expect(err).ToNot(gm.HaveOccurred()) + } - // First Part: For CDTs - list := []interface{}{} - for j, key := range keys { - for i := 1; i <= listSize; i++ { - list = append(list, i*100) + // First Part: For CDTs + list := []interface{}{} + for j, key := range keys { + for i := 1; i <= listSize; i++ { + list = append(list, i*100) - sz, err := client.Operate(wpolicy, key, as.ListAppendOp(cdtBinName, j+i*100)) - gm.Expect(err).ToNot(gm.HaveOccurred()) - gm.Expect(sz.Bins[cdtBinName]).To(gm.Equal(i)) + sz, err := client.Operate(wpolicy, key, as.ListAppendOp(cdtBinName, j+i*100)) + gm.Expect(err).ToNot(gm.HaveOccurred()) + gm.Expect(sz.Bins[cdtBinName]).To(gm.Equal(i)) + } } - } - records, err := client.BatchGetOperate(bpolicy, keys, - as.ListSizeOp(cdtBinName), - as.ListGetByIndexOp(cdtBinName, -1, as.ListReturnTypeValue), - as.ListGetByIndexOp(cdtBinName, 0, as.ListReturnTypeValue), - as.ListGetByIndexOp(cdtBinName, 2, as.ListReturnTypeValue), - ) - gm.Expect(err).ToNot(gm.HaveOccurred()) + records, err := client.BatchGetOperate(bpolicy, keys, + as.ListSizeOp(cdtBinName), + as.ListGetByIndexOp(cdtBinName, -1, as.ListReturnTypeValue), + as.ListGetByIndexOp(cdtBinName, 0, as.ListReturnTypeValue), + as.ListGetByIndexOp(cdtBinName, 2, as.ListReturnTypeValue), + ) + gm.Expect(err).ToNot(gm.HaveOccurred()) - for i, key := range keys { - rec := records[i] - gm.Expect(rec.Key.Digest()).To(gm.Equal(key.Digest())) - gm.Expect(rec.Bins[cdtBinName]).To(gm.Equal(as.OpResults{listSize, i + listSize*100, i + 100, i + 300})) + for i, key := range keys { + rec := records[i] + gm.Expect(rec.Key.Digest()).To(gm.Equal(key.Digest())) + gm.Expect(rec.Bins[cdtBinName]).To(gm.Equal(as.OpResults{listSize, i + listSize*100, i + 100, i + 300})) + } } - }) gg.It("must execute BatchGetComplex with Operations", func() { - const keyCount = 10 const listSize = 10 const cdtBinName = "cdtBin" - keys := make([]*as.Key, 10) - for i := 0; i < keyCount; i++ { - keys[i], err = as.NewKey(ns, set, randString(10)) - gm.Expect(err).ToNot(gm.HaveOccurred()) - } + for keyCount := 1; keyCount < 10; keyCount++ { + keys := make([]*as.Key, keyCount) + for i := 0; i < keyCount; i++ { + keys[i], err = as.NewKey(ns, set, randString(10)) + gm.Expect(err).ToNot(gm.HaveOccurred()) + } - brecords := make([]*as.BatchRead, len(keys)) - for i := range keys { - brecords[i] = &as.BatchRead{ - BatchRecord: as.BatchRecord{ - Key: keys[i], - }, - ReadAllBins: false, // just to check if the internal flags are set correctly regardless - Ops: []*as.Operation{ - as.ListSizeOp(cdtBinName), - as.ListGetByIndexOp(cdtBinName, -1, as.ListReturnTypeValue), - as.ListGetByIndexOp(cdtBinName, 0, as.ListReturnTypeValue), - as.ListGetByIndexOp(cdtBinName, 2, as.ListReturnTypeValue), - }, + brecords := make([]*as.BatchRead, len(keys)) + for i := range keys { + brecords[i] = &as.BatchRead{ + BatchRecord: as.BatchRecord{ + Key: keys[i], + }, + ReadAllBins: false, // just to check if the internal flags are set correctly regardless + Ops: []*as.Operation{ + as.ListSizeOp(cdtBinName), + as.ListGetByIndexOp(cdtBinName, -1, as.ListReturnTypeValue), + as.ListGetByIndexOp(cdtBinName, 0, as.ListReturnTypeValue), + as.ListGetByIndexOp(cdtBinName, 2, as.ListReturnTypeValue), + }, + } } - } - // First Part: For CDTs - list := []interface{}{} - for j, key := range keys { - for i := 1; i <= listSize; i++ { - list = append(list, i*100) + // First Part: For CDTs + list := []interface{}{} + for j, key := range keys { + for i := 1; i <= listSize; i++ { + list = append(list, i*100) - sz, err := client.Operate(wpolicy, key, as.ListAppendOp(cdtBinName, j+i*100)) - gm.Expect(err).ToNot(gm.HaveOccurred()) - gm.Expect(sz.Bins[cdtBinName]).To(gm.Equal(i)) + sz, err := client.Operate(wpolicy, key, as.ListAppendOp(cdtBinName, j+i*100)) + gm.Expect(err).ToNot(gm.HaveOccurred()) + gm.Expect(sz.Bins[cdtBinName]).To(gm.Equal(i)) + } } - } - err = client.BatchGetComplex(bpolicy, brecords) - gm.Expect(err).ToNot(gm.HaveOccurred()) + err = client.BatchGetComplex(bpolicy, brecords) + gm.Expect(err).ToNot(gm.HaveOccurred()) - for i, key := range keys { - rec := brecords[i].Record - gm.Expect(rec.Key.Digest()).To(gm.Equal(key.Digest())) - gm.Expect(rec.Bins[cdtBinName]).To(gm.Equal(as.OpResults{listSize, i + listSize*100, i + 100, i + 300})) + for i, key := range keys { + rec := brecords[i].Record + gm.Expect(rec.Key.Digest()).To(gm.Equal(key.Digest())) + gm.Expect(rec.Bins[cdtBinName]).To(gm.Equal(as.OpResults{listSize, i + listSize*100, i + 100, i + 300})) + } } - }) }) gg.Context("Batch Get Header operations", func() { bin := as.NewBin("Aerospike", rand.Int()) - const keyCount = 1024 + var keyCount = []int{1, 1024} gg.BeforeEach(func() { }) - for _, useInline := range []bool{true, false} { - gg.It(fmt.Sprintf("must return the record headers with same ordering as keys. AllowInline: %v", useInline), func() { - var records []*as.Record - type existence struct { - key *as.Key - shouldExist bool // set randomly and checked against later - } - - exList := []existence{} - keys := []*as.Key{} - - for i := 0; i < keyCount; i++ { - key, err := as.NewKey(ns, set, randString(50)) - gm.Expect(err).ToNot(gm.HaveOccurred()) - e := existence{key: key, shouldExist: rand.Intn(100) > 50} - exList = append(exList, e) - keys = append(keys, key) - - // if key shouldExist == true, put it in the DB - if e.shouldExist { - err = client.PutBins(wpolicy, key, bin) - gm.Expect(err).ToNot(gm.HaveOccurred()) + for _, keyCount := range keyCount { + for _, useInline := range []bool{true, false} { + gg.It(fmt.Sprintf("must return the record headers with same ordering as keys. KeyCount: %d, AllowInline: %v", keyCount, useInline), func() { + var records []*as.Record + type existence struct { + key *as.Key + shouldExist bool // set randomly and checked against later + } - // update generation - err = client.Touch(wpolicy, key) - gm.Expect(err).ToNot(gm.HaveOccurred()) + exList := []existence{} + keys := []*as.Key{} - // make sure they exists in the DB - exists, err := client.Exists(rpolicy, key) + for i := 0; i < keyCount; i++ { + key, err := as.NewKey(ns, set, randString(50)) gm.Expect(err).ToNot(gm.HaveOccurred()) - gm.Expect(exists).To(gm.Equal(true)) + e := existence{key: key, shouldExist: rand.Intn(100) > 50} + exList = append(exList, e) + keys = append(keys, key) + + // if key shouldExist == true, put it in the DB + if e.shouldExist { + err = client.PutBins(wpolicy, key, bin) + gm.Expect(err).ToNot(gm.HaveOccurred()) + + // update generation + err = client.Touch(wpolicy, key) + gm.Expect(err).ToNot(gm.HaveOccurred()) + + // make sure they exists in the DB + exists, err := client.Exists(rpolicy, key) + gm.Expect(err).ToNot(gm.HaveOccurred()) + gm.Expect(exists).To(gm.Equal(true)) + } } - } - bpolicy.AllowInline = useInline - records, err = client.BatchGetHeader(bpolicy, keys) - gm.Expect(err).ToNot(gm.HaveOccurred()) - gm.Expect(len(records)).To(gm.Equal(len(keys))) - for idx, rec := range records { - if exList[idx].shouldExist { - gm.Expect(rec.Bins[bin.Name]).To(gm.BeNil()) - gm.Expect(rec.Generation).To(gm.Equal(uint32(2))) - } else { - gm.Expect(rec).To(gm.BeNil()) + bpolicy.AllowInline = useInline + records, err = client.BatchGetHeader(bpolicy, keys) + gm.Expect(err).ToNot(gm.HaveOccurred()) + gm.Expect(len(records)).To(gm.Equal(len(keys))) + for idx, rec := range records { + if exList[idx].shouldExist { + gm.Expect(rec.Bins[bin.Name]).To(gm.BeNil()) + gm.Expect(rec.Generation).To(gm.Equal(uint32(2))) + } else { + gm.Expect(rec).To(gm.BeNil()) + } } - } - }) + }) + } } }) // Batch Get Header context diff --git a/operate_command.go b/operate_command.go index 8b3e115f..d29f9a3b 100644 --- a/operate_command.go +++ b/operate_command.go @@ -17,20 +17,22 @@ package aerospike type operateCommand struct { readCommand - policy *WritePolicy - args operateArgs + policy *WritePolicy + args operateArgs + useOpResults bool } -func newOperateCommand(cluster *Cluster, policy *WritePolicy, key *Key, args operateArgs) (operateCommand, Error) { +func newOperateCommand(cluster *Cluster, policy *WritePolicy, key *Key, args operateArgs, useOpResults bool) (operateCommand, Error) { rdCommand, err := newReadCommand(cluster, &policy.BasePolicy, key, nil, args.partition) if err != nil { return operateCommand{}, err } return operateCommand{ - readCommand: rdCommand, - policy: policy, - args: args, + readCommand: rdCommand, + policy: policy, + args: args, + useOpResults: useOpResults, }, nil } diff --git a/proxy_client.go b/proxy_client.go index 9171b6bd..5c15b4ad 100644 --- a/proxy_client.go +++ b/proxy_client.go @@ -776,13 +776,17 @@ func (clnt *ProxyClient) BatchExecute(policy *BatchPolicy, udfPolicy *BatchUDFPo // // If the policy is nil, the default relevant policy will be used. func (clnt *ProxyClient) Operate(policy *WritePolicy, key *Key, operations ...*Operation) (*Record, Error) { + return clnt.operate(policy, key, false, operations...) +} + +func (clnt *ProxyClient) operate(policy *WritePolicy, key *Key, useOpResults bool, operations ...*Operation) (*Record, Error) { policy = clnt.getUsableWritePolicy(policy) args, err := newOperateArgs(nil, policy, key, operations) if err != nil { return nil, err } - command, err := newOperateCommand(nil, policy, key, args) + command, err := newOperateCommand(nil, policy, key, args, useOpResults) if err != nil { return nil, err } diff --git a/read_command.go b/read_command.go index ebe80dcb..b6a6aaf5 100644 --- a/read_command.go +++ b/read_command.go @@ -195,8 +195,7 @@ func (cmd *readCommand) parseRecord( var bins BinMap receiveOffset := 0 - type opList []interface{} - _, isOperate := ifc.(*operateCommand) + opCmd, isOperate := ifc.(*operateCommand) var binNamesSet []string // There can be fields in the response (setname etc). @@ -233,12 +232,12 @@ func (cmd *readCommand) parseRecord( if isOperate { // for operate list command results if prev, exists := bins[name]; exists { - if res, ok := prev.(opList); ok { + if res, ok := prev.(OpResults); ok { // List already exists. Add to it. bins[name] = append(res, value) } else { // Make a list to store all values. - bins[name] = opList{prev, value} + bins[name] = OpResults{prev, value} binNamesSet = append(binNamesSet, name) } } else { @@ -249,9 +248,10 @@ func (cmd *readCommand) parseRecord( } } - if isOperate { + // TODO: Remove this in the next major release + if isOperate && !opCmd.useOpResults { for i := range binNamesSet { - bins[binNamesSet[i]] = []interface{}(bins[binNamesSet[i]].(opList)) + bins[binNamesSet[i]] = []interface{}(bins[binNamesSet[i]].(OpResults)) } } From 399a4e8a1595422e1a3e280177d4db956078a3b4 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Wed, 17 Jul 2024 23:06:44 +0200 Subject: [PATCH 11/15] [CLIENT-3022] Optimize the ticker --- README.md | 2 +- proxy_auth_interceptor.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 36e82911..976cf1c9 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,7 @@ To make the library both flexible and fast, we had to integrate the reflection A ## Proxy Client / DBAAS -To compile the client for the proxy server in out database as a service environment, pass `-tags as_proxy` to the compiler on build. +To compile the client for the proxy server in our database-as-a-service (dbaas) environment, pass `-tags as_proxy` to the compiler on build. ## License diff --git a/proxy_auth_interceptor.go b/proxy_auth_interceptor.go index 788d7386..54124339 100644 --- a/proxy_auth_interceptor.go +++ b/proxy_auth_interceptor.go @@ -92,11 +92,13 @@ func (interceptor *authInterceptor) tokenRefresher() { // provide 5 secs of buffer before expiry due to network latency wait := interceptor.expiry.Sub(time.Now()) - 5*time.Second + ticker := time.NewTicker(wait) + // defer ticker.Close() + for { - ticker := time.NewTicker(wait) + ticker.Reset(wait) select { case <-ticker.C: - ticker.Stop() // prevent goroutine leak err := interceptor.refreshToken() if err != nil { wait = time.Second @@ -105,7 +107,6 @@ func (interceptor *authInterceptor) tokenRefresher() { } case <-interceptor.closer: - ticker.Stop() // prevent goroutine leak // channel closed; return from the goroutine return } From dd4e7562f9cd6f82362babf341553e30b94f4c55 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Thu, 18 Jul 2024 14:00:27 +0200 Subject: [PATCH 12/15] [CLIENT-3022] Close the ticker --- README.md | 1 + proxy_auth_interceptor.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 976cf1c9..0ee90b12 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Aerospike Client Go](https://goreportcard.com/badge/github.com/aerospike/aerospike-client-go/v7)](https://goreportcard.com/report/github.com/aerospike/aerospike-client-go/v7) [![Godoc](https://godoc.org/github.com/aerospike/aerospike-client-go/v7?status.svg)](https://pkg.go.dev/github.com/aerospike/aerospike-client-go/v7) +[![Tests](https://github.com/aerospike/aerospike-client-go/actions/workflows/build.yml/badge.svg?branch=v7&event=push)](github.com/aerospike/aerospike-client-go/actions) An Aerospike library for Go. diff --git a/proxy_auth_interceptor.go b/proxy_auth_interceptor.go index 54124339..adbe4ee6 100644 --- a/proxy_auth_interceptor.go +++ b/proxy_auth_interceptor.go @@ -93,7 +93,7 @@ func (interceptor *authInterceptor) tokenRefresher() { // provide 5 secs of buffer before expiry due to network latency wait := interceptor.expiry.Sub(time.Now()) - 5*time.Second ticker := time.NewTicker(wait) - // defer ticker.Close() + defer ticker.Stop() for { ticker.Reset(wait) From fee6173c5a15e47362bb63ed786e309d3475ae32 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Fri, 19 Jul 2024 18:03:26 +0200 Subject: [PATCH 13/15] [CLIENT-3049] Use a specialized pool for grpc conns to prevent premature reaping Also fixes namespace parameter for a few code examples. --- .github/workflows/build.yml | 2 +- README.md | 2 +- example_client_test.go | 4 +- example_listiter_int_test.go | 2 +- example_listiter_string_test.go | 2 +- example_listiter_time_test.go | 2 +- example_mapiter_test.go | 2 +- proxy_client.go | 97 ++++++++++++++++++++++++++++++++- tools/benchmark/README.md | 2 +- types/pool/tiered_buffer.go | 2 +- 10 files changed, 104 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d1b7efc0..bb1436e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: - name: Test pkg tests run: go run github.com/onsi/ginkgo/v2/ginkgo -cover -race -r -keep-going -succinct -randomize-suites pkg - name: Build Benchmark tool - run: cd tools/benchmark | go build -o benchmark . + run: cd tools/benchmark | go build -tags as_proxy -o benchmark . - name: Build asinfo tool run: cd tools/asinfo | go build -o asinfo . - name: Build cli tool diff --git a/README.md b/README.md index 0ee90b12..c6e6de09 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ Using [gopkg.in](https://gopkg.in/) is also supported: `go get -u gopkg.in/aeros - To run a go program directly: ```go run ``` - to build: ```go build -o ``` -- example: ```go build -o benchmark tools/benchmark/benchmark.go``` +- example: ```go build -tags as_proxy -o benchmark tools/benchmark/benchmark.go``` ## Performance Tweaking diff --git a/example_client_test.go b/example_client_test.go index d6f55959..df884991 100644 --- a/example_client_test.go +++ b/example_client_test.go @@ -25,7 +25,7 @@ import ( ) func ExampleClient_Add() { - key, err := as.NewKey("test", "test", "addkey") + key, err := as.NewKey(*namespace, "test", "addkey") if err != nil { log.Fatal(err) } @@ -66,7 +66,7 @@ func ExampleClient_Add() { } func ExampleClient_Append() { - key, err := as.NewKey("test", "test", "appendkey") + key, err := as.NewKey(*namespace, "test", "appendkey") if err != nil { log.Fatal(err) } diff --git a/example_listiter_int_test.go b/example_listiter_int_test.go index 0d7aa123..13e44515 100644 --- a/example_listiter_int_test.go +++ b/example_listiter_int_test.go @@ -54,7 +54,7 @@ func ExampleListIter_int() { // } var v as.Value = as.NewValue(myListInt([]int{1, 2, 3})) - key, err := as.NewKey("test", "test", 1) + key, err := as.NewKey(*namespace, "test", 1) if err != nil { log.Fatal(err) } diff --git a/example_listiter_string_test.go b/example_listiter_string_test.go index 252e8207..8d7a4232 100644 --- a/example_listiter_string_test.go +++ b/example_listiter_string_test.go @@ -53,7 +53,7 @@ func ExampleListIter_string() { // } var v as.Value = as.NewValue(myListString([]string{"a", "b", "c"})) - key, err := as.NewKey("test", "test", 1) + key, err := as.NewKey(*namespace, "test", 1) if err != nil { log.Fatal(err) } diff --git a/example_listiter_time_test.go b/example_listiter_time_test.go index ab12cdf9..9fb41538 100644 --- a/example_listiter_time_test.go +++ b/example_listiter_time_test.go @@ -57,7 +57,7 @@ func ExampleListIter_time() { now2 := time.Unix(123123124, 0) now3 := time.Unix(123123125, 0) var v as.Value = as.NewValue(myListTime([]time.Time{now1, now2, now3})) - key, err := as.NewKey("test", "test", 1) + key, err := as.NewKey(*namespace, "test", 1) if err != nil { log.Fatal(err) } diff --git a/example_mapiter_test.go b/example_mapiter_test.go index f4914a4f..8da567a5 100644 --- a/example_mapiter_test.go +++ b/example_mapiter_test.go @@ -61,7 +61,7 @@ func ExampleMapIter() { now := time.Unix(123123123, 0) var v as.Value = as.NewValue(myMapStringTime(map[string]time.Time{"now": now})) - key, err := as.NewKey("test", "test", 1) + key, err := as.NewKey(*namespace, "test", 1) if err != nil { log.Fatal(err) } diff --git a/proxy_client.go b/proxy_client.go index 5c15b4ad..627cbe4e 100644 --- a/proxy_client.go +++ b/proxy_client.go @@ -40,7 +40,7 @@ const notSupportedInProxyClient = "NOT SUPPORTED IN THE PROXY CLIENT" type ProxyClient struct { // only for GRPC clientPolicy ClientPolicy - grpcConnPool *sync.Pool + grpcConnPool *grpcConnectionHeap grpcHost *Host dialOptions []grpc.DialOption @@ -96,7 +96,7 @@ func NewProxyClientWithPolicyAndHost(policy *ClientPolicy, host *Host, dialOptio grpcClient := &ProxyClient{ clientPolicy: *policy, - grpcConnPool: new(sync.Pool), + grpcConnPool: newGrpcConnectionHeap(policy.ConnectionQueueSize), grpcHost: host, dialOptions: dialOptions, @@ -265,7 +265,7 @@ func (clnt *ProxyClient) setAuthToken(token string) { func (clnt *ProxyClient) grpcConn() (*grpc.ClientConn, Error) { pconn := clnt.grpcConnPool.Get() if pconn != nil { - return pconn.(*grpc.ClientConn), nil + return pconn, nil } return clnt.createGrpcConn(!clnt.clientPolicy.RequiresAuthentication()) @@ -309,6 +309,7 @@ func (clnt *ProxyClient) createGrpcConn(noInterceptor bool) (*grpc.ClientConn, E // Close closes all Grpcclient connections to database server nodes. func (clnt *ProxyClient) Close() { clnt.active.Set(false) + clnt.grpcConnPool.cleanup() if clnt.authInterceptor != nil { clnt.authInterceptor.close() } @@ -1410,3 +1411,93 @@ func (clnt *ProxyClient) getUsableInfoPolicy(policy *InfoPolicy) *InfoPolicy { //------------------------------------------------------- // Utility Functions //------------------------------------------------------- + +// grpcConnectionHeap is a non-blocking LIFO heap. +// If the heap is empty, nil is returned. +// if the heap is full, offer will return false +type grpcConnectionHeap struct { + head, tail uint32 + data []*grpc.ClientConn + size uint32 + full bool + mutex sync.Mutex +} + +// newGrpcConnectionHeap creates a new heap with initial size. +func newGrpcConnectionHeap(size int) *grpcConnectionHeap { + if size <= 0 { + panic("Heap size cannot be less than 1") + } + + return &grpcConnectionHeap{ + full: false, + data: make([]*grpc.ClientConn, uint32(size)), + size: uint32(size), + } +} + +func (h *grpcConnectionHeap) cleanup() { + h.mutex.Lock() + defer h.mutex.Unlock() + + for i := range h.data { + if h.data[i] != nil { + h.data[i].Close() + } + + h.data[i] = nil + } + + // make sure offer and poll both fail + h.data = nil + h.full = true + h.head = 0 + h.tail = 0 +} + +// Put adds an item to the heap unless the heap is full. +// In case the heap is full, the item will not be added to the heap +// and false will be returned +func (h *grpcConnectionHeap) Put(conn *grpc.ClientConn) bool { + h.mutex.Lock() + + // make sure heap is not full or cleaned up + if h.full || len(h.data) == 0 { + h.mutex.Unlock() + return false + } + + h.head = (h.head + 1) % h.size + h.full = (h.head == h.tail) + h.data[h.head] = conn + h.mutex.Unlock() + return true +} + +// Poll removes and returns an item from the heap. +// If the heap is empty, nil will be returned. +func (h *grpcConnectionHeap) Get() (res *grpc.ClientConn) { + h.mutex.Lock() + + // the heap has been cleaned up + if len(h.data) == 0 { + h.mutex.Unlock() + return nil + } + + // if heap is not empty + if (h.tail != h.head) || h.full { + res = h.data[h.head] + h.data[h.head] = nil + + h.full = false + if h.head == 0 { + h.head = h.size - 1 + } else { + h.head-- + } + } + + h.mutex.Unlock() + return res +} diff --git a/tools/benchmark/README.md b/tools/benchmark/README.md index 5c85ac15..23d5d3c0 100644 --- a/tools/benchmark/README.md +++ b/tools/benchmark/README.md @@ -9,7 +9,7 @@ To build this tool: ``` cd $GOPATH/src/github.com/aerospike/aerospike-client-go/tools/benchmark -go build . +go build -tags as_proxy . ``` To see available switches: diff --git a/types/pool/tiered_buffer.go b/types/pool/tiered_buffer.go index 43afff73..bed1ecea 100644 --- a/types/pool/tiered_buffer.go +++ b/types/pool/tiered_buffer.go @@ -26,7 +26,7 @@ type TieredBufferPool struct { // Min is Minimum the minimum buffer size. Min int - // Max is the maximum buffer is. The pool will allocate buffers of that size, + // Max is the maximum buffer size. The pool will allocate buffers of that size, // But will not store them back. Max int From a9275eda9a1ef823b43ba6d0b3683d4ab41b9dd0 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Fri, 19 Jul 2024 23:46:09 +0200 Subject: [PATCH 14/15] Update CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9caf2a4f..69dc59e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change History -## July 17 2024: v7.6.0 +## July 19 2024: v7.6.0 Minor fix release. @@ -14,6 +14,7 @@ - [CLIENT-3046] Wrong return type in Single Key Batch Operations with Multiple Ops per Bin. - [CLIENT-3047] Fix pointer value assignment in baseMultiCommand.parseKey (#443). - [CLIENT-3048] Use precomputed ops variable in batchIndexCommandGet.executeSingle (#442). + - [CLIENT-3049] Use a specialized pool for grpc conns to prevent premature reaping. ## July 1 2024: v7.5.0 From 02468292aeae747747592386606cf06e30ed5812 Mon Sep 17 00:00:00 2001 From: Khosrow Afroozeh Date: Sat, 20 Jul 2024 14:01:44 +0200 Subject: [PATCH 15/15] Skip a few unsupported tests in the DBAAS environment --- expression_test.go | 7 ++++++- load_test.go | 6 ++++++ random_operation_test.go | 6 ++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/expression_test.go b/expression_test.go index 75c4cf43..6af030c0 100644 --- a/expression_test.go +++ b/expression_test.go @@ -508,9 +508,14 @@ var _ = gg.Describe("Expression Filters", func() { var _ = gg.Context("Record Ops", func() { gg.It("ExpRecordSize must work", func() { + if *dbaas { + gg.Skip("Not supported in DBAAS environment") + } + if serverIsOlderThan("7") { - return + gg.Skip("Not supported servers before v7") } + // storage-engine could be memory for which deviceSize() returns zero. // This just tests that the expression was sent correctly // because all device sizes are effectively allowed. diff --git a/load_test.go b/load_test.go index 0552e2f3..a79b126e 100644 --- a/load_test.go +++ b/load_test.go @@ -33,6 +33,12 @@ func init() { // ALL tests are isolated by SetName and Key, which are 50 random characters var _ = gg.Describe("Aerospike load tests", func() { + gg.BeforeEach(func() { + if *dbaas { + gg.Skip("Not supported in DBAAS environment") + } + }) + gg.Describe("Single long random string test", func() { var ns = *namespace var set = "load" diff --git a/random_operation_test.go b/random_operation_test.go index c3bfa374..450c384b 100644 --- a/random_operation_test.go +++ b/random_operation_test.go @@ -30,6 +30,12 @@ const RANDOM_OPS_RUNS = 1000 // ALL tests are isolated by SetName and Key, which are 50 random characters var _ = gg.Describe("Aerospike", func() { + gg.BeforeEach(func() { + if *dbaas { + gg.Skip("Not supported in DBAAS environment") + } + }) + gg.Describe("Random Data Operations", func() { // connection data var err error