/** * Copyright 2013-2015 Seagate Technology LLC. * * This Source Code Form is subject to the terms of the Mozilla * Public License, v. 2.0. If a copy of the MPL was not * distributed with this file, You can obtain one at * https://mozilla.org/MP:/2.0/. * * This program is distributed in the hope that it will be useful, * but is provided AS-IS, WITHOUT ANY WARRANTY; including without * the implied warranty of MERCHANTABILITY, NON-INFRINGEMENT or * FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public * License for more details. * * See www.openkinetic.org for more project information */ package com.seagate.kinetic.concurrent; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import org.testng.annotations.BeforeMethod; import org.testng.Assert; import static com.seagate.kinetic.KineticTestHelpers.toByteArray; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Iterator; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Logger; import kinetic.client.Entry; import kinetic.client.EntryMetadata; import kinetic.client.KineticClient; import kinetic.client.KineticClientFactory; import kinetic.client.KineticException; import com.seagate.kinetic.IntegrationTestCase; import com.seagate.kinetic.IntegrationTestLoggerFactory; import com.seagate.kinetic.KVGenerator; import com.seagate.kinetic.client.internal.DefaultKineticClient; /** * * Multi client(thread) concurrent put different entry into simulator/drive. * Verify the put result and put order. * <p> * */ @Test(groups = {"simulator", "drive"}) public class KineticClientConcurrentTest extends IntegrationTestCase { private static final Logger logger = IntegrationTestLoggerFactory .getLogger(KineticClientConcurrentTest.class.getName()); private final byte[] INIT_VERSION = "0".getBytes(); private KVGenerator kvGenerator; private final int writeThreads = 2; private final int writesEachThread = 300; /** * * Initialize a key/value generator. * */ @BeforeMethod public void setUp() throws KineticException, IOException, InterruptedException { kvGenerator = new KVGenerator(); } /** * * Concurrent threads put test. * * @throws KineticException * if any kinetic internal error occurred. * @throws InterruptedException * if any interrupt error occurred. */ @Test(dataProvider = "transportProtocolOptions") public void concurrentTest(String clientName) throws InterruptedException, KineticException, UnsupportedEncodingException { int totalWrites = writeThreads * writesEachThread; CountDownLatch latch = new CountDownLatch(writeThreads); ExecutorService pool = Executors.newCachedThreadPool(); cleanData(totalWrites, getClient(clientName)); // thread pool generate threads(concurrent client number) // logger.info("launch " + writeThreads + " write threads to write " // + totalWrites + " kv pairs"); kvGenerator = new KVGenerator(); kvGenerator.reset(); KineticClient kineticClient; for (int i = 0; i < writeThreads; i++) { kineticClient = KineticClientFactory .createInstance(kineticClientConfigutations.get(clientName)); pool.execute(new WriteThread(kineticClient, kvGenerator, writesEachThread, latch)); } // wait all threads finish latch.await(); pool.shutdown(); // reset kvGenerator kvGenerator.reset(); kineticClient = KineticClientFactory.createInstance(getClientConfig()); // verify results until all WriteThread finished // logger.info("verify the kv pairs....."); String key = ""; String rightValue = ""; String returnValue = ""; Entry returnVersioned = null; for (int i = 0; i < totalWrites; i++) { // logger.info("key=" + key + ", rightValue=" + rightValue // + ", returnValue=" + returnValue); key = kvGenerator.getNextKey(); rightValue = kvGenerator.getValue(key); returnVersioned = kineticClient.get(toByteArray(key)); if (returnVersioned == null) { Assert.fail("return null when get key " + key); } else { if (returnVersioned.getValue() == null) { Assert.fail("the value is null when get key " + key); } else { returnValue = new String(returnVersioned.getValue()); assertEquals(rightValue, returnValue); } } } kineticClient.close(); // record the startKey and endKey String endKey = key; kvGenerator.reset(); String startKey = kvGenerator.getNextKey(); // verify that the keys are sorted // logger.info("verify the data orders"); DefaultKineticClient defaultKineticClient = new DefaultKineticClient( getClientConfig()); Iterator<Entry> iterator = defaultKineticClient.getRange( startKey.getBytes(), true, endKey.getBytes(), true).iterator(); String currentKey = ""; String nextKey = ""; int count = 0; Entry currentVersioned = null; if (iterator.hasNext()) { currentVersioned = iterator.next(); if (currentVersioned == null) { Assert.fail("iterator has null element"); } else { if (currentVersioned.getKey() == null) { Assert.fail("the key is null"); } else { currentKey = new String(currentVersioned.getKey()); ++count; } } } while (iterator.hasNext()) { nextKey = new String(iterator.next().getKey()); assertTrue(currentKey.compareTo(nextKey) < 0); currentKey = nextKey; ++count; } // check the amount of kv pairs assertTrue(count == totalWrites); defaultKineticClient.close(); cleanData(totalWrites, getClient(clientName)); logger.info(this.testEndInfo()); } private void cleanData(int dataCount, KineticClient client) throws KineticException { KVGenerator kvGenerator = new KVGenerator(); for(int i = 0; i < dataCount; i++){ client.deleteForced(toByteArray(kvGenerator.getNextKey())); } } /** * * Every thread(client) execute writeCount number * <p> * */ class WriteThread implements Runnable { private int writeCount = 0; private final CountDownLatch latch; private final KVGenerator kvGenerator; private final KineticClient kineticClient; public WriteThread(KineticClient kineticClient, KVGenerator kvGenerator, int writeCount, CountDownLatch latch) { this.kineticClient = kineticClient; this.kvGenerator = kvGenerator; this.writeCount = writeCount; this.latch = latch; } @Override public void run() { String key = ""; String value = ""; byte[] version = INIT_VERSION; for (int i = 0; i < writeCount; i++) { key = kvGenerator.getNextKey(); value = kvGenerator.getValue(key); try { EntryMetadata entryMetadata = new EntryMetadata(); kineticClient.put(new Entry(toByteArray(key), toByteArray(value), entryMetadata), version); } catch (KineticException e) { Assert.fail("put key=" + key + ", value=" + value + " failed, " + e.getMessage()); } catch (Exception e) { Assert.fail("put key=" + key + ", value=" + value + " failed, " + e.getMessage()); } } try { kineticClient.close(); } catch (KineticException e) { Assert.fail("close kineticClient failed, " + e.getMessage()); } catch (Exception e) { Assert.fail("close kineticClient failed, " + e.getMessage()); } // latch count down latch.countDown(); } } }