/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.voltdb.importer;
import static com.google_voltpatches.common.base.Predicates.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.net.URI;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import org.apache.zookeeper_voltpatches.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.voltcore.zk.ZKTestBase;
import com.google_voltpatches.common.collect.FluentIterable;
import com.google_voltpatches.common.collect.ImmutableMap;
import com.google_voltpatches.common.collect.ImmutableSet;
import com.google_voltpatches.common.collect.Maps;
import com.google_voltpatches.common.collect.Sets;
public class TestChannelDistributer extends ZKTestBase {
private final static String ZERO = "zero";
private final static String UNO = "uno";
private final static String DUE = "due";
private final static String YO = "yo";
Map<String, ZooKeeper> zks;
Map<String, ChannelDistributer> distributers;
BlockingDeque<ImporterChannelAssignment> queue;
public class Collector implements ChannelChangeCallback {
@Override
public void onChange(ImporterChannelAssignment assignment) {
queue.offer(assignment);
}
@Override
public void onClusterStateChange(VersionedOperationMode mode) {
}
}
static Set<URI> generateURIs(int count) {
ImmutableSet.Builder<URI> sbldr = ImmutableSet.builder();
for (int i=0; i < count; ++i) {
sbldr.add(URI.create(String.format("x-import://yo/no%04d", i)));
}
return sbldr.build();
}
static Set<ChannelSpec> asSpecs(Set<URI> uris) {
return FluentIterable.from(uris).transform(ChannelSpec.fromUri(YO)).toSet();
}
Set<URI> getRemoved(int expected) throws Exception {
int received = 0;
ImmutableSet.Builder<URI> sbldr = ImmutableSet.builder();
ImporterChannelAssignment assignment = null;
while (received < expected && (assignment=queue.poll(200,TimeUnit.MILLISECONDS)) != null) {
received += assignment.getRemoved().size();
sbldr.addAll(assignment.getRemoved());
}
assertEquals("failed to poll the expected number of removed", expected, received);
assertTrue(queue.isEmpty());
return sbldr.build();
}
Set<URI> getAdded(int expected) throws Exception {
int received = 0;
ImmutableSet.Builder<URI> sbldr = ImmutableSet.builder();
ImporterChannelAssignment assignment = null;
while (received < expected && (assignment=queue.poll(200, TimeUnit.MILLISECONDS)) != null) {
received += assignment.getAdded().size();
sbldr.addAll(assignment.getAdded());
}
assertEquals("failed to poll the expected number of removed", expected, received);
assertTrue(queue.isEmpty());
return sbldr.build();
}
@Before
public void setup() throws Exception {
setUpZK(3);
queue = new LinkedBlockingDeque<>();
zks = ImmutableMap.<String, ZooKeeper>builder()
.put(ZERO, getClient(0))
.put(UNO, getClient(1))
.put(DUE, getClient(2))
.build();
distributers = ImmutableMap.<String, ChannelDistributer>builder()
.put(ZERO, new ChannelDistributer(zks.get(ZERO), ZERO))
.put(UNO, new ChannelDistributer(zks.get(UNO), UNO))
.put(DUE, new ChannelDistributer(zks.get(DUE), DUE))
.build();
for (ChannelDistributer cd: distributers.values()) {
cd.registerCallback(YO, new Collector());
}
}
@Test
public void testRegistration() throws Exception {
Set<URI> uris = generateURIs(9);
Set<URI> expected = uris;
// add nine
distributers.get(UNO).registerChannels(YO, uris);
Set<URI> actual = getAdded(9);
assertEquals(expected, actual);
Set<URI> pruned = generateURIs(6);
expected = Sets.difference(uris, pruned);
// remove 3
distributers.get(DUE).registerChannels(YO, pruned);
actual = getRemoved(3);
assertEquals(expected, actual);
// register the same
distributers.get(ZERO).registerChannels(YO, pruned);
assertNull(queue.poll(200, TimeUnit.MILLISECONDS));
uris = generateURIs(8);
expected = Sets.difference(uris, pruned);
// add two
distributers.get(UNO).registerChannels(YO, uris);
actual = getAdded(2);
assertEquals(expected, actual);
expected = uris;
// remove all
distributers.get(UNO).registerChannels(YO, ImmutableSet.<URI>of());
actual = getRemoved(8);
assertEquals(expected, actual);
int leaderCount = 0;
for (ChannelDistributer distributer: distributers.values()) {
if (distributer.m_isLeader) {
++leaderCount;
}
}
assertEquals(1, leaderCount);
}
@Test
public void testHostFailure() throws Exception {
Set<URI> uris = generateURIs(9);
Set<URI> expected = uris;
// add nine
distributers.get(UNO).registerChannels(YO, uris);
Set<URI> actual = getAdded(9);
assertEquals(expected, actual);
// let's wait for the mesh to settle
int attempts = 4;
boolean settled = false;
while (!settled && --attempts >=0) {
Thread.sleep(50);
settled = true;
int stamp = distributers.get(ZERO).m_specs.getStamp();
for (ChannelDistributer distributer: distributers.values()) {
settled = settled && stamp == distributer.m_specs.getStamp();
}
}
assertTrue(settled);
Set<ChannelSpec> inZERO = Maps.filterValues(
distributers.get(DUE).m_specs.getReference(),
equalTo(ZERO))
.navigableKeySet();
assertTrue(inZERO.size() > 0);
zks.get(ZERO).close();
actual = getAdded(inZERO.size());
assertEquals(inZERO, asSpecs(actual));
}
@After
public void tearDown() throws Exception {
for (ChannelDistributer distributer: distributers.values()) {
distributer.shutdown();
}
tearDownZK();
}
}