/** * Copyright 2015, Xiaomi. * All rights reserved. * Author: yongxing@xiaomi.com */ package com.xiaomi.infra.galaxy.talos.producer; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.Random; import libthrift091.TException; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; import com.xiaomi.infra.galaxy.talos.admin.TalosAdmin; import com.xiaomi.infra.galaxy.talos.client.Constants; import com.xiaomi.infra.galaxy.talos.client.SimpleTopicAbnormalCallback; import com.xiaomi.infra.galaxy.talos.client.TalosClientConfigKeys; import com.xiaomi.infra.galaxy.talos.client.TalosClientFactory; import com.xiaomi.infra.galaxy.talos.thrift.DescribeTopicRequest; import com.xiaomi.infra.galaxy.talos.thrift.ErrorCode; import com.xiaomi.infra.galaxy.talos.thrift.GalaxyTalosException; import com.xiaomi.infra.galaxy.talos.thrift.Message; import com.xiaomi.infra.galaxy.talos.thrift.MessageService; import com.xiaomi.infra.galaxy.talos.thrift.PutMessageRequest; import com.xiaomi.infra.galaxy.talos.thrift.PutMessageResponse; import com.xiaomi.infra.galaxy.talos.thrift.Topic; import com.xiaomi.infra.galaxy.talos.thrift.TopicAttribute; import com.xiaomi.infra.galaxy.talos.thrift.TopicInfo; import com.xiaomi.infra.galaxy.talos.thrift.TopicState; import com.xiaomi.infra.galaxy.talos.thrift.TopicStatus; import com.xiaomi.infra.galaxy.talos.thrift.TopicTalosResourceName; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyListOf; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.when; public class TalosProducerTest { private static final String base = "abcdefgh ijklmnopqr stuvwxyz 0123456789"; private static final String resourceName = "12345#TopicName#july777777000999"; private static final String anotherResourceName = "12345#TopicName#july777777000629"; private static final String topicName = "TopicName"; private static final String ownerId = "12345"; private static final int messageRetentionMs = 1000; private static final int partitionNumber = 8; private static final int partitionNumber2 = 16; private static final int randomStrLen = 15; private static final int producerMaxBufferedMillSecs = 10; private static final int producerMaxPutMsgNumber = 10; private static final int producerMaxPutMsgBytes = 100; private static final int checkPartitionInterval = 200; private TalosProducerConfig talosProducerConfig; private TalosProducer talosProducer; private List<Message> messageList; private Topic topic; private TalosAdmin talosAdminMock; private TalosClientFactory talosClientFactoryMock; private MessageService.Iface messageClientMock; private PartitionSender partitionSenderMock; private volatile int msgPutSuccessCount; private volatile int msgPutFailureCount; // generate random string as message for putMessage private static String getRandomString(int randomStrLen) { Random random = new Random(); StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < randomStrLen; i++) { int number = random.nextInt(base.length()); stringBuffer.append(base.charAt(number)); } return stringBuffer.toString(); } private synchronized void addSuccessCounter(int counter) { msgPutSuccessCount += counter; } private synchronized void addFailureCounter(int counter) { msgPutFailureCount += counter; } // define callback for asynchronously putmessage private class TestCallback implements UserMessageCallback { @Override public void onSuccess(UserMessageResult userMessageResult) { addSuccessCounter(userMessageResult.getMessageList().size()); } @Override public void onError(UserMessageResult userMessageResult) { addFailureCounter(userMessageResult.getMessageList().size()); } } @Before public void setUp() throws TException { // set properties Properties properties = new Properties(); properties.setProperty( TalosClientConfigKeys.GALAXY_TALOS_PRODUCER_MAX_BUFFERED_MILLI_SECS, String.valueOf(producerMaxBufferedMillSecs)); properties.setProperty( TalosClientConfigKeys.GALAXY_TALOS_PRODUCER_MAX_PUT_MESSAGE_NUMBER, String.valueOf(producerMaxPutMsgNumber)); properties.setProperty( TalosClientConfigKeys.GALAXY_TALOS_PRODUCER_MAX_PUT_MESSAGE_BYTES, String.valueOf(producerMaxPutMsgBytes)); properties.setProperty( TalosClientConfigKeys.GALAXY_TALOS_PRODUCER_CHECK_PARTITION_INTERVAL, String.valueOf(checkPartitionInterval)); properties.setProperty( TalosClientConfigKeys.GALAXY_TALOS_SERVICE_ENDPOINT, "testURI"); talosProducerConfig = new TalosProducerConfig(properties, false); // construct a topic TopicInfo topicInfo = new TopicInfo( topicName, new TopicTalosResourceName(resourceName), ownerId); TopicAttribute topicAttribute = new TopicAttribute() .setPartitionNumber(partitionNumber) .setMessageRetentionSecs(messageRetentionMs); TopicState topicState = new TopicState() .setTopicStatus(TopicStatus.ACTIVE) .setCreateTimestamp(System.currentTimeMillis()); topic = new Topic(topicInfo, topicAttribute, topicState); // mock some return value talosAdminMock = Mockito.mock(TalosAdmin.class); talosClientFactoryMock = Mockito.mock(TalosClientFactory.class); messageClientMock = Mockito.mock(MessageService.Iface.class); partitionSenderMock = Mockito.mock(PartitionSender.class); // generate 100 random messages messageList = new ArrayList<Message>(); for (int i = 0; i < 100; ++i) { messageList.add(new Message(ByteBuffer.wrap( getRandomString(randomStrLen).getBytes()))); } // mock putMessageResponse msgPutFailureCount = 0; msgPutSuccessCount = 0; } @After public void tearDown() { } @Test public void testAsynchronouslyAddUserMessage() throws Exception { when(talosAdminMock.describeTopic(new DescribeTopicRequest(topicName))).thenReturn(topic); talosProducer = new TalosProducer(talosProducerConfig, new TopicTalosResourceName(resourceName), talosAdminMock, talosClientFactoryMock, partitionSenderMock, new SimpleTopicAbnormalCallback(), new TestCallback()); doNothing().when(partitionSenderMock).addMessage(anyListOf(UserMessage.class)); talosProducer.addUserMessage(messageList); // wait for execute finished Thread.sleep(producerMaxBufferedMillSecs * 10); } @Test(expected = ProducerNotActiveException.class) public void testProducerNotActiveError() throws Exception { TopicInfo topicInfo = new TopicInfo( topicName, new TopicTalosResourceName(anotherResourceName), ownerId); TopicAttribute topicAttribute = new TopicAttribute() .setPartitionNumber(partitionNumber) .setMessageRetentionSecs(messageRetentionMs); TopicState topicState = new TopicState() .setTopicStatus(TopicStatus.ACTIVE) .setCreateTimestamp(System.currentTimeMillis()); Topic another = new Topic(topicInfo, topicAttribute, topicState); when(talosAdminMock.describeTopic(new DescribeTopicRequest(topicName))) .thenReturn(topic).thenReturn(another); doNothing().when(partitionSenderMock).shutdown(); talosProducer = new TalosProducer(talosProducerConfig, new TopicTalosResourceName(resourceName), talosAdminMock, talosClientFactoryMock, partitionSenderMock, new SimpleTopicAbnormalCallback(), new TestCallback()); // wait check partition interval Thread.sleep(checkPartitionInterval * 2); doNothing().when(partitionSenderMock).addMessage(anyListOf(UserMessage.class)); talosProducer.addUserMessage(messageList); } // addUserMessage check message validity @Test(expected = IllegalArgumentException.class) public void testAddUserMessageValidity() throws Exception { when(talosAdminMock.describeTopic(new DescribeTopicRequest(topicName))).thenReturn(topic); talosProducer = new TalosProducer(talosProducerConfig, new TopicTalosResourceName(resourceName), talosAdminMock, talosClientFactoryMock, partitionSenderMock, new SimpleTopicAbnormalCallback(), new TestCallback()); String partitionKey = getRandomString( Constants.TALOS_PARTITION_KEY_LENGTH_MAXIMAL + 1); ArrayList<Message> list = new ArrayList<Message>(); list.add(new Message( ByteBuffer.wrap("hello".getBytes())).setPartitionKey(partitionKey)); talosProducer.addUserMessage(list); } @Test(expected = NullPointerException.class) public void testAddUserMessageValidity2() throws Exception { when(talosAdminMock.describeTopic(new DescribeTopicRequest(topicName))).thenReturn(topic); talosProducer = new TalosProducer(talosProducerConfig, new TopicTalosResourceName(resourceName), talosAdminMock, talosClientFactoryMock, partitionSenderMock, new SimpleTopicAbnormalCallback(), new TestCallback()); ArrayList<Message> list = new ArrayList<Message>(); list.add(null); talosProducer.addUserMessage(list); } @Test(expected = IllegalArgumentException.class) public void testAddUserMessageValidity3() throws Exception { when(talosAdminMock.describeTopic(new DescribeTopicRequest(topicName))).thenReturn(topic); talosProducer = new TalosProducer(talosProducerConfig, new TopicTalosResourceName(resourceName), talosAdminMock, talosClientFactoryMock, partitionSenderMock, new SimpleTopicAbnormalCallback(), new TestCallback()); String bigStr = getRandomString( Constants.TALOS_SINGLE_MESSAGE_BYTES_MAXIMAL + 1); ArrayList<Message> list = new ArrayList<Message>(); list.add(new Message(ByteBuffer.wrap(bigStr.getBytes()))); talosProducer.addUserMessage(list); } // check topic not exist when init Producer @Test(expected = GalaxyTalosException.class) public void testTopicNotExist() throws Exception { doThrow(new GalaxyTalosException().setErrorCode(ErrorCode.TOPIC_NOT_EXIST)) .when(talosAdminMock).describeTopic(new DescribeTopicRequest(topicName)); talosProducer = new TalosProducer(talosProducerConfig, new TopicTalosResourceName(resourceName), talosAdminMock, talosClientFactoryMock, partitionSenderMock, new SimpleTopicAbnormalCallback(), new TestCallback()); } @Test(expected = IllegalArgumentException.class) public void testTopicNotExistForDifferentResourceName() throws Exception { when(talosAdminMock.describeTopic(new DescribeTopicRequest(topicName))).thenReturn(topic); talosProducer = new TalosProducer(talosProducerConfig, new TopicTalosResourceName(anotherResourceName), talosAdminMock, talosClientFactoryMock, partitionSenderMock, new SimpleTopicAbnormalCallback(), new TestCallback()); } // check partition change when producer running @Test public void testPartitionChangeDuringProducerRunning() throws Exception { TopicInfo topicInfo = new TopicInfo( topicName, new TopicTalosResourceName(resourceName), ownerId); TopicAttribute topicAttribute = new TopicAttribute() .setPartitionNumber(partitionNumber2) .setMessageRetentionSecs(messageRetentionMs); TopicState topicState = new TopicState() .setTopicStatus(TopicStatus.ACTIVE) .setCreateTimestamp(System.currentTimeMillis()); Topic another = new Topic(topicInfo, topicAttribute, topicState); when(talosAdminMock.describeTopic(new DescribeTopicRequest(topicName))) .thenReturn(topic).thenReturn(another); talosProducer = new TalosProducer(talosProducerConfig, new TopicTalosResourceName(resourceName), talosAdminMock, talosClientFactoryMock, partitionSenderMock, new SimpleTopicAbnormalCallback(), new TestCallback()); // wait check partition interval Thread.sleep(checkPartitionInterval * 2); // check the partition number and outgoingMessageMap changing by log info } // check topic be deleted when producer running @Test public void testTopicBeDeletedDuringProducerRunning() throws Exception { TopicInfo topicInfo = new TopicInfo( topicName, new TopicTalosResourceName(anotherResourceName), ownerId); TopicAttribute topicAttribute = new TopicAttribute() .setPartitionNumber(partitionNumber) .setMessageRetentionSecs(messageRetentionMs); TopicState topicState = new TopicState() .setTopicStatus(TopicStatus.ACTIVE) .setCreateTimestamp(System.currentTimeMillis()); Topic another = new Topic(topicInfo, topicAttribute, topicState); when(talosAdminMock.describeTopic(new DescribeTopicRequest(topicName))) .thenReturn(topic).thenReturn(another); doNothing().when(partitionSenderMock).shutdown(); talosProducer = new TalosProducer(talosProducerConfig, new TopicTalosResourceName(resourceName), talosAdminMock, talosClientFactoryMock, partitionSenderMock, new SimpleTopicAbnormalCallback(), new TestCallback()); // wait check partition interval Thread.sleep(checkPartitionInterval * 2); } @Test public void testAddUserMessage() throws Exception { topic.getTopicAttribute().setPartitionNumber(1); when(talosAdminMock.describeTopic(new DescribeTopicRequest(topicName))).thenReturn(topic); doReturn(messageClientMock).when(talosClientFactoryMock).newMessageClient(); doReturn(new PutMessageResponse()).when(messageClientMock).putMessage(any(PutMessageRequest.class)); talosProducer = new TalosProducer(talosProducerConfig, new TopicTalosResourceName(resourceName), talosAdminMock, talosClientFactoryMock, partitionSenderMock, new SimpleTopicAbnormalCallback(), new TestCallback()); for (int i = 0; i < 100; ++i) { talosProducer.addUserMessage(messageList); } talosProducer.shutdown(); assertEquals(100 * messageList.size(), msgPutSuccessCount); } }