/* * Copyright (c) 2008-2017, 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 com.hazelcast.map; import com.hazelcast.config.Config; import com.hazelcast.config.InMemoryFormat; import com.hazelcast.config.MapConfig; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.IMap; import com.hazelcast.core.Offloadable; import com.hazelcast.core.ReadOnly; import com.hazelcast.test.HazelcastSerialClassRunner; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.annotation.SlowTest; import com.hazelcast.test.bounce.BounceMemberRule; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import java.io.Serializable; import java.util.Map; import static java.util.concurrent.TimeUnit.MINUTES; @RunWith(HazelcastSerialClassRunner.class) @Category(SlowTest.class) public class EntryProcessorOffloadableBouncingNodesTest extends HazelcastTestSupport { public static final String MAP_NAME = "EntryProcessorOffloadableTest"; public static final int COUNT_ENTRIES = 1000; private static final int CONCURRENCY = Runtime.getRuntime().availableProcessors(); @Rule public BounceMemberRule bounceMemberRule = BounceMemberRule.with(getBouncingTestConfig()).build(); private void populateMap(IMap<Integer, SimpleValue> map) { for (int i = 0; i < COUNT_ENTRIES; i++) { map.put(i, new SimpleValue(i)); } } public Config getBouncingTestConfig() { Config config = getConfig(); MapConfig mapConfig = new MapConfig(MAP_NAME); mapConfig.setInMemoryFormat(InMemoryFormat.OBJECT); mapConfig.setAsyncBackupCount(1); mapConfig.setBackupCount(0); config.addMapConfig(mapConfig); return config; } @Test public void testOffloadableEntryProcessor() { populateMap(getMap()); testForKey(0); } private void testForKey(int key) { EntryProcessorRunnable[] tasks = new EntryProcessorRunnable[CONCURRENCY * 2]; // off-loadable writers for (int i = 0; i < CONCURRENCY; i++) { tasks[i] = new EntryProcessorRunnable(bounceMemberRule.getNextTestDriver(), new EntryIncOffloadable(), key); } // off-loadable readers for (int i = CONCURRENCY; i < CONCURRENCY * 2; i++) { tasks[i] = new EntryProcessorRunnable(bounceMemberRule.getNextTestDriver(), new EntryReadOnlyOffloadable(), key); } bounceMemberRule.testRepeatedly(tasks, MINUTES.toSeconds(3)); } public static class EntryProcessorRunnable implements Runnable { private final IMap map; private final EntryProcessor ep; private final int key; public EntryProcessorRunnable(HazelcastInstance hz, EntryProcessor ep, int key) { this.map = hz.getMap(MAP_NAME); this.ep = ep; this.key = key; } @Override public void run() { map.executeOnKey(key, ep); } } private static class EntryIncOffloadable implements EntryProcessor<Integer, SimpleValue>, Offloadable, EntryBackupProcessor<Integer, SimpleValue> { @Override public Object process(final Map.Entry<Integer, SimpleValue> entry) { final SimpleValue value = entry.getValue(); int result = value.i; value.i++; entry.setValue(value); sleepAtLeastMillis(10); return result; } @Override public EntryBackupProcessor<Integer, SimpleValue> getBackupProcessor() { return this; } @Override public String getExecutorName() { return Offloadable.OFFLOADABLE_EXECUTOR; } @Override public void processBackup(Map.Entry<Integer, SimpleValue> entry) { process(entry); } } private static class EntryReadOnlyOffloadable implements EntryProcessor<Integer, SimpleValue>, Offloadable, ReadOnly { @Override public Object process(final Map.Entry<Integer, SimpleValue> entry) { return null; } @Override public EntryBackupProcessor<Integer, SimpleValue> getBackupProcessor() { return null; } @Override public String getExecutorName() { return Offloadable.OFFLOADABLE_EXECUTOR; } } private IMap<Integer, SimpleValue> getMap() { return bounceMemberRule.getSteadyMember().getMap(MAP_NAME); } private static class SimpleValue implements Serializable { public int i; SimpleValue() { } SimpleValue(final int i) { this.i = i; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SimpleValue that = (SimpleValue) o; if (i != that.i) { return false; } return true; } @Override public String toString() { return "value: " + i; } } }