blob: 7a8193f3b1270bbd68dc6818460bbe15c3f50340 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.ignite.internal.processors.cache.binary;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteException;
import org.apache.ignite.binary.BinaryObjectBuilder;
import org.apache.ignite.binary.BinaryObjectException;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.internal.IgniteInterruptedCheckedException;
import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage;
import org.apache.ignite.internal.util.IgniteUtils;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.spi.discovery.DiscoverySpiCustomMessage;
import org.apache.ignite.spi.discovery.tcp.TestTcpDiscoverySpi;
import org.apache.ignite.testframework.GridTestUtils;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
/**
*
*/
public class BinaryMetadataRemoveTest extends GridCommonAbstractTest {
/** Max retry cont. */
private static final int MAX_RETRY_CONT = 10;
/** */
private static final String CACHE_NAME = "cache";
/** */
private GridTestUtils.DiscoveryHook discoveryHook;
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
final GridTestUtils.DiscoveryHook discoveryHook0 = discoveryHook;
if (discoveryHook0 != null) {
assert cfg.getDiscoverySpi() instanceof TestTcpDiscoverySpi;
((TestTcpDiscoverySpi)cfg.getDiscoverySpi()).discoveryHook(discoveryHook0);
}
cfg.setCacheConfiguration(new CacheConfiguration().setName(CACHE_NAME));
return cfg;
}
/**
*
*/
protected void startCluster() throws Exception {
startGrid("srv0");
startGrid("srv1");
startGrid("srv2");
startClientGrid("cli0");
startClientGrid("cli1");
startClientGrid("cli2");
}
/** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
super.beforeTest();
startCluster();
discoveryHook = null;
}
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
stopAllGrids();
super.afterTest();
}
/**
* Tests remove not existent type and checks the exception.
*/
@Test
public void testRemoveNotExistentType() {
for (Ignite testNode : G.allGrids()) {
GridTestUtils.assertThrows(log, () -> {
((IgniteEx)testNode).context().cacheObjects().removeType(
((IgniteEx)testNode).context().cacheObjects().typeId("NotExistentType"));
return null;
},
IgniteException.class, "Failed to remove metadata, type not found");
}
}
/**
* Tests remove type metadata at all nodes (coordinator, server, client).
*/
@Test
public void testRemoveTypeOnNodes() throws Exception {
List<IgniteEx[]> testNodeSets = new ArrayList<>();
// Add all servers permutations to tests sets.
for (Ignite ign0 : G.allGrids()) {
for (Ignite ign1 : G.allGrids()) {
for (Ignite ign2 : G.allGrids()) {
IgniteEx ignx0 = (IgniteEx)ign0;
IgniteEx ignx1 = (IgniteEx)ign1;
IgniteEx ignx2 = (IgniteEx)ign2;
if (!ignx0.context().clientNode()
&& !ignx1.context().clientNode()
&& !ignx2.context().clientNode())
testNodeSets.add(new IgniteEx[] {ignx0, ignx1, ignx2});
}
}
}
testNodeSets.add(new IgniteEx[] {grid("srv0"), grid("cli0"), grid("cli0")});
testNodeSets.add(new IgniteEx[] {grid("cli0"), grid("cli0"), grid("cli0")});
testNodeSets.add(new IgniteEx[] {grid("cli0"), grid("cli1"), grid("cli2")});
for (IgniteEx[] testNodeSet : testNodeSets) {
IgniteEx ignCreateType = testNodeSet[0];
IgniteEx ignRemoveType = testNodeSet[1];
IgniteEx ignRecreateType = testNodeSet[2];
log.info("+++ Check [createOn=" + ignCreateType.name() +
", removeOn=" + ignRemoveType.name() + ", recreateOn=" + ignRecreateType.name());
BinaryObjectBuilder builder0 = ignCreateType.binary().builder("Type0");
builder0.setField("f", 1);
builder0.build();
delayIfClient(ignCreateType, ignRemoveType, ignRecreateType);
removeType(ignRemoveType, "Type0");
delayIfClient(ignCreateType, ignRemoveType, ignRecreateType);
BinaryObjectBuilder builder1 = ignRecreateType.binary().builder("Type0");
builder1.setField("f", "string");
builder1.build();
delayIfClient(ignCreateType, ignRemoveType, ignRecreateType);
// Remove type at the end of test case.
removeType(grid("srv0"), "Type0");
delayIfClient(ignCreateType, ignRemoveType, ignRecreateType);
}
}
/**
* Tests reject metadata update on coordinator when remove type is processed.
*/
@Test
public void testChangeMetaWhenTypeRemoving() throws Exception {
final CyclicBarrier barrier0 = new CyclicBarrier(2);
final CyclicBarrier barrier1 = new CyclicBarrier(2);
AtomicBoolean hookMsgs = new AtomicBoolean(true);
discoveryHook = new GridTestUtils.DiscoveryHook() {
@Override public void beforeDiscovery(DiscoverySpiCustomMessage msg) {
if (!hookMsgs.get())
return;
DiscoveryCustomMessage customMsg = msg == null ? null
: (DiscoveryCustomMessage)IgniteUtils.field(msg, "delegate");
if (customMsg instanceof MetadataRemoveProposedMessage) {
try {
barrier0.await();
barrier1.await();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
};
// Install discovery hoot at the node 'srv1'
stopGrid("srv1");
IgniteEx ign = startGrid("srv1");
discoveryHook = null;
// Move srv2 node an the end of the discovery circle.
stopGrid("srv2");
startGrid("srv2");
BinaryObjectBuilder builder0 = ign.binary().builder("Type0");
builder0.setField("f", 1);
builder0.build();
GridTestUtils.runAsync(() -> {
try {
removeType(ign, "Type0");
}
catch (Exception e) {
log.error("Unexpected exception", e);
fail("Unexpected exception.");
}
});
barrier0.await();
GridTestUtils.assertThrowsAnyCause(log, () -> {
BinaryObjectBuilder bld = grid("srv2").binary().builder("Type0");
bld.setField("f1", 1);
// Short delay guarantee that we go into update metadata before remove metadata continue processing.
GridTestUtils.runAsync(() -> {
try {
U.sleep(200);
hookMsgs.set(false);
barrier1.await();
}
catch (Exception e) {
// No-op.
}
});
bld.build();
return null;
}, BinaryObjectException.class, "The type is removing now");
}
/**
* @param ign Node to remove type.
* @param typeName Binary type name.
*/
protected void removeType(IgniteEx ign, String typeName) throws Exception {
Exception err = null;
for (int i = 0; i < MAX_RETRY_CONT; ++i) {
try {
ign.context().cacheObjects().removeType(ign.context().cacheObjects().typeId(typeName));
err = null;
break;
}
catch (Exception e) {
err = e;
U.sleep(200);
}
}
if (err != null)
throw err;
}
/**
* Delay operation if an operation is executed on a client node.
*
* @param igns Tests nodes.
*/
protected void delayIfClient(Ignite... igns) throws IgniteInterruptedCheckedException {
boolean isThereCli = Arrays.stream(igns).anyMatch(ign -> ((IgniteEx)ign).context().clientNode());
if (isThereCli)
U.sleep(500);
}
}