/* * Copyright (C) 2012, 2016 higherfrequencytrading.com * Copyright (C) 2016 Roman Leventov * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.chronicle.map; import com.google.common.collect.HashBiMap; import com.google.common.primitives.Ints; import net.openhft.chronicle.bytes.BytesStore; import net.openhft.chronicle.core.values.IntValue; import net.openhft.chronicle.core.values.LongValue; import net.openhft.chronicle.set.Builder; import net.openhft.chronicle.values.Values; import org.junit.Ignore; import org.junit.Test; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import static java.util.stream.Collectors.toSet; import static org.junit.Assert.*; @SuppressWarnings({"unchecked", "ResultOfMethodCallIgnored"}) public class ChronicleMapTest { static long count = 0; private StringBuilder sb = new StringBuilder(); static void assertKeySet(Set<Integer> keySet, int[] expectedKeys) { Set<Integer> expectedSet = new HashSet<Integer>(); for (int expectedKey : expectedKeys) { expectedSet.add(expectedKey); } assertEquals(expectedSet, keySet); } static void assertValues(Collection<CharSequence> values, CharSequence[] expectedValues) { List<String> expectedList = new ArrayList<String>(); for (CharSequence expectedValue : expectedValues) { expectedList.add(expectedValue.toString()); } Collections.sort(expectedList); List<String> actualList = new ArrayList<String>(); for (CharSequence actualValue : values) { actualList.add(actualValue.toString()); } Collections.sort(actualList); assertEquals(expectedList, actualList); } static void assertEntrySet(Set<Map.Entry<Integer, CharSequence>> entrySet, int[] expectedKeys, CharSequence[] expectedValues) { Set<Map.Entry<Integer, CharSequence>> expectedSet = new HashSet<Map.Entry<Integer, CharSequence>>(); for (int i = 0; i < expectedKeys.length; i++) { expectedSet.add(new AbstractMap.SimpleEntry<>(expectedKeys[i], expectedValues[i].toString())); } entrySet = entrySet.stream().map(e -> new AbstractMap.SimpleImmutableEntry<Integer, CharSequence>( e.getKey(), e.getValue().toString())) .collect(toSet()); assertEquals(expectedSet, entrySet); } static void assertMap(Map<Integer, CharSequence> map, int[] expectedKeys, CharSequence[] expectedValues) { assertEquals(expectedKeys.length, map.size()); for (int i = 0; i < expectedKeys.length; i++) { assertEquals("On position " + i, expectedValues[i].toString(), map.get(expectedKeys[i]).toString()); } } public static LongValue nativeLongValue() { return Values.newNativeReference(LongValue.class); } public static IntValue nativeIntValue() { return Values.newNativeReference(IntValue.class); } static File getPersistenceFile() { String TMP = System.getProperty("java.io.tmpdir"); File file = new File(TMP + "/chm-test" + System.nanoTime() + count++); file.deleteOnExit(); return file; } private ChronicleMap<Integer, CharSequence> getViewTestMap(int noOfElements) throws IOException { ChronicleMap<Integer, CharSequence> map = ChronicleMapBuilder.of(Integer.class, CharSequence.class) .entries(noOfElements * 2 + 100) .averageValueSize((noOfElements + "").length()) .putReturnsNull(true) .removeReturnsNull(true).create(); int[] expectedKeys = new int[noOfElements]; String[] expectedValues = new String[noOfElements]; for (int i = 1; i <= noOfElements; i++) { String value = "" + i; map.put(i, value); expectedKeys[i - 1] = i; expectedValues[i - 1] = value; } return map; } private static void printStatus() { if (!new File("/proc/self/status").exists()) return; try { BufferedReader br = new BufferedReader(new FileReader("/proc/self/status")); for (String line; (line = br.readLine()) != null; ) if (line.startsWith("Vm")) System.out.print(line.replaceAll(" +", " ") + ", "); br.close(); } catch (IOException e) { e.printStackTrace(); } } @Test public void testRemoveWithKey() { try (final ChronicleMap<CharSequence, CharSequence> map = ChronicleMapBuilder .of(CharSequence.class, CharSequence.class) .entries(10) .averageKey("key1").averageValue("one") .minSegments(2).create()) { assertFalse(map.containsKey("key3")); map.put("key1", "one"); map.put("key2", "two"); assertEquals(2, map.size()); assertTrue(map.containsKey("key1")); assertTrue(map.containsKey("key2")); assertFalse(map.containsKey("key3")); assertEquals("one", map.get("key1").toString()); assertEquals("two", map.get("key2").toString()); final CharSequence result = map.remove("key1"); assertEquals(1, map.size()); assertEquals("one", result.toString()); assertFalse(map.containsKey("key1")); assertEquals(null, map.get("key1")); assertEquals("two", map.get("key2").toString()); assertFalse(map.containsKey("key3")); // lets add one more item for luck ! map.put("key3", "three"); assertEquals("three", map.get("key3").toString()); assertTrue(map.containsKey("key3")); assertEquals(2, map.size()); // and just for kicks we'll overwrite what we have map.put("key3", "overwritten"); assertEquals("overwritten", map.get("key3").toString()); assertTrue(map.containsKey("key3")); assertEquals(2, map.size()); } } @Test public void testByteArrayPersistenceFileReuse() throws IOException { final File persistenceFile = Builder.getPersistenceFile(); for (int i = 0; i < 3; i++) { try (ChronicleMap<byte[], byte[]> map = ChronicleMap.of(byte[].class, byte[].class) .entries(1) .averageKey("hello".getBytes()).averageValue("world".getBytes()) .createPersistedTo(persistenceFile)) { byte[] o = map.get("hello".getBytes()); System.out.println(o == null ? "null" : new String(o)); map.put("hello".getBytes(), "world".getBytes()); } } persistenceFile.delete(); } @Test public void testEqualsCharSequence() { ChronicleMapBuilder<CharSequence, CharSequence> builder = ChronicleMapBuilder .of(CharSequence.class, CharSequence.class) .entries(1) .averageKey("hello").averageValue("world"); try (final ChronicleMap<CharSequence, CharSequence> map1 = builder.create()) { map1.put("hello", "world"); try (final ChronicleMap<CharSequence, CharSequence> map2 = builder.create()) { map2.put("hello", "world"); assertEquals(map1, map2); } } } @Test public void testEqualsCharArray() { char[] value = new char[5]; Arrays.fill(value, 'X'); ChronicleMapBuilder<CharSequence, char[]> builder = ChronicleMapBuilder .of(CharSequence.class, char[].class) .entries(1) .averageKey("hello").averageValue(value); try (final ChronicleMap<CharSequence, char[]> map1 = builder.create()) { map1.put("hello", value); try (final ChronicleMap<CharSequence, char[]> map2 = builder.create()) { map2.put("hello", value); assertEquals(map1, map2); } } } @Test public void testEqualsByteArray() { byte[] value = new byte[5]; Arrays.fill(value, (byte) 'X'); ChronicleMapBuilder<CharSequence, byte[]> builder = ChronicleMapBuilder .of(CharSequence.class, byte[].class) .entries(1) .averageKey("hello").averageValue(value); try (final ChronicleMap<CharSequence, byte[]> map1 = builder.create()) { map1.put("hello", value); try (final ChronicleMap<CharSequence, byte[]> map2 = builder.create()) { map2.put("hello", value); assertEquals(map1, map2); } } } @Test public void testSize() { try (final ChronicleMap<CharSequence, CharSequence> map = ChronicleMap.of(CharSequence.class, CharSequence.class) .averageKey("key-1024").averageValue("value") .minSegments(1024) .entries(1024) .removeReturnsNull(true).create()) { for (int i = 1; i < 1024; i++) { map.put("key" + i, "value"); assertEquals(i, map.size()); } for (int i = 1023; i >= 1; ) { map.remove("key" + i); i--; assertEquals(i, map.size()); } } } @Test public void testRemoveInteger() throws IOException { int count = 300; try (final ChronicleMap<Object, Object> map = ChronicleMapBuilder .of(Object.class, Object.class) .averageKey(1).averageValue(1) .entries(count) .minSegments(2).create()) { for (int i = 1; i < count; i++) { map.put(i, i); assertEquals(i, map.size()); } for (int i = count - 1; i >= 1; ) { Integer j = (Integer) map.put(i, i); assertEquals(i, j.intValue()); Integer j2 = (Integer) map.remove(i); assertEquals(i, j2.intValue()); i--; assertEquals(i, map.size()); } } } @Test public void testRemoveWithKeyAndRemoveReturnsNull() { try (final ChronicleMap<CharSequence, CharSequence> map = ChronicleMapBuilder.of(CharSequence.class, CharSequence.class) .entries(10) .averageKey("key1").averageValue("one") .minSegments(2) .removeReturnsNull(true).create()) { assertFalse(map.containsKey("key3")); map.put("key1", "one"); map.put("key2", "two"); assertEquals(2, map.size()); assertTrue(map.containsKey("key1")); assertTrue(map.containsKey("key2")); assertFalse(map.containsKey("key3")); assertEquals("one", map.get("key1").toString()); assertEquals("two", map.get("key2").toString()); final CharSequence result = map.remove("key1"); assertEquals(null, result); assertEquals(1, map.size()); assertFalse(map.containsKey("key1")); assertEquals(null, map.get("key1")); assertEquals("two", map.get("key2").toString()); assertFalse(map.containsKey("key3")); // lets add one more item for luck ! map.put("key3", "three"); assertEquals("three", map.get("key3").toString()); assertTrue(map.containsKey("key3")); assertEquals(2, map.size()); // and just for kicks we'll overwrite what we have map.put("key3", "overwritten"); assertEquals("overwritten", map.get("key3").toString()); assertTrue(map.containsKey("key3")); assertEquals(2, map.size()); } } @Test public void testReplaceWithKey() { try (final ChronicleMap<CharSequence, CharSequence> map = ChronicleMapBuilder.of(CharSequence.class, CharSequence.class) .entries(10) .averageKey("key1").averageValue("one") .minSegments(2).create()) { map.put("key1", "one"); map.put("key2", "two"); assertEquals(2, map.size()); assertEquals("one", map.get("key1").toString()); assertEquals("two", map.get("key2").toString()); assertTrue(map.containsKey("key1")); assertTrue(map.containsKey("key2")); final CharSequence result = map.replace("key1", "newValue"); assertEquals("one", result.toString()); assertTrue(map.containsKey("key1")); assertTrue(map.containsKey("key2")); assertEquals(2, map.size()); assertEquals("newValue", map.get("key1").toString()); assertEquals("two", map.get("key2").toString()); assertTrue(map.containsKey("key1")); assertTrue(map.containsKey("key2")); assertFalse(map.containsKey("key3")); assertEquals(2, map.size()); // let and one more item for luck ! map.put("key3", "three"); assertEquals(3, map.size()); assertTrue(map.containsKey("key1")); assertTrue(map.containsKey("key2")); assertTrue(map.containsKey("key3")); assertEquals("three", map.get("key3").toString()); // and just for kicks we'll overwrite what we have map.put("key3", "overwritten"); assertEquals("overwritten", map.get("key3").toString()); assertTrue(map.containsKey("key1")); assertTrue(map.containsKey("key2")); assertTrue(map.containsKey("key3")); final CharSequence result2 = map.replace("key2", "newValue"); assertEquals("two", result2.toString()); assertEquals("newValue", map.get("key2").toString()); final CharSequence result3 = map.replace("rubbish", "newValue"); assertEquals(null, result3); assertFalse(map.containsKey("rubbish")); assertEquals(3, map.size()); } } @Test public void testReplaceWithKeyAnd2Params() { try (final ChronicleMap<CharSequence, CharSequence> map = ChronicleMapBuilder.of(CharSequence.class, CharSequence.class) .entries(10) .averageKey("key1").averageValue("one") .minSegments(2).create()) { map.put("key1", "one"); map.put("key2", "two"); assertEquals("one", map.get("key1").toString()); assertEquals("two", map.get("key2").toString()); final boolean result = map.replace("key1", "one", "newValue"); assertEquals(true, result); assertEquals("newValue", map.get("key1").toString()); assertEquals("two", map.get("key2").toString()); // let and one more item for luck ! map.put("key3", "three"); assertEquals("three", map.get("key3").toString()); // and just for kicks we'll overwrite what we have map.put("key3", "overwritten"); assertEquals("overwritten", map.get("key3").toString()); final boolean result2 = map.replace("key2", "two", "newValue2"); assertEquals(true, result2); assertEquals("newValue2", map.get("key2").toString()); final boolean result3 = map.replace("newKey", "", "newValue"); assertEquals(false, result3); final boolean result4 = map.replace("key2", "newValue2", "newValue2"); assertEquals(true, result4); } } @Test public void testRemoveWithKeyAndValue() { try (final ChronicleMap<CharSequence, CharSequence> map = ChronicleMapBuilder.of(CharSequence.class, CharSequence.class) .entries(10) .averageKey("key1").averageValue("one") .minSegments(2).create()) { map.put("key1", "one"); map.put("key2", "two"); assertEquals("one", map.get("key1").toString()); assertEquals("two", map.get("key2").toString()); // a false remove final boolean wasRemoved1 = map.remove("key1", "three"); assertFalse(wasRemoved1); assertEquals(null, map.get("key1").toString(), "one"); assertEquals("two", map.get("key2").toString(), "two"); map.put("key1", "one"); final boolean wasRemoved2 = map.remove("key1", "three"); assertFalse(wasRemoved2); // lets add one more item for luck ! map.put("key3", "three"); assertEquals("three", map.get("key3").toString()); // and just for kicks we'll overwrite what we have map.put("key3", "overwritten"); assertEquals("overwritten", map.get("key3").toString()); } } @Test public void testAcquireWithNullContainer() { try (ChronicleMap<CharSequence, LongValue> map = ChronicleMapBuilder.of(CharSequence.class, LongValue.class) .averageKey("key") .entries(1000) .entryAndValueOffsetAlignment(4) .create()) { map.acquireUsing("key", Values.newNativeReference(LongValue.class)); assertEquals(0, map.acquireUsing("key", null).getValue()); } } // i7-3970X CPU @ 3.50GHz, hex core: -verbose:gc -Xmx64m // to tmpfs file system // 10M users, updated 12 times. Throughput 19.3 M ops/sec, no GC! // 50M users, updated 12 times. Throughput 19.8 M ops/sec, no GC! // 100M users, updated 12 times. Throughput 19.0M ops/sec, no GC! // 200M users, updated 12 times. Throughput 18.4 M ops/sec, no GC! // 400M users, updated 12 times. Throughput 18.4 M ops/sec, no GC! // to ext4 file system. // 10M users, updated 12 times. Throughput 17.7 M ops/sec, no GC! // 50M users, updated 12 times. Throughput 16.5 M ops/sec, no GC! // 100M users, updated 12 times. Throughput 15.9 M ops/sec, no GC! // 200M users, updated 12 times. Throughput 15.4 M ops/sec, no GC! // 400M users, updated 12 times. Throughput 7.8 M ops/sec, no GC! // 600M users, updated 12 times. Throughput 5.8 M ops/sec, no GC! // dual E5-2650v2 @ 2.6 GHz, 128 GB: -verbose:gc -Xmx32m // to tmpfs // TODO small GC on startup should be tidied up, [GC 9216K->1886K(31744K), 0.0036750 secs] // 10M users, updated 16 times. Throughput 33.0M ops/sec, VmPeak: 5373848 kB, VmRSS: 544252 kB // 50M users, updated 16 times. Throughput 31.2 M ops/sec, VmPeak: 9091804 kB, VmRSS: 3324732 kB // 250M users, updated 16 times. Throughput 30.0 M ops/sec, VmPeak: 24807836 kB, VmRSS: 14329112 kB // 1000M users, updated 16 times, Throughput 24.1 M ops/sec, VmPeak: 85312732 kB, VmRSS: 57165952 kB // 2500M users, updated 16 times, Throughput 23.5 M ops/sec, VmPeak: 189545308 kB, VmRSS: 126055868 kB // to ext4 // 10M users, updated 16 times. Throughput 28.4 M ops/sec, VmPeak: 5438652 kB, VmRSS: 544624 kB // 50M users, updated 16 times. Throughput 28.2 M ops/sec, VmPeak: 9091804 kB, VmRSS: 9091804 kB // 250M users, updated 16 times. Throughput 26.1 M ops/sec, VmPeak: 24807836 kB, VmRSS: 24807836 kB // 1000M users, updated 16 times, Throughput 1.3 M ops/sec, TODO FIX this @Test public void testGetWithNullContainer() { try (ChronicleMap<CharSequence, LongValue> map = ChronicleMapBuilder.of(CharSequence.class, LongValue.class) .averageKey("key") .entries(10) .entryAndValueOffsetAlignment(4) .create()) { map.acquireUsing("key", Values.newNativeReference(LongValue.class)); assertEquals(0, map.getUsing("key", null).getValue()); } } @Test public void testGetWithoutAcquireFirst() { try (ChronicleMap<CharSequence, LongValue> map = ChronicleMapBuilder.of(CharSequence.class, LongValue.class) .averageKey("key") .entries(10) .entryAndValueOffsetAlignment(4) .create()) { assertNull(map.getUsing("key", Values.newNativeReference(LongValue.class))); } } // i7-3970X CPU @ 3.50GHz, hex core: -Xmx30g -verbose:gc // 10M users, updated 12 times. Throughput 16.2 M ops/sec, longest [Full GC 853669K->852546K(3239936K), 0.8255960 secs] // 50M users, updated 12 times. Throughput 13.3 M ops/sec, longest [Full GC 5516214K->5511353K(13084544K), 3.5752970 secs] // 100M users, updated 12 times. Throughput 11.8 M ops/sec, longest [Full GC 11240703K->11233711K(19170432K), 5.8783010 secs] // 200M users, updated 12 times. Throughput 4.2 M ops/sec, longest [Full GC 25974721K->22897189K(27962048K), 21.7962600 secs] // dual E5-2650v2 @ 2.6 GHz, 128 GB: -verbose:gc -Xmx100g // 10M users, updated 16 times. Throughput 155.3 M ops/sec, VmPeak: 113291428 kB, VmRSS: 9272176 kB, [Full GC 1624336K->1616457K(7299072K), 2.5381610 secs] // 50M users, updated 16 times. Throughput 120.4 M ops/sec, VmPeak: 113291428 kB, VmRSS: 28436248 kB [Full GC 6545332K->6529639K(18179584K), 6.9053810 secs] // 250M users, updated 16 times. Throughput 114.1 M ops/sec, VmPeak: 113291428 kB, VmRSS: 76441464 kB [Full GC 41349527K->41304543K(75585024K), 17.3217490 secs] // 1000M users, OutOfMemoryError. @Test public void testAcquireAndGet() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException { int entries = 3/*00 * 1000*/; try (ChronicleMap<CharSequence, LongValue> map2 = ChronicleMapBuilder.of(CharSequence.class, LongValue.class) .entries((long) entries) .minSegments(1) .averageKeySize(10) .entryAndValueOffsetAlignment(4) .create()) { LongValue value4 = Values.newNativeReference(LongValue.class); LongValue value22 = Values.newNativeReference(LongValue.class); LongValue value32 = Values.newNativeReference(LongValue.class); for (int j2 = 1; j2 <= 3; j2++) { for (int i2 = 0; i2 < entries; i2++) { CharSequence userCS2 = getUserCharSequence(i2); if (j2 > 1) { assertNotNull(userCS2.toString(), map2.getUsing(userCS2, value4)); } else { map2.acquireUsing(userCS2, value4); } if (i2 >= 1) assertTrue(userCS2.toString(), map2.containsKey(getUserCharSequence(1))); assertEquals(userCS2.toString(), j2 - 1, value4.getValue()); value4.addAtomicValue(1); assertEquals(value22, map2.acquireUsing(userCS2, value22)); assertEquals(j2, value22.getValue()); assertEquals(value32, map2.getUsing(userCS2, value32)); assertEquals(j2, value32.getValue()); } } try (ChronicleMap<CharSequence, LongValue> map1 = ChronicleMapBuilder.of(CharSequence.class, LongValue.class) .entries((long) entries) .minSegments(1) .averageKeySize(10) .entryAndValueOffsetAlignment(1) .create()) { LongValue value1 = Values.newNativeReference(LongValue.class); LongValue value21 = Values.newNativeReference(LongValue.class); LongValue value31 = Values.newNativeReference(LongValue.class); for (int j1 = 1; j1 <= 3; j1++) { for (int i1 = 0; i1 < entries; i1++) { CharSequence userCS1 = getUserCharSequence(i1); if (j1 > 1) { assertNotNull(userCS1.toString(), map1.getUsing(userCS1, value1)); } else { map1.acquireUsing(userCS1, value1); } if (i1 >= 1) assertTrue(userCS1.toString(), map1.containsKey(getUserCharSequence(1))); assertEquals(userCS1.toString(), j1 - 1, value1.getValue()); value1.addAtomicValue(1); assertEquals(value21, map1.acquireUsing(userCS1, value21)); assertEquals(j1, value21.getValue()); assertEquals(value31, map1.getUsing(userCS1, value31)); assertEquals(j1, value31.getValue()); } } } try (ChronicleMap<CharSequence, LongValue> map = ChronicleMapBuilder.of(CharSequence .class, LongValue.class) .entries((long) entries) .minSegments(1) .averageKeySize(10) .entryAndValueOffsetAlignment(8) .create()) { LongValue value = Values.newNativeReference(LongValue.class); LongValue value2 = Values.newNativeReference(LongValue.class); LongValue value3 = Values.newNativeReference(LongValue.class); for (int j = 1; j <= 3; j++) { for (int i = 0; i < entries; i++) { CharSequence userCS = getUserCharSequence(i); if (j > 1) { assertNotNull(userCS.toString(), map.getUsing(userCS, value)); } else { map.acquireUsing(userCS, value); } if (i >= 1) assertTrue(userCS.toString(), map.containsKey(getUserCharSequence(1))); assertEquals(userCS.toString(), j - 1, value.getValue()); value.addAtomicValue(1); assertEquals(value2, map.acquireUsing(userCS, value2)); assertEquals(j, value2.getValue()); assertEquals(value3, map.getUsing(userCS, value3)); assertEquals(j, value3.getValue()); } } } } } @Test public void testAcquireFromMultipleThreads() throws InterruptedException { int entries = 1000 * 1000; try (ChronicleMap<CharSequence, LongValue> map2 = ChronicleMapBuilder.of(CharSequence.class, LongValue.class) .entries((long) entries) .minSegments(128) .averageKeySize(10) .entryAndValueOffsetAlignment(1) .create()) { CharSequence key2 = getUserCharSequence(0); map2.acquireUsing(key2, Values.newNativeReference(LongValue.class)); int iterations2 = 10000; int noOfThreads2 = 10; CyclicBarrier barrier2 = new CyclicBarrier(noOfThreads2); Thread[] threads2 = new Thread[noOfThreads2]; for (int t2 = 0; t2 < noOfThreads2; t2++) { threads2[t2] = new Thread(new IncrementRunnable(map2, key2, iterations2, barrier2)); threads2[t2].start(); } for (int t2 = 0; t2 < noOfThreads2; t2++) { threads2[t2].join(); } assertEquals(noOfThreads2 * iterations2, map2.acquireUsing(key2, Values.newNativeReference(LongValue.class)).getValue()); try (ChronicleMap<CharSequence, LongValue> map1 = ChronicleMapBuilder.of(CharSequence .class, LongValue.class) .entries((long) entries) .minSegments(128) .averageKeySize(10) .entryAndValueOffsetAlignment(4) .create()) { CharSequence key1 = getUserCharSequence(0); map1.acquireUsing(key1, Values.newNativeReference(LongValue.class)); int iterations1 = 10000; int noOfThreads1 = 10; CyclicBarrier barrier1 = new CyclicBarrier(noOfThreads1); Thread[] threads1 = new Thread[noOfThreads1]; for (int t1 = 0; t1 < noOfThreads1; t1++) { threads1[t1] = new Thread(new IncrementRunnable(map1, key1, iterations1, barrier1)); threads1[t1].start(); } for (int t1 = 0; t1 < noOfThreads1; t1++) { threads1[t1].join(); } assertEquals(noOfThreads1 * iterations1, map1.acquireUsing(key1, Values.newNativeReference(LongValue.class)).getValue()); try (ChronicleMap<CharSequence, LongValue> map = ChronicleMapBuilder.of(CharSequence .class, LongValue.class) .entries((long) entries) .minSegments(128) .averageKeySize(10) .entryAndValueOffsetAlignment(8) .create()) { CharSequence key = getUserCharSequence(0); map.acquireUsing(key, Values.newNativeReference(LongValue.class)); int iterations = 10000; int noOfThreads = 10; CyclicBarrier barrier = new CyclicBarrier(noOfThreads); Thread[] threads = new Thread[noOfThreads]; for (int t = 0; t < noOfThreads; t++) { threads[t] = new Thread(new IncrementRunnable(map, key, iterations, barrier)); threads[t].start(); } for (int t = 0; t < noOfThreads; t++) { threads[t].join(); } assertEquals(noOfThreads * iterations, map.acquireUsing(key, Values.newNativeReference(LongValue.class)).getValue()); } } } } @Test public void testLargerEntries() { for (int segments : new int[]{128, 256, 512, 1024}) { int entries = 100000, entrySize = 512; ChronicleMapBuilder<CharSequence, CharSequence> builder = ChronicleMapBuilder .of(CharSequence.class, CharSequence.class) .entries(entries * 11 / 10) .actualSegments(segments) .averageKeySize(14) .averageValueSize(entrySize - 14 - 2); try (ChronicleMap<CharSequence, CharSequence> map = builder.create()) { StringBuilder sb = new StringBuilder(); while (sb.length() < entrySize - 14 - 2) sb.append('+'); for (int i = 0; i < entries; i++) { map.put("us:" + i, sb); } } } } @Test @Ignore("Performance test") public void testAcquirePerf256() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, InterruptedException, ExecutionException { // int runs = Integer.getInteger("runs", 10); int procs = 1; // Runtime.getRuntime().availableProcessors(); int threads = procs * 3; for (int runs : new int[]{1, /*10, 250, 500, 1000, 2500*/}) { for (int entrySize : new int[]{240, 256}) { int valuePadding = entrySize - 16; char[] chars = new char[valuePadding]; Arrays.fill(chars, 'x'); final StringBuilder value0 = new StringBuilder(); value0.append(chars); for (int segments : new int[]{/*128, 256, */512/*, 1024, 2048*/}) { final long entries = runs * 1000 * 1000L; ChronicleMapBuilder<CharSequence, CharSequence> builder = ChronicleMapBuilder .of(CharSequence.class, CharSequence.class) .entries(entries) .actualSegments(segments) .averageKeySize(14) .averageValueSize(value0.length() + 4); // File tmpFile = File.createTempFile("testAcquirePerf", ".deleteme"); // tmpFile.deleteOnExit(); //createPersistedTo(tmpFile); try (final ChronicleMap<CharSequence, CharSequence> map = builder.create()) { int count = runs > 500 ? 2 : 3; System.out.println("\nKey size: " + runs + " Million entries. " + builder); for (int j = 0; j < count; j++) { long start = System.currentTimeMillis(); for (int i = 0; i < threads; i++) { final int t = i; Random rand = new Random(t); StringBuilder key = new StringBuilder(); StringBuilder value = new StringBuilder(); long next = 50 * 1000 * 1000; // use a factor to give up to 10 digit numbers. int factor = Math.max(1, (int) ((10 * 1000 * 1000 * 1000L - 1) / entries)); for (long k = t; k < entries; k++) { key.setLength(0); key.append("us:"); key.append(k * factor); // 75% reads, 25% writes. if (rand.nextInt(4) > 0) { map.getUsing(key, value); } else { try (net.openhft.chronicle.core.io.Closeable c = map.acquireContext(key, value)) { if (value.length() < value0.length() - 1) value.append(value0); else if (value.length() > value0.length()) value.setLength(value0.length() - 1); else value.append('+'); } } } long time = System.currentTimeMillis() - start; System.out.printf("EntrySize: %,d Entries: %,d M Segments: " + "%,d Throughput %.1f M ops/sec%n", entrySize, runs, segments, threads * entries / 1000.0 / time); } printStatus(); } } } } } } @Test @Ignore("Performance test") public void testAcquirePerf() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, InterruptedException, ExecutionException { // int runs = Integer.getInteger("runs", 10); int procs = 1; // Runtime.getRuntime().availableProcessors(); int threads = procs * 3; ExecutorService es = Executors.newFixedThreadPool(procs); for (int runs : new int[]{1, /*10, 250, 500, 1000, 2500*/}) { for (int entrySize : new int[]{240, 256}) { int valuePadding = entrySize - 16; char[] chars = new char[valuePadding]; Arrays.fill(chars, 'x'); final StringBuilder value0 = new StringBuilder(); value0.append(chars); for (int segments : new int[]{/*128, 256, */512/*, 1024, 2048*/}) { final long entries = runs * 1000 * 1000L; ChronicleMapBuilder<CharSequence, CharSequence> builder = ChronicleMapBuilder .of(CharSequence.class, CharSequence.class) .entries(entries) .actualSegments(segments) .averageKeySize(14) .averageValueSize(value0.length() + 4); // File tmpFile = File.createTempFile("testAcquirePerf", ".deleteme"); // tmpFile.deleteOnExit(); try (final ChronicleMap<CharSequence, CharSequence> map = builder.create()) { int count = runs > 500 ? 2 : 3; final int independence = Math.min(procs, runs > 500 ? 8 : 4); System.out.println("\nKey size: " + runs + " Million entries. " + builder); for (int j = 0; j < count; j++) { long start = System.currentTimeMillis(); List<Future> futures = new ArrayList<>(); for (int i = 0; i < threads; i++) { final int t = i; futures.add(es.submit(new Runnable() { @Override public void run() { Random rand = new Random(t); StringBuilder key = new StringBuilder(); StringBuilder value = new StringBuilder(); long next = 50 * 1000 * 1000; // use a factor to give up to 10 digit numbers. int factor = Math.max(1, (int) ((10 * 1000 * 1000 * 1000L - 1) / entries)); for (long j = t % independence; j < entries + independence - 1; j += independence) { key.setLength(0); key.append("us:"); key.append(j * factor); // 75% reads, 25% writes. if (rand.nextInt(4) > 0) { map.getUsing(key, value); } else { try (net.openhft.chronicle.core.io.Closeable c = map.acquireContext(key, value)) { if (value.length() < value0.length() - 1) value.append(value0); else if (value.length() > value0.length()) value.setLength(value0.length() - 1); else value.append('+'); } } } } })); } for (Future future : futures) { future.get(); } long time = System.currentTimeMillis() - start; System.out.printf("EntrySize: %,d Entries: %,d M " + "Segments: %,d Throughput %.1f M ops/sec%n", entrySize, runs, segments, threads * entries / independence / 1000.0 / time); } } printStatus(); } } } es.shutdown(); es.awaitTermination(1, TimeUnit.MINUTES); } static final LongValue ONE = Values.newHeapInstance(LongValue.class); static { ONE.setValue(1); } @Test @Ignore("Performance test") public void testAcquireLockedPerf() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, InterruptedException, ExecutionException { // int runs = Integer.getInteger("runs", 10); int procs = Runtime.getRuntime().availableProcessors(); if (procs > 8) procs--; int threads = procs * 3; ExecutorService es = Executors.newFixedThreadPool(procs); for (int runs : new int[]{1, 2, 5, 10, 25, 50, 100, 500, 1000, 2500}) { final long entries = runs * 1000 * 1000L; ChronicleMapBuilder<CharSequence, LongValue> builder = ChronicleMapBuilder .of(CharSequence.class, LongValue.class) .entries(entries) .entryAndValueOffsetAlignment(8) .actualSegments(256) .averageKeySize(13); File tmpFile = File.createTempFile("testAcquirePerf", ".deleteme"); tmpFile.deleteOnExit(); try (ChronicleMap<CharSequence, LongValue> map = builder.createPersistedTo(tmpFile)) { int count = runs >= 5 ? 2 : 3; final int independence = Math.min(procs, runs > 500 ? 8 : 4); System.out.println("\nKey size: " + runs + " Million entries. " + builder); for (int j = 0; j < count; j++) { long start = System.currentTimeMillis(); List<Future> futures = new ArrayList<>(); for (int i = 0; i < threads; i++) { final int t = i; futures.add(es.submit(new Runnable() { @Override public void run() { LongValue value = nativeLongValue(); StringBuilder sb = new StringBuilder(); long next = 50 * 1000 * 1000; Random rand = new Random(); // use a factor to give up to 10 digit numbers. int factor = Math.max(1, (int) ((10 * 1000 * 1000 * 1000L - 1) / entries)); for (long j = t % independence; j < entries + independence - 1; j += independence) { sb.setLength(0); sb.append("us:"); sb.append(j * factor); long n; // 75% read if (rand.nextBoolean() || rand.nextBoolean()) { try (ExternalMapQueryContext<?, LongValue, ?> c = map.queryContext(sb)) { MapEntry<?, LongValue> entry = c.entry(); if (entry != null) { // Attempt to pass abstraction hierarchies net.openhft.chronicle.hash.Data<LongValue> v = entry.value(); n = v.bytes().readVolatileLong(v.offset()) + 1; } else { n = 1; } } } else { try (ExternalMapQueryContext<CharSequence, LongValue, ?> c = map.queryContext(sb)) { c.updateLock().lock(); MapEntry<?, LongValue> entry = c.entry(); if (entry != null) { net.openhft.chronicle.hash.Data<LongValue> v = entry.value(); n = ((BytesStore) v.bytes()).addAndGetLong( v.offset(), 1); } else { c.insert(c.absentEntry(), c.wrapValueAsData(ONE)); n = 1; } } } } } })); } for (Future future : futures) { future.get(); } long time = System.currentTimeMillis() - start; System.out.printf("Throughput %.1f M ops/sec%n", threads * entries / independence / 1000.0 / time); } } printStatus(); tmpFile.delete(); } es.shutdown(); es.awaitTermination(1, TimeUnit.MINUTES); } @Test @Ignore("Performance test") public void testAcquireLockedLLPerf() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, InterruptedException, ExecutionException { // int runs = Integer.getInteger("runs", 10); int procs = Runtime.getRuntime().availableProcessors(); int threads = procs * 3; // runs > 100 ? procs / 2 : procs; ExecutorService es = Executors.newFixedThreadPool(procs); for (int runs : new int[]{10, 50, 100, 250, 500, 1000, 2500}) { // JAVA 8 produces more garbage than previous versions for internal work. // System.gc(); final long entries = runs * 1000 * 1000L; ChronicleMapBuilder<LongValue, LongValue> builder = ChronicleMapBuilder .of(LongValue.class, LongValue.class) .entries(entries) .actualSegments(8 * 1024); File tmpFile = File.createTempFile("testAcquirePerf", ".deleteme"); tmpFile.deleteOnExit(); try (ChronicleMap<LongValue, LongValue> map = builder.createPersistedTo(tmpFile)) { int count = runs > 500 ? runs > 1200 ? 3 : 5 : 5; final int independence = Math.min(procs, runs > 500 ? 8 : 4); System.out.println("\nKey size: " + runs + " Million entries. " + builder); for (int j = 0; j < count; j++) { long start = System.currentTimeMillis(); List<Future> futures = new ArrayList<Future>(); for (int i = 0; i < threads; i++) { final int t = i; futures.add(es.submit(new Runnable() { @Override public void run() { LongValue key = Values.newHeapInstance(LongValue.class); LongValue value = nativeLongValue(); long next = 50 * 1000 * 1000; // use a factor to give up to 10 digit numbers. int factor = Math.max(1, (int) ((10 * 1000 * 1000 * 1000L - 1) / entries)); for (long j = t % independence; j < entries + independence - 1; j += independence) { key.setValue(j * factor); long n; try (net.openhft.chronicle.core.io.Closeable c = map.acquireContext(key, value)) { n = value.addValue(1); } assert n > 0 && n < 1000 : "Counter corrupted " + n; if (t == 0 && j >= next) { long size = map.longSize(); if (size < 0) throw new AssertionError("size: " + size); System.out.println(j + ", size: " + size); next += 50 * 1000 * 1000; } } } })); } for (Future future : futures) { future.get(); } long time = System.currentTimeMillis() - start; System.out.printf("Throughput %.1f M ops/sec%n", threads * entries / independence / 1000.0 / time); } } printStatus(); tmpFile.delete(); } es.shutdown(); es.awaitTermination(1, TimeUnit.MINUTES); } @Test @Ignore("Performance test") public void testCHMAcquirePerf() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, InterruptedException { for (int runs : new int[]{10, 50, 250, 500, 1000, 2500}) { System.out.println("Testing " + runs + " million entries"); final long entries = runs * 1000 * 1000L; int procs = Runtime.getRuntime().availableProcessors(); int threads = procs * 2; int count = runs > 500 ? runs > 1200 ? 1 : 2 : 5; final int independence = Math.min(procs, runs > 500 ? 8 : 4); for (int j = 0; j < count; j++) { final ConcurrentMap<String, AtomicInteger> map = new ConcurrentHashMap<String, AtomicInteger>((int) (entries * 5 / 4), 1.0f, 1024); long start = System.currentTimeMillis(); ExecutorService es = Executors.newFixedThreadPool(procs); for (int i = 0; i < threads; i++) { final int t = i; es.submit(new Runnable() { @Override public void run() { StringBuilder sb = new StringBuilder(); int next = 50 * 1000 * 1000; // use a factor to give up to 10 digit numbers. int factor = Math.max(1, (int) ((10 * 1000 * 1000 * 1000L - 1) / entries)); for (long i = t % independence; i < entries; i += independence) { sb.setLength(0); sb.append("u:"); sb.append(i * factor); String key = sb.toString(); AtomicInteger count = map.get(key); if (count == null) { map.putIfAbsent(key, new AtomicInteger()); count = map.get(key); } count.getAndIncrement(); if (t == 0 && i == next) { System.out.println(i); next += 50 * 1000 * 1000; } } } }); } es.shutdown(); es.awaitTermination(10, TimeUnit.MINUTES); printStatus(); long time = System.currentTimeMillis() - start; System.out.printf("Throughput %.1f M ops/sec%n", threads * entries / 1000.0 / time); } } } private CharSequence getUserCharSequence(int i) { sb.setLength(0); sb.append("u:"); sb.append(i * 9876); // test 10 digit user numbers. return sb; } @Test public void testPutAndRemove() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException { int entries = 100 * 1000; try (ChronicleMap<CharSequence, CharSequence> map = ChronicleMapBuilder.of(CharSequence.class, CharSequence.class) .entries(entries) .minSegments(16) .averageKeySize("user:".length() + 6) .averageValueSize("value:".length() + 6) .putReturnsNull(true) .removeReturnsNull(true).create()) { StringBuilder key = new StringBuilder(); StringBuilder value = new StringBuilder(); StringBuilder value2 = new StringBuilder(); for (int j = 1; j <= 3; j++) { for (int i = 0; i < entries; i++) { key.setLength(0); key.append("user:").append(i); value.setLength(0); value.append("value:").append(i); // System.out.println(key); assertNull(map.getUsing(key, value)); assertNull(map.put(key, value)); assertNotNull(map.getUsing(key, value2)); assertEquals(value.toString(), value2.toString()); assertNull(map.remove(key)); assertNull(map.getUsing(key, value)); } } } } @Test public void mapRemoveReflectedInViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3);) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); map.remove(2); assertMap(map, new int[]{1, 3}, new CharSequence[]{"1", "3"}); assertEntrySet(entrySet, new int[]{1, 3}, new CharSequence[]{"1", "3"}); assertEntrySet(map.entrySet(), new int[]{1, 3}, new CharSequence[]{"1", "3"}); assertKeySet(keySet, new int[]{1, 3}); assertKeySet(map.keySet(), new int[]{1, 3}); assertValues(values, new CharSequence[]{"1", "3"}); assertValues(map.values(), new CharSequence[]{"1", "3"}); } } @Test public void mapPutReflectedInViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); map.put(4, "4"); assertMap(map, new int[]{4, 2, 3, 1}, new CharSequence[]{"4", "2", "3", "1"}); assertEntrySet(entrySet, new int[]{4, 2, 3, 1}, new CharSequence[]{"4", "2", "3", "1"}); assertEntrySet(map.entrySet(), new int[]{4, 2, 3, 1}, new CharSequence[]{"4", "2", "3", "1"}); assertKeySet(keySet, new int[]{4, 2, 3, 1}); assertKeySet(map.keySet(), new int[]{4, 2, 3, 1}); assertValues(values, new CharSequence[]{"2", "1", "4", "3"}); assertValues(map.values(), new CharSequence[]{"2", "1", "4", "3"}); } } @Test public void entrySetRemoveReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); entrySet.remove(new AbstractMap.SimpleEntry<Integer, CharSequence>(2, "2")); assertMap(map, new int[]{1, 3}, new CharSequence[]{"1", "3"}); assertEntrySet(entrySet, new int[]{1, 3}, new CharSequence[]{"1", "3"}); assertKeySet(keySet, new int[]{1, 3}); assertValues(values, new CharSequence[]{"1", "3"}); } } @Test public void keySetRemoveReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); keySet.remove(2); assertMap(map, new int[]{1, 3}, new CharSequence[]{"1", "3"}); assertEntrySet(entrySet, new int[]{1, 3}, new CharSequence[]{"1", "3"}); assertKeySet(keySet, new int[]{1, 3}); assertValues(values, new CharSequence[]{"1", "3"}); } } @Test public void valuesRemoveReflectedInMap() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); values.removeIf("2"::contentEquals); assertMap(map, new int[]{1, 3}, new CharSequence[]{"1", "3"}); assertEntrySet(entrySet, new int[]{1, 3}, new CharSequence[]{"1", "3"}); assertKeySet(keySet, new int[]{1, 3}); assertValues(values, new CharSequence[]{"1", "3"}); } } @Test public void entrySetIteratorRemoveReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Map<Integer, CharSequence> refMap = new HashMap(map); Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); Iterator<Map.Entry<Integer, CharSequence>> entryIterator = entrySet.iterator(); entryIterator.next(); refMap.remove(entryIterator.next().getKey()); entryIterator.remove(); int[] expectedKeys = Ints.toArray(refMap.keySet()); CharSequence[] expectedValues = refMap.values().toArray(new CharSequence[0]); assertMap(map, expectedKeys, expectedValues); assertEntrySet(entrySet, expectedKeys, expectedValues); assertKeySet(keySet, expectedKeys); assertValues(values, expectedValues); } } @Test public void keySetIteratorRemoveReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Map<Integer, CharSequence> refMap = new HashMap(map); Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); Iterator<Integer> keyIterator = keySet.iterator(); keyIterator.next(); refMap.remove(keyIterator.next()); keyIterator.remove(); int[] expectedKeys = Ints.toArray(refMap.keySet()); CharSequence[] expectedValues = refMap.values().toArray(new CharSequence[0]); assertMap(map, expectedKeys, expectedValues); assertEntrySet(entrySet, expectedKeys, expectedValues); assertKeySet(keySet, expectedKeys); assertValues(values, expectedValues); } } @Test public void valuesIteratorRemoveReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { HashBiMap<Integer, CharSequence> refMap = HashBiMap.create(); map.forEach((k, v) -> refMap.put(k, v.toString())); Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); Iterator<CharSequence> valueIterator = values.iterator(); valueIterator.next(); refMap.inverse().remove(valueIterator.next().toString()); valueIterator.remove(); int[] expectedKeys = Ints.toArray(refMap.keySet()); CharSequence[] expectedValues = new CharSequence[expectedKeys.length]; for (int i = 0; i < expectedKeys.length; i++) { expectedValues[i] = refMap.get(expectedKeys[i]); } assertMap(map, expectedKeys, expectedValues); assertEntrySet(entrySet, expectedKeys, expectedValues); assertKeySet(keySet, expectedKeys); assertValues(values, expectedValues); } } @Test public void entrySetRemoveAllReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); entrySet.removeAll( Arrays.asList( new AbstractMap.SimpleEntry<Integer, CharSequence>(1, "1"), new AbstractMap.SimpleEntry<Integer, CharSequence>(2, "2") ) ); assertMap(map, new int[]{3}, new CharSequence[]{"3"}); assertEntrySet(entrySet, new int[]{3}, new CharSequence[]{"3"}); assertKeySet(keySet, new int[]{3}); assertValues(values, new CharSequence[]{"3"}); } } @Test public void keySetRemoveAllReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); keySet.removeAll(Arrays.asList(1, 2)); assertMap(map, new int[]{3}, new CharSequence[]{"3"}); assertEntrySet(entrySet, new int[]{3}, new CharSequence[]{"3"}); assertKeySet(keySet, new int[]{3}); assertValues(values, new CharSequence[]{"3"}); } } @Test public void valuesRemoveAllReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); values.removeIf(e -> "1".contentEquals(e) || "2".contentEquals(e)); assertMap(map, new int[]{3}, new CharSequence[]{"3"}); assertEntrySet(entrySet, new int[]{3}, new CharSequence[]{"3"}); assertKeySet(keySet, new int[]{3}); assertValues(values, new CharSequence[]{"3"}); } } @Test public void entrySetRetainAllReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); entrySet.removeIf(e -> !(e.getKey().equals(1) && "1".contentEquals(e.getValue())) && !(e.getKey().equals(2) && "2".contentEquals(e.getValue()))); assertMap(map, new int[]{2, 1}, new CharSequence[]{"2", "1"}); assertEntrySet(entrySet, new int[]{2, 1}, new CharSequence[]{"2", "1"}); assertKeySet(keySet, new int[]{2, 1}); assertValues(values, new CharSequence[]{"2", "1"}); } } @Test public void keySetRetainAllReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); keySet.retainAll(Arrays.asList(1, 2)); assertMap(map, new int[]{2, 1}, new CharSequence[]{"2", "1"}); assertEntrySet(entrySet, new int[]{2, 1}, new CharSequence[]{"2", "1"}); assertKeySet(keySet, new int[]{2, 1}); assertValues(values, new CharSequence[]{"2", "1"}); } } @Test public void valuesRetainAllReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); values.removeIf(v -> !"1".contentEquals(v) && !"2".contentEquals(v)); assertMap(map, new int[]{2, 1}, new CharSequence[]{"2", "1"}); assertEntrySet(entrySet, new int[]{2, 1}, new CharSequence[]{"2", "1"}); assertKeySet(keySet, new int[]{2, 1}); assertValues(values, new CharSequence[]{"2", "1"}); } } @Test public void entrySetClearReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); entrySet.clear(); org.junit.Assert.assertTrue(map.isEmpty()); org.junit.Assert.assertTrue(entrySet.isEmpty()); org.junit.Assert.assertTrue(keySet.isEmpty()); org.junit.Assert.assertTrue(values.isEmpty()); } } @Test public void keySetClearReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); keySet.clear(); org.junit.Assert.assertTrue(map.isEmpty()); org.junit.Assert.assertTrue(entrySet.isEmpty()); org.junit.Assert.assertTrue(keySet.isEmpty()); org.junit.Assert.assertTrue(values.isEmpty()); } } @Test public void valuesClearReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(3)) { Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); values.clear(); org.junit.Assert.assertTrue(map.isEmpty()); org.junit.Assert.assertTrue(entrySet.isEmpty()); org.junit.Assert.assertTrue(keySet.isEmpty()); org.junit.Assert.assertTrue(values.isEmpty()); } } @Test public void clearMapViaEntryIteratorRemoves() throws IOException { int noOfElements = 16 * 1024; try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(noOfElements)) { int sum = 0; for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) { it.next(); it.remove(); ++sum; } assertEquals(noOfElements, sum); } } @Test public void clearMapViaKeyIteratorRemoves() throws IOException { int noOfElements = 16 * 1024; try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(noOfElements)) { Set<Integer> keys = new HashSet<Integer>(); for (int i = 1; i <= noOfElements; i++) { keys.add(i); } int sum = 0; for (Iterator it = map.keySet().iterator(); it.hasNext(); ) { Object key = it.next(); keys.remove(key); it.remove(); ++sum; } assertEquals(noOfElements, sum); } } @Test(expected = IllegalStateException.class) public void testRemoveWhenNextIsNotCalled() throws IOException { ChronicleMap<Integer, CharSequence> map = getViewTestMap(2); Iterator<Integer> iterator = map.keySet().iterator(); iterator.remove(); } @Test public void clearMapViaValueIteratorRemoves() throws IOException { int noOfElements = 16 * 1024; try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(noOfElements)) { int sum = 0; for (Iterator it = map.values().iterator(); it.hasNext(); ) { it.next(); it.remove(); ++sum; } assertEquals(noOfElements, sum); } } @Test public void entrySetValueReflectedInMapAndOtherViews() throws IOException { try (ChronicleMap<Integer, CharSequence> map = getViewTestMap(0)) { map.put(1, "A"); Set<Map.Entry<Integer, CharSequence>> entrySet = map.entrySet(); Set<Integer> keySet = map.keySet(); Collection<CharSequence> values = map.values(); assertMap(map, new int[]{1}, new CharSequence[]{"A"}); assertEntrySet(entrySet, new int[]{1}, new CharSequence[]{"A"}); assertKeySet(keySet, new int[]{1}); assertValues(values, new String[]{"A"}); entrySet.iterator().next().setValue("B"); assertMap(map, new int[]{1}, new CharSequence[]{"B"}); assertEntrySet(entrySet, new int[]{1}, new CharSequence[]{"B"}); assertEntrySet(map.entrySet(), new int[]{1}, new CharSequence[]{"B"}); assertKeySet(keySet, new int[]{1}); assertKeySet(map.keySet(), new int[]{1}); assertValues(values, new String[]{"B"}); assertValues(map.values(), new String[]{"B"}); } } @Test public void equalsTest() throws IOException { try (final ChronicleMap<Integer, String> map1 = ChronicleMap.of(Integer.class, String.class) .averageValue("one").entries(2).create()) { map1.put(1, "one"); map1.put(2, "two"); try (ChronicleMap<Integer, String> map2 = ChronicleMap.of(Integer.class, String.class) .averageValue("one").entries(2).create()) { map2.put(1, "one"); map2.put(2, "two"); assertEquals(map1, map2); } } } private static final class IncrementRunnable implements Runnable { private final ChronicleMap<CharSequence, LongValue> map; private final CharSequence key; private final int iterations; private final CyclicBarrier barrier; private IncrementRunnable(ChronicleMap<CharSequence, LongValue> map, CharSequence key, int iterations, CyclicBarrier barrier) { this.map = map; this.key = key; this.iterations = iterations; this.barrier = barrier; } @Override public void run() { try { LongValue value = Values.newNativeReference(LongValue.class); barrier.await(); for (int i = 0; i < iterations; i++) { map.acquireUsing(key, value); value.addAtomicValue(1); } } catch (Exception e) { e.printStackTrace(); } } } @Test public void testPutLongValue() throws IOException { final ChronicleMapBuilder<CharSequence, LongValue> builder = ChronicleMapBuilder .of(CharSequence.class, LongValue.class) .entries(1000) .averageKeySize("x".length()); try (final ChronicleMap<CharSequence, LongValue> map = builder.create()) { LongValue value = nativeLongValue(); try { map.put("x", value); } catch (IllegalStateException | NullPointerException e) { // ok return; } throw new AssertionError("Should throw either IllegalStateException or " + "NullPointerException, but succeed"); } } @Test public void testOffheapAcquireUsingLocked() throws IOException { ChronicleMapBuilder<CharSequence, LongValue> builder = ChronicleMapBuilder .of(CharSequence.class, LongValue.class) .entries(1000) .averageKeySize("one".length()); try (final ChronicleMap<CharSequence, LongValue> map = builder.create()) { LongValue value = nativeLongValue(); // this will add the entry try (net.openhft.chronicle.core.io.Closeable c = map.acquireContext("one", value)) { assertEquals(0, value.getValue()); value.addValue(1); } // check that the entry was added try (ExternalMapQueryContext<CharSequence, LongValue, ?> c = map.queryContext("one")) { MapEntry<CharSequence, LongValue> entry = c.entry(); assertNotNull(entry); LongValue v = entry.value().getUsing(value); assert v == value; assertEquals(1, v.getValue()); } // this will remove the entry try (ExternalMapQueryContext<CharSequence, LongValue, ?> c = map.queryContext("one")) { c.updateLock().lock(); c.remove(c.entry()); } // check that the entry was removed try (ExternalMapQueryContext<CharSequence, LongValue, ?> c = map.queryContext("one")) { c.updateLock().lock(); assertNotNull(c.absentEntry()); } try (net.openhft.chronicle.core.io.Closeable c = map.acquireContext("one", value)) { assertEquals(0, value.getValue()); } try (net.openhft.chronicle.core.io.Closeable c = map.acquireContext("one", value)) { value.addValue(1); } // check that the entry was removed try (ExternalMapQueryContext<CharSequence, LongValue, ?> c = map.queryContext("one")) { LongValue v = c.entry().value().getUsing(value); assert value == v; assertEquals(1, c.entry().value().get().getValue()); } } } @Test(expected = IllegalArgumentException.class) public void testAcquireUsingLockedWithString() throws IOException { ChronicleMapBuilder<CharSequence, String> builder = ChronicleMapBuilder .of(CharSequence.class, String.class) .averageKey("one").averageValue("") .entries(1000); try (final ChronicleMap<CharSequence, String> map = builder.create()) { // this will add the entry try (net.openhft.chronicle.core.io.Closeable c = map.acquireContext("one", "")) { // do nothing } } } @Test public void testOnheapAcquireUsingLockedStringBuilder() throws IOException { try (final ChronicleMap<CharSequence, CharSequence> map = ChronicleMapBuilder .of(CharSequence.class, CharSequence.class) .entries(1000) .averageKeySize("one".length()) .averageValueSize("Hello World".length()) .create()) { StringBuilder value = new StringBuilder(); try (net.openhft.chronicle.core.io.Closeable c = map.acquireContext("one", value)) { value.append("Hello World"); } assertEquals("Hello World", value.toString()); } } @Test public void testOnheapAcquireUsingLocked() throws IOException { File tmpFile = File.createTempFile("testAcquireUsingLocked", ".deleteme"); tmpFile.deleteOnExit(); try (final ChronicleMap<CharSequence, LongValue> map = ChronicleMapBuilder .of(CharSequence.class, LongValue.class) .entries(1000) .averageKeySize("one".length()).createPersistedTo(tmpFile)) { LongValue value = Values.newNativeReference(LongValue.class); try (ExternalMapQueryContext<CharSequence, LongValue, ?> c = map.queryContext("one")) { assertNotNull(c.absentEntry()); } try (net.openhft.chronicle.core.io.Closeable c = map.acquireContext("one", value)) { value.setValue(10); } // this will add the entry try (net.openhft.chronicle.core.io.Closeable c = map.acquireContext("one", value)) { value.addValue(1); } // check that the entry was added try (ExternalMapQueryContext<CharSequence, LongValue, ?> c = map.queryContext("one")) { MapEntry<CharSequence, LongValue> entry = c.entry(); assertNotNull(entry); assertEquals(11, entry.value().get().getValue()); } // this will remove the entry try (ExternalMapQueryContext<CharSequence, LongValue, ?> c = map.queryContext("one")) { c.updateLock().lock(); c.remove(c.entry()); } // check that the entry was removed try (ExternalMapQueryContext<CharSequence, LongValue, ?> c = map.queryContext("one")) { assertNotNull(c.absentEntry()); } try (net.openhft.chronicle.core.io.Closeable c = map.acquireContext("one", value)) { assertEquals(0, value.getValue()); } value.setValue(1); try (ExternalMapQueryContext<CharSequence, LongValue, ?> c = map.queryContext("one")) { assertEquals(1, c.entry().value().get().getValue()); } try (net.openhft.chronicle.core.io.Closeable c = map.acquireContext("one", value)) { value.addValue(1); } // check that the entry was removed try (ExternalMapQueryContext<CharSequence, LongValue, ?> c = map.queryContext("one")) { LongValue value1 = c.entry().value().get(); assertEquals(2, value1.getValue()); } } tmpFile.delete(); } }