blob: 7b7165457e3a5bac751ce0ddc83f79cbc44d5228 [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.geode.internal.cache.tier.sockets;
import static org.apache.geode.cache.Region.Entry;
import static org.apache.geode.cache.Region.SEPARATOR;
import static org.apache.geode.test.dunit.IgnoredException.addIgnoredException;
import static org.apache.geode.test.dunit.NetworkUtils.getServerHostName;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.cache.EntryDestroyedException;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ServerConnectivityException;
import org.apache.geode.test.awaitility.GeodeAwaitility;
import org.apache.geode.test.dunit.NetworkUtils;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.WaitCriterion;
import org.apache.geode.test.junit.categories.ClientSubscriptionTest;
@SuppressWarnings({"rawtypes", "serial"})
@Category({ClientSubscriptionTest.class})
public class HAInterestPart2DUnitTest extends HAInterestTestCase {
public HAInterestPart2DUnitTest() {
super();
}
/**
* Tests if Primary fails during interest un registration should initiate failover should pick new
* primary
*/
@Test
public void testPrimaryFailureInUNregisterInterest() throws Exception {
createClientPoolCache(this.getName(), NetworkUtils.getServerHostName(server1.getHost()));
createEntriesK1andK2();
server1.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
server2.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
server3.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
registerK1AndK2();
VM oldPrimary = getPrimaryVM();
stopPrimaryAndUnregisterRegisterK1();
verifyDeadAndLiveServers(1, 2);
VM newPrimary = getPrimaryVM(oldPrimary);
newPrimary.invoke(() -> HAInterestTestCase.verifyDispatcherIsAlive());
// primary
newPrimary.invoke(() -> HAInterestTestCase.verifyInterestUNRegistration());
// secondary
getBackupVM().invoke(() -> HAInterestTestCase.verifyInterestUNRegistration());
}
/**
* Tests if Secondary fails during interest un registration should add to dead Ep list
*/
@Test
public void testSecondaryFailureInUNRegisterInterest() throws Exception {
createClientPoolCache(this.getName(), NetworkUtils.getServerHostName(server1.getHost()));
createEntriesK1andK2();
server1.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
server2.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
server3.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
registerK1AndK2();
VM stoppedBackup = stopSecondaryAndUNregisterK1();
verifyDeadAndLiveServers(1, 2);
// still primary
getPrimaryVM().invoke(() -> HAInterestTestCase.verifyDispatcherIsAlive());
// primary
getPrimaryVM().invoke(() -> HAInterestTestCase.verifyInterestUNRegistration());
// secondary
getBackupVM(stoppedBackup).invoke(() -> HAInterestTestCase.verifyInterestUNRegistration());
}
/**
* Tests a scenario in which Dead Server Monitor detects Server Live Just before interest
* registration then interest should be registered on the newly detected live server as well
*/
@Test
public void testDSMDetectsServerLiveJustBeforeInterestRegistration() throws Exception {
createClientPoolCache(this.getName(), NetworkUtils.getServerHostName(server1.getHost()));
createEntriesK1andK2();
server1.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
server2.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
server3.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
VM backup = getBackupVM();
backup.invoke(() -> HAInterestTestCase.stopServer());
verifyDeadAndLiveServers(1, 2);
setClientServerObserverForBeforeRegistration(backup);
try {
registerK1AndK2();
waitForBeforeRegistrationCallback();
} finally {
unSetClientServerObserverForRegistrationCallback();
}
server1.invoke(() -> HAInterestTestCase.verifyInterestRegistration());
server2.invoke(() -> HAInterestTestCase.verifyInterestRegistration());
server3.invoke(() -> HAInterestTestCase.verifyInterestRegistration());
}
/**
* Tests a scenario in which Dead Server Monitor detects Server Live Just After interest
* registration then interest should be registered on the newly detected live server as well
*/
@Test
public void testDSMDetectsServerLiveJustAfterInterestRegistration() throws Exception {
createClientPoolCache(this.getName(), NetworkUtils.getServerHostName(server1.getHost()));
createEntriesK1andK2();
server1.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
server2.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
server3.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
VM backup = getBackupVM();
backup.invoke(() -> HAInterestTestCase.stopServer());
verifyDeadAndLiveServers(1, 2);
setClientServerObserverForAfterRegistration(backup);
try {
registerK1AndK2();
waitForAfterRegistrationCallback();
} finally {
unSetClientServerObserverForRegistrationCallback();
}
server1.invoke(() -> HAInterestTestCase.verifyInterestRegistration());
server2.invoke(() -> HAInterestTestCase.verifyInterestRegistration());
server3.invoke(() -> HAInterestTestCase.verifyInterestRegistration());
}
/**
* Tests a Scenario: Only one server, register interest on the server stop server , and update the
* registered entries on the server start the server , DSM will recover interest list on this live
* server and verify that as a part of recovery it refreshes registered entries from the server,
* because it is primary
*/
@Test
public void testRefreshEntriesFromPrimaryWhenDSMDetectsServerLive() throws Exception {
addIgnoredException(ServerConnectivityException.class.getName());
PORT1 = ((Integer) server1.invoke(() -> createServerCache())).intValue();
server1.invoke(() -> createEntriesK1andK2());
createClientPoolCacheConnectionToSingleServer(this.getName(),
getServerHostName(server1.getHost()));
registerK1AndK2();
verifyRefreshedEntriesFromServer();
server1.invoke(() -> stopServer());
verifyDeadAndLiveServers(1, 0);
server1.invoke(() -> putK1andK2());
server1.invoke(() -> startServer());
verifyDeadAndLiveServers(0, 1);
final Region r1 = cache.getRegion(SEPARATOR + REGION_NAME);
assertNotNull(r1);
// Verify for interest registration after cache-server is started.
server1.invoke(() -> verifyInterestRegistration());
WaitCriterion wc = new WaitCriterion() {
private String excuse;
@Override
public boolean done() {
Entry e1;
Entry e2;
try {
e1 = r1.getEntry(k1);
if (e1 == null) {
excuse = "Entry for k1 is null";
return false;
}
} catch (EntryDestroyedException e) {
excuse = "Entry destroyed";
return false;
}
if (!server_k1.equals(e1.getValue())) {
excuse = "e1 value is not server_k1";
return false;
}
try {
e2 = r1.getEntry(k2);
if (e2 == null) {
excuse = "Entry for k2 is null";
return false;
}
} catch (EntryDestroyedException e) {
excuse = "Entry destroyed";
return false;
}
if (!server_k2.equals(e2.getValue())) {
excuse = "e2 value is not server_k2";
return false;
}
return true;
}
@Override
public String description() {
return excuse;
}
};
GeodeAwaitility.await().untilAsserted(wc);
}
/**
* Tests a Scenario: stop a secondary server and update the registered entries on the stopped
* server start the server , DSM will recover interest list on this live server and verify that as
* a part of recovery it does not refreshes registered entries from the server, because it is
* secondary
*/
@Test
public void testGIIFromSecondaryWhenDSMDetectsServerLive() throws Exception {
server1.invoke(() -> HAInterestTestCase.closeCache());
server2.invoke(() -> HAInterestTestCase.closeCache());
server3.invoke(() -> HAInterestTestCase.closeCache());
PORT1 = ((Integer) server1.invoke(() -> HAInterestTestCase.createServerCacheWithLocalRegion()))
.intValue();
PORT2 = ((Integer) server2.invoke(() -> HAInterestTestCase.createServerCacheWithLocalRegion()))
.intValue();
PORT3 = ((Integer) server3.invoke(() -> HAInterestTestCase.createServerCacheWithLocalRegion()))
.intValue();
server1.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
server2.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
server3.invoke(() -> HAInterestTestCase.createEntriesK1andK2());
createClientPoolCache(this.getName(), NetworkUtils.getServerHostName(server1.getHost()));
VM backup1 = getBackupVM();
VM backup2 = getBackupVM(backup1);
backup1.invoke(() -> HAInterestTestCase.stopServer());
backup2.invoke(() -> HAInterestTestCase.stopServer());
verifyDeadAndLiveServers(2, 1);
registerK1AndK2();
verifyRefreshedEntriesFromServer();
backup1.invoke(() -> HAInterestTestCase.putK1andK2());
backup1.invoke(() -> HAInterestTestCase.startServer());
verifyDeadAndLiveServers(1, 2);
verifyRefreshedEntriesFromServer();
}
/**
* Bug Test for Bug # 35945 A java level Deadlock between acquireConnection and RegionEntry during
* processRecoveredEndpoint by Dead Server Monitor Thread.
*
*/
@Test
public void testBug35945() throws Exception {
PORT1 = ((Integer) server1.invoke(() -> createServerCache())).intValue();
server1.invoke(() -> createEntriesK1andK2());
createClientPoolCacheConnectionToSingleServer(this.getName(),
getServerHostName(server1.getHost()));
registerK1AndK2();
verifyRefreshedEntriesFromServer();
server1.invoke(() -> stopServer());
verifyDeadAndLiveServers(1, 0);
// put on stopped server
server1.invoke(() -> putK1andK2());
// spawn a thread to put on server , which will acquire a lock on entry
setClientServerObserverForBeforeInterestRecovery();
server1.invoke(() -> startServer());
verifyDeadAndLiveServers(0, 1);
waitForBeforeInterestRecoveryCallBack();
// verify updated value of k1 as a refreshEntriesFromServer
final Region r1 = cache.getRegion(SEPARATOR + REGION_NAME);
assertNotNull(r1);
WaitCriterion wc = new WaitCriterion() {
private String excuse;
@Override
public boolean done() {
Entry e1 = r1.getEntry(k1);
Entry e2 = r1.getEntry(k2);
Object v1 = null;
if (e1 != null) {
try {
v1 = e1.getValue();
} catch (EntryDestroyedException ignore) {
// handled to fix GEODE-296
}
}
if (e1 == null || !server_k1_updated.equals(v1)) {
excuse = "v1=" + v1;
return false;
}
Object v2 = null;
if (e2 != null) {
try {
v2 = e2.getValue();
} catch (EntryDestroyedException ignore) {
// handled to fix GEODE-296
}
}
if (e2 == null || !server_k2.equals(v2)) {
excuse = "v2=" + v2;
return false;
}
return true;
}
@Override
public String description() {
return excuse;
}
};
GeodeAwaitility.await().untilAsserted(wc);
}
/**
* Tests if failure occurred in Interest recovery thread, then it should select new endpoint to
* register interest
*/
@Test
public void testInterestRecoveryFailure() throws Exception {
addIgnoredException("Server unreachable");
PORT1 = ((Integer) server1.invoke(() -> createServerCache())).intValue();
server1.invoke(() -> createEntriesK1andK2());
PORT2 = ((Integer) server2.invoke(() -> createServerCache())).intValue();
server2.invoke(() -> createEntriesK1andK2());
createClientPoolCacheWithSmallRetryInterval(this.getName(),
getServerHostName(server1.getHost()));
registerK1AndK2();
verifyRefreshedEntriesFromServer();
VM backup = getBackupVM();
VM primary = getPrimaryVM();
backup.invoke(() -> stopServer());
primary.invoke(() -> stopServer());
verifyDeadAndLiveServers(2, 0);
primary.invoke(() -> putK1andK2());
setClientServerObserverForBeforeInterestRecoveryFailure();
primary.invoke(() -> startServer());
waitForBeforeInterestRecoveryCallBack();
if (exceptionOccurred) {
fail("The DSM could not ensure that server 1 is started & serevr 2 is stopped");
}
final Region r1 = cache.getRegion(SEPARATOR + REGION_NAME);
assertNotNull(r1);
WaitCriterion wc = new WaitCriterion() {
private String excuse;
@Override
public boolean done() {
Entry e1 = r1.getEntry(k1);
Entry e2 = r1.getEntry(k2);
if (e1 == null) {
excuse = "Entry for k1 still null";
return false;
}
if (e2 == null) {
excuse = "Entry for k2 still null";
return false;
}
if (!(server_k1.equals(e1.getValue()))) {
excuse = "Value for k1 wrong";
return false;
}
if (!(server_k2.equals(e2.getValue()))) {
excuse = "Value for k2 wrong";
return false;
}
return true;
}
@Override
public String description() {
return excuse;
}
};
GeodeAwaitility.await().untilAsserted(wc);
}
}