Skip to content

Commit

Permalink
[API-2247] Fixed map.GetAll near cache behavior and ouput (hazelcast#998
Browse files Browse the repository at this point in the history
)

* Fixes `map.GetAll` behavior when near cache is enabled.
* Fixes `map.GetAll` output when there are missing keys.
  • Loading branch information
yuce authored Aug 1, 2024
1 parent 4d5e3f4 commit 16cd443
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 26 deletions.
4 changes: 2 additions & 2 deletions client_it_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2023, Hazelcast, Inc. All Rights Reserved.
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -897,7 +897,7 @@ func clientFixConnectionTest(t *testing.T) {
func clientVersionTest(t *testing.T) {
t.Parallel()
// adding this test here, so there's no "unused lint warning.
assert.Equal(t, "1.4.1", hz.ClientVersion)
assert.Equal(t, "1.4.2", hz.ClientVersion)
}

func clientInvocationTimeoutTest(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions internal/common.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2023, Hazelcast, Inc. All Rights Reserved.
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
Expand All @@ -19,7 +19,7 @@ package internal
const (
AggregateFactoryID = -29
// CurrentClientVersion should be manually set
CurrentClientVersion = "1.4.1"
CurrentClientVersion = "1.4.2"
)

// ClientType is used in the Management Center
Expand Down
119 changes: 119 additions & 0 deletions nearcache/sup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* 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 nearcache_test

import (
"context"
"testing"

"github.com/stretchr/testify/require"

"github.com/hazelcast/hazelcast-go-client/internal/it"
"github.com/hazelcast/hazelcast-go-client/nearcache"
"github.com/hazelcast/hazelcast-go-client/types"
)

func TestGetAllAfterInvalidation(t *testing.T) {
tcx := newNearCacheMapTestContext(t, nearcache.InMemoryFormatObject, false)
tcx.Tester(func(tcx it.MapTestContext) {
t := tcx.T
m := tcx.M
ctx := context.Background()
var keys []interface{}
var target []types.Entry
const size = 3
for i := int64(0); i < size; i++ {
if _, err := m.Put(ctx, i, i); err != nil {
t.Fatal(err)
}
keys = append(keys, i)
target = append(target, types.Entry{Key: i, Value: i})
}
vs, err := m.GetAll(ctx, keys...)
if err != nil {
t.Fatal(err)
}
require.ElementsMatch(t, target, vs)
stats := m.LocalMapStats().NearCacheStats
require.Equal(t, int64(size), stats.OwnedEntryCount)
// change just one of the values
target[1] = types.Entry{
Key: keys[1],
Value: "foo",
}
it.Must(m.Set(ctx, target[1].Key, target[1].Value))
vs, err = m.GetAll(ctx, keys...)
if err != nil {
t.Fatal(err)
}
require.ElementsMatch(t, target, vs)
})
}

func TestGetAllNonexistentEntries(t *testing.T) {
tcx := newNearCacheMapTestContext(t, nearcache.InMemoryFormatObject, false)
tcx.Tester(func(tcx it.MapTestContext) {
t := tcx.T
m := tcx.M
ctx := context.Background()
var keys []interface{}
var target []types.Entry
const size = 3
for i := int64(0); i < size; i++ {
keys = append(keys, i)
}
// no values
vs, err := m.GetAll(ctx, keys...)
if err != nil {
t.Fatal(err)
}
require.ElementsMatch(t, target, vs)
// single value
it.Must(m.Set(ctx, keys[0], "value0"))
vs, err = m.GetAll(ctx, keys...)
if err != nil {
t.Fatal(err)
}
target = []types.Entry{
{Key: keys[0], Value: "value0"},
}
require.ElementsMatch(t, target, vs)
// two values
it.Must(m.Set(ctx, keys[2], "value2"))
vs, err = m.GetAll(ctx, keys...)
if err != nil {
t.Fatal(err)
}
target = []types.Entry{
{Key: keys[0], Value: "value0"},
{Key: keys[2], Value: "value2"},
}
require.ElementsMatch(t, target, vs)
// three values
it.Must(m.Set(ctx, keys[1], "value1"))
vs, err = m.GetAll(ctx, keys...)
if err != nil {
t.Fatal(err)
}
target = []types.Entry{
{Key: keys[0], Value: "value0"},
{Key: keys[1], Value: "value1"},
{Key: keys[2], Value: "value2"},
}
require.ElementsMatch(t, target, vs)
})
}
40 changes: 18 additions & 22 deletions nearcache_map.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2022, Hazelcast, Inc. All Rights Reserved.
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -246,27 +246,26 @@ func (ncm *nearCacheMap) Get(ctx context.Context, m *Map, key interface{}) (inte

func (ncm *nearCacheMap) GetAll(ctx context.Context, m *Map, keys []interface{}) ([]types.Entry, error) {
// see: com.hazelcast.client.map.impl.nearcache.NearCachedClientMapProxy#getAllInternal
entries := make([]types.Entry, len(keys))
missKeys, err := ncm.populateResultFromNearCache(keys, entries)
missKeys, entries, err := ncm.populateResultFromNearCache(keys)
if err != nil {
return nil, fmt.Errorf("nearCacheMap.GetAll: populating result from near cache: %w", err)
}
if len(missKeys) == 0 {
return entries, nil
}
keyDatas := make([]serialization.Data, len(keys))
keyDatas := make([]serialization.Data, len(missKeys))
for i, k := range missKeys {
kd, err := ncm.ss.ToData(k)
if err != nil {
return nil, err
}
keyDatas[i] = kd
}
resMap, err := ncm.getNearCacheReservations(keys, keyDatas)
resMap, err := ncm.getNearCacheReservations(missKeys, keyDatas)
if err != nil {
return nil, err
}
partitionToKeys, err := m.partitionToKeys(keys, false)
partitionToKeys, err := m.partitionToKeys(missKeys, false)
if err != nil {
return nil, err
}
Expand All @@ -275,11 +274,11 @@ func (ncm *nearCacheMap) GetAll(ctx context.Context, m *Map, keys []interface{})
if err != nil {
return nil, fmt.Errorf("nearCacheMap.GetAll: getting keys from remote: %w", err)
}
keyCount, err := ncm.populateResultFromRemote(pairs, entries, resMap)
entries, err = ncm.populateResultFromRemote(pairs, entries, resMap)
if err != nil {
return nil, err
}
return entries[:keyCount], nil
return entries, nil
}

func (ncm *nearCacheMap) LoadAll(ctx context.Context, m *Map, replaceExisting bool, keys []interface{}) error {
Expand Down Expand Up @@ -498,27 +497,26 @@ func (ncm *nearCacheMap) handleBatchInvalidationMsg(rth *inearcache.RepairingHan
return rth.HandleBatch(keys, sources, partitions, seqs)
}

func (ncm *nearCacheMap) populateResultFromNearCache(keys []interface{}, entries []types.Entry) ([]interface{}, error) {
func (ncm *nearCacheMap) populateResultFromNearCache(keys []interface{}) ([]interface{}, []types.Entry, error) {
// see: com.hazelcast.client.map.impl.nearcache.NearCachedClientMapProxy#populateResultFromNearCache
var entries []types.Entry
var missKeys []interface{}
var i int
for _, k := range keys {
nk, err := ncm.toNearCacheKey(k)
if err != nil {
return nil, err
return nil, nil, err
}
cached, ok, err := ncm.getCachedValue(nk, true)
if err != nil {
return nil, err
return nil, nil, err
}
if ok && cached != nil {
entries[i] = types.Entry{Key: k, Value: cached}
i++
entries = append(entries, types.Entry{Key: k, Value: cached})
continue
}
missKeys = append(missKeys, k)
}
return missKeys, nil
return missKeys, entries, nil
}

func (ncm *nearCacheMap) getNearCacheReservations(keys []interface{}, keyDatas []serialization.Data) (map[inearcache.DataString]keyReservation, error) {
Expand Down Expand Up @@ -550,16 +548,15 @@ func (ncm *nearCacheMap) releaseRemainingReservedKeys(rks map[inearcache.DataStr
}
}

func (ncm *nearCacheMap) populateResultFromRemote(pairs []proto.Pair, entries []types.Entry, reservations map[inearcache.DataString]keyReservation) (int, error) {
func (ncm *nearCacheMap) populateResultFromRemote(pairs []proto.Pair, entries []types.Entry, reservations map[inearcache.DataString]keyReservation) ([]types.Entry, error) {
// see: com.hazelcast.client.map.impl.nearcache.NearCachedClientMapProxy#populateResultFromRemote
// assumes entries has max capacity.
serialize := ncm.serializeKeys
i := len(entries) - len(pairs)
for _, p := range pairs {
kd := p.Key.(serialization.Data)
k, err := ncm.ss.ToObject(kd)
if err != nil {
return 0, err
return nil, err
}
e := types.Entry{Key: k}
kds := inearcache.DataString(kd)
Expand All @@ -573,12 +570,11 @@ func (ncm *nearCacheMap) populateResultFromRemote(pairs []proto.Pair, entries []
vd := p.Value.(serialization.Data)
v, err := ncm.nc.TryPublishReserved(k, vd, kr.ID)
if err != nil {
return 0, err
return nil, err
}
e.Value = v
entries[i] = e
i++
entries = append(entries, e)
delete(reservations, kds)
}
return i, nil
return entries, nil
}

0 comments on commit 16cd443

Please sign in to comment.