io.pravega.shared.protocol.netty.AppendEncodeDecodeTest.java Source code

Java tutorial

Introduction

Here is the source code for io.pravega.shared.protocol.netty.AppendEncodeDecodeTest.java

Source

/**
 * Copyright (c) 2017 Dell Inc., or its subsidiaries. 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
 */
package io.pravega.shared.protocol.netty;

import io.pravega.shared.protocol.netty.WireCommands.KeepAlive;
import io.pravega.shared.protocol.netty.WireCommands.SetupAppend;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static io.pravega.shared.protocol.netty.WireCommandType.EVENT;
import static io.pravega.shared.protocol.netty.WireCommands.TYPE_PLUS_LENGTH_SIZE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.util.ResourceLeakDetector;
import io.netty.util.ResourceLeakDetector.Level;
import lombok.Cleanup;
import lombok.RequiredArgsConstructor;

public class AppendEncodeDecodeTest {

    private final int appendBlockSize = 1024;
    private final UUID connectionId = new UUID(1, 2);
    private final String streamName = "Test Stream Name";
    private final CommandEncoder encoder = new CommandEncoder(new FixedBatchSizeTracker(appendBlockSize));
    private final FakeLengthDecoder lengthDecoder = new FakeLengthDecoder();
    private final AppendDecoder appendDecoder = new AppendDecoder();
    private Level origionalLogLevel;

    @Before
    public void setup() {
        origionalLogLevel = ResourceLeakDetector.getLevel();
        ResourceLeakDetector.setLevel(Level.PARANOID);
    }

    @After
    public void teardown() {
        ResourceLeakDetector.setLevel(origionalLogLevel);
    }

    @RequiredArgsConstructor
    private static final class FixedBatchSizeTracker implements AppendBatchSizeTracker {
        private final int appendBlockSize;

        @Override
        public int getAppendBlockSize() {
            return appendBlockSize;
        }

        @Override
        public void recordAppend(long eventNumber, int size) {

        }

        @Override
        public void recordAck(long eventNumber) {
        }

        @Override
        public int getBatchTimeout() {
            return 10;
        }

    }

    private static final class FakeLengthDecoder extends LengthFieldBasedFrameDecoder {
        FakeLengthDecoder() {
            super(1024 * 1024, 4, 4);
        }

        @Override
        protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
            return super.decode(ctx, in);
        }
    }

    @Test(expected = InvalidMessageException.class)
    public void testAppendWithoutSetup() throws Exception {
        int size = 10;
        @Cleanup("release")
        ByteBuf fakeNetwork = ByteBufAllocator.DEFAULT.buffer();
        append(streamName, connectionId, 0, 1, size, fakeNetwork);
    }

    @Test
    public void testVerySmallBlockSize() throws Exception {
        @Cleanup("release")
        ByteBuf fakeNetwork = ByteBufAllocator.DEFAULT.buffer();
        byte[] content = new byte[100];
        Arrays.fill(content, (byte) 1);
        ByteBuf buffer = Unpooled.wrappedBuffer(content);
        Append msg = new Append("segment", connectionId, 1, buffer, null);
        CommandEncoder commandEncoder = new CommandEncoder(new FixedBatchSizeTracker(3));
        SetupAppend setupAppend = new SetupAppend(1, connectionId, "segment");
        commandEncoder.encode(null, setupAppend, fakeNetwork);
        appendDecoder.processCommand(setupAppend);

        ArrayList<Object> received = new ArrayList<>();
        commandEncoder.encode(null, msg, fakeNetwork);
        read(fakeNetwork, received);
        assertEquals(2, received.size());
        Append readAppend = (Append) received.get(1);
        assertEquals(msg.data.readableBytes() + TYPE_PLUS_LENGTH_SIZE, readAppend.data.readableBytes());
    }

    @Test
    public void testSwitchingStream() throws Exception {
        int size = appendBlockSize;
        int numEvents = 2;
        UUID c1 = new UUID(1, 1);
        UUID c2 = new UUID(2, 2);
        String s1 = "Stream 1";
        String s2 = "Stream 2";
        sendAndVerifyEvents(s1, c1, numEvents, size, numEvents);
        sendAndVerifyEvents(s2, c2, numEvents, size, numEvents);
    }

    private void sendAndVerifyEvents(String segment, UUID connectionId, int numEvents, int eventSize,
            int expectedMessages) throws Exception {
        @Cleanup("release")
        ByteBuf fakeNetwork = ByteBufAllocator.DEFAULT.buffer();
        ArrayList<Object> received = setupAppend(segment, connectionId, fakeNetwork);
        for (int i = 0; i < numEvents; i++) {
            append(segment, connectionId, eventSize * (i + 1L), i, eventSize, fakeNetwork);
            read(fakeNetwork, received);
        }
        KeepAlive keepAlive = new KeepAlive();
        encoder.encode(null, keepAlive, fakeNetwork);
        read(fakeNetwork, received);

        assertEquals(expectedMessages + 1, received.size());
        assertEquals(received.get(received.size() - 1), keepAlive);
        received.remove(received.size() - 1);
        verify(received, numEvents, eventSize);
    }

    @Test
    public void testFlushBeforeEndOfBlock() throws Exception {
        testFlush(appendBlockSize / 2);
    }

    @Test
    public void testFlushWhenAtBlockBoundry() throws Exception {
        testFlush(appendBlockSize);
    }

    private void testFlush(int size) throws Exception {
        @Cleanup("release")
        ByteBuf fakeNetwork = ByteBufAllocator.DEFAULT.buffer();
        ArrayList<Object> received = setupAppend(streamName, connectionId, fakeNetwork);

        append(streamName, connectionId, size, 0, size, fakeNetwork);
        read(fakeNetwork, received);

        KeepAlive keepAlive = new KeepAlive();
        encoder.encode(null, keepAlive, fakeNetwork);
        read(fakeNetwork, received);
        assertEquals(2, received.size());

        Append one = (Append) received.get(0);
        assertEquals(size + TYPE_PLUS_LENGTH_SIZE, one.getData().readableBytes());
        KeepAlive two = (KeepAlive) received.get(1);
        assertEquals(keepAlive, two);
    }

    @Test
    public void testSmallAppends() throws Exception {
        int eventSize = 10;
        int numEvents = 10000;
        sendAndVerifyEvents(streamName, connectionId, numEvents, eventSize,
                numEvents * (eventSize + TYPE_PLUS_LENGTH_SIZE) / appendBlockSize + 1);
    }

    @Test
    public void testAppendSpanningBlockBound() throws Exception {
        int numEvents = 4;
        int size = (appendBlockSize * 3) / numEvents;
        sendAndVerifyEvents(streamName, connectionId, numEvents, size, 2);
    }

    @Test
    public void testBlockSizeAppend() throws Exception {
        int numEvents = 4;
        int size = appendBlockSize;
        sendAndVerifyEvents(streamName, connectionId, numEvents, size, numEvents);
    }

    @Test
    public void testAlmostBlockSizeAppend1() throws Exception {
        int numEvents = 4;
        int size = appendBlockSize - 1;
        sendAndVerifyEvents(streamName, connectionId, numEvents, size, numEvents);
    }

    @Test
    public void testAlmostBlockSizeAppend8() throws Exception {
        int numEvents = 4;
        int size = appendBlockSize - 8;
        sendAndVerifyEvents(streamName, connectionId, numEvents, size, numEvents);
    }

    @Test
    public void testAlmostBlockSizeAppend16() throws Exception {
        int numEvents = 4;
        int size = appendBlockSize - 16;
        sendAndVerifyEvents(streamName, connectionId, numEvents, size, numEvents);
    }

    @Test
    public void testAppendAtBlockBound() throws Exception {
        int size = appendBlockSize;
        @Cleanup("release")
        ByteBuf fakeNetwork = ByteBufAllocator.DEFAULT.buffer();
        ArrayList<Object> received = setupAppend(streamName, connectionId, fakeNetwork);

        append(streamName, connectionId, size, 1, size, fakeNetwork);
        read(fakeNetwork, received);
        assertEquals(1, received.size());

        append(streamName, connectionId, size + size / 2, 2, size / 2, fakeNetwork);
        read(fakeNetwork, received);
        assertEquals(1, received.size());

        KeepAlive keepAlive = new KeepAlive();
        encoder.encode(null, keepAlive, fakeNetwork);
        read(fakeNetwork, received);
        assertEquals(3, received.size());

        Append one = (Append) received.get(0);
        Append two = (Append) received.get(1);
        assertEquals(size + TYPE_PLUS_LENGTH_SIZE, one.getData().readableBytes());
        assertEquals(size / 2 + TYPE_PLUS_LENGTH_SIZE, two.getData().readableBytes());
        KeepAlive three = (KeepAlive) received.get(2);
        assertEquals(keepAlive, three);
    }

    @Test
    public void testLargeAppend() throws Exception {
        int size = 10 * appendBlockSize;
        sendAndVerifyEvents(streamName, connectionId, 2, size, 2);
    }

    private ArrayList<Object> setupAppend(String testStream, UUID connectionId, ByteBuf fakeNetwork)
            throws Exception {
        SetupAppend setupAppend = new SetupAppend(1, connectionId, testStream);
        encoder.encode(null, setupAppend, fakeNetwork);
        ArrayList<Object> received = new ArrayList<>();
        WireCommand command = CommandDecoder.parseCommand(fakeNetwork);
        assertTrue(appendDecoder.acceptInboundMessage(command));
        assertEquals(setupAppend, appendDecoder.processCommand(command));
        return received;
    }

    private long append(String segment, UUID connectionId, long offset, int messageNumber, int length, ByteBuf out)
            throws Exception {
        byte[] content = new byte[length];
        Arrays.fill(content, (byte) messageNumber);
        ByteBuf buffer = Unpooled.wrappedBuffer(content);
        Append msg = new Append(segment, connectionId, messageNumber, buffer, null);
        encoder.encode(null, msg, out);
        return offset + length;
    }

    private void read(ByteBuf in, List<Object> results) throws Exception {
        ByteBuf segmented = (ByteBuf) lengthDecoder.decode(null, in);
        while (segmented != null) {
            int before = results.size();
            WireCommand command = CommandDecoder.parseCommand(segmented);
            if (appendDecoder.acceptInboundMessage(command)) {
                Request request = appendDecoder.processCommand(command);
                if (request != null) {
                    results.add(request);
                }
            } else {
                results.add(command);
            }
            assertTrue(results.size() == before || results.size() == before + 1);
            segmented.release();
            segmented = (ByteBuf) lengthDecoder.decode(null, in);
        }
    }

    private void verify(List<Object> results, int numValues, int sizeOfEachValue) {
        int currentValue = -1;
        int currentCount = sizeOfEachValue;
        for (Object r : results) {
            Append append = (Append) r;
            assertEquals("Append split mid event", sizeOfEachValue, currentCount);
            while (append.getData().isReadable()) {
                if (currentCount == sizeOfEachValue) {
                    assertEquals(EVENT.getCode(), append.getData().readInt());
                    assertEquals(sizeOfEachValue, append.getData().readInt());
                    currentCount = 0;
                    currentValue++;
                }
                byte readByte = append.getData().readByte();
                assertEquals((byte) currentValue, readByte);
                currentCount++;
            }
            assertEquals(currentValue, append.getEventNumber());
        }
        assertEquals(numValues - 1, currentValue);
        assertEquals(currentCount, sizeOfEachValue);
    }

}