package org.littlewings.hazelcast.ringbuffer; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.IntStream; import com.hazelcast.config.ClasspathXmlConfig; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.ICompletableFuture; import com.hazelcast.ringbuffer.OverflowPolicy; import com.hazelcast.ringbuffer.ReadResultSet; import com.hazelcast.ringbuffer.Ringbuffer; import com.hazelcast.ringbuffer.StaleSequenceException; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.fail; public class HazelcastRingbufferTest { @Test public void testSimpleRingbuffer() { withHazelcast(1, hazelcast -> { Ringbuffer<String> ringbuffer = hazelcast.getRingbuffer("default"); IntStream.rangeClosed(1, 100).forEach(i -> ringbuffer.add("element" + i)); assertThat(ringbuffer.size()) .isEqualTo(100L); long headSequence = ringbuffer.headSequence(); assertThat(headSequence) .isEqualTo(0L); try { long currentSequence = headSequence; assertThat(ringbuffer.readOne(currentSequence)) .isEqualTo("element1"); currentSequence++; String lastElement = null; while (ringbuffer.size() > 0) { lastElement = ringbuffer.readOne(currentSequence); assertThat(lastElement) .isNotNull(); if (currentSequence == 99) { break; } currentSequence++; } assertThat(lastElement) .isEqualTo("element100"); assertThat(ringbuffer.headSequence()) .isEqualTo(0L); assertThat(ringbuffer.tailSequence()) .isEqualTo(99L); } catch (InterruptedException e) { fail(e.getMessage()); } }); } @Test public void testRingbufferReadMany() { withHazelcast(1, hazelcast -> { Ringbuffer<String> ringbuffer = hazelcast.getRingbuffer("default"); IntStream.rangeClosed(1, 100).forEach(i -> ringbuffer.add("element" + i)); assertThat(ringbuffer.size()) .isEqualTo(100L); long headSequence = ringbuffer.headSequence(); assertThat(headSequence) .isEqualTo(0L); try { long currentSequence = headSequence; assertThat(ringbuffer.readOne(currentSequence)) .isEqualTo("element1"); currentSequence++; ICompletableFuture<ReadResultSet<String>> completableFuture = ringbuffer .readManyAsync(currentSequence, 1, 10, null); ReadResultSet<String> readResultSet = completableFuture.get(); int readCount = readResultSet.readCount(); List<String> resultElements = IntStream .range(0, readCount) .mapToObj(i -> readResultSet.get(i)) .collect(Collectors.toList()); List<String> verifyResultElements = IntStream .range(2, readCount + 2) .mapToObj(i -> "element" + i) .collect(Collectors.toList()); assertThat(resultElements) .isEqualTo(verifyResultElements); currentSequence += readCount; assertThat(currentSequence) .isEqualTo(headSequence + readCount + 1); assertThat(ringbuffer.tailSequence()) .isEqualTo(99L); } catch (InterruptedException | ExecutionException e) { fail(e.getMessage()); } }); } @Test public void testRingbufferOverflow() { withHazelcast(1, hazelcast -> { Ringbuffer<String> ringbuffer = hazelcast.getRingbuffer("small-ringbuffer"); IntStream.rangeClosed(1, 100).forEach(i -> ringbuffer.add("element" + i)); assertThat(ringbuffer.size()) .isEqualTo(10L); long headSequence = ringbuffer.headSequence(); assertThat(headSequence) .isEqualTo(90L); try { long currentSequence = headSequence; assertThat(ringbuffer.readOne(currentSequence)) .isEqualTo("element91"); currentSequence++; String lastElement = null; while (ringbuffer.size() > 0) { lastElement = ringbuffer.readOne(currentSequence); assertThat(lastElement) .isNotNull(); if (currentSequence == 99) { break; } currentSequence++; } assertThat(lastElement) .isEqualTo("element100"); assertThat(ringbuffer.headSequence()) .isEqualTo(90L); assertThat(ringbuffer.tailSequence()) .isEqualTo(99L); } catch (InterruptedException e) { fail(e.getMessage()); } }); } @Test public void testRingbufferSize() { withHazelcast(1, hazelcast -> { Ringbuffer<String> ringbuffer = hazelcast.getRingbuffer("default"); IntStream.rangeClosed(1, 100).forEach(i -> ringbuffer.add("element" + i)); assertThat(ringbuffer.size()) .isEqualTo(100L); long headSequence = ringbuffer.headSequence(); assertThat(headSequence) .isEqualTo(0L); long tailSequence = ringbuffer.tailSequence(); assertThat(tailSequence) .isEqualTo(99L); assertThat(ringbuffer.capacity()) .isEqualTo(10000L); assertThat(ringbuffer.remainingCapacity()) .isEqualTo(10000L); try { long currentSequence = headSequence; assertThat(ringbuffer.readOne(currentSequence)) .isEqualTo("element1"); currentSequence++; ICompletableFuture<ReadResultSet<String>> completableFuture = ringbuffer .readManyAsync(currentSequence, 5, 10, null); ReadResultSet<String> readResultSet = completableFuture.get(); int readCount = readResultSet.readCount(); currentSequence += readCount; String lastElement = null; while (ringbuffer.size() > 0) { lastElement = ringbuffer.readOne(currentSequence); assertThat(lastElement) .isNotNull(); if (currentSequence == 99) { break; } currentSequence++; } assertThat(lastElement) .isEqualTo("element100"); assertThat(ringbuffer.headSequence()) .isEqualTo(0L); assertThat(ringbuffer.tailSequence()) .isEqualTo(99L); assertThat(ringbuffer.capacity()) .isEqualTo(10000L); assertThat(ringbuffer.remainingCapacity()) .isEqualTo(10000L); } catch (InterruptedException | ExecutionException e) { fail(e.getMessage()); } }); } @Test public void testRingbufferSizeWithExpiry() { withHazelcast(1, hazelcast -> { Ringbuffer<String> ringbuffer = hazelcast.getRingbuffer("with-expired"); IntStream.rangeClosed(1, 100).forEach(i -> ringbuffer.add("element" + i)); assertThat(ringbuffer.size()) .isEqualTo(100L); long headSequence = ringbuffer.headSequence(); assertThat(headSequence) .isEqualTo(0L); long tailSequence = ringbuffer.tailSequence(); assertThat(tailSequence) .isEqualTo(99L); assertThat(ringbuffer.capacity()) .isEqualTo(10000L); assertThat(ringbuffer.remainingCapacity()) .isEqualTo(9900L); try { long currentSequence = headSequence; assertThat(ringbuffer.readOne(currentSequence)) .isEqualTo("element1"); currentSequence++; ICompletableFuture<ReadResultSet<String>> completableFuture = ringbuffer .readManyAsync(currentSequence, 5, 10, null); ReadResultSet<String> readResultSet = completableFuture.get(); int readCount = readResultSet.readCount(); currentSequence += readCount; String lastElement = null; while (ringbuffer.size() > 0) { lastElement = ringbuffer.readOne(currentSequence); assertThat(lastElement) .isNotNull(); if (currentSequence == 99) { break; } currentSequence++; } assertThat(lastElement) .isEqualTo("element100"); assertThat(ringbuffer.headSequence()) .isEqualTo(0L); assertThat(ringbuffer.tailSequence()) .isEqualTo(99L); assertThat(ringbuffer.capacity()) .isEqualTo(10000L); assertThat(ringbuffer.remainingCapacity()) .isEqualTo(9900L); } catch (InterruptedException | ExecutionException e) { fail(e.getMessage()); } }); } @Test public void testRingbufferWithExpiry() { withHazelcast(1, hazelcast -> { Ringbuffer<String> ringbuffer = hazelcast.getRingbuffer("with-expired"); IntStream.rangeClosed(1, 100).forEach(i -> ringbuffer.add("element" + i)); assertThat(ringbuffer.size()) .isEqualTo(100L); long headSequence = ringbuffer.headSequence(); assertThat(headSequence) .isEqualTo(0L); long tailSequence = ringbuffer.tailSequence(); assertThat(tailSequence) .isEqualTo(99L); assertThat(ringbuffer.capacity()) .isEqualTo(10000L); assertThat(ringbuffer.remainingCapacity()) .isEqualTo(9900L); try { long currentSequence = headSequence; assertThat(ringbuffer.readOne(currentSequence)) .isEqualTo("element1"); currentSequence++; ICompletableFuture<ReadResultSet<String>> completableFuture = ringbuffer .readManyAsync(currentSequence, 5, 10, null); ReadResultSet<String> readResultSet = completableFuture.get(); int readCount = readResultSet.readCount(); currentSequence += readCount; TimeUnit.SECONDS.sleep(5); long seq1 = currentSequence++; assertThatThrownBy(() -> ringbuffer.readOne(seq1)) .isInstanceOf(StaleSequenceException.class); long seq2 = currentSequence++; assertThatThrownBy(() -> ringbuffer.readOne(seq2)) .isInstanceOf(StaleSequenceException.class); assertThat(ringbuffer.headSequence()) .isEqualTo(100L); assertThat(ringbuffer.tailSequence()) .isEqualTo(99L); assertThat(ringbuffer.capacity()) .isEqualTo(10000L); assertThat(ringbuffer.remainingCapacity()) .isEqualTo(10000L); } catch (InterruptedException | ExecutionException e) { fail(e.getMessage()); } }); } @Test public void testRingbufferOverflowFail() { withHazelcast(1, hazelcast -> { Ringbuffer<String> ringbuffer = hazelcast.getRingbuffer("small-ringbuffer"); List<ICompletableFuture<Long>> futures = IntStream .rangeClosed(1, 100) .mapToObj(i -> ringbuffer.addAsync("element" + i, OverflowPolicy.FAIL)) .collect(Collectors.toList()); List<Long> results = futures .stream() .map(f -> { try { return f.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } }) .collect(Collectors.toList()); assertThat(results) .hasSize(100); assertThat(results) .doesNotContain(-1L); try { long headSequence = ringbuffer.headSequence(); assertThat(ringbuffer.readOne(headSequence)) .isEqualTo("element91"); assertThat(headSequence) .isEqualTo(90L); long tailSequence = ringbuffer.tailSequence(); assertThat(ringbuffer.readOne(tailSequence)) .isEqualTo("element100"); assertThat(tailSequence) .isEqualTo(99L); } catch (InterruptedException e) { fail(e.getMessage()); } }); } @Test public void testRingbufferOverflowOverwrite() { withHazelcast(1, hazelcast -> { Ringbuffer<String> ringbuffer = hazelcast.getRingbuffer("small-ringbuffer"); List<ICompletableFuture<Long>> futures = IntStream .rangeClosed(1, 100) .mapToObj(i -> ringbuffer.addAsync("element" + i, OverflowPolicy.OVERWRITE)) .collect(Collectors.toList()); List<Long> results = futures .stream() .map(f -> { try { return f.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } }) .collect(Collectors.toList()); assertThat(results) .hasSize(100); assertThat(results) .doesNotContain(-1L); try { long headSequence = ringbuffer.headSequence(); assertThat(ringbuffer.readOne(headSequence)) .isEqualTo("element91"); assertThat(headSequence) .isEqualTo(90L); long tailSequence = ringbuffer.tailSequence(); assertThat(ringbuffer.readOne(tailSequence)) .isEqualTo("element100"); assertThat(tailSequence) .isEqualTo(99L); } catch (InterruptedException e) { fail(e.getMessage()); } }); } @Test public void testRingbufferOverflowFailWithExpiry() { withHazelcast(1, hazelcast -> { Ringbuffer<String> ringbuffer = hazelcast.getRingbuffer("small-ringbuffer-expired"); List<ICompletableFuture<Long>> futures = IntStream .rangeClosed(1, 100) .mapToObj(i -> ringbuffer.addAsync("element" + i, OverflowPolicy.FAIL)) .collect(Collectors.toList()); List<Long> results = futures .stream() .map(f -> { try { return f.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } }) .collect(Collectors.toList()); assertThat(results) .hasSize(100); assertThat(results) .contains(-1L); try { long headSequence = ringbuffer.headSequence(); assertThat(ringbuffer.readOne(headSequence)) .isEqualTo("element1"); assertThat(headSequence) .isEqualTo(0L); long tailSequence = ringbuffer.tailSequence(); assertThat(ringbuffer.readOne(tailSequence)) .isEqualTo("element10"); assertThat(tailSequence) .isEqualTo(9L); } catch (InterruptedException e) { fail(e.getMessage()); } }); } @Test public void testRingbufferOverflowOverwriteWithExpiry() { withHazelcast(1, hazelcast -> { Ringbuffer<String> ringbuffer = hazelcast.getRingbuffer("small-ringbuffer-expired"); List<ICompletableFuture<Long>> futures = IntStream .rangeClosed(1, 100) .mapToObj(i -> ringbuffer.addAsync("element" + i, OverflowPolicy.OVERWRITE)) .collect(Collectors.toList()); List<Long> results = futures .stream() .map(f -> { try { return f.get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } }) .collect(Collectors.toList()); assertThat(results) .hasSize(100); assertThat(results) .doesNotContain(-1L); try { long headSequence = ringbuffer.headSequence(); assertThat(ringbuffer.readOne(headSequence)) .isEqualTo("element91"); assertThat(headSequence) .isEqualTo(90L); long tailSequence = ringbuffer.tailSequence(); assertThat(ringbuffer.readOne(tailSequence)) .isEqualTo("element100"); assertThat(tailSequence) .isEqualTo(99L); } catch (InterruptedException e) { fail(e.getMessage()); } }); } protected void withHazelcast(int numInstances, Consumer<HazelcastInstance> consumer) { List<HazelcastInstance> hazelcastInstances = IntStream .rangeClosed(1, numInstances) .mapToObj(i -> Hazelcast.newHazelcastInstance(new ClasspathXmlConfig("hazelcast.xml"))) .collect(Collectors.toList()); try { consumer.accept(hazelcastInstances.get(0)); } finally { hazelcastInstances.forEach(h -> h.getLifecycleService().shutdown()); Hazelcast.shutdownAll(); } } }