diff --git a/.travis/wait_for_node.sh b/.travis/wait_for_node.sh index 4d8f0209..71513c45 100755 --- a/.travis/wait_for_node.sh +++ b/.travis/wait_for_node.sh @@ -1,6 +1,6 @@ #!/bin/bash ################################################################################ -# Copyright 2013-2016 Aerospike, Inc. +# Copyright 2013-2020 Aerospike, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/CHANGELOG.md b/CHANGELOG.md index e37a614c..abbd71a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Change History +## June 8 2020: v3.0.0 + + Major feature release. There are a few minor breaking API changes. See `ClientPolicy`. + + Note: There has been significant changes to clustering code. We recommend extensive testing before using in production. + + * **New Features** + + - Adds support for Relaxed Strong Consistency mode. + - Adds support for whitelists in Roles. + ## May 28 2020: v2.12.0 Minor feature release. diff --git a/LICENSE b/LICENSE index 1006ac10..d76ea4a6 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2014-2016 Aerospike, Inc. + Copyright 2014-2020 Aerospike, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/admin_command.go b/admin_command.go index 46d33c03..dcf9f85a 100644 --- a/admin_command.go +++ b/admin_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use acmd file except in compliance with the License. @@ -39,6 +39,7 @@ const ( _DROP_ROLE byte = 11 _GRANT_PRIVILEGES byte = 12 _REVOKE_PRIVILEGES byte = 13 + _SET_WHITELIST byte = 14 _QUERY_ROLES byte = 16 _LOGIN byte = 20 @@ -53,6 +54,7 @@ const ( _ROLES byte = 10 _ROLE byte = 11 _PRIVILEGES byte = 12 + _WHITELIST byte = 13 // Misc _MSG_VERSION int64 = 2 @@ -122,12 +124,27 @@ func (acmd *adminCommand) revokeRoles(cluster *Cluster, policy *AdminPolicy, use return acmd.executeCommand(cluster, policy) } -func (acmd *adminCommand) createRole(cluster *Cluster, policy *AdminPolicy, roleName string, privileges []Privilege) error { - acmd.writeHeader(_CREATE_ROLE, 2) +func (acmd *adminCommand) createRole(cluster *Cluster, policy *AdminPolicy, roleName string, privileges []Privilege, whitelist []string) error { + fieldcount := 1 + if len(privileges) > 1 { + fieldcount++ + } + if len(whitelist) > 1 { + fieldcount++ + } + acmd.writeHeader(_CREATE_ROLE, fieldcount) acmd.writeFieldStr(_ROLE, roleName) - if err := acmd.writePrivileges(privileges); err != nil { - return err + + if len(privileges) > 0 { + if err := acmd.writePrivileges(privileges); err != nil { + return err + } + } + + if len(whitelist) > 0 { + acmd.writeWhitelist(whitelist) } + return acmd.executeCommand(cluster, policy) } @@ -155,6 +172,19 @@ func (acmd *adminCommand) revokePrivileges(cluster *Cluster, policy *AdminPolicy return acmd.executeCommand(cluster, policy) } +func (acmd *adminCommand) setWhitelist(cluster *Cluster, policy *AdminPolicy, roleName string, whitelist []string) error { + fieldCount := 1 + if len(whitelist) > 0 { + fieldCount++ + } + acmd.writeHeader(_REVOKE_PRIVILEGES, fieldCount) + acmd.writeFieldStr(_ROLE, roleName) + if len(whitelist) > 0 { + acmd.writeWhitelist(whitelist) + } + return acmd.executeCommand(cluster, policy) +} + func (acmd *adminCommand) queryUser(cluster *Cluster, policy *AdminPolicy, user string) (*UserRoles, error) { acmd.writeHeader(_QUERY_USERS, 1) acmd.writeFieldStr(_USER, user) @@ -258,6 +288,26 @@ func (acmd *adminCommand) writePrivileges(privileges []Privilege) error { return nil } + +func (acmd *adminCommand) writeWhitelist(whitelist []string) { + offset := acmd.dataOffset + int(_FIELD_HEADER_SIZE) + + comma := false + for _, address := range whitelist { + if comma { + acmd.dataBuffer[acmd.dataOffset] = ',' + acmd.dataOffset++ + } else { + comma = true + } + + offset += copy(acmd.dataBuffer[offset:], address) + } + + size := offset - acmd.dataOffset - int(_FIELD_HEADER_SIZE) + acmd.writeFieldHeader(_WHITELIST, size) + acmd.dataOffset = offset +} func (acmd *adminCommand) writeSize() { // Write total size of message which is the current offset. var size = int64(acmd.dataOffset-8) | (_MSG_VERSION << 56) | (_MSG_TYPE << 48) @@ -565,6 +615,8 @@ func (acmd *adminCommand) parseRolesFull(receiveSize int) (int, []*Role, error) acmd.dataOffset += len } else if id == _PRIVILEGES { acmd.parsePrivileges(role) + } else if id == _WHITELIST { + acmd.parseWhitelist(len) } else { acmd.dataOffset += len } @@ -606,3 +658,31 @@ func (acmd *adminCommand) parsePrivileges(role *Role) { role.Privileges = append(role.Privileges, priv) } } + +func (acmd *adminCommand) parseWhitelist(length int) []string { + list := []string{} + begin := acmd.dataOffset + max := begin + length + + for acmd.dataOffset < max { + if acmd.dataBuffer[acmd.dataOffset] == ',' { + l := acmd.dataOffset - begin + if l > 0 { + s := string(acmd.dataBuffer[begin : begin+l]) + list = append(list, s) + } + acmd.dataOffset++ + begin = acmd.dataOffset + } else { + acmd.dataOffset++ + } + } + + l := acmd.dataOffset - begin + if l > 0 { + s := string(acmd.dataBuffer[begin : begin+l]) + list = append(list, s) + } + + return list +} diff --git a/admin_policy.go b/admin_policy.go index f300db7d..ecf2509f 100644 --- a/admin_policy.go +++ b/admin_policy.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/aerospike.go b/aerospike.go index e66d4277..ccf058d6 100644 --- a/aerospike.go +++ b/aerospike.go @@ -1 +1,16 @@ +// Copyright 2013-2020 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 provides a client to connect and interact with an Aerospike cluster. package aerospike diff --git a/aerospike_bench_reflect_test.go b/aerospike_bench_reflect_test.go index 751931ff..9d4a3b9c 100644 --- a/aerospike_bench_reflect_test.go +++ b/aerospike_bench_reflect_test.go @@ -1,6 +1,6 @@ // +build !as_performance -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/aerospike_bench_test.go b/aerospike_bench_test.go index 98831297..49d29d02 100644 --- a/aerospike_bench_test.go +++ b/aerospike_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/aerospike_suite_test.go b/aerospike_suite_test.go index 9919c718..1319e302 100644 --- a/aerospike_suite_test.go +++ b/aerospike_suite_test.go @@ -1,3 +1,17 @@ +// Copyright 2013-2020 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 ( @@ -17,17 +31,19 @@ import ( asl "github.com/aerospike/aerospike-client-go/logger" ) -var host = flag.String("h", "127.0.0.1", "Aerospike server seed hostnames or IP addresses") -var port = flag.Int("p", 3000, "Aerospike server seed hostname or IP address port number.") -var user = flag.String("U", "", "Username.") -var password = flag.String("P", "", "Password.") -var authMode = flag.String("A", "internal", "Authentication mode: internal | external") -var clientPolicy *as.ClientPolicy -var client *as.Client -var useReplicas = flag.Bool("use-replicas", false, "Aerospike will use replicas as well as master partitions.") -var debug = flag.Bool("debug", false, "Will set the logging level to DEBUG.") - -var namespace = flag.String("n", "test", "Namespace") +var ( + host = flag.String("h", "127.0.0.1", "Aerospike server seed hostnames or IP addresses") + port = flag.Int("p", 3000, "Aerospike server seed hostname or IP address port number.") + user = flag.String("U", "", "Username.") + password = flag.String("P", "", "Password.") + authMode = flag.String("A", "internal", "Authentication mode: internal | external") + useReplicas = flag.Bool("use-replicas", false, "Aerospike will use replicas as well as master partitions.") + debug = flag.Bool("debug", false, "Will set the logging level to DEBUG.") + namespace = flag.String("n", "test", "Namespace") + + clientPolicy *as.ClientPolicy + client *as.Client +) func initTestVars() { var buf bytes.Buffer diff --git a/auth_mode.go b/auth_mode.go index 3536e78e..599b345c 100644 --- a/auth_mode.go +++ b/auth_mode.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/batch_command.go b/batch_command.go new file mode 100644 index 00000000..b48e82b8 --- /dev/null +++ b/batch_command.go @@ -0,0 +1,106 @@ +// Copyright 2013-2020 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 "time" + +type batcher interface { + command + + cloneBatchCommand(batch *batchNode) batcher + filteredOut() int + + retryBatch(ifc batcher, cluster *Cluster, deadline time.Time, iteration, commandSentCounter int) (bool, error) + generateBatchNodes(*Cluster) ([]*batchNode, error) + setSequence(int, int) +} + +type batchCommand struct { + baseMultiCommand + + batch *batchNode + policy *BatchPolicy + sequenceAP int + sequenceSC int + + filteredOutCnt int +} + +func (cmd *batchCommand) prepareRetry(ifc command, isTimeout bool) bool { + if !(cmd.policy.ReplicaPolicy == SEQUENCE || cmd.policy.ReplicaPolicy == PREFER_RACK) { + // Perform regular retry to same node. + return true + } + + cmd.sequenceAP++ + + if !isTimeout || cmd.policy.ReadModeSC != ReadModeSCLinearize { + cmd.sequenceSC++ + } + return false +} + +func (cmd *batchCommand) retryBatch(ifc batcher, cluster *Cluster, deadline time.Time, iteration, commandSentCounter int) (bool, error) { + // Retry requires keys for this node to be split among other nodes. + // This is both recursive and exponential. + batchNodes, err := ifc.generateBatchNodes(cluster) + if err != nil { + return false, err + } + + if len(batchNodes) == 1 && batchNodes[0].Node == cmd.batch.Node { + // Batch node is the same. Go through normal retry. + return false, nil + } + + // Run batch requests sequentially in same thread. + for _, batchNode := range batchNodes { + command := ifc.cloneBatchCommand(batchNode) + command.setSequence(cmd.sequenceAP, cmd.sequenceSC) + if err := command.executeAt(command, cmd.policy.GetBasePolicy(), true, deadline, iteration, commandSentCounter); err != nil { + return false, err + } + } + + return true, nil +} + +func (cmd *batchCommand) setSequence(ap, sc int) { + cmd.sequenceAP, cmd.sequenceSC = ap, sc +} + +func (cmd *batchCommand) getPolicy(ifc command) Policy { + return cmd.policy +} + +func (cmd *batchCommand) Execute() error { + return cmd.execute(cmd, true) +} + +func (cmd *batchCommand) filteredOut() int { + return cmd.filteredOutCnt +} + +func (cmd *batchCommand) generateBatchNodes(cluster *Cluster) ([]*batchNode, error) { + panic("Unreachable") +} + +func (cmd *batchCommand) cloneBatchCommand(batch *batchNode) batcher { + panic("Unreachable") +} + +func (cmd *batchCommand) writeBuffer(ifc command) error { + panic("Unreachable") +} diff --git a/batch_command_exists.go b/batch_command_exists.go index 0bc95161..4ea771d7 100644 --- a/batch_command_exists.go +++ b/batch_command_exists.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,51 +22,41 @@ import ( ) type batchCommandExists struct { - baseMultiCommand - - batchNamespace *batchNamespace - batch *batchNode - policy *BatchPolicy - keys []*Key - existsArray []bool - index int - filteredOutCnt int + batchCommand + + keys []*Key + existsArray []bool + index int } func newBatchCommandExists( node *Node, - batchNamespace *batchNamespace, batch *batchNode, policy *BatchPolicy, keys []*Key, existsArray []bool, ) *batchCommandExists { res := &batchCommandExists{ - baseMultiCommand: *newMultiCommand(node, nil), - batchNamespace: batchNamespace, - policy: policy, - keys: keys, - existsArray: existsArray, + batchCommand: batchCommand{ + baseMultiCommand: *newMultiCommand(node, nil), + policy: policy, + batch: batch, + }, + keys: keys, + existsArray: existsArray, } res.oneShot = false return res } -func (cmd *batchCommandExists) filteredOut() int { return cmd.filteredOutCnt } - -func (cmd *batchCommandExists) cloneBatchCommand(batch *batchNode, bns *batchNamespace) command { +func (cmd *batchCommandExists) cloneBatchCommand(batch *batchNode) batcher { res := *cmd res.node = batch.Node res.batch = batch - res.batchNamespace = bns return &res } -func (cmd *batchCommandExists) getPolicy(ifc command) Policy { - return cmd.policy -} - func (cmd *batchCommandExists) writeBuffer(ifc command) error { return cmd.setBatchIndexReadCompat(cmd.policy, cmd.keys, cmd.batch, nil, _INFO1_READ|_INFO1_NOBINDATA) } @@ -132,3 +122,7 @@ func (cmd *batchCommandExists) parseRecordResults(ifc command, receiveSize int) func (cmd *batchCommandExists) Execute() error { return cmd.execute(cmd, true) } + +func (cmd *batchCommandExists) generateBatchNodes(cluster *Cluster) ([]*batchNode, error) { + return newBatchNodeListKeys(cluster, cmd.policy, cmd.keys, cmd.sequenceAP, cmd.sequenceSC, cmd.batch) +} diff --git a/batch_command_get.go b/batch_command_get.go index 8d471d1c..f73867dd 100644 --- a/batch_command_get.go +++ b/batch_command_get.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,25 +21,16 @@ import ( Buffer "github.com/aerospike/aerospike-client-go/utils/buffer" ) -type batcher interface { - cloneBatchCommand(batch *batchNode, bns *batchNamespace) command - filteredOut() int -} - type batchCommandGet struct { - baseMultiCommand - - batchNamespace *batchNamespace - batch *batchNode - policy *BatchPolicy - keys []*Key - binNames []string - records []*Record - indexRecords []*BatchRead - readAttr int - index int - key Key - filteredOutCnt int + batchCommand + + keys []*Key + binNames []string + records []*Record + indexRecords []*BatchRead + readAttr int + index int + key Key // pointer to the object that's going to be unmarshalled objects []*reflect.Value @@ -59,7 +50,6 @@ var batchObjectParser func( func newBatchCommandGet( node *Node, - batchNamespace *batchNamespace, batch *batchNode, policy *BatchPolicy, keys []*Key, @@ -68,36 +58,28 @@ func newBatchCommandGet( readAttr int, ) *batchCommandGet { res := &batchCommandGet{ - baseMultiCommand: *newMultiCommand(node, nil), - batchNamespace: batchNamespace, - batch: batch, - policy: policy, - keys: keys, - binNames: binNames, - records: records, - readAttr: readAttr, + batchCommand: batchCommand{ + baseMultiCommand: *newMultiCommand(node, nil), + policy: policy, + batch: batch, + }, + keys: keys, + binNames: binNames, + records: records, + readAttr: readAttr, } res.oneShot = false return res } -func (cmd *batchCommandGet) filteredOut() int { - return cmd.filteredOutCnt -} - -func (cmd *batchCommandGet) cloneBatchCommand(batch *batchNode, bns *batchNamespace) command { +func (cmd *batchCommandGet) cloneBatchCommand(batch *batchNode) batcher { res := *cmd res.node = batch.Node res.batch = batch - res.batchNamespace = bns return &res } -func (cmd *batchCommandGet) getPolicy(ifc command) Policy { - return cmd.policy -} - func (cmd *batchCommandGet) writeBuffer(ifc command) error { return cmd.setBatchIndexReadCompat(cmd.policy, cmd.keys, cmd.batch, cmd.binNames, cmd.readAttr) } @@ -246,3 +228,7 @@ func (cmd *batchCommandGet) parseRecord(key *Key, opCount int, generation, expir func (cmd *batchCommandGet) Execute() error { return cmd.execute(cmd, true) } + +func (cmd *batchCommandGet) generateBatchNodes(cluster *Cluster) ([]*batchNode, error) { + return newBatchNodeListKeys(cluster, cmd.policy, cmd.keys, cmd.sequenceAP, cmd.sequenceSC, cmd.batch) +} diff --git a/batch_command_get_reflect.go b/batch_command_get_reflect.go index 731e851a..b15998df 100644 --- a/batch_command_get_reflect.go +++ b/batch_command_get_reflect.go @@ -1,6 +1,6 @@ // +build !as_performance -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/batch_command_reflect.go b/batch_command_reflect.go index 626a4b7f..9a7a3412 100644 --- a/batch_command_reflect.go +++ b/batch_command_reflect.go @@ -1,6 +1,6 @@ // +build !as_performance -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/batch_index_command_get.go b/batch_index_command_get.go index ceb4ff43..7460af43 100644 --- a/batch_index_command_get.go +++ b/batch_index_command_get.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,14 +14,8 @@ package aerospike -type batchIndexer interface { - cloneBatchIndexCommand(batch *batchNode) command -} - type batchIndexCommandGet struct { batchCommandGet - - batch *batchNode } func newBatchIndexCommandGet( @@ -36,29 +30,25 @@ func newBatchIndexCommandGet( return &batchIndexCommandGet{ batchCommandGet{ - baseMultiCommand: *newMultiCommand(node, nil), - batchNamespace: nil, - policy: policy, - records: nil, - indexRecords: records, + batchCommand: batchCommand{ + baseMultiCommand: *newMultiCommand(node, nil), + policy: policy, + batch: batch, + }, + records: nil, + indexRecords: records, }, - batch, } } -func (cmd *batchIndexCommandGet) cloneBatchIndexCommand(batch *batchNode) command { +func (cmd *batchIndexCommandGet) cloneBatchCommand(batch *batchNode) batcher { res := *cmd res.batch = batch res.node = batch.Node - res.batchNamespace = nil return &res } -func (cmd *batchIndexCommandGet) getPolicy(ifc command) Policy { - return cmd.policy -} - func (cmd *batchIndexCommandGet) writeBuffer(ifc command) error { return cmd.setBatchIndexRead(cmd.policy, cmd.indexRecords, cmd.batch) } @@ -66,3 +56,7 @@ func (cmd *batchIndexCommandGet) writeBuffer(ifc command) error { func (cmd *batchIndexCommandGet) Execute() error { return cmd.execute(cmd, true) } + +func (cmd *batchIndexCommandGet) generateBatchNodes(cluster *Cluster) ([]*batchNode, error) { + return newBatchNodeListRecords(cluster, cmd.policy, cmd.indexRecords, cmd.sequenceAP, cmd.sequenceSC, cmd.batch) +} diff --git a/batch_node.go b/batch_node.go index f1775260..c8316721 100644 --- a/batch_node.go +++ b/batch_node.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,8 +21,6 @@ import ( type batchNode struct { Node *Node offsets []int - // offsetsSize int - BatchNamespaces []*batchNamespace } func newBatchNodeList(cluster *Cluster, policy *BatchPolicy, keys []*Key) ([]*batchNode, error) { @@ -41,15 +39,14 @@ func newBatchNodeList(cluster *Cluster, policy *BatchPolicy, keys []*Key) ([]*ba keysPerNode = 10 } + replicaPolicy := policy.ReplicaPolicy + replicaPolicySC := GetReplicaPolicySC(policy.GetBasePolicy()) + // Split keys by server node. batchNodes := make([]*batchNode, 0, len(nodes)) for i := range keys { - partition := NewPartitionByKey(keys[i]) - - // error not required - seq := 0 - node, err := cluster.getReadNode(partition, policy.ReplicaPolicy, &seq) + node, err := GetNodeBatchRead(cluster, keys[i], replicaPolicy, replicaPolicySC, 0, 0) if err != nil { return nil, err } @@ -63,7 +60,7 @@ func newBatchNodeList(cluster *Cluster, policy *BatchPolicy, keys []*Key) ([]*ba return batchNodes, nil } -func newBatchIndexNodeList(cluster *Cluster, policy *BatchPolicy, records []*BatchRead) ([]*batchNode, error) { +func newBatchNodeListKeys(cluster *Cluster, policy *BatchPolicy, keys []*Key, sequenceAP, sequenceSC int, batchSeed *batchNode) ([]*batchNode, error) { nodes := cluster.GetNodes() if len(nodes) == 0 { @@ -71,7 +68,7 @@ func newBatchIndexNodeList(cluster *Cluster, policy *BatchPolicy, records []*Bat } // Create initial key capacity for each node as average + 25%. - keysPerNode := len(records) / len(nodes) + keysPerNode := len(keys) / len(nodes) keysPerNode += keysPerNode / 2 // The minimum key capacity is 10. @@ -79,15 +76,14 @@ func newBatchIndexNodeList(cluster *Cluster, policy *BatchPolicy, records []*Bat keysPerNode = 10 } + replicaPolicy := policy.ReplicaPolicy + replicaPolicySC := GetReplicaPolicySC(policy.GetBasePolicy()) + // Split keys by server node. batchNodes := make([]*batchNode, 0, len(nodes)) - for i := range records { - partition := NewPartitionByKey(records[i].Key) - - // error not required - seq := 0 - node, err := cluster.getReadNode(partition, policy.ReplicaPolicy, &seq) + for i, offset := range batchSeed.offsets { + node, err := GetNodeBatchRead(cluster, keys[offset], replicaPolicy, replicaPolicySC, sequenceAP, sequenceSC) if err != nil { return nil, err } @@ -101,100 +97,100 @@ func newBatchIndexNodeList(cluster *Cluster, policy *BatchPolicy, records []*Bat return batchNodes, nil } -func newBatchNode(node *Node, capacity int, offset int) *batchNode { - res := &batchNode{ - Node: node, - offsets: make([]int, 1, capacity), - // offsetsSize: 1, - BatchNamespaces: nil, //[]*batchNamespace{newBatchNamespace(namespace, keyCapacity, offset)}, +func newBatchNodeListRecords(cluster *Cluster, policy *BatchPolicy, records []*BatchRead, sequenceAP, sequenceSC int, batchSeed *batchNode) ([]*batchNode, error) { + nodes := cluster.GetNodes() + + if len(nodes) == 0 { + return nil, NewAerospikeError(SERVER_NOT_AVAILABLE, "command failed because cluster is empty.") } - res.offsets[0] = offset - return res -} + // Create initial key capacity for each node as average + 25%. + keysPerNode := len(batchSeed.offsets) / len(nodes) + keysPerNode += keysPerNode / 2 -func (bn *batchNode) AddKey(offset int) { - bn.offsets = append(bn.offsets, offset) - // bn.offsetsSize++ -} + // The minimum key capacity is 10. + if keysPerNode < 10 { + keysPerNode = 10 + } + + replicaPolicy := policy.ReplicaPolicy + replicaPolicySC := GetReplicaPolicySC(policy.GetBasePolicy()) + + // Split keys by server node. + batchNodes := make([]*batchNode, 0, len(nodes)) -func (bn *batchNode) findNamespace(batchNamespaces []*batchNamespace, ns string) *batchNamespace { - for _, batchNamespace := range batchNamespaces { - // Note: use both pointer equality and equals. - if batchNamespace.namespace == ns { - return batchNamespace + for i, offset := range batchSeed.offsets { + node, err := GetNodeBatchRead(cluster, records[offset].Key, replicaPolicy, replicaPolicySC, sequenceAP, sequenceSC) + if err != nil { + return nil, err } - } - return nil -} -func findBatchNode(nodes []*batchNode, node *Node) *batchNode { - for i := range nodes { - // Note: using pointer equality for performance. - if nodes[i].Node == node { - return nodes[i] + if batchNode := findBatchNode(batchNodes, node); batchNode == nil { + batchNodes = append(batchNodes, newBatchNode(node, keysPerNode, i)) + } else { + batchNode.AddKey(i) } } - return nil + return batchNodes, nil } -func (bn *batchNode) splitByNamespace(keys []*Key) { - first := keys[bn.offsets[0]].namespace +func newBatchIndexNodeList(cluster *Cluster, policy *BatchPolicy, records []*BatchRead) ([]*batchNode, error) { + nodes := cluster.GetNodes() - // Optimize for single namespace. - if bn.isSingleNamespace(keys, first) { - bn.BatchNamespaces = []*batchNamespace{newBatchNamespace(first, bn.offsets)} - return + if len(nodes) == 0 { + return nil, NewAerospikeError(SERVER_NOT_AVAILABLE, "command failed because cluster is empty.") } - // Process multiple namespaces. - bn.BatchNamespaces = make([]*batchNamespace, 0, 4) - - for i := range bn.offsets { - offset := bn.offsets[i] - ns := keys[offset].namespace - batchNamespace := bn.findNamespace(bn.BatchNamespaces, ns) + // Create initial key capacity for each node as average + 25%. + keysPerNode := len(records) / len(nodes) + keysPerNode += keysPerNode / 2 - if batchNamespace == nil { - bn.BatchNamespaces = append(bn.BatchNamespaces, newBatchNamespaceDirect(ns, len(bn.offsets), offset)) - } else { - batchNamespace.add(offset) - } + // The minimum key capacity is 10. + if keysPerNode < 10 { + keysPerNode = 10 } -} -func (bn *batchNode) isSingleNamespace(keys []*Key, first string) bool { - for i := range bn.offsets { - ns := keys[bn.offsets[i]].namespace + replicaPolicy := policy.ReplicaPolicy + replicaPolicySC := GetReplicaPolicySC(policy.GetBasePolicy()) + + // Split keys by server node. + batchNodes := make([]*batchNode, 0, len(nodes)) - if ns != first { - return false + for i := range records { + node, err := GetNodeBatchRead(cluster, records[i].Key, replicaPolicy, replicaPolicySC, 0, 0) + if err != nil { + return nil, err } - } - return true -} -type batchNamespace struct { - namespace string - offsets []int + if batchNode := findBatchNode(batchNodes, node); batchNode == nil { + batchNodes = append(batchNodes, newBatchNode(node, keysPerNode, i)) + } else { + batchNode.AddKey(i) + } + } + return batchNodes, nil } -func newBatchNamespaceDirect(namespace string, capacity, offset int) *batchNamespace { - res := &batchNamespace{ - namespace: namespace, - offsets: make([]int, 1, capacity), +func newBatchNode(node *Node, capacity int, offset int) *batchNode { + res := &batchNode{ + Node: node, + offsets: make([]int, 1, capacity), } + res.offsets[0] = offset return res } -func newBatchNamespace(namespace string, offsets []int) *batchNamespace { - return &batchNamespace{ - namespace: namespace, - offsets: offsets, - } +func (bn *batchNode) AddKey(offset int) { + bn.offsets = append(bn.offsets, offset) } -func (bn *batchNamespace) add(offset int) { - bn.offsets = append(bn.offsets, offset) +func findBatchNode(nodes []*batchNode, node *Node) *batchNode { + for i := range nodes { + // Note: using pointer equality for performance. + if nodes[i].Node == node { + return nodes[i] + } + } + return nil } diff --git a/batch_policy.go b/batch_policy.go index 65ae5ea9..ff42322a 100644 --- a/batch_policy.go +++ b/batch_policy.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/batch_read.go b/batch_read.go index ad5b4e85..ff01a305 100644 --- a/batch_read.go +++ b/batch_read.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bench_batchget_test.go b/bench_batchget_test.go index 2fbbf8c0..8e4c36cd 100644 --- a/bench_batchget_test.go +++ b/bench_batchget_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bench_cdt_list_test.go b/bench_cdt_list_test.go index b598d5ce..4d52ac4d 100644 --- a/bench_cdt_list_test.go +++ b/bench_cdt_list_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bench_packing_test.go b/bench_packing_test.go index 8b707dd0..e898c2f8 100644 --- a/bench_packing_test.go +++ b/bench_packing_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bench_rand_gen_test.go b/bench_rand_gen_test.go index e4fd5ee3..9052eb61 100644 --- a/bench_rand_gen_test.go +++ b/bench_rand_gen_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bench_read_command_test.go b/bench_read_command_test.go index 345ad661..e8397134 100644 --- a/bench_read_command_test.go +++ b/bench_read_command_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ import ( ) func doGet(set string, value interface{}, b *testing.B) { - var err error policy := NewPolicy() dataBuffer := make([]byte, 1024*1024) @@ -35,7 +34,10 @@ func doGet(set string, value interface{}, b *testing.B) { key, _ := NewKey("test", set, 1000) for i := 0; i < b.N; i++ { - command := newReadCommand(nil, policy, key, binNames) + command, err := newReadCommand(nil, policy, key, binNames, nil) + if err != nil { + panic(err) + } command.baseCommand.dataBuffer = dataBuffer err = command.writeBuffer(&command) if err != nil { diff --git a/bench_values_test.go b/bench_values_test.go index 86b951e6..6f0aebde 100644 --- a/bench_values_test.go +++ b/bench_values_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bench_write_command_test.go b/bench_write_command_test.go index 3f104f46..25fc850e 100644 --- a/bench_write_command_test.go +++ b/bench_write_command_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ import ( ) func doPut(set string, value interface{}, b *testing.B) { - var err error policy := NewWritePolicy(0, 0) dataBuffer := make([]byte, 1024*1024) @@ -35,9 +34,12 @@ func doPut(set string, value interface{}, b *testing.B) { key, _ := NewKey("test", set, 1000) for i := 0; i < b.N; i++ { - command := newWriteCommand(nil, policy, key, bins, nil, _WRITE) + command, err := newWriteCommand(nil, policy, key, bins, nil, _WRITE) + if err != nil { + panic(err) + } command.baseCommand.dataBuffer = dataBuffer - err = command.writeBuffer(command) + err = command.writeBuffer(&command) if err != nil { panic(err) } diff --git a/bin.go b/bin.go index 40a7aaed..787ac05e 100644 --- a/bin.go +++ b/bin.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bit_overflow_action.go b/bit_overflow_action.go index d4802fc4..4fb5f3f7 100644 --- a/bit_overflow_action.go +++ b/bit_overflow_action.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bit_policy.go b/bit_policy.go index a25e31d4..af0ff8d3 100644 --- a/bit_policy.go +++ b/bit_policy.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bit_resize_flags.go b/bit_resize_flags.go index 424fa741..900b7230 100644 --- a/bit_resize_flags.go +++ b/bit_resize_flags.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/bit_write_flags.go b/bit_write_flags.go index 9640784b..643ee93a 100644 --- a/bit_write_flags.go +++ b/bit_write_flags.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/buffered_connection.go b/buffered_connection.go index b1004f26..175100dc 100644 --- a/buffered_connection.go +++ b/buffered_connection.go @@ -17,6 +17,7 @@ package aerospike import ( "fmt" + . "github.com/aerospike/aerospike-client-go/logger" . "github.com/aerospike/aerospike-client-go/types" ) @@ -93,7 +94,8 @@ func (bc *bufferedConn) readConn(minLength int) error { bc.remaining -= n if err != nil { - return fmt.Errorf("Requested to read %d bytes, but %d was read. (%v)", minLength, n, err) + Logger.Debug("Requested to read %d bytes, but %d was read. (%v)", minLength, n, err) + return err } return nil diff --git a/bytes_buffer.go b/bytes_buffer.go index ee021691..db27d8ef 100644 --- a/bytes_buffer.go +++ b/bytes_buffer.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cdt.go b/cdt.go index 8f511cf0..bdfce72a 100644 --- a/cdt.go +++ b/cdt.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Portions may be licensed to Aerospike, Inc. under one or more contributor // license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. diff --git a/cdt_bitwise_test.go b/cdt_bitwise_test.go index d56a702b..09dc4c5b 100644 --- a/cdt_bitwise_test.go +++ b/cdt_bitwise_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cdt_list.go b/cdt_list.go index 93094fd8..75a349b9 100644 --- a/cdt_list.go +++ b/cdt_list.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cdt_list_test.go b/cdt_list_test.go index 11876727..ae6065db 100644 --- a/cdt_list_test.go +++ b/cdt_list_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/cdt_map.go b/cdt_map.go index d86d8f4e..a3587394 100644 --- a/cdt_map.go +++ b/cdt_map.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Portions may be licensed to Aerospike, Inc. under one or more contributor // license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. diff --git a/cdt_map_test.go b/cdt_map_test.go index 91c82215..785975f2 100644 --- a/cdt_map_test.go +++ b/cdt_map_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/client.go b/client.go index 003e305c..ee1d1281 100644 --- a/client.go +++ b/client.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package aerospike import ( "bytes" + "context" "encoding/base64" "encoding/json" "errors" @@ -27,6 +28,8 @@ import ( "sync" "time" + "golang.org/x/sync/semaphore" + . "github.com/aerospike/aerospike-client-go/internal/atomic" . "github.com/aerospike/aerospike-client-go/logger" . "github.com/aerospike/aerospike-client-go/types" @@ -143,7 +146,11 @@ func (clnt *Client) GetNodeNames() []string { // If the policy is nil, the default relevant policy will be used. func (clnt *Client) Put(policy *WritePolicy, key *Key, binMap BinMap) error { policy = clnt.getUsableWritePolicy(policy) - command := newWriteCommand(clnt.cluster, policy, key, nil, binMap, _WRITE) + command, err := newWriteCommand(clnt.cluster, policy, key, nil, binMap, _WRITE) + if err != nil { + return err + } + return command.Execute() } @@ -154,7 +161,11 @@ func (clnt *Client) Put(policy *WritePolicy, key *Key, binMap BinMap) error { // If the policy is nil, the default relevant policy will be used. func (clnt *Client) PutBins(policy *WritePolicy, key *Key, bins ...*Bin) error { policy = clnt.getUsableWritePolicy(policy) - command := newWriteCommand(clnt.cluster, policy, key, bins, nil, _WRITE) + command, err := newWriteCommand(clnt.cluster, policy, key, bins, nil, _WRITE) + if err != nil { + return err + } + return command.Execute() } @@ -169,14 +180,22 @@ func (clnt *Client) PutBins(policy *WritePolicy, key *Key, bins ...*Bin) error { // If the policy is nil, the default relevant policy will be used. func (clnt *Client) Append(policy *WritePolicy, key *Key, binMap BinMap) error { policy = clnt.getUsableWritePolicy(policy) - command := newWriteCommand(clnt.cluster, policy, key, nil, binMap, _APPEND) + command, err := newWriteCommand(clnt.cluster, policy, key, nil, binMap, _APPEND) + if err != nil { + return err + } + return command.Execute() } // AppendBins works the same as Append, but avoids BinMap allocation and iteration. func (clnt *Client) AppendBins(policy *WritePolicy, key *Key, bins ...*Bin) error { policy = clnt.getUsableWritePolicy(policy) - command := newWriteCommand(clnt.cluster, policy, key, bins, nil, _APPEND) + command, err := newWriteCommand(clnt.cluster, policy, key, bins, nil, _APPEND) + if err != nil { + return err + } + return command.Execute() } @@ -187,14 +206,22 @@ func (clnt *Client) AppendBins(policy *WritePolicy, key *Key, bins ...*Bin) erro // If the policy is nil, the default relevant policy will be used. func (clnt *Client) Prepend(policy *WritePolicy, key *Key, binMap BinMap) error { policy = clnt.getUsableWritePolicy(policy) - command := newWriteCommand(clnt.cluster, policy, key, nil, binMap, _PREPEND) + command, err := newWriteCommand(clnt.cluster, policy, key, nil, binMap, _PREPEND) + if err != nil { + return err + } + return command.Execute() } // PrependBins works the same as Prepend, but avoids BinMap allocation and iteration. func (clnt *Client) PrependBins(policy *WritePolicy, key *Key, bins ...*Bin) error { policy = clnt.getUsableWritePolicy(policy) - command := newWriteCommand(clnt.cluster, policy, key, bins, nil, _PREPEND) + command, err := newWriteCommand(clnt.cluster, policy, key, bins, nil, _PREPEND) + if err != nil { + return err + } + return command.Execute() } @@ -209,14 +236,22 @@ func (clnt *Client) PrependBins(policy *WritePolicy, key *Key, bins ...*Bin) err // If the policy is nil, the default relevant policy will be used. func (clnt *Client) Add(policy *WritePolicy, key *Key, binMap BinMap) error { policy = clnt.getUsableWritePolicy(policy) - command := newWriteCommand(clnt.cluster, policy, key, nil, binMap, _ADD) + command, err := newWriteCommand(clnt.cluster, policy, key, nil, binMap, _ADD) + if err != nil { + return err + } + return command.Execute() } // AddBins works the same as Add, but avoids BinMap allocation and iteration. func (clnt *Client) AddBins(policy *WritePolicy, key *Key, bins ...*Bin) error { policy = clnt.getUsableWritePolicy(policy) - command := newWriteCommand(clnt.cluster, policy, key, bins, nil, _ADD) + command, err := newWriteCommand(clnt.cluster, policy, key, bins, nil, _ADD) + if err != nil { + return err + } + return command.Execute() } @@ -229,8 +264,12 @@ func (clnt *Client) AddBins(policy *WritePolicy, key *Key, bins ...*Bin) error { // If the policy is nil, the default relevant policy will be used. func (clnt *Client) Delete(policy *WritePolicy, key *Key) (bool, error) { policy = clnt.getUsableWritePolicy(policy) - command := newDeleteCommand(clnt.cluster, policy, key) - err := command.Execute() + command, err := newDeleteCommand(clnt.cluster, policy, key) + if err != nil { + return false, err + } + + err = command.Execute() return command.Existed(), err } @@ -244,7 +283,11 @@ func (clnt *Client) Delete(policy *WritePolicy, key *Key) (bool, error) { // If the record doesn't exist, it will return an error. func (clnt *Client) Touch(policy *WritePolicy, key *Key) error { policy = clnt.getUsableWritePolicy(policy) - command := newTouchCommand(clnt.cluster, policy, key) + command, err := newTouchCommand(clnt.cluster, policy, key) + if err != nil { + return err + } + return command.Execute() } @@ -257,8 +300,12 @@ func (clnt *Client) Touch(policy *WritePolicy, key *Key) error { // If the policy is nil, the default relevant policy will be used. func (clnt *Client) Exists(policy *BasePolicy, key *Key) (bool, error) { policy = clnt.getUsablePolicy(policy) - command := newExistsCommand(clnt.cluster, policy, key) - err := command.Execute() + command, err := newExistsCommand(clnt.cluster, policy, key) + if err != nil { + return false, err + } + + err = command.Execute() return command.Exists(), err } @@ -273,9 +320,14 @@ func (clnt *Client) BatchExists(policy *BatchPolicy, keys []*Key) ([]bool, error // when a key exists, the corresponding index will be marked true existsArray := make([]bool, len(keys)) + batchNodes, err := newBatchNodeList(clnt.cluster, policy, keys) + if err != nil { + return nil, err + } + // pass nil to make sure it will be cloned and prepared - cmd := newBatchCommandExists(nil, nil, nil, policy, keys, existsArray) - err, filteredOut := clnt.batchExecute(policy, keys, cmd) + cmd := newBatchCommandExists(nil, nil, policy, keys, existsArray) + err, filteredOut := clnt.batchExecute(policy, batchNodes, cmd) if err != nil { return nil, err } @@ -297,7 +349,11 @@ func (clnt *Client) BatchExists(policy *BatchPolicy, keys []*Key) ([]bool, error func (clnt *Client) Get(policy *BasePolicy, key *Key, binNames ...string) (*Record, error) { policy = clnt.getUsablePolicy(policy) - command := newReadCommand(clnt.cluster, policy, key, binNames) + command, err := newReadCommand(clnt.cluster, policy, key, binNames, nil) + if err != nil { + return nil, err + } + if err := command.Execute(); err != nil { return nil, err } @@ -311,7 +367,11 @@ func (clnt *Client) Get(policy *BasePolicy, key *Key, binNames ...string) (*Reco func (clnt *Client) GetHeader(policy *BasePolicy, key *Key) (*Record, error) { policy = clnt.getUsablePolicy(policy) - command := newReadHeaderCommand(clnt.cluster, policy, key) + command, err := newReadHeaderCommand(clnt.cluster, policy, key) + if err != nil { + return nil, err + } + if err := command.Execute(); err != nil { return nil, err } @@ -334,8 +394,13 @@ func (clnt *Client) BatchGet(policy *BatchPolicy, keys []*Key, binNames ...strin // when a key exists, the corresponding index will be set to record records := make([]*Record, len(keys)) - cmd := newBatchCommandGet(nil, nil, nil, policy, keys, binNames, records, _INFO1_READ) - err, filteredOut := clnt.batchExecute(policy, keys, cmd) + batchNodes, err := newBatchNodeList(clnt.cluster, policy, keys) + if err != nil { + return nil, err + } + + cmd := newBatchCommandGet(nil, nil, policy, keys, binNames, records, _INFO1_READ) + err, filteredOut := clnt.batchExecute(policy, batchNodes, cmd) if err != nil && !policy.AllowPartialResults { return nil, err } @@ -362,7 +427,12 @@ func (clnt *Client) BatchGetComplex(policy *BatchPolicy, records []*BatchRead) e cmd := newBatchIndexCommandGet(nil, policy, records) - err, filteredOut := clnt.batchIndexExecute(policy, records, cmd) + batchNodes, err := newBatchIndexNodeList(clnt.cluster, policy, records) + if err != nil { + return err + } + + err, filteredOut := clnt.batchExecute(policy, batchNodes, cmd) if err != nil && !policy.AllowPartialResults { return err } @@ -390,8 +460,13 @@ func (clnt *Client) BatchGetHeader(policy *BatchPolicy, keys []*Key) ([]*Record, // when a key exists, the corresponding index will be set to record records := make([]*Record, len(keys)) - cmd := newBatchCommandGet(nil, nil, nil, policy, keys, nil, records, _INFO1_READ|_INFO1_NOBINDATA) - err, filteredOut := clnt.batchExecute(policy, keys, cmd) + batchNodes, err := newBatchNodeList(clnt.cluster, policy, keys) + if err != nil { + return nil, err + } + + cmd := newBatchCommandGet(nil, nil, policy, keys, nil, records, _INFO1_READ|_INFO1_NOBINDATA) + err, filteredOut := clnt.batchExecute(policy, batchNodes, cmd) if err != nil && !policy.AllowPartialResults { return nil, err } @@ -418,7 +493,11 @@ func (clnt *Client) BatchGetHeader(policy *BatchPolicy, keys []*Key) ([]*Record, // If the policy is nil, the default relevant policy will be used. func (clnt *Client) Operate(policy *WritePolicy, key *Key, operations ...*Operation) (*Record, error) { policy = clnt.getUsableWritePolicy(policy) - command := newOperateCommand(clnt.cluster, policy, key, operations) + command, err := newOperateCommand(clnt.cluster, policy, key, operations) + if err != nil { + return nil, err + } + if err := command.Execute(); err != nil { return nil, err } @@ -663,7 +742,11 @@ func (clnt *Client) ListUDF(policy *BasePolicy) ([]*UDF, error) { // If the policy is nil, the default relevant policy will be used. func (clnt *Client) Execute(policy *WritePolicy, key *Key, packageName string, functionName string, args ...Value) (interface{}, error) { policy = clnt.getUsableWritePolicy(policy) - command := newExecuteCommand(clnt.cluster, policy, key, packageName, functionName, NewValueArray(args)) + command, err := newExecuteCommand(clnt.cluster, policy, key, packageName, functionName, NewValueArray(args)) + if err != nil { + return nil, err + } + if err := command.Execute(); err != nil { return nil, err } @@ -939,7 +1022,7 @@ func (clnt *Client) CreateComplexIndex( } response := responseMap[strCmd.String()] - if strings.ToUpper(response) == "OK" { + if strings.EqualFold(response, "OK") { // Return task that could optionally be polled for completion. return NewIndexTask(clnt.cluster, namespace, indexName), nil } @@ -981,7 +1064,7 @@ func (clnt *Client) DropIndex( response := responseMap[strCmd.String()] - if strings.ToUpper(response) == "OK" { + if strings.EqualFold(response, "OK") { // Return task that could optionally be polled for completion. task := NewDropIndexTask(clnt.cluster, namespace, indexName) return <-task.OnComplete() @@ -1049,7 +1132,7 @@ func (clnt *Client) Truncate(policy *WritePolicy, namespace, set string, beforeL } response := responseMap[strCmd.String()] - if strings.ToUpper(response) == "OK" { + if strings.EqualFold(response, "OK") { return nil } @@ -1161,11 +1244,11 @@ func (clnt *Client) QueryRoles(policy *AdminPolicy) ([]*Role, error) { } // CreateRole creates a user-defined role. -func (clnt *Client) CreateRole(policy *AdminPolicy, roleName string, privileges []Privilege) error { +func (clnt *Client) CreateRole(policy *AdminPolicy, roleName string, privileges []Privilege, whitelist []string) error { policy = clnt.getUsableAdminPolicy(policy) command := newAdminCommand(nil) - return command.createRole(clnt.cluster, policy, roleName, privileges) + return command.createRole(clnt.cluster, policy, roleName, privileges, whitelist) } // DropRole removes a user-defined role. @@ -1192,6 +1275,14 @@ func (clnt *Client) RevokePrivileges(policy *AdminPolicy, roleName string, privi return command.revokePrivileges(clnt.cluster, policy, roleName, privileges) } +// SetWhitelist sets IP address whitelist for a role. If whitelist is nil or empty, it removes existing whitelist from role. +func (clnt *Client) SetWhitelist(policy *AdminPolicy, roleName string, whitelist []string) error { + policy = clnt.getUsableAdminPolicy(policy) + + command := newAdminCommand(nil) + return command.setWhitelist(clnt.cluster, policy, roleName, whitelist) +} + // Cluster exposes the cluster object to the user func (clnt *Client) Cluster() *Cluster { return clnt.cluster @@ -1269,12 +1360,7 @@ func (clnt *Client) sendInfoCommand(timeout time.Duration, command string) (map[ // batchExecute Uses sync.WaitGroup to run commands using multiple goroutines, // and waits for their return -func (clnt *Client) batchExecute(policy *BatchPolicy, keys []*Key, cmd batcher) (error, int) { - batchNodes, err := newBatchNodeList(clnt.cluster, policy, keys) - if err != nil { - return err, 0 - } - +func (clnt *Client) batchExecute(policy *BatchPolicy, batchNodes []*batchNode, cmd batcher) (error, int) { var wg sync.WaitGroup filteredOut := 0 @@ -1282,18 +1368,10 @@ func (clnt *Client) batchExecute(policy *BatchPolicy, keys []*Key, cmd batcher) errs := []error{} errm := new(sync.Mutex) - for _, batchNode := range batchNodes { - // There may be multiple goroutines for a single node because the - // wire protocol only allows one namespace per command. Multiple namespaces - // require multiple goroutines per node. - batchNode.splitByNamespace(keys) - - wg.Add(len(batchNode.BatchNamespaces)) - } - - for _, batchNode := range batchNodes { - for _, bns := range batchNode.BatchNamespaces { - newCmd := cmd.cloneBatchCommand(batchNode, bns) + wg.Add(len(batchNodes)) + if policy.ConcurrentNodes <= 0 { + for _, batchNode := range batchNodes { + newCmd := cmd.cloneBatchCommand(batchNode) go func(cmd command) { defer wg.Done() err := cmd.Execute() @@ -1305,41 +1383,33 @@ func (clnt *Client) batchExecute(policy *BatchPolicy, keys []*Key, cmd batcher) errm.Unlock() }(newCmd) } - } - - wg.Wait() - return mergeErrors(errs), filteredOut -} - -// batchExecute Uses sync.WaitGroup to run commands using multiple goroutines, -// and waits for their return -func (clnt *Client) batchIndexExecute(policy *BatchPolicy, records []*BatchRead, cmd batchIndexer) (error, int) { - batchNodes, err := newBatchIndexNodeList(clnt.cluster, policy, records) - if err != nil { - return err, 0 - } - - filteredOut := 0 - var wg sync.WaitGroup - - // Use a goroutine per namespace per node - errs := []error{} - errm := new(sync.Mutex) + } else { + sem := semaphore.NewWeighted(int64(policy.ConcurrentNodes)) + ctx := context.Background() - wg.Add(len(batchNodes)) - for _, batchNode := range batchNodes { - // copy to avoid race condition - newCmd := cmd.cloneBatchIndexCommand(batchNode) - go func(cmd command) { - defer wg.Done() - err := cmd.Execute() - errm.Lock() - if err != nil { - errs = append(errs, err) + for _, batchNode := range batchNodes { + if err := sem.Acquire(ctx, 1); err != nil { + errm.Lock() + if err != nil { + errs = append(errs, err) + } + errm.Unlock() + continue } - filteredOut += cmd.(batcher).filteredOut() - errm.Unlock() - }(newCmd) + + newCmd := cmd.cloneBatchCommand(batchNode) + go func(cmd command) { + defer sem.Release(1) + defer wg.Done() + err := cmd.Execute() + errm.Lock() + if err != nil { + errs = append(errs, err) + } + filteredOut += cmd.(batcher).filteredOut() + errm.Unlock() + }(newCmd) + } } wg.Wait() @@ -1412,9 +1482,13 @@ func (clnt *Client) getUsableAdminPolicy(policy *AdminPolicy) *AdminPolicy { // mergeErrors merges several errors into one func mergeErrors(errs []error) error { - if len(errs) == 0 { + switch len(errs) { + case 0: return nil + case 1: + return errs[0] } + var msg bytes.Buffer for _, err := range errs { if _, err := msg.WriteString(err.Error()); err != nil { diff --git a/client_appengine_exclusions.go b/client_appengine_exclusions.go index 155012d9..1b345587 100644 --- a/client_appengine_exclusions.go +++ b/client_appengine_exclusions.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/client_object_test.go b/client_object_test.go index 9e8bd888..532cde38 100644 --- a/client_object_test.go +++ b/client_object_test.go @@ -1,6 +1,6 @@ // +build !as_performance -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/client_policy.go b/client_policy.go index 5b30bb60..e0942b91 100644 --- a/client_policy.go +++ b/client_policy.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/client_reflect.go b/client_reflect.go index 16493c83..be8bd2a7 100644 --- a/client_reflect.go +++ b/client_reflect.go @@ -1,6 +1,6 @@ // +build !as_performance -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -50,7 +50,11 @@ func (clnt *Client) PutObject(policy *WritePolicy, key *Key, obj interface{}) (e policy = clnt.getUsableWritePolicy(policy) binMap := marshal(obj, clnt.cluster.supportsFloat.Get()) - command := newWriteCommand(clnt.cluster, policy, key, nil, binMap, _WRITE) + command, err := newWriteCommand(clnt.cluster, policy, key, nil, binMap, _WRITE) + if err != nil { + return err + } + res := command.Execute() return res } @@ -64,7 +68,11 @@ func (clnt *Client) GetObject(policy *BasePolicy, key *Key, obj interface{}) err rval := reflect.ValueOf(obj) binNames := objectMappings.getFields(rval.Type()) - command := newReadCommand(clnt.cluster, policy, key, binNames) + command, err := newReadCommand(clnt.cluster, policy, key, binNames, nil) + if err != nil { + return err + } + command.object = &rval return command.Execute() } @@ -101,11 +109,16 @@ func (clnt *Client) BatchGetObjects(policy *BatchPolicy, keys []*Key, objects [] } objectsFound := make([]bool, len(keys)) - cmd := newBatchCommandGet(nil, nil, nil, policy, keys, binNames, nil, _INFO1_READ) + cmd := newBatchCommandGet(nil, nil, policy, keys, binNames, nil, _INFO1_READ) cmd.objects = objectsVal cmd.objectsFound = objectsFound - err, filteredOut := clnt.batchExecute(policy, keys, cmd) + batchNodes, err := newBatchNodeList(clnt.cluster, policy, keys) + if err != nil { + return nil, err + } + + err, filteredOut := clnt.batchExecute(policy, batchNodes, cmd) if err != nil { return nil, err } diff --git a/client_reflect_test.go b/client_reflect_test.go index d0ddef46..f30428f5 100644 --- a/client_reflect_test.go +++ b/client_reflect_test.go @@ -1,6 +1,6 @@ // +build !as_performance -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/client_test.go b/client_test.go index ba7da50b..cb28f152 100644 --- a/client_test.go +++ b/client_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -152,15 +152,17 @@ var _ = Describe("Aerospike", func() { nclient, err := as.NewClientWithPolicy(&cpolicy, *host, *port) Expect(err).NotTo(HaveOccurred()) - replicaPolicy := as.PREFER_RACK + wpolicy := as.NewWritePolicy(0, 0) + wpolicy.ReplicaPolicy = as.PREFER_RACK for i := 0; i < 12; i++ { - seq := 0 key, _ := as.NewKey(*namespace, "test", 1) - partition := as.NewPartitionByKey(key) - masterNode, err := nclient.Cluster().GetMasterNode(partition) + partition, err := as.PartitionForWrite(nclient.Cluster(), wpolicy.GetBasePolicy(), key) + Expect(err).NotTo(HaveOccurred()) + masterNode, err := partition.GetMasterNode(nclient.Cluster()) Expect(err).NotTo(HaveOccurred()) - node, err := nclient.Cluster().GetReadNode(partition, replicaPolicy, &seq) + // node, err := nclient.Cluster().GetReadNode(partition, replicaPolicy, &seq) + node, err := partition.GetNodeRead(nclient.Cluster()) Expect(err).NotTo(HaveOccurred()) Expect(node).NotTo(BeNil()) Expect(node).To(Equal(masterNode)) @@ -175,18 +177,22 @@ var _ = Describe("Aerospike", func() { // cpolicy.Timeout = 10 * time.Second // cpolicy.RackAware = true + // rpolicy := as.NewPolicy() + // rpolicy.ReplicaPolicy = as.PREFER_RACK + // for rid := 1; rid <= 20; rid++ { // cpolicy.RackId = (rid % 2) + 1 // nclient, err := as.NewClientWithPolicy(&cpolicy, *host, *port) // Expect(err).NotTo(HaveOccurred()) - // replicaPolicy := as.PREFER_RACK // for i := 0; i < 12; i++ { - // seq := 0 + // println(i) // key, _ := as.NewKey(*namespace, "test", 1) - // partition := as.NewPartitionByKey(key) - // node, err := nclient.Cluster().GetReadNode(partition, replicaPolicy, &seq) + // partition, err := as.PartitionForRead(nclient.Cluster(), rpolicy.GetBasePolicy(), key) + // Expect(err).NotTo(HaveOccurred()) + + // node, err := partition.GetNodeRead(nclient.Cluster()) // Expect(err).NotTo(HaveOccurred()) // Expect(node.Rack("test")).To(Equal(cpolicy.RackId)) // } diff --git a/cluster.go b/cluster.go index a48d9ffc..85621771 100644 --- a/cluster.go +++ b/cluster.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -809,134 +809,6 @@ func (clstr *Cluster) IsConnected() bool { return (len(nodeArray) > 0) && !clstr.closed.Get() } -func (clstr *Cluster) getReadNode(partition *Partition, replica ReplicaPolicy, seq *int) (*Node, error) { - switch replica { - case SEQUENCE: - return clstr.getSequenceNode(partition, seq) - case MASTER: - return clstr.getMasterNode(partition) - case MASTER_PROLES: - return clstr.getMasterProleNode(partition) - case PREFER_RACK: - return clstr.getSameRackNode(partition, seq) - default: - // includes case RANDOM: - return clstr.GetRandomNode() - } -} - -// getSameRackNode returns either a node on the same rack, or in Replica Sequence -func (clstr *Cluster) getSameRackNode(partition *Partition, seq *int) (*Node, error) { - // RackAware has not been enabled in client policy. - if !clstr.clientPolicy.RackAware { - return nil, NewAerospikeError(UNSUPPORTED_FEATURE, "ReplicaPolicy is set to PREFER_RACK but ClientPolicy.RackAware is not set.") - } - - pmap := clstr.getPartitions() - partitions := pmap[partition.Namespace] - if partitions == nil { - return nil, NewAerospikeError(PARTITION_UNAVAILABLE, "Invalid namespace in partition table:", partition.Namespace) - } - - // CP mode (Strong Consistency) does not support the RackAware feature. - if partitions.CPMode { - return nil, NewAerospikeError(UNSUPPORTED_FEATURE, "ReplicaPolicy is set to PREFER_RACK but the cluster is in Strong Consistency Mode.") - } - - replicaArray := partitions.Replicas - - var seqNode *Node - for range replicaArray { - index := *seq % len(replicaArray) - node := replicaArray[index][partition.PartitionId] - *seq++ - - if node != nil && node.IsActive() { - // assign a node to seqNode in case no node was found on the same rack was found - if seqNode == nil { - seqNode = node - } - - // if the node didn't belong to rack for that namespace, continue - nodeRack, err := node.Rack(partition.Namespace) - if err != nil { - continue - } - - if node.IsActive() && nodeRack == clstr.clientPolicy.RackId { - return node, nil - } - } - } - - // if no nodes were found belonging to the same rack, and no other node was also found - // then the partition table replicas are empty for that namespace - if seqNode == nil { - return nil, newInvalidNodeError(len(clstr.GetNodes()), partition) - } - - return seqNode, nil -} - -func (clstr *Cluster) getSequenceNode(partition *Partition, seq *int) (*Node, error) { - pmap := clstr.getPartitions() - partitions := pmap[partition.Namespace] - if partitions == nil { - return nil, NewAerospikeError(PARTITION_UNAVAILABLE, "Invalid namespace in partition table:", partition.Namespace) - } - - replicaArray := partitions.Replicas - - if replicaArray != nil { - index := *seq % len(replicaArray) - node := replicaArray[index][partition.PartitionId] - *seq++ - - if node != nil && node.IsActive() { - return node, nil - } - } - - return nil, newInvalidNodeError(len(clstr.GetNodes()), partition) -} - -func (clstr *Cluster) getMasterNode(partition *Partition) (*Node, error) { - pmap := clstr.getPartitions() - partitions := pmap[partition.Namespace] - if partitions == nil { - return nil, NewAerospikeError(PARTITION_UNAVAILABLE, "Invalid namespace in partition table:", partition.Namespace) - } - - node := partitions.Replicas[0][partition.PartitionId] - if node != nil && node.IsActive() { - return node, nil - } - - return nil, newInvalidNodeError(len(clstr.GetNodes()), partition) -} - -func (clstr *Cluster) getMasterProleNode(partition *Partition) (*Node, error) { - pmap := clstr.getPartitions() - partitions := pmap[partition.Namespace] - if partitions == nil { - return nil, NewAerospikeError(PARTITION_UNAVAILABLE, "Invalid namespace in partition table:", partition.Namespace) - } - - replicaArray := partitions.Replicas - - if replicaArray != nil { - for range replicaArray { - index := int(atomic.AddUint64(&clstr.replicaIndex, 1) % uint64(len(replicaArray))) - node := replicaArray[index][partition.PartitionId] - if node != nil && node.IsActive() { - return node, nil - } - } - } - - return nil, newInvalidNodeError(len(clstr.GetNodes()), partition) -} - // GetRandomNode returns a random node on the cluster func (clstr *Cluster) GetRandomNode() (*Node, error) { // Must copy array reference for copy on write semantics to work. diff --git a/command.go b/command.go index 21db466e..c08b48ef 100644 --- a/command.go +++ b/command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -45,7 +45,7 @@ const ( _INFO1_NOBINDATA int = (1 << 5) // Involve all replicas in read operation. - _INFO1_CONSISTENCY_ALL = (1 << 6) + _INFO1_READ_MODE_AP_ALL = (1 << 6) // Tell server to compress its response. _INFO1_COMPRESS_RESPONSE = (1 << 7) @@ -77,8 +77,23 @@ const ( _INFO3_CREATE_OR_REPLACE int = (1 << 4) // Completely replace existing record only. _INFO3_REPLACE_ONLY int = (1 << 5) - // Linearize read when in CP mode. - _INFO3_LINEARIZE_READ int = (1 << 6) + // See Below + _INFO3_SC_READ_TYPE int = (1 << 6) + // See Below + _INFO3_SC_READ_RELAX int = (1 << 7) + + // Interpret SC_READ bits in info3. + // + // RELAX TYPE + // strict + // ------ + // 0 0 sequential (default) + // 0 1 linearize + // + // relaxed + // ------- + // 1 0 allow replica + // 1 1 allow unavailable _MSG_TOTAL_HEADER_SIZE uint8 = 30 _FIELD_HEADER_SIZE uint8 = 5 @@ -101,8 +116,11 @@ type command interface { putConnection(conn *Connection) parseResult(ifc command, conn *Connection) error parseRecordResults(ifc command, receiveSize int) (bool, error) + prepareRetry(ifc command, isTimeout bool) bool execute(ifc command, isRead bool) error + executeAt(ifc command, policy *BasePolicy, isRead bool, deadline time.Time, iterations, commandSentCounter int) error + // Executes the command Execute() error } @@ -581,8 +599,8 @@ func (cmd *baseCommand) setBatchIndexReadCompat(policy *BatchPolicy, keys []*Key return err } - if policy.ConsistencyLevel == CONSISTENCY_ALL { - readAttr |= _INFO1_CONSISTENCY_ALL + if policy.ReadModeAP == ReadModeAPAll { + readAttr |= _INFO1_READ_MODE_AP_ALL } if len(binNames) == 0 { @@ -708,8 +726,8 @@ func (cmd *baseCommand) setBatchIndexRead(policy *BatchPolicy, records []*BatchR } readAttr := _INFO1_READ - if policy.ConsistencyLevel == CONSISTENCY_ALL { - readAttr |= _INFO1_CONSISTENCY_ALL + if policy.ReadModeAP == ReadModeAPAll { + readAttr |= _INFO1_READ_MODE_AP_ALL } cmd.writeHeader(&policy.BasePolicy, readAttr|_INFO1_BATCH, 0, fieldCount, 0) @@ -1250,12 +1268,19 @@ func (cmd *baseCommand) estimatePredExpSize(predExp []PredExp) int { // Generic header write. func (cmd *baseCommand) writeHeader(policy *BasePolicy, readAttr int, writeAttr int, fieldCount int, operationCount int) { infoAttr := 0 - if policy.LinearizeRead { - infoAttr |= _INFO3_LINEARIZE_READ + + switch policy.ReadModeSC { + case ReadModeSCSession: + case ReadModeSCLinearize: + infoAttr |= _INFO3_SC_READ_TYPE + case ReadModeSCAllowReplica: + infoAttr |= _INFO3_SC_READ_RELAX + case ReadModeSCAllowUnavailable: + infoAttr |= _INFO3_SC_READ_TYPE | _INFO3_SC_READ_RELAX } - if policy.ConsistencyLevel == CONSISTENCY_ALL { - readAttr |= _INFO1_CONSISTENCY_ALL + if policy.ReadModeAP == ReadModeAPAll { + readAttr |= _INFO1_READ_MODE_AP_ALL } if policy.UseCompression { @@ -1309,16 +1334,22 @@ func (cmd *baseCommand) writeHeaderWithPolicy(policy *WritePolicy, readAttr int, infoAttr |= _INFO3_COMMIT_MASTER } - if policy.LinearizeRead { - infoAttr |= _INFO3_LINEARIZE_READ + if policy.DurableDelete { + writeAttr |= _INFO2_DURABLE_DELETE } - if policy.ConsistencyLevel == CONSISTENCY_ALL { - readAttr |= _INFO1_CONSISTENCY_ALL + switch policy.ReadModeSC { + case ReadModeSCSession: + case ReadModeSCLinearize: + infoAttr |= _INFO3_SC_READ_TYPE + case ReadModeSCAllowReplica: + infoAttr |= _INFO3_SC_READ_RELAX + case ReadModeSCAllowUnavailable: + infoAttr |= _INFO3_SC_READ_TYPE | _INFO3_SC_READ_RELAX } - if policy.DurableDelete { - writeAttr |= _INFO2_DURABLE_DELETE + if policy.ReadModeAP == ReadModeAPAll { + readAttr |= _INFO1_READ_MODE_AP_ALL } if policy.UseCompression { @@ -1369,9 +1400,6 @@ func (cmd *baseCommand) writeKey(key *Key, sendKey bool) { func (cmd *baseCommand) writeOperationForBin(bin *Bin, operation OperationType) error { nameLength := copy(cmd.dataBuffer[(cmd.dataOffset+int(_OPERATION_HEADER_SIZE)):], bin.Name) - // check for float support - cmd.checkServerCompatibility(bin.Value) - valueLength, err := bin.Value.EstimateSize() if err != nil { return err @@ -1392,9 +1420,6 @@ func (cmd *baseCommand) writeOperationForBinNameAndValue(name string, val interf v := NewValue(val) - // check for float support - cmd.checkServerCompatibility(v) - valueLength, err := v.EstimateSize() if err != nil { return err @@ -1413,9 +1438,6 @@ func (cmd *baseCommand) writeOperationForBinNameAndValue(name string, val interf func (cmd *baseCommand) writeOperationForOperation(operation *Operation) error { nameLength := copy(cmd.dataBuffer[(cmd.dataOffset+int(_OPERATION_HEADER_SIZE)):], operation.binName) - // check for float support - cmd.checkServerCompatibility(operation.binValue) - if operation.used { // cahce will set the used flag to false again operation.cache() @@ -1482,29 +1504,7 @@ func (cmd *baseCommand) writePredExp(predExp []PredExp, predSize int) error { return nil } -// TODO: Remove this method and move it to the appropriate VALUE method -func (cmd *baseCommand) checkServerCompatibility(val Value) { - if val == nil { - return - } - - // check for float support - switch val.GetType() { - case ParticleType.FLOAT: - if !cmd.node.supportsFloat.Get() { - panic("This cluster node doesn't support double precision floating-point values.") - } - case ParticleType.GEOJSON: - if !cmd.node.supportsGeo.Get() { - panic("This cluster node doesn't support geo-spatial features.") - } - } -} - func (cmd *baseCommand) writeFieldValue(value Value, ftype FieldType) error { - // check for float support - cmd.checkServerCompatibility(value) - vlen, err := value.EstimateSize() if err != nil { return err @@ -1791,23 +1791,40 @@ func setInDoubt(err error, isRead bool, commandSentCounter int) error { func (cmd *baseCommand) execute(ifc command, isRead bool) error { policy := ifc.getPolicy(ifc).GetBasePolicy() - iterations := -1 - commandSentCounter := 0 + deadline := policy.deadline() + + return cmd.executeAt(ifc, policy, isRead, deadline, -1, 0) +} +func (cmd *baseCommand) executeAt(ifc command, policy *BasePolicy, isRead bool, deadline time.Time, iterations, commandSentCounter int) (err error) { // for exponential backoff interval := policy.SleepBetweenRetries - // set timeout outside the loop - deadline := policy.deadline() - - var err error - shouldSleep := false + isClientTimeout := false // Execute command until successful, timed out or maximum iterations have been reached. for { + iterations++ + + if shouldSleep { + aerr, ok := err.(AerospikeError) + if !ifc.prepareRetry(ifc, isClientTimeout || (ok && aerr.ResultCode() != SERVER_NOT_AVAILABLE)) { + if bc, ok := ifc.(batcher); ok { + // Batch may be retried in separate commands. + if retry, err := bc.retryBatch(bc, cmd.node.cluster, deadline, iterations, commandSentCounter); retry { + // Batch was retried in separate commands. Complete this command. + return err + } + } + } + } + + // NOTE: This is important to be after the prepareRetry block above + isClientTimeout = false + // too many retries - if iterations++; (policy.MaxRetries <= 0 && iterations > 0) || (policy.MaxRetries > 0 && iterations > policy.MaxRetries) { + if (policy.MaxRetries <= 0 && iterations > 0) || (policy.MaxRetries > 0 && iterations > policy.MaxRetries) { if ae, ok := err.(AerospikeError); ok { err = NewAerospikeError(ae.ResultCode(), fmt.Sprintf("command execution timed out on client: Exceeded number of retries. See `Policy.MaxRetries`. (last error: %s)", err.Error())) } @@ -1838,12 +1855,16 @@ func (cmd *baseCommand) execute(ifc command, isRead bool) error { // set command node, so when you return a record it has the node cmd.node, err = ifc.getNode(ifc) if cmd.node == nil || !cmd.node.IsActive() || err != nil { + isClientTimeout = true + // Node is currently inactive. Retry. continue } cmd.conn, err = ifc.getConnection(policy) if err != nil { + isClientTimeout = true + if err == ErrConnectionPoolEmpty { // if the connection pool is empty, we still haven't tried // the transaction to increase the iteration count. @@ -1884,6 +1905,8 @@ func (cmd *baseCommand) execute(ifc command, isRead bool) error { // Send command. _, err = cmd.conn.Write(cmd.dataBuffer[:cmd.dataOffset]) if err != nil { + isClientTimeout = true + // IO errors are considered temporary anomalies. Retry. // Close socket to flush out possible garbage. Do not put back in pool. cmd.conn.Close() @@ -1898,6 +1921,13 @@ func (cmd *baseCommand) execute(ifc command, isRead bool) error { err = ifc.parseResult(ifc, cmd.conn) if err != nil { if _, ok := err.(net.Error); err == ErrTimeout || err == io.EOF || ok { + isClientTimeout = true + if err != ErrTimeout { + if aerr, ok := err.(AerospikeError); ok && aerr.ResultCode() == TIMEOUT { + isClientTimeout = false + } + } + // IO errors are considered temporary anomalies. Retry. // Close socket to flush out possible garbage. Do not put back in pool. cmd.conn.Close() diff --git a/commit_policy.go b/commit_policy.go index 7b12fce5..a64067df 100644 --- a/commit_policy.go +++ b/commit_policy.go @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 Aerospike, Inc. + * Copyright 2013-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/compat_after_go1.8.go b/compat_after_go1.8.go index ed1ddf63..9b8dee21 100644 --- a/compat_after_go1.8.go +++ b/compat_after_go1.8.go @@ -1,6 +1,6 @@ // +build go1.8 -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/compat_before_go1.8.go b/compat_before_go1.8.go index d972c357..63ed6f19 100644 --- a/compat_before_go1.8.go +++ b/compat_before_go1.8.go @@ -1,6 +1,6 @@ // +build !go1.8 -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/complex_index_test.go b/complex_index_test.go index 8b049485..d96cafe8 100644 --- a/complex_index_test.go +++ b/complex_index_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/complex_query_test.go b/complex_query_test.go index 7b3d0ed9..e774f267 100644 --- a/complex_query_test.go +++ b/complex_query_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/connection.go b/connection.go index 97b18cb4..76c2b772 100644 --- a/connection.go +++ b/connection.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/connection_heap.go b/connection_heap.go index 3612d1c4..bec89a26 100644 --- a/connection_heap.go +++ b/connection_heap.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/connection_heap_test.go b/connection_heap_test.go index b2ddc4fb..01693404 100644 --- a/connection_heap_test.go +++ b/connection_heap_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/delete_command.go b/delete_command.go index 76d2f9be..e63a4c32 100644 --- a/delete_command.go +++ b/delete_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -29,13 +29,18 @@ type deleteCommand struct { existed bool } -func newDeleteCommand(cluster *Cluster, policy *WritePolicy, key *Key) *deleteCommand { +func newDeleteCommand(cluster *Cluster, policy *WritePolicy, key *Key) (*deleteCommand, error) { + partition, err := PartitionForWrite(cluster, &policy.BasePolicy, key) + if err != nil { + return nil, err + } + newDeleteCmd := &deleteCommand{ - singleCommand: newSingleCommand(cluster, key), + singleCommand: newSingleCommand(cluster, key, partition), policy: policy, } - return newDeleteCmd + return newDeleteCmd, nil } func (cmd *deleteCommand) getPolicy(ifc command) Policy { @@ -47,7 +52,12 @@ func (cmd *deleteCommand) writeBuffer(ifc command) error { } func (cmd *deleteCommand) getNode(ifc command) (*Node, error) { - return cmd.cluster.getMasterNode(&cmd.partition) + return cmd.partition.GetNodeWrite(cmd.cluster) +} + +func (cmd *deleteCommand) prepareRetry(ifc command, isTimeout bool) bool { + cmd.partition.PrepareRetryWrite(isTimeout) + return true } func (cmd *deleteCommand) parseResult(ifc command, conn *Connection) error { diff --git a/example_client_test.go b/example_client_test.go index ff1ece97..74dcada8 100644 --- a/example_client_test.go +++ b/example_client_test.go @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 Aerospike, Inc. + * Copyright 2013-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/example_listiter_int_test.go b/example_listiter_int_test.go index e23cb89e..bc79c3cb 100644 --- a/example_listiter_int_test.go +++ b/example_listiter_int_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/example_listiter_string_test.go b/example_listiter_string_test.go index 4d19d50c..a56b299e 100644 --- a/example_listiter_string_test.go +++ b/example_listiter_string_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/example_listiter_time_test.go b/example_listiter_time_test.go index 3c78b202..6a50550f 100644 --- a/example_listiter_time_test.go +++ b/example_listiter_time_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/example_mapiter_test.go b/example_mapiter_test.go index b4226e11..36cc50c8 100644 --- a/example_mapiter_test.go +++ b/example_mapiter_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/examples/add.go b/examples/add.go index 2bba03bd..ec383fae 100644 --- a/examples/add.go +++ b/examples/add.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/append.go b/examples/append.go index e3ce07da..1aa52675 100644 --- a/examples/append.go +++ b/examples/append.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/batch.go b/examples/batch.go index 1b6c793d..e73dea43 100644 --- a/examples/batch.go +++ b/examples/batch.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/blob.go b/examples/blob.go index 5a74a3ae..926edc72 100644 --- a/examples/blob.go +++ b/examples/blob.go @@ -1,4 +1,4 @@ -// Copyright 2013-2016 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/examples/expire.go b/examples/expire.go index 6e605cc0..918cd849 100644 --- a/examples/expire.go +++ b/examples/expire.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/generation.go b/examples/generation.go index df575960..eece3cfa 100644 --- a/examples/generation.go +++ b/examples/generation.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/get/get.go b/examples/get/get.go index fc1912d6..03f68d19 100644 --- a/examples/get/get.go +++ b/examples/get/get.go @@ -1,4 +1,4 @@ -// Copyright 2013-2016 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/examples/info.go b/examples/info.go index c5fc49e4..9307001e 100644 --- a/examples/info.go +++ b/examples/info.go @@ -1,4 +1,4 @@ -// Copyright 2013-2016 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/examples/list_map.go b/examples/list_map.go index c8afd4ca..cddcd93a 100644 --- a/examples/list_map.go +++ b/examples/list_map.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/operate.go b/examples/operate.go index 41ac1274..a9933d6a 100644 --- a/examples/operate.go +++ b/examples/operate.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/prepend.go b/examples/prepend.go index 812ff86c..098aa668 100644 --- a/examples/prepend.go +++ b/examples/prepend.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/put/put.go b/examples/put/put.go index 3b2f9727..41222edf 100644 --- a/examples/put/put.go +++ b/examples/put/put.go @@ -1,4 +1,4 @@ -// Copyright 2013-2016 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/examples/putget.go b/examples/putget.go index 247a148b..24ef78ae 100644 --- a/examples/putget.go +++ b/examples/putget.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/query-aggregate/average.go b/examples/query-aggregate/average.go index a05d733e..83a6140b 100644 --- a/examples/query-aggregate/average.go +++ b/examples/query-aggregate/average.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/query-aggregate/single_bin_sum.go b/examples/query-aggregate/single_bin_sum.go index 22bf47f1..aa954c36 100644 --- a/examples/query-aggregate/single_bin_sum.go +++ b/examples/query-aggregate/single_bin_sum.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/replace.go b/examples/replace.go index 99ecf6ba..41f6dac3 100644 --- a/examples/replace.go +++ b/examples/replace.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/scan_parallel.go b/examples/scan_parallel.go index 8c91c9ba..6e5a0a8d 100644 --- a/examples/scan_parallel.go +++ b/examples/scan_parallel.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/scan_serial.go b/examples/scan_serial.go index 2b695988..745e6962 100644 --- a/examples/scan_serial.go +++ b/examples/scan_serial.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/shared/shared.go b/examples/shared/shared.go index 59f52dcc..5d5fc8e1 100644 --- a/examples/shared/shared.go +++ b/examples/shared/shared.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/simple.go b/examples/simple.go index 4f1111e9..6b90d24c 100644 --- a/examples/simple.go +++ b/examples/simple.go @@ -1,4 +1,4 @@ -// Copyright 2013-2016 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/examples/tls_secure_connection.go b/examples/tls_secure_connection.go index f517e84f..e6a659be 100644 --- a/examples/tls_secure_connection.go +++ b/examples/tls_secure_connection.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/touch.go b/examples/touch.go index 1b9ceb58..57a6dc2c 100644 --- a/examples/touch.go +++ b/examples/touch.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/examples/udf.go b/examples/udf.go index 05a46600..d85dac00 100644 --- a/examples/udf.go +++ b/examples/udf.go @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 Aerospike, Inc. + * Copyright 2012-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/execute_command.go b/execute_command.go index 85ee5b08..8d8fcf81 100644 --- a/execute_command.go +++ b/execute_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,14 +31,24 @@ func newExecuteCommand( packageName string, functionName string, args *ValueArray, -) executeCommand { +) (executeCommand, error) { + partition, err := PartitionForWrite(cluster, &policy.BasePolicy, key) + if err != nil { + return executeCommand{}, err + } + + readCommand, err := newReadCommand(cluster, &policy.BasePolicy, key, nil, partition) + if err != nil { + return executeCommand{}, err + } + return executeCommand{ - readCommand: newReadCommand(cluster, &policy.BasePolicy, key, nil), + readCommand: readCommand, policy: policy, packageName: packageName, functionName: functionName, args: args, - } + }, nil } func (cmd *executeCommand) writeBuffer(ifc command) error { @@ -46,7 +56,12 @@ func (cmd *executeCommand) writeBuffer(ifc command) error { } func (cmd *executeCommand) getNode(ifc command) (*Node, error) { - return cmd.cluster.getMasterNode(&cmd.partition) + return cmd.partition.GetNodeWrite(cmd.cluster) +} + +func (cmd *executeCommand) prepareRetry(ifc command, isTimeout bool) bool { + cmd.partition.PrepareRetryWrite(isTimeout) + return true } func (cmd *executeCommand) Execute() error { diff --git a/execute_task.go b/execute_task.go index 5cbd4eff..fa1110de 100644 --- a/execute_task.go +++ b/execute_task.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/exists_command.go b/exists_command.go index edbbb75d..643ad38d 100644 --- a/exists_command.go +++ b/exists_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,11 +31,16 @@ type existsCommand struct { replicaSequence int } -func newExistsCommand(cluster *Cluster, policy *BasePolicy, key *Key) *existsCommand { +func newExistsCommand(cluster *Cluster, policy *BasePolicy, key *Key) (*existsCommand, error) { + partition, err := PartitionForRead(cluster, policy, key) + if err != nil { + return nil, err + } + return &existsCommand{ - singleCommand: newSingleCommand(cluster, key), + singleCommand: newSingleCommand(cluster, key, partition), policy: policy, - } + }, nil } func (cmd *existsCommand) getPolicy(ifc command) Policy { @@ -47,7 +52,12 @@ func (cmd *existsCommand) writeBuffer(ifc command) error { } func (cmd *existsCommand) getNode(ifc command) (*Node, error) { - return cmd.cluster.getReadNode(&cmd.partition, cmd.policy.ReplicaPolicy, &cmd.replicaSequence) + return cmd.partition.GetNodeRead(cmd.cluster) +} + +func (cmd *existsCommand) prepareRetry(ifc command, isTimeout bool) bool { + cmd.partition.PrepareRetryRead(isTimeout) + return true } func (cmd *existsCommand) parseResult(ifc command, conn *Connection) error { diff --git a/field_type.go b/field_type.go index 687a9b94..3e96fd63 100644 --- a/field_type.go +++ b/field_type.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/filter.go b/filter.go index 46355bd5..3f6e7f1d 100644 --- a/filter.go +++ b/filter.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/generation_policy.go b/generation_policy.go index ccd06298..1b8fa14a 100644 --- a/generation_policy.go +++ b/generation_policy.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/generics.go b/generics.go index c14042a1..973bb91e 100644 --- a/generics.go +++ b/generics.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/geo_test.go b/geo_test.go index eea2f3ee..59b93b21 100644 --- a/geo_test.go +++ b/geo_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/helper_test.go b/helper_test.go index 2d3b5925..a7da7ee2 100644 --- a/helper_test.go +++ b/helper_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,12 +14,16 @@ package aerospike -func (clstr *Cluster) GetReadNode(partition *Partition, replica ReplicaPolicy, seq *int) (*Node, error) { - return clstr.getReadNode(partition, replica, seq) +func (clstr *Cluster) GetMasterNode(partition *Partition) (*Node, error) { + return partition.getMasterNode(clstr) } -func (clstr *Cluster) GetMasterNode(partition *Partition) (*Node, error) { - return clstr.getMasterNode(partition) +func (ptn *Partition) GetMasterNode(cluster *Cluster) (*Node, error) { + return ptn.getMasterNode(cluster) +} + +func (ptn *Partition) GetMasterProlesNode(cluster *Cluster) (*Node, error) { + return ptn.getMasterProlesNode(cluster) } // fillMinCounts will fill the connection pool to the minimum required diff --git a/host.go b/host.go index 529f5f2a..57248570 100644 --- a/host.go +++ b/host.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/index_collection_type.go b/index_collection_type.go index e395b54e..a1472d7a 100644 --- a/index_collection_type.go +++ b/index_collection_type.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/index_test.go b/index_test.go index 902c8800..64d81e54 100644 --- a/index_test.go +++ b/index_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/index_type.go b/index_type.go index 7fbaff99..62c9a6c5 100644 --- a/index_type.go +++ b/index_type.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/info.go b/info.go index 10b129d4..b0cab7cd 100644 --- a/info.go +++ b/info.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/info_policy.go b/info_policy.go index eb81d4d7..c1c4c6dd 100644 --- a/info_policy.go +++ b/info_policy.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/atomic/array.go b/internal/atomic/array.go index aa6f4f0e..b2740ade 100644 --- a/internal/atomic/array.go +++ b/internal/atomic/array.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/atomic/atomic_test.go b/internal/atomic/atomic_test.go index 25c29804..3e59b553 100644 --- a/internal/atomic/atomic_test.go +++ b/internal/atomic/atomic_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/atomic/bool.go b/internal/atomic/bool.go index 6392aa9f..58070bfd 100644 --- a/internal/atomic/bool.go +++ b/internal/atomic/bool.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/atomic/bool_test.go b/internal/atomic/bool_test.go index 5792d52d..15d214bb 100644 --- a/internal/atomic/bool_test.go +++ b/internal/atomic/bool_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/atomic/int.go b/internal/atomic/int.go index 815ac1fd..db00e802 100644 --- a/internal/atomic/int.go +++ b/internal/atomic/int.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/atomic/int_test.go b/internal/atomic/int_test.go index 3a8fef41..9b520ff4 100644 --- a/internal/atomic/int_test.go +++ b/internal/atomic/int_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/atomic/queue.go b/internal/atomic/queue.go index c29916a3..ef280edb 100644 --- a/internal/atomic/queue.go +++ b/internal/atomic/queue.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/atomic/queue_test.go b/internal/atomic/queue_test.go index ed795943..1d91d19f 100644 --- a/internal/atomic/queue_test.go +++ b/internal/atomic/queue_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/lua/instance.go b/internal/lua/instance.go index 94f12e4b..a9fdb228 100644 --- a/internal/lua/instance.go +++ b/internal/lua/instance.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/lua/lua.go b/internal/lua/lua.go index 1af8b356..093b9a33 100644 --- a/internal/lua/lua.go +++ b/internal/lua/lua.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/lua/lua_aerospike.go b/internal/lua/lua_aerospike.go index b5efde7e..593225d7 100644 --- a/internal/lua/lua_aerospike.go +++ b/internal/lua/lua_aerospike.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/lua/lua_aerospike_test.go b/internal/lua/lua_aerospike_test.go index f2364ccc..4ce9221e 100644 --- a/internal/lua/lua_aerospike_test.go +++ b/internal/lua/lua_aerospike_test.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/lua/lua_list.go b/internal/lua/lua_list.go index a78a88bd..0acaff02 100644 --- a/internal/lua/lua_list.go +++ b/internal/lua/lua_list.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/lua/lua_list_test.go b/internal/lua/lua_list_test.go index f3569630..7bb13952 100644 --- a/internal/lua/lua_list_test.go +++ b/internal/lua/lua_list_test.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/lua/lua_map.go b/internal/lua/lua_map.go index db6eaacd..d07b36f9 100644 --- a/internal/lua/lua_map.go +++ b/internal/lua/lua_map.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/lua/lua_map_test.go b/internal/lua/lua_map_test.go index 137b6262..ff32298a 100644 --- a/internal/lua/lua_map_test.go +++ b/internal/lua/lua_map_test.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/lua/lua_stream.go b/internal/lua/lua_stream.go index 518adc16..2890d903 100644 --- a/internal/lua/lua_stream.go +++ b/internal/lua/lua_stream.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/lua/lua_suite_test.go b/internal/lua/lua_suite_test.go index a214a7c2..3a1cfa70 100644 --- a/internal/lua/lua_suite_test.go +++ b/internal/lua/lua_suite_test.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/particle_type/particle_type.go b/internal/particle_type/particle_type.go index 656e916a..4e6ad326 100644 --- a/internal/particle_type/particle_type.go +++ b/internal/particle_type/particle_type.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/key.go b/key.go index 400083b8..399cc46e 100644 --- a/key.go +++ b/key.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/key_bench_test.go b/key_bench_test.go index 09a90a44..59a17c58 100644 --- a/key_bench_test.go +++ b/key_bench_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/key_helper.go b/key_helper.go index 43cae493..bb814a6e 100644 --- a/key_helper.go +++ b/key_helper.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/key_reflect_test.go b/key_reflect_test.go index cc6c74e0..e9abc312 100644 --- a/key_reflect_test.go +++ b/key_reflect_test.go @@ -1,6 +1,6 @@ // +build !as_performance -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/key_test.go b/key_test.go index 67ad2aab..687104f6 100644 --- a/key_test.go +++ b/key_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/language.go b/language.go index a8ddcf55..5a06309f 100644 --- a/language.go +++ b/language.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/load_test.go b/load_test.go index c562dc71..d6fa0b6f 100644 --- a/load_test.go +++ b/load_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/logger/logger.go b/logger/logger.go index 6b5941e3..9147c39f 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/login_command.go b/login_command.go index 7b96d8fa..bf255170 100644 --- a/login_command.go +++ b/login_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use acmd file except in compliance with the License. diff --git a/multi_command.go b/multi_command.go index 6d2b8b40..7a01d79f 100644 --- a/multi_command.go +++ b/multi_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -96,6 +96,10 @@ func (cmd *baseMultiCommand) getNode(ifc command) (*Node, error) { return cmd.node, nil } +func (cmd *baseMultiCommand) prepareRetry(ifc command, isTimeout bool) bool { + return false +} + func (cmd *baseMultiCommand) getConnection(policy Policy) (*Connection, error) { return cmd.node.getConnectionWithHint(policy.GetBasePolicy().deadline(), policy.GetBasePolicy().socketTimeout(), byte(xrand.Int64()%256)) } diff --git a/multi_policy.go b/multi_policy.go index 68c0eef8..109bd902 100644 --- a/multi_policy.go +++ b/multi_policy.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/node.go b/node.go index 802c92a4..d50bee38 100644 --- a/node.go +++ b/node.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -895,6 +895,18 @@ func (nd *Node) Rack(namespace string) (int, error) { return -1, newAerospikeNodeError(nd, RACK_NOT_DEFINED) } +// Rack returns the rack number for the namespace. +func (nd *Node) hasRack(namespace string, rack int) bool { + racks := nd.racks.Load().(map[string]int) + v, exists := racks[namespace] + + if !exists { + return false + } + + return v == rack +} + // WarmUp fills the node's connection pool with connections. // This is necessary on startup for high traffic programs. // If the count is <= 0, the connection queue will be filled. diff --git a/node_error.go b/node_error.go index 55603aee..35f50a3c 100644 --- a/node_error.go +++ b/node_error.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/node_stats.go b/node_stats.go index 08dd1bd4..9cee617d 100644 --- a/node_stats.go +++ b/node_stats.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/node_test.go b/node_test.go index 9034cd33..88e3bf5c 100644 --- a/node_test.go +++ b/node_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/node_validator.go b/node_validator.go index b019cd69..4f03d247 100644 --- a/node_validator.go +++ b/node_validator.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/operate_command.go b/operate_command.go index d60cdd48..1c29c6be 100644 --- a/operate_command.go +++ b/operate_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,14 +23,33 @@ type operateCommand struct { hasWrite bool } -func newOperateCommand(cluster *Cluster, policy *WritePolicy, key *Key, operations []*Operation) operateCommand { +func newOperateCommand(cluster *Cluster, policy *WritePolicy, key *Key, operations []*Operation) (operateCommand, error) { + hasWrite := hasWriteOp(operations) + + var partition *Partition + var err error + if hasWrite { + partition, err = PartitionForWrite(cluster, &policy.BasePolicy, key) + } else { + partition, err = PartitionForRead(cluster, &policy.BasePolicy, key) + } + + if err != nil { + return operateCommand{}, err + } + + readCommand, err := newReadCommand(cluster, &policy.BasePolicy, key, nil, partition) + if err != nil { + return operateCommand{}, err + } + return operateCommand{ - readCommand: newReadCommand(cluster, &policy.BasePolicy, key, nil), + readCommand: readCommand, policy: policy, operations: operations, - hasWrite: hasWriteOp(operations), - } + hasWrite: hasWrite, + }, nil } func (cmd *operateCommand) writeBuffer(ifc command) (err error) { @@ -40,11 +59,20 @@ func (cmd *operateCommand) writeBuffer(ifc command) (err error) { func (cmd *operateCommand) getNode(ifc command) (*Node, error) { if cmd.hasWrite { - return cmd.cluster.getMasterNode(&cmd.partition) + return cmd.partition.GetNodeWrite(cmd.cluster) } // this may be affected by Rackaware - return cmd.cluster.getReadNode(&cmd.partition, cmd.policy.ReplicaPolicy, &cmd.replicaSequence) + return cmd.partition.GetNodeRead(cmd.cluster) +} + +func (cmd *operateCommand) prepareRetry(ifc command, isTimeout bool) bool { + if cmd.hasWrite { + cmd.partition.PrepareRetryWrite(isTimeout) + } else { + cmd.partition.PrepareRetryRead(isTimeout) + } + return true } func (cmd *operateCommand) Execute() error { diff --git a/operation.go b/operation.go index c08aa9e8..ee31931e 100644 --- a/operation.go +++ b/operation.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/packer.go b/packer.go index 7589f713..2d691b46 100644 --- a/packer.go +++ b/packer.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/packer_reflect.go b/packer_reflect.go index 91e00f38..fbc722d5 100644 --- a/packer_reflect.go +++ b/packer_reflect.go @@ -1,6 +1,6 @@ // +build !as_performance -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/packing_test.go b/packing_test.go index a198b06a..c9927efa 100644 --- a/packing_test.go +++ b/packing_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/partition.go b/partition.go index 6471c751..6a8a2b89 100644 --- a/partition.go +++ b/partition.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,7 +16,9 @@ package aerospike import ( "fmt" + "sync/atomic" + . "github.com/aerospike/aerospike-client-go/types" Buffer "github.com/aerospike/aerospike-client-go/utils/buffer" ) @@ -24,34 +26,234 @@ import ( type Partition struct { Namespace string PartitionId int + partitions *Partitions + replica ReplicaPolicy + sequence int + linearize bool } -// NewPartitionByKey initializes a partition and determines the Partition Id -// from key digest automatically. -func NewPartitionByKey(key *Key) *Partition { - partition := newPartitionByKey(key) - return &partition -} - -// newPartitionByKey initializes a partition and determines the Partition Id -// from key digest automatically. It return the struct itself, and not the address -func newPartitionByKey(key *Key) Partition { - return Partition{ - Namespace: key.namespace, - +// NewPartition returns a partition representation +func NewPartition(partitions *Partitions, key *Key, replica ReplicaPolicy, linearize bool) *Partition { + return &Partition{ + partitions: partitions, + Namespace: key.Namespace(), + replica: replica, + linearize: linearize, // CAN'T USE MOD directly - mod will give negative numbers. // First AND makes positive and negative correctly, then mod. - // For any x, y : x % 2^y = x & (2^y - 1); the second method is twice as fast PartitionId: int(Buffer.LittleBytesToInt32(key.digest[:], 0)&0xFFFF) & (_PARTITIONS - 1), } } -// NewPartition generates a partition instance. -func NewPartition(namespace string, partitionID int) *Partition { - return &Partition{ - Namespace: namespace, - PartitionId: partitionID, +// PartitionForWrite returns a partition for write purposes +func PartitionForWrite(cluster *Cluster, policy *BasePolicy, key *Key) (*Partition, error) { + // Must copy hashmap reference for copy on write semantics to work. + pmap := cluster.getPartitions() + partitions := pmap[key.namespace] + + if partitions == nil { + return nil, newInvalidNamespaceError(key.namespace, len(pmap)) + } + + return NewPartition(partitions, key, policy.ReplicaPolicy, false), nil +} + +// PartitionForRead returns a partition for read purposes +func PartitionForRead(cluster *Cluster, policy *BasePolicy, key *Key) (*Partition, error) { + // Must copy hashmap reference for copy on write semantics to work. + pmap := cluster.getPartitions() + partitions := pmap[key.namespace] + + if partitions == nil { + return nil, newInvalidNamespaceError(key.namespace, len(pmap)) + } + + var replica ReplicaPolicy + var linearize bool + + if partitions.SCMode { + switch policy.ReadModeSC { + case ReadModeSCSession: + replica = MASTER + linearize = false + + case ReadModeSCLinearize: + replica = policy.ReplicaPolicy + if policy.ReplicaPolicy == PREFER_RACK { + replica = SEQUENCE + } + linearize = true + + default: + replica = policy.ReplicaPolicy + linearize = false + } + } else { + replica = policy.ReplicaPolicy + linearize = false + } + return NewPartition(partitions, key, replica, linearize), nil +} + +// GetReplicaPolicySC returns a ReplicaPolicy based on different variables in SC mode +func GetReplicaPolicySC(policy *BasePolicy) ReplicaPolicy { + switch policy.ReadModeSC { + case ReadModeSCSession: + return MASTER + + case ReadModeSCLinearize: + if policy.ReplicaPolicy == PREFER_RACK { + return SEQUENCE + } + return policy.ReplicaPolicy + + default: + return policy.ReplicaPolicy + } +} + +// GetNodeBatchRead returns a node for batch reads +func GetNodeBatchRead(cluster *Cluster, key *Key, replica ReplicaPolicy, replicaSC ReplicaPolicy, sequence int, sequenceSC int) (*Node, error) { + // Must copy hashmap reference for copy on write semantics to work. + pmap := cluster.getPartitions() + partitions := pmap[key.namespace] + + if partitions == nil { + return nil, newInvalidNamespaceError(key.namespace, len(pmap)) + } + + if partitions.SCMode { + replica = replicaSC + sequence = sequenceSC } + + p := NewPartition(partitions, key, replica, false) + p.sequence = sequence + return p.GetNodeRead(cluster) +} + +// GetNodeRead returns a node for read operations +func (ptn *Partition) GetNodeRead(cluster *Cluster) (*Node, error) { + switch ptn.replica { + default: + fallthrough + case SEQUENCE: + return ptn.getSequenceNode(cluster) + + case PREFER_RACK: + return ptn.getRackNode(cluster) + + case MASTER: + return ptn.getMasterNode(cluster) + + case MASTER_PROLES: + return ptn.getMasterProlesNode(cluster) + + case RANDOM: + return cluster.GetRandomNode() + } +} + +// GetNodeWrite returns a node for write operations +func (ptn *Partition) GetNodeWrite(cluster *Cluster) (*Node, error) { + switch ptn.replica { + default: + fallthrough + case SEQUENCE: + fallthrough + case PREFER_RACK: + return ptn.getSequenceNode(cluster) + + case MASTER: + fallthrough + case MASTER_PROLES: + fallthrough + case RANDOM: + return ptn.getMasterNode(cluster) + } +} + +// PrepareRetryRead increases sequence number before read retries +func (ptn *Partition) PrepareRetryRead(isClientTimeout bool) { + if !isClientTimeout || !ptn.linearize { + ptn.sequence++ + } +} + +// PrepareRetryRead increases sequence number before write retries +func (ptn *Partition) PrepareRetryWrite(isClientTimeout bool) { + if !isClientTimeout { + ptn.sequence++ + } +} + +func (ptn *Partition) getSequenceNode(cluster *Cluster) (*Node, error) { + replicas := ptn.partitions.Replicas + + for range replicas { + index := ptn.sequence % len(replicas) + node := replicas[index][ptn.PartitionId] + + if node != nil && node.IsActive() { + return node, nil + } + ptn.sequence++ + } + nodeArray := cluster.GetNodes() + return nil, newInvalidNodeError(len(nodeArray), ptn) +} + +func (ptn *Partition) getRackNode(cluster *Cluster) (*Node, error) { + replicas := ptn.partitions.Replicas + var fallback *Node + + for range replicas { + index := ptn.sequence % len(replicas) + node := replicas[index][ptn.PartitionId] + + if node != nil && node.IsActive() { + if node.hasRack(ptn.Namespace, cluster.clientPolicy.RackId) { + return node, nil + } + + if fallback == nil { + fallback = node + } + } + ptn.sequence++ + } + + if fallback != nil { + return fallback, nil + } + + nodeArray := cluster.GetNodes() + return nil, newInvalidNodeError(len(nodeArray), ptn) +} + +func (ptn *Partition) getMasterNode(cluster *Cluster) (*Node, error) { + node := ptn.partitions.Replicas[0][ptn.PartitionId] + + if node != nil && node.IsActive() { + return node, nil + } + nodeArray := cluster.GetNodes() + return nil, newInvalidNodeError(len(nodeArray), ptn) +} + +func (ptn *Partition) getMasterProlesNode(cluster *Cluster) (*Node, error) { + replicas := ptn.partitions.Replicas + + for range replicas { + index := int(atomic.AddUint64(&cluster.replicaIndex, 1) % uint64(len(replicas))) + node := replicas[index][ptn.PartitionId] + + if node != nil && node.IsActive() { + return node, nil + } + } + nodeArray := cluster.GetNodes() + return nil, newInvalidNodeError(len(nodeArray), ptn) } // String implements the Stringer interface. @@ -63,3 +265,13 @@ func (ptn *Partition) String() string { func (ptn *Partition) Equals(other *Partition) bool { return ptn.PartitionId == other.PartitionId && ptn.Namespace == other.Namespace } + +// newnewInvalidNamespaceError creates an AerospikeError with Resultcode INVALID_NAMESPACE +// and a corresponding message. +func newInvalidNamespaceError(ns string, mapSize int) error { + s := "Partition map empty" + if mapSize != 0 { + s = "Namespace not found in partition map: " + ns + } + return NewAerospikeError(INVALID_NAMESPACE, s) +} diff --git a/partition_parser.go b/partition_parser.go index 2570eaec..5d70ef46 100644 --- a/partition_parser.go +++ b/partition_parser.go @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 Aerospike, Inc. + * Copyright 2013-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. diff --git a/partitions.go b/partitions.go index 1c68fc42..5ad21545 100644 --- a/partitions.go +++ b/partitions.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ import ( type Partitions struct { Replicas [][]*Node - CPMode bool + SCMode bool regimes []int } @@ -37,7 +37,7 @@ func newPartitions(partitionCount int, replicaCount int, cpMode bool) *Partition return &Partitions{ Replicas: replicas, - CPMode: cpMode, + SCMode: cpMode, regimes: make([]int, partitionCount), } } @@ -71,7 +71,7 @@ func (p *Partitions) clone() *Partitions { return &Partitions{ Replicas: replicas, - CPMode: p.CPMode, + SCMode: p.SCMode, regimes: regimes, } } @@ -119,7 +119,7 @@ func (pm partitionMap) String() string { res.WriteString("-----------------------------------------------------------------------\n") res.WriteString("Namespace: " + ns + "\n") res.WriteString(fmt.Sprintf("Regimes: %v\n", partitions.regimes)) - res.WriteString(fmt.Sprintf("CPMode: %v\n", partitions.CPMode)) + res.WriteString(fmt.Sprintf("SCMode: %v\n", partitions.SCMode)) replicaArray := partitions.Replicas for i, nodeArray := range replicaArray { if i == 0 { diff --git a/peers.go b/peers.go index bc4086af..a8c3a7fd 100644 --- a/peers.go +++ b/peers.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/peers_parser.go b/peers_parser.go index bebe6889..66e783bf 100644 --- a/peers_parser.go +++ b/peers_parser.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/performance/bench_get_test.go b/performance/bench_get_test.go index 9c8b30c3..072e14ce 100644 --- a/performance/bench_get_test.go +++ b/performance/bench_get_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/performance/bench_key_test.go b/performance/bench_key_test.go index 75da9837..98026b77 100644 --- a/performance/bench_key_test.go +++ b/performance/bench_key_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/policy.go b/policy.go index ddd2505a..bdd9e5a0 100644 --- a/policy.go +++ b/policy.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ type Policy interface { // Retrieves BasePolicy GetBasePolicy() *BasePolicy + // determines if the command should be compressed compress() bool } @@ -41,10 +42,11 @@ type BasePolicy struct { // Currently, only used for scans. Priority Priority //= Priority.DEFAULT; - // How replicas should be consulted in a read operation to provide the desired - // consistency guarantee. Default to allowing one replica to be used in the - // read operation. - ConsistencyLevel ConsistencyLevel //= CONSISTENCY_ONE + // ReadModeAP indicates read policy for AP (availability) namespaces. + ReadModeAP ReadModeAP //= ONE + + // ReadModeSC indicates read policy for SC (strong consistency) namespaces. + ReadModeSC ReadModeSC //= SESSION; // TotalTimeout specifies total transaction timeout. // @@ -129,14 +131,10 @@ type BasePolicy struct { // Default: false UseCompression bool // = false - // Force reads to be linearized for server namespaces that support CP mode. - // The default is false. - LinearizeRead bool - // ReplicaPolicy determines the node to send the read commands containing the key's partition replica type. // Write commands are not affected by this setting, because all writes are directed // to the node containing the key's master partition. - // Batch, scan and query are also not affected by replica algorithms. + // Scan and query are also not affected by replica algorithms. // Default to sending read commands to the node containing the key's master partition. ReplicaPolicy ReplicaPolicy } @@ -145,7 +143,8 @@ type BasePolicy struct { func NewPolicy() *BasePolicy { return &BasePolicy{ Priority: DEFAULT, - ConsistencyLevel: CONSISTENCY_ONE, + ReadModeAP: ReadModeAPOne, + ReadModeSC: ReadModeSCSession, TotalTimeout: 0 * time.Millisecond, SocketTimeout: 30 * time.Second, MaxRetries: 2, @@ -153,7 +152,6 @@ func NewPolicy() *BasePolicy { SleepMultiplier: 1.0, ReplicaPolicy: SEQUENCE, SendKey: false, - LinearizeRead: false, UseCompression: false, } } diff --git a/predexp_ops_test.go b/predexp_ops_test.go index c2d22e63..10e7945d 100644 --- a/predexp_ops_test.go +++ b/predexp_ops_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/priority.go b/priority.go index 91fca9c7..e18bca08 100644 --- a/priority.go +++ b/priority.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/privilege.go b/privilege.go index d8ad92da..3fdd3836 100644 --- a/privilege.go +++ b/privilege.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Portions may be licensed to Aerospike, Inc. under one or more contributor // license agreements. diff --git a/query_aggregate_command.go b/query_aggregate_command.go index 5c44b52d..1dabca45 100644 --- a/query_aggregate_command.go +++ b/query_aggregate_command.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/query_aggregate_test.go b/query_aggregate_test.go index be54eda8..b61df19a 100644 --- a/query_aggregate_test.go +++ b/query_aggregate_test.go @@ -1,6 +1,6 @@ // +build !app_engine -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/query_command.go b/query_command.go index 2e11aa53..5510ea57 100644 --- a/query_command.go +++ b/query_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/query_objects_command.go b/query_objects_command.go index 4d242db0..ee42c737 100644 --- a/query_objects_command.go +++ b/query_objects_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/query_policy.go b/query_policy.go index 0c456363..6a5faf9b 100644 --- a/query_policy.go +++ b/query_policy.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/query_record_command.go b/query_record_command.go index 2c4550f6..d9f53d87 100644 --- a/query_record_command.go +++ b/query_record_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/query_test.go b/query_test.go index 8acf917c..eca01c10 100644 --- a/query_test.go +++ b/query_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/query_validate.go b/query_validate.go index ceed83b6..286e2018 100644 --- a/query_validate.go +++ b/query_validate.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/random_operation_test.go b/random_operation_test.go index 9a3defa1..3cd683fb 100644 --- a/random_operation_test.go +++ b/random_operation_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/read_command.go b/read_command.go index 65293d9f..9b6ca210 100644 --- a/read_command.go +++ b/read_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -47,12 +47,20 @@ var objectParser func( expiration uint32, ) error -func newReadCommand(cluster *Cluster, policy *BasePolicy, key *Key, binNames []string) readCommand { +func newReadCommand(cluster *Cluster, policy *BasePolicy, key *Key, binNames []string, partition *Partition) (readCommand, error) { + var err error + if partition == nil { + partition, err = PartitionForRead(cluster, policy, key) + if err != nil { + return readCommand{}, err + } + } + return readCommand{ - singleCommand: newSingleCommand(cluster, key), + singleCommand: newSingleCommand(cluster, key, partition), binNames: binNames, policy: policy, - } + }, nil } func (cmd *readCommand) getPolicy(ifc command) Policy { @@ -64,7 +72,12 @@ func (cmd *readCommand) writeBuffer(ifc command) error { } func (cmd *readCommand) getNode(ifc command) (*Node, error) { - return cmd.cluster.getReadNode(&cmd.partition, cmd.policy.ReplicaPolicy, &cmd.replicaSequence) + return cmd.partition.GetNodeRead(cmd.cluster) +} + +func (cmd *readCommand) prepareRetry(ifc command, isTimeout bool) bool { + cmd.partition.PrepareRetryRead(isTimeout) + return true } func (cmd *readCommand) parseResult(ifc command, conn *Connection) error { diff --git a/read_command_reflect.go b/read_command_reflect.go index c108e73b..3e64417c 100644 --- a/read_command_reflect.go +++ b/read_command_reflect.go @@ -1,6 +1,6 @@ // +build !as_performance -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/read_header_command.go b/read_header_command.go index d1f291f4..a8b7c0e3 100644 --- a/read_header_command.go +++ b/read_header_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -28,13 +28,18 @@ type readHeaderCommand struct { replicaSequence int } -func newReadHeaderCommand(cluster *Cluster, policy *BasePolicy, key *Key) *readHeaderCommand { - newReadHeaderCmd := &readHeaderCommand{ - singleCommand: newSingleCommand(cluster, key), +func newReadHeaderCommand(cluster *Cluster, policy *BasePolicy, key *Key) (readHeaderCommand, error) { + partition, err := PartitionForRead(cluster, policy, key) + if err != nil { + return readHeaderCommand{}, err + } + + newReadHeaderCmd := readHeaderCommand{ + singleCommand: newSingleCommand(cluster, key, partition), policy: policy, } - return newReadHeaderCmd + return newReadHeaderCmd, nil } func (cmd *readHeaderCommand) getPolicy(ifc command) Policy { @@ -46,7 +51,12 @@ func (cmd *readHeaderCommand) writeBuffer(ifc command) error { } func (cmd *readHeaderCommand) getNode(ifc command) (*Node, error) { - return cmd.cluster.getReadNode(&cmd.partition, cmd.policy.ReplicaPolicy, &cmd.replicaSequence) + return cmd.partition.GetNodeRead(cmd.cluster) +} + +func (cmd *readHeaderCommand) prepareRetry(ifc command, isTimeout bool) bool { + cmd.partition.PrepareRetryRead(isTimeout) + return true } func (cmd *readHeaderCommand) parseResult(ifc command, conn *Connection) error { diff --git a/consistency_level.go b/read_mode_ap.go similarity index 66% rename from consistency_level.go rename to read_mode_ap.go index 84030f67..c265a03e 100644 --- a/consistency_level.go +++ b/read_mode_ap.go @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 Aerospike, Inc. + * Copyright 2013-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. @@ -17,15 +17,16 @@ package aerospike -// ConsistencyLevel indicates how duplicates should be consulted in a read operation. +// 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. -type ConsistencyLevel int +type ReadModeAP int const ( - // CONSISTENCY_ONE indicates that only master should be involved in the read operation. - CONSISTENCY_ONE = iota + // ONE indicates that a single node should be involved in the read operation. + ReadModeAPOne ReadModeAP = iota - // CONSISTENCY_ALL indicates that all duplicates should be consulted in + // ALL indicates that all duplicates should be consulted in // the read operation. - CONSISTENCY_ALL + ReadModeAPAll ) diff --git a/read_mode_sc.go b/read_mode_sc.go new file mode 100644 index 00000000..f0ac21df --- /dev/null +++ b/read_mode_sc.go @@ -0,0 +1,40 @@ +/* + * Copyright 2013-2020 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements. + * + * 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 + +// ReadModeSC is the read policy in SC (strong consistency) mode namespaces. +// Determines SC read consistency options. +type ReadModeSC int + +const ( + // ReadModeSCSession ensures this client will only see an increasing sequence of record versions. + // Server only reads from master. This is the default. + ReadModeSCSession ReadModeSC = iota + + // ReadModeSCLinearize ensures ALL clients will only see an increasing sequence of record versions. + // Server only reads from master. + ReadModeSCLinearize + + // ReadModeSCAllowReplica indicates that the server may read from master or any full (non-migrating) replica. + // Increasing sequence of record versions is not guaranteed. + ReadModeSCAllowReplica + + // ReadModeSCAllowUnavailable indicates that the server may read from master or any full (non-migrating) replica or from unavailable + // partitions. Increasing sequence of record versions is not guaranteed. + ReadModeSCAllowUnavailable +) diff --git a/record.go b/record.go index 15a5920c..9e5ef8d9 100644 --- a/record.go +++ b/record.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/record_exists_action.go b/record_exists_action.go index b6d8c34f..6b344d45 100644 --- a/record_exists_action.go +++ b/record_exists_action.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/recordset.go b/recordset.go index 480a4f87..c3eee971 100644 --- a/recordset.go +++ b/recordset.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/recordset_test.go b/recordset_test.go index 62aa826d..7bb845f9 100644 --- a/recordset_test.go +++ b/recordset_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/replica_policy.go b/replica_policy.go index 9b728c5a..61f19574 100644 --- a/replica_policy.go +++ b/replica_policy.go @@ -1,5 +1,5 @@ /* - * Copyright 2013-2019 Aerospike, Inc. + * Copyright 2013-2020 Aerospike, Inc. * * Portions may be licensed to Aerospike, Inc. under one or more contributor * license agreements. diff --git a/role.go b/role.go index 662df095..5b28bf19 100644 --- a/role.go +++ b/role.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Portions may be licensed to Aerospike, Inc. under one or more contributor // license agreements. @@ -20,6 +20,7 @@ type Role struct { Name string Privileges []Privilege + Whitelist []string } // Pre-defined user roles. diff --git a/scan_command.go b/scan_command.go index 90d566a9..5dad2984 100644 --- a/scan_command.go +++ b/scan_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/scan_objects_command.go b/scan_objects_command.go index 6a2fb752..5437c15e 100644 --- a/scan_objects_command.go +++ b/scan_objects_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/scan_policy.go b/scan_policy.go index 75e72c22..f26d1437 100644 --- a/scan_policy.go +++ b/scan_policy.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/scan_test.go b/scan_test.go index 679f4b90..383ad8a7 100644 --- a/scan_test.go +++ b/scan_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/security_test.go b/security_test.go index db243610..440b9555 100644 --- a/security_test.go +++ b/security_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -60,10 +60,10 @@ var _ = Describe("Security tests", func() { defer client.DropRole(nil, "dummy-role") // Add a user defined Role - err := client.CreateRole(nil, "role-read-test-test", []as.Privilege{{Code: as.Read, Namespace: ns, SetName: "test"}}) + err := client.CreateRole(nil, "role-read-test-test", []as.Privilege{{Code: as.Read, Namespace: ns, SetName: "test"}}, []string{getOutboundIP().String()}) Expect(err).ToNot(HaveOccurred()) - err = client.CreateRole(nil, "role-write-test", []as.Privilege{{Code: as.ReadWrite, Namespace: ns, SetName: ""}}) + err = client.CreateRole(nil, "role-write-test", []as.Privilege{{Code: as.ReadWrite, Namespace: ns, SetName: ""}}, []string{getOutboundIP().String()}) Expect(err).ToNot(HaveOccurred()) time.Sleep(time.Second) @@ -79,7 +79,7 @@ var _ = Describe("Security tests", func() { err = client.RevokePrivileges(nil, "role-read-test-test", []as.Privilege{{Code: as.ReadWriteUDF, Namespace: ns, SetName: "test"}}) Expect(err).ToNot(HaveOccurred()) - err = client.CreateRole(nil, "dummy-role", []as.Privilege{{Code: as.Read, Namespace: "", SetName: ""}}) + err = client.CreateRole(nil, "dummy-role", []as.Privilege{{Code: as.Read, Namespace: "", SetName: ""}}, []string{getOutboundIP().String()}) Expect(err).ToNot(HaveOccurred()) time.Sleep(time.Second) diff --git a/server_command.go b/server_command.go index a58e0dae..1045e1c5 100644 --- a/server_command.go +++ b/server_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/single_command.go b/single_command.go index 6a36a69a..c57d13a0 100644 --- a/single_command.go +++ b/single_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,15 +23,15 @@ type singleCommand struct { cluster *Cluster key *Key - partition Partition + partition *Partition } -func newSingleCommand(cluster *Cluster, key *Key) singleCommand { +func newSingleCommand(cluster *Cluster, key *Key, partition *Partition) singleCommand { return singleCommand{ baseCommand: baseCommand{}, cluster: cluster, key: key, - partition: newPartitionByKey(key), + partition: partition, } } diff --git a/statement.go b/statement.go index 5f5b3206..d7952125 100644 --- a/statement.go +++ b/statement.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/task.go b/task.go index 163ef83c..8d48520d 100644 --- a/task.go +++ b/task.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/task_drop_index.go b/task_drop_index.go index e961b792..2f72887b 100644 --- a/task_drop_index.go +++ b/task_drop_index.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/task_index.go b/task_index.go index 5fd4edf2..f74b32c6 100644 --- a/task_index.go +++ b/task_index.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/task_register.go b/task_register.go index 130e41d7..287ec249 100644 --- a/task_register.go +++ b/task_register.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/task_remove.go b/task_remove.go index 9ef80fcc..5f2bb6c5 100644 --- a/task_remove.go +++ b/task_remove.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/test_utils_test.go b/test_utils_test.go index c1e167f6..eddc2d8b 100644 --- a/test_utils_test.go +++ b/test_utils_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,7 +15,9 @@ package aerospike_test import ( + "log" "math/rand" + "net" "reflect" . "github.com/onsi/gomega" @@ -122,3 +124,15 @@ func mapsEqual(ia, ib interface{}) { Expect(len(a)).To(Equal(len(b))) Expect(a).To(BeEquivalentTo(b)) } + +func getOutboundIP() net.IP { + conn, err := net.Dial("udp", "8.8.8.8:80") + if err != nil { + log.Fatal(err) + } + defer conn.Close() + + localAddr := conn.LocalAddr().(*net.UDPAddr) + + return localAddr.IP +} diff --git a/tools/asinfo/asinfo.go b/tools/asinfo/asinfo.go index 7caf05c5..7dc3f2f5 100644 --- a/tools/asinfo/asinfo.go +++ b/tools/asinfo/asinfo.go @@ -1,4 +1,4 @@ -// Copyright 2013-2016 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/tools/benchmark/benchmark.go b/tools/benchmark/benchmark.go index 75955d7f..9eb4ff08 100644 --- a/tools/benchmark/benchmark.go +++ b/tools/benchmark/benchmark.go @@ -1,4 +1,4 @@ -// Copyright 2013-2016 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -365,7 +365,7 @@ func randBytes(size int, xr *XorRand) []byte { } func incOnError(op, timeout *int, err error) { - if ae, ok := err.(ast.AerospikeError); ok && ae.ResultCode() == ast.TIMEOUT { + if ae, ok := err.(ast.AerospikeError); ok && (ae.ResultCode() == ast.TIMEOUT || err == ast.ErrConnectionPoolEmpty || err == ast.ErrTooManyConnectionsForNode || err == ast.ErrTooManyOpeningConnections) { *timeout++ } else { *op++ diff --git a/tools/cli/cli.go b/tools/cli/cli.go index 9eca79f4..644c2265 100644 --- a/tools/cli/cli.go +++ b/tools/cli/cli.go @@ -1,4 +1,4 @@ -// Copyright 2013-2016 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/touch_command.go b/touch_command.go index c90c8ef0..1399c770 100644 --- a/touch_command.go +++ b/touch_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -31,13 +31,18 @@ type touchCommand struct { policy *WritePolicy } -func newTouchCommand(cluster *Cluster, policy *WritePolicy, key *Key) *touchCommand { - newTouchCmd := &touchCommand{ - singleCommand: newSingleCommand(cluster, key), +func newTouchCommand(cluster *Cluster, policy *WritePolicy, key *Key) (touchCommand, error) { + partition, err := PartitionForWrite(cluster, &policy.BasePolicy, key) + if err != nil { + return touchCommand{}, err + } + + newTouchCmd := touchCommand{ + singleCommand: newSingleCommand(cluster, key, partition), policy: policy, } - return newTouchCmd + return newTouchCmd, nil } func (cmd *touchCommand) getPolicy(ifc command) Policy { @@ -49,7 +54,12 @@ func (cmd *touchCommand) writeBuffer(ifc command) error { } func (cmd *touchCommand) getNode(ifc command) (*Node, error) { - return cmd.cluster.getMasterNode(&cmd.partition) + return cmd.partition.GetNodeWrite(cmd.cluster) +} + +func (cmd *touchCommand) prepareRetry(ifc command, isTimeout bool) bool { + cmd.partition.PrepareRetryWrite(isTimeout) + return true } func (cmd *touchCommand) parseResult(ifc command, conn *Connection) error { diff --git a/truncate_test.go b/truncate_test.go index cbb58bbe..ed397f24 100644 --- a/truncate_test.go +++ b/truncate_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/types/buffer_pool.go b/types/buffer_pool.go index ec72ca4a..74e85ad9 100644 --- a/types/buffer_pool.go +++ b/types/buffer_pool.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/types/error.go b/types/error.go index d2d364d7..61568fdb 100644 --- a/types/error.go +++ b/types/error.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/types/message.go b/types/message.go index 23523a90..da57fa91 100644 --- a/types/message.go +++ b/types/message.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/types/pool.go b/types/pool.go index b6ffd318..2ed7c8eb 100644 --- a/types/pool.go +++ b/types/pool.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/types/rand/xor_shift128.go b/types/rand/xor_shift128.go index c8ea6a4d..e645c25d 100644 --- a/types/rand/xor_shift128.go +++ b/types/rand/xor_shift128.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/types/result_code.go b/types/result_code.go index 60a63be6..c871fa92 100644 --- a/types/result_code.go +++ b/types/result_code.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -187,6 +187,9 @@ const ( // Security credential is invalid. INVALID_CREDENTIAL ResultCode = 65 + // Login session expired. + EXPIRED_SESSION ResultCode = 66 + // Role name is invalid. INVALID_ROLE ResultCode = 70 @@ -196,12 +199,18 @@ const ( // Privilege is invalid. INVALID_PRIVILEGE ResultCode = 72 + // Invalid IP address whiltelist + INVALID_WHITELIST = 73 + // User must be authentication before performing database operations. NOT_AUTHENTICATED ResultCode = 80 // User does not posses the required role to perform the database operation. ROLE_VIOLATION ResultCode = 81 + // Command not allowed because sender IP address not whitelisted. + NOT_WHITELISTED = 82 + // A user defined function returned an error code. UDF_BAD_RESPONSE ResultCode = 100 @@ -457,6 +466,9 @@ func ResultCodeToString(resultCode ResultCode) string { case INVALID_CREDENTIAL: return "Invalid credential" + case EXPIRED_SESSION: + return "Login session expired" + case INVALID_ROLE: return "Invalid role" @@ -466,12 +478,18 @@ func ResultCodeToString(resultCode ResultCode) string { case INVALID_PRIVILEGE: return "Invalid privilege" + case INVALID_WHITELIST: + return "Invalid whitelist" + case NOT_AUTHENTICATED: return "Not authenticated" case ROLE_VIOLATION: return "Role violation" + case NOT_WHITELISTED: + return "Command not whitelisted" + case UDF_BAD_RESPONSE: return "UDF returned error" diff --git a/types/types.go b/types/types.go index 946c0ee8..84bbc098 100644 --- a/types/types.go +++ b/types/types.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/types/types_test.go b/types/types_test.go index e3b1284c..e6cccbf2 100644 --- a/types/types_test.go +++ b/types/types_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/udf_test.go b/udf_test.go index 2e24deb2..17383865 100644 --- a/udf_test.go +++ b/udf_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/unpacker.go b/unpacker.go index 6307ce2b..378afcf0 100644 --- a/unpacker.go +++ b/unpacker.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/user_roles.go b/user_roles.go index d8059ac4..a9461a33 100644 --- a/user_roles.go +++ b/user_roles.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Portions may be licensed to Aerospike, Inc. under one or more contributor // license agreements. diff --git a/utils/buffer/buffer.go b/utils/buffer/buffer.go index 297aa8c9..6bd54fbe 100644 --- a/utils/buffer/buffer.go +++ b/utils/buffer/buffer.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/value.go b/value.go index 863ec582..9850ada7 100644 --- a/value.go +++ b/value.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/value_helpers.go b/value_helpers.go index 77daa25d..e01a3e3e 100644 --- a/value_helpers.go +++ b/value_helpers.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/value_reflect.go b/value_reflect.go index f95b991c..340eab35 100644 --- a/value_reflect.go +++ b/value_reflect.go @@ -1,6 +1,6 @@ // +build !as_performance -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/value_test.go b/value_test.go index c9880da5..58807106 100644 --- a/value_test.go +++ b/value_test.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/write_command.go b/write_command.go index b31c8065..679e0e39 100644 --- a/write_command.go +++ b/write_command.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -36,17 +36,22 @@ func newWriteCommand(cluster *Cluster, key *Key, bins []*Bin, binMap BinMap, - operation OperationType) *writeCommand { + operation OperationType) (writeCommand, error) { - newWriteCmd := &writeCommand{ - singleCommand: newSingleCommand(cluster, key), + partition, err := PartitionForWrite(cluster, &policy.BasePolicy, key) + if err != nil { + return writeCommand{}, err + } + + newWriteCmd := writeCommand{ + singleCommand: newSingleCommand(cluster, key, partition), policy: policy, bins: bins, binMap: binMap, operation: operation, } - return newWriteCmd + return newWriteCmd, nil } func (cmd *writeCommand) getPolicy(ifc command) Policy { @@ -58,7 +63,12 @@ func (cmd *writeCommand) writeBuffer(ifc command) error { } func (cmd *writeCommand) getNode(ifc command) (*Node, error) { - return cmd.cluster.getMasterNode(&cmd.partition) + return cmd.partition.GetNodeWrite(cmd.cluster) +} + +func (cmd *writeCommand) prepareRetry(ifc command, isTimeout bool) bool { + cmd.partition.PrepareRetryWrite(isTimeout) + return true } func (cmd *writeCommand) parseResult(ifc command, conn *Connection) error { diff --git a/write_policy.go b/write_policy.go index 6e325a72..27e2ebd7 100644 --- a/write_policy.go +++ b/write_policy.go @@ -1,4 +1,4 @@ -// Copyright 2013-2019 Aerospike, Inc. +// Copyright 2013-2020 Aerospike, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License.