Skip to content

Commit 029420b

Browse files
authored
Merge pull request #393 from aerospike/v6.7.0
v6.7.0
2 parents 65bfc45 + c1be1aa commit 029420b

9 files changed

+198
-18
lines changed

CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Change History
22

3+
## December 5 2022: v6.7.0
4+
5+
This is a minor improvement and fix release.
6+
7+
* **Improvements**
8+
9+
- Improves testing for float64 precision formatting between amd64 and aarch64.
10+
11+
* **Fixes**
12+
13+
- [CLIENT-2019] Write Batch operation with an invalid namespace record causes all batch transactions to fail.
14+
- [CLIENT-2020] Support `QueryPartitions` with non-nil filter (secondary index query)
15+
316
## November 8 2022: v6.6.0
417

518
This is a minor improvement and fix release.

batch_node_list.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,10 @@ func newBatchOperateNodeListIfc(cluster *Cluster, policy *BatchPolicy, records [
276276
}
277277

278278
if err != nil {
279+
records[i].chainError(err)
280+
records[i].setError(err.resultCode(), false)
279281
errs = chainErrors(err, errs)
280-
// return nil, err
282+
continue
281283
}
282284

283285
if batchNode := findBatchNode(batchNodes, node); batchNode == nil {

batch_test.go

+90
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,96 @@ var _ = gg.Describe("Aerospike", func() {
264264
})
265265

266266
gg.Context("BatchUDF operations", func() {
267+
gg.It("must return the results when one operation is against an invalid namespace", func() {
268+
luaCode := []byte(`-- Create a record
269+
function rec_create(rec, bins)
270+
if bins ~= nil then
271+
for b, bv in map.pairs(bins) do
272+
rec[b] = bv
273+
end
274+
end
275+
status = aerospike:create(rec)
276+
return status
277+
end`)
278+
279+
client.RemoveUDF(nil, "test_ops.lua")
280+
client.RegisterUDF(nil, luaCode, "test_ops.lua", as.LUA)
281+
282+
batchRecords := []as.BatchRecordIfc{}
283+
284+
key1, _ := as.NewKey(randString(10), set, 1)
285+
args := make(map[interface{}]interface{})
286+
args["bin1_str"] = "a"
287+
batchRecords = append(batchRecords, as.NewBatchUDF(
288+
nil,
289+
key1,
290+
"test_ops",
291+
"rec_create",
292+
as.NewMapValue(args),
293+
))
294+
295+
key2, _ := as.NewKey(ns, set, 2)
296+
batchRecords = append(batchRecords, as.NewBatchWrite(
297+
nil,
298+
key2,
299+
as.PutOp(as.NewBin("bin1_str", "aa")),
300+
))
301+
302+
key3, _ := as.NewKey(ns, set, 3)
303+
batchRecords = append(batchRecords, as.NewBatchWrite(
304+
nil,
305+
key3,
306+
as.PutOp(as.NewBin("bin1_str", "aaa")),
307+
))
308+
309+
batchRecords = append(batchRecords, as.NewBatchRead(
310+
key1,
311+
[]string{"bin1_str"},
312+
))
313+
314+
batchRecords = append(batchRecords, as.NewBatchRead(
315+
key2,
316+
[]string{"bin1_str"},
317+
))
318+
319+
batchRecords = append(batchRecords, as.NewBatchRead(
320+
key3,
321+
[]string{"bin1_str"},
322+
))
323+
324+
bp := as.NewBatchPolicy()
325+
bp.RespondAllKeys = false
326+
err := client.BatchOperate(bp, batchRecords)
327+
gm.Expect(err).ToNot(gm.HaveOccurred())
328+
329+
gm.Expect(batchRecords[0].BatchRec().Err.Matches(types.INVALID_NAMESPACE)).To(gm.BeTrue())
330+
gm.Expect(batchRecords[0].BatchRec().ResultCode).To(gm.Equal(types.INVALID_NAMESPACE))
331+
332+
gm.Expect(batchRecords[1].BatchRec().Err).To(gm.BeNil())
333+
gm.Expect(batchRecords[1].BatchRec().ResultCode).To(gm.Equal(types.OK))
334+
gm.Expect(batchRecords[1].BatchRec().Record.Bins).To(gm.Equal(as.BinMap{"bin1_str": nil}))
335+
336+
gm.Expect(batchRecords[2].BatchRec().Err).To(gm.BeNil())
337+
gm.Expect(batchRecords[2].BatchRec().ResultCode).To(gm.Equal(types.OK))
338+
gm.Expect(batchRecords[2].BatchRec().Record.Bins).To(gm.Equal(as.BinMap{"bin1_str": nil}))
339+
340+
gm.Expect(batchRecords[3].BatchRec().Err.Matches(types.INVALID_NAMESPACE)).To(gm.BeTrue())
341+
gm.Expect(batchRecords[3].BatchRec().ResultCode).To(gm.Equal(types.INVALID_NAMESPACE))
342+
343+
gm.Expect(batchRecords[4].BatchRec().Err).To(gm.BeNil())
344+
gm.Expect(batchRecords[4].BatchRec().ResultCode).To(gm.Equal(types.OK))
345+
gm.Expect(batchRecords[4].BatchRec().Record.Bins).To(gm.Equal(as.BinMap{"bin1_str": "aa"}))
346+
347+
gm.Expect(batchRecords[5].BatchRec().Err).To(gm.BeNil())
348+
gm.Expect(batchRecords[5].BatchRec().ResultCode).To(gm.Equal(types.OK))
349+
gm.Expect(batchRecords[5].BatchRec().Record.Bins).To(gm.Equal(as.BinMap{"bin1_str": "aaa"}))
350+
351+
bp.RespondAllKeys = true
352+
err = client.BatchOperate(bp, batchRecords)
353+
gm.Expect(err).To(gm.HaveOccurred())
354+
gm.Expect(err.Matches(types.INVALID_NAMESPACE)).To(gm.BeTrue())
355+
})
356+
267357
gg.It("must return the result with same ordering", func() {
268358
const keyCount = 50
269359
keys := []*as.Key{}

client.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ func (clnt *Client) BatchOperate(policy *BatchPolicy, records []BatchRecordIfc)
542542
policy = clnt.getUsableBatchPolicy(policy)
543543

544544
batchNodes, err := newBatchOperateNodeListIfc(clnt.cluster, policy, records)
545-
if err != nil {
545+
if err != nil && policy.RespondAllKeys {
546546
return err
547547
}
548548

@@ -1024,10 +1024,6 @@ func parseIndexErrorCode(response string) types.ResultCode {
10241024
// This method is only supported by Aerospike 4.9+ servers.
10251025
// If the policy is nil, the default relevant policy will be used.
10261026
func (clnt *Client) QueryPartitions(policy *QueryPolicy, statement *Statement, partitionFilter *PartitionFilter) (*Recordset, Error) {
1027-
if statement.Filter != nil {
1028-
return nil, ErrPartitionScanQueryNotSupported.err()
1029-
}
1030-
10311027
policy = clnt.getUsableQueryPolicy(policy)
10321028
nodes := clnt.cluster.GetNodes()
10331029
if len(nodes) == 0 {

client_object_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1123,7 +1123,7 @@ var _ = gg.Describe("Aerospike", func() {
11231123
gm.Expect(err).ToNot(gm.HaveOccurred())
11241124

11251125
gm.Expect(t.Test).To(gm.Equal(float64(i)))
1126-
gm.Expect(t.TestLua).To(gm.Equal(testLua))
1126+
gm.Expect(t.TestLua).To(gm.BeNumerically("~", testLua))
11271127
}
11281128

11291129
}) // it

multi_command.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func (cmd *baseMultiCommand) parseKey(fieldCount int, bval *int64) (*Key, Error)
218218
return nil, err
219219
}
220220
case BVAL_ARRAY:
221-
*bval = Buffer.LittleBytesToInt64(cmd.dataBuffer, cmd.dataOffset)
221+
*bval = Buffer.LittleBytesToInt64(cmd.dataBuffer, 1)
222222
}
223223
}
224224

query_executor.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,13 @@ func (clnt *Client) queryPartitions(policy *QueryPolicy, tracker *partitionTrack
4343
cmd := newQueryPartitionCommand(policy, tracker, nodePartition, statement, recordset)
4444
weg.execute(cmd)
4545
}
46-
// no need to manage the errors; they are send back via the recordset
47-
weg.wait()
46+
errs = chainErrors(weg.wait(), errs)
4847

4948
done, err := tracker.isComplete(clnt.Cluster(), &policy.BasePolicy)
5049
if done || err != nil {
50+
errs = chainErrors(err, errs)
5151
// Query is complete.
52-
if err != nil {
53-
errs = chainErrors(err, errs)
52+
if errs != nil {
5453
recordset.sendError(errs)
5554
}
5655
return

query_test.go

+83-2
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,11 @@ var _ = gg.Describe("Query operations", func() {
5858
bin4 := as.NewBin("Aerospike4", "constValue")
5959
bin5 := as.NewBin("Aerospike5", -1)
6060
bin6 := as.NewBin("Aerospike6", 1)
61+
bin7 := as.NewBin("Aerospike7", nil)
6162
var keys map[string]*as.Key
6263
var indexName string
6364
var indexName2 string
65+
var indexName3 string
6466

6567
// read all records from the channel and make sure all of them are returned
6668
var checkResults = func(recordset *as.Recordset, cancelCnt int) {
@@ -118,7 +120,8 @@ var _ = gg.Describe("Query operations", func() {
118120

119121
keys[string(key.Digest())] = key
120122
bin3 = as.NewBin("Aerospike3", rand.Intn(math.MaxInt16))
121-
err = client.PutBins(wpolicy, key, bin1, bin2, bin3, bin4, bin5, bin6)
123+
bin7 = as.NewBin("Aerospike7", i%3)
124+
err = client.PutBins(wpolicy, key, bin1, bin2, bin3, bin4, bin5, bin6, bin7)
122125
gm.Expect(err).ToNot(gm.HaveOccurred())
123126
}
124127

@@ -129,6 +132,10 @@ var _ = gg.Describe("Query operations", func() {
129132
// queries only work on indices
130133
indexName2 = set + bin6.Name
131134
createIndex(wpolicy, ns, set, indexName2, bin6.Name, as.NUMERIC)
135+
136+
// queries only work on indices
137+
indexName3 = set + bin7.Name
138+
createIndex(wpolicy, ns, set, indexName3, bin7.Name, as.NUMERIC)
132139
})
133140

134141
gg.AfterEach(func() {
@@ -137,6 +144,9 @@ var _ = gg.Describe("Query operations", func() {
137144

138145
indexName = set + bin6.Name
139146
gm.Expect(client.DropIndex(nil, ns, set, indexName)).ToNot(gm.HaveOccurred())
147+
148+
indexName = set + bin7.Name
149+
gm.Expect(client.DropIndex(nil, ns, set, indexName)).ToNot(gm.HaveOccurred())
140150
})
141151

142152
var queryPolicy = as.NewQueryPolicy()
@@ -170,7 +180,7 @@ var _ = gg.Describe("Query operations", func() {
170180
gm.Expect(counter).To(gm.Equal(keyCount))
171181
})
172182

173-
gg.It("must Query and get all partition records back for a specified key", func() {
183+
gg.It("must Scan and get all partition records back for a specified key", func() {
174184
gm.Expect(len(keys)).To(gm.Equal(keyCount))
175185

176186
counter := 0
@@ -203,6 +213,77 @@ var _ = gg.Describe("Query operations", func() {
203213
gm.Expect(counter).To(gm.BeNumerically("<", keyCount))
204214
})
205215

216+
gg.It("must Query per key partition and get all partition records back for a specified key and filter", func() {
217+
gm.Expect(len(keys)).To(gm.Equal(keyCount))
218+
219+
counter := 0
220+
221+
var rkey *as.Key
222+
for _, k := range keys {
223+
rkey = k
224+
225+
pf := as.NewPartitionFilterByKey(rkey)
226+
stm := as.NewStatement(ns, set)
227+
stm.SetFilter(as.NewRangeFilter(bin7.Name, 1, 2))
228+
recordset, err := client.QueryPartitions(queryPolicy, stm, pf)
229+
gm.Expect(err).ToNot(gm.HaveOccurred())
230+
231+
for res := range recordset.Results() {
232+
gm.Expect(res.Err).NotTo(gm.HaveOccurred())
233+
gm.Expect(res.Record.Bins[bin1.Name]).To(gm.Equal(bin1.Value.GetObject()))
234+
gm.Expect(res.Record.Bins[bin2.Name]).To(gm.Equal(bin2.Value.GetObject()))
235+
236+
delete(keys, string(res.Record.Key.Digest()))
237+
238+
counter++
239+
}
240+
}
241+
242+
gm.Expect(len(keys)).To(gm.Equal(334))
243+
// This depends on how many keys end up in the same partition.
244+
// Since keys are statistically distributed randomly and uniformly,
245+
// we expect that there aren't many partitions that share more than one key.
246+
gm.Expect(counter).To(gm.BeNumerically("~", keyCount - 334, 50))
247+
})
248+
249+
gg.It("must Query and get all partition records back for a specified key and filter", func() {
250+
gm.Expect(len(keys)).To(gm.Equal(keyCount))
251+
252+
counter := 0
253+
254+
pf := as.NewPartitionFilterAll()
255+
stm := as.NewStatement(ns, set)
256+
stm.SetFilter(as.NewRangeFilter(bin7.Name, 1, 2))
257+
recordset, err := client.QueryPartitions(queryPolicy, stm, pf)
258+
gm.Expect(err).ToNot(gm.HaveOccurred())
259+
260+
for res := range recordset.Results() {
261+
gm.Expect(res.Err).NotTo(gm.HaveOccurred())
262+
gm.Expect(res.Record.Bins[bin1.Name]).To(gm.Equal(bin1.Value.GetObject()))
263+
gm.Expect(res.Record.Bins[bin2.Name]).To(gm.Equal(bin2.Value.GetObject()))
264+
265+
delete(keys, string(res.Record.Key.Digest()))
266+
267+
counter++
268+
}
269+
270+
gm.Expect(len(keys)).To(gm.Equal(334))
271+
gm.Expect(counter).To(gm.Equal(keyCount - 334))
272+
})
273+
274+
gg.It("must return error on a Query when index is not found", func() {
275+
pf := as.NewPartitionFilterAll()
276+
stm := as.NewStatement(ns, set)
277+
stm.SetFilter(as.NewRangeFilter(randString(10), 1, 2))
278+
recordset, err := client.QueryPartitions(queryPolicy, stm, pf)
279+
gm.Expect(err).ToNot(gm.HaveOccurred())
280+
281+
for res := range recordset.Results() {
282+
gm.Expect(res.Err).To(gm.HaveOccurred())
283+
gm.Expect(res.Err.Matches(ast.INDEX_NOTFOUND)).To(gm.BeTrue())
284+
}
285+
})
286+
206287
gg.It("must Query and get all partition records back for a specified partition range", func() {
207288
gm.Expect(len(keys)).To(gm.Equal(keyCount))
208289

scan_executor.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,12 @@ func (clnt *Client) scanPartitions(policy *ScanPolicy, tracker *partitionTracker
4343
cmd := newScanPartitionCommand(policy, tracker, nodePartition, namespace, setName, binNames, recordset)
4444
weg.execute(cmd)
4545
}
46-
// no need to manage the errors; they are send back via the recordset
47-
weg.wait()
46+
errs = chainErrors(weg.wait(), errs)
4847

4948
if done, err := tracker.isComplete(clnt.Cluster(), &policy.BasePolicy); done || err != nil {
49+
errs = chainErrors(err, errs)
5050
// Scan is complete.
51-
if err != nil {
52-
errs = chainErrors(err, errs)
51+
if errs != nil {
5352
recordset.sendError(errs)
5453
}
5554
return

0 commit comments

Comments
 (0)