/*
* Copyright 2017 Real Logic Ltd.
*
* 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 io.aeron;
import io.aeron.driver.*;
import io.aeron.driver.ext.*;
import io.aeron.driver.reports.LossReport;
import io.aeron.logbuffer.*;
import org.agrona.DirectBuffer;
import org.agrona.concurrent.*;
import org.junit.Test;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicLong;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class GapFillLossTest
{
private static final String CHANNEL = "aeron:udp?endpoint=localhost:54325";
private static final String UNRELIABLE_CHANNEL =
CHANNEL + "|" + CommonContext.RELIABLE_STREAM_PARAM_NAME + "=false";
private static final int STREAM_ID = 1;
private static final int FRAGMENT_COUNT_LIMIT = 10;
private static final int MSG_LENGTH = 1024;
private static final int TERM_BUFFER_LENGTH = 1024 * 64;
private static final int NUM_MESSAGES = 10_000;
private static final AtomicLong FINAL_POSITION = new AtomicLong(Long.MAX_VALUE);
@Test(timeout = 10000)
public void shouldGapFillWhenLossOccurs() throws Exception
{
final UnsafeBuffer srcBuffer = new UnsafeBuffer(ByteBuffer.allocateDirect(MSG_LENGTH));
srcBuffer.setMemory(0, MSG_LENGTH, (byte)7);
final MediaDriver.Context ctx = new MediaDriver.Context()
.threadingMode(ThreadingMode.SHARED)
.publicationTermBufferLength(TERM_BUFFER_LENGTH);
final LossReport lossReport = mock(LossReport.class);
ctx.lossReport(lossReport);
final LossGenerator dataLossGenerator =
DebugChannelEndpointConfiguration.lossGeneratorSupplier(0.20, 0xcafebabeL);
final LossGenerator noLossGenerator =
DebugChannelEndpointConfiguration.lossGeneratorSupplier(0, 0);
ctx.sendChannelEndpointSupplier(
(udpChannel, statusIndicator, context) -> new DebugSendChannelEndpoint(
udpChannel, statusIndicator, context, noLossGenerator, noLossGenerator));
ctx.receiveChannelEndpointSupplier(
(udpChannel, dispatcher, statusIndicator, context) -> new DebugReceiveChannelEndpoint(
udpChannel, dispatcher, statusIndicator, context, dataLossGenerator, noLossGenerator));
try (MediaDriver ignore = MediaDriver.launch(ctx);
Aeron aeron = Aeron.connect();
Publication publication = aeron.addPublication(CHANNEL, STREAM_ID);
Subscription subscription = aeron.addSubscription(UNRELIABLE_CHANNEL, STREAM_ID))
{
final IdleStrategy idleStrategy = new YieldingIdleStrategy();
final Subscriber subscriber = new Subscriber(subscription);
final Thread subscriberThread = new Thread(subscriber);
subscriberThread.start();
long position = 0;
for (int i = 0; i < NUM_MESSAGES; i++)
{
srcBuffer.putLong(0, i);
while ((position = publication.offer(srcBuffer)) < 0L)
{
idleStrategy.idle();
}
}
FINAL_POSITION.set(position);
subscriberThread.join();
assertThat(subscriber.messageCount, lessThan(NUM_MESSAGES));
verify(lossReport).createEntry(anyLong(), anyLong(), anyInt(), eq(STREAM_ID), anyString(), anyString());
}
finally
{
ctx.deleteAeronDirectory();
}
}
static class Subscriber implements Runnable, FragmentHandler
{
private final Subscription subscription;
int messageCount = 0;
Subscriber(final Subscription subscription)
{
this.subscription = subscription;
}
public void run()
{
final IdleStrategy idleStrategy = new YieldingIdleStrategy();
while (subscription.hasNoImages())
{
idleStrategy.idle();
}
final Image image = subscription.getImage(0);
while (image.position() < FINAL_POSITION.get())
{
idleStrategy.idle(subscription.poll(this, FRAGMENT_COUNT_LIMIT));
}
}
public void onFragment(final DirectBuffer buffer, final int offset, final int length, final Header header)
{
messageCount++;
}
}
}