/*
* 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.ha;
import static org.apache.geode.distributed.ConfigurationProperties.DURABLE_CLIENT_ID;
import static org.apache.geode.distributed.ConfigurationProperties.DURABLE_CLIENT_TIMEOUT;
import static org.apache.geode.distributed.ConfigurationProperties.LOCATORS;
import static org.apache.geode.distributed.ConfigurationProperties.LOG_FILE;
import static org.apache.geode.distributed.ConfigurationProperties.LOG_LEVEL;
import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
import static org.apache.geode.distributed.ConfigurationProperties.STATISTIC_ARCHIVE_FILE;
import static org.apache.geode.distributed.ConfigurationProperties.STATISTIC_SAMPLING_ENABLED;
import static org.junit.Assert.assertEquals;
import java.util.Collection;
import java.util.Iterator;
import java.util.Properties;
import org.apache.geode.test.junit.categories.ClientSubscriptionTest;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.EntryEvent;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.RegionFactory;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.cache.client.ClientCacheFactory;
import org.apache.geode.cache.client.ClientRegionFactory;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.cache.util.CacheListenerAdapter;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.internal.OSProcess;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.internal.cache.tier.sockets.CacheClientNotifier;
import org.apache.geode.internal.cache.tier.sockets.CacheClientProxy;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.test.dunit.DistributedTestUtils;
import org.apache.geode.test.dunit.Host;
import org.apache.geode.test.dunit.IgnoredException;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.Wait;
import org.apache.geode.test.dunit.WaitCriterion;
import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase;
import org.apache.geode.test.junit.categories.DistributedTest;
import org.apache.geode.test.junit.categories.FlakyTest;
@Category({DistributedTest.class, ClientSubscriptionTest.class})
public class Bug48571DUnitTest extends JUnit4DistributedTestCase {
private static VM server = null;
private VM client = null;
private static GemFireCacheImpl cache = null;
private static final String region = Bug48571DUnitTest.class.getSimpleName() + "_region";
private static int numOfCreates = 0;
private static int numOfUpdates = 0;
private static int numOfInvalidates = 0;
private static boolean lastKeyReceived = false;
@Override
public final void postSetUp() throws Exception {
final Host host = Host.getHost(0);
server = host.getVM(0);
client = host.getVM(1);
}
@Override
public final void preTearDown() throws Exception {
reset();
server.invoke(() -> Bug48571DUnitTest.reset());
client.invoke(() -> Bug48571DUnitTest.reset());
}
public static void reset() throws Exception {
lastKeyReceived = false;
numOfCreates = 0;
numOfUpdates = 0;
numOfInvalidates = 0;
if (cache != null && !cache.isClosed()) {
cache.close();
cache.getDistributedSystem().disconnect();
}
}
private static void verifyProxyHasBeenPaused() {
WaitCriterion criterion = new WaitCriterion() {
@Override
public boolean done() {
CacheClientNotifier ccn = CacheClientNotifier.getInstance();
Collection<CacheClientProxy> ccProxies = ccn.getClientProxies();
Iterator<CacheClientProxy> itr = ccProxies.iterator();
while (itr.hasNext()) {
CacheClientProxy ccp = itr.next();
System.out.println("proxy status " + ccp.getState());
if (ccp.isPaused())
return true;
}
return false;
}
@Override
public String description() {
return "Proxy has not paused yet";
}
};
Wait.waitForCriterion(criterion, 15 * 1000, 200, true);
}
@Category(FlakyTest.class) // GEODE-510
@Test
public void testStatsMatchWithSize() throws Exception {
IgnoredException.addIgnoredException("Unexpected IOException||Connection reset");
// start a server
int port = (Integer) server.invoke(() -> Bug48571DUnitTest.createServerCache());
// create durable client, with durable RI
client.invoke(() -> Bug48571DUnitTest.createClientCache(client.getHost(), port));
// do puts on server from three different threads, pause after 500 puts each.
server.invoke(() -> Bug48571DUnitTest.doPuts());
// close durable client
client.invoke(() -> Bug48571DUnitTest.closeClientCache());
server.invoke("verifyProxyHasBeenPaused", () -> verifyProxyHasBeenPaused());
// resume puts on server, add another 100.
server.invokeAsync(() -> Bug48571DUnitTest.resumePuts()); // TODO: join or await result
// start durable client
client.invoke(() -> Bug48571DUnitTest.createClientCache(client.getHost(), port));
// wait for full queue dispatch
client.invoke(() -> Bug48571DUnitTest.waitForLastKey());
// verify the stats
server.invoke(() -> Bug48571DUnitTest.verifyStats());
}
public static int createServerCache() throws Exception {
Properties props = new Properties();
props.setProperty(LOCATORS, "localhost[" + DistributedTestUtils.getDUnitLocatorPort() + "]");
props.setProperty(LOG_FILE, "server_" + OSProcess.getId() + ".log");
props.setProperty(LOG_LEVEL, "info");
props.setProperty(STATISTIC_ARCHIVE_FILE, "server_" + OSProcess.getId() + ".gfs");
props.setProperty(STATISTIC_SAMPLING_ENABLED, "true");
CacheFactory cf = new CacheFactory(props);
DistributedSystem ds = new Bug48571DUnitTest().getSystem(props);
ds.disconnect();
cache = (GemFireCacheImpl) cf.create();
RegionFactory<String, String> rf = cache.createRegionFactory(RegionShortcut.REPLICATE);
rf.setConcurrencyChecksEnabled(false);
rf.create(region);
CacheServer server1 = cache.addCacheServer();
server1.setPort(0);
server1.start();
return server1.getPort();
}
public static void closeClientCache() throws Exception {
cache.close(true);
}
public static void createClientCache(Host host, Integer port) throws Exception {
Properties props = new Properties();
props.setProperty(MCAST_PORT, "0");
props.setProperty(LOCATORS, "");
props.setProperty(DURABLE_CLIENT_ID, "durable-48571");
props.setProperty(DURABLE_CLIENT_TIMEOUT, "300000");
props.setProperty(LOG_FILE, "client_" + OSProcess.getId() + ".log");
props.setProperty(LOG_LEVEL, "info");
props.setProperty(STATISTIC_ARCHIVE_FILE, "client_" + OSProcess.getId() + ".gfs");
props.setProperty(STATISTIC_SAMPLING_ENABLED, "true");
ClientCacheFactory ccf = new ClientCacheFactory(props);
ccf.setPoolSubscriptionEnabled(true);
ccf.setPoolSubscriptionAckInterval(50);
ccf.setPoolSubscriptionRedundancy(0);
ccf.addPoolServer(host.getHostName(), port);
DistributedSystem ds = new Bug48571DUnitTest().getSystem(props);
ds.disconnect();
cache = (GemFireCacheImpl) ccf.create();
ClientRegionFactory<String, String> crf =
cache.createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY);
crf.setConcurrencyChecksEnabled(false);
crf.addCacheListener(new CacheListenerAdapter<String, String>() {
public void afterInvalidate(EntryEvent<String, String> event) {
cache.getLoggerI18n()
.fine("Invalidate Event: " + event.getKey() + ", " + event.getNewValue());
numOfInvalidates++;
}
public void afterCreate(EntryEvent<String, String> event) {
if (((String) event.getKey()).equals("last_key")) {
lastKeyReceived = true;
}
cache.getLoggerI18n().fine("Create Event: " + event.getKey() + ", " + event.getNewValue());
numOfCreates++;
}
public void afterUpdate(EntryEvent<String, String> event) {
cache.getLoggerI18n().fine("Update Event: " + event.getKey() + ", " + event.getNewValue());
numOfUpdates++;
}
});
Region<String, String> r = crf.create(region);
r.registerInterest("ALL_KEYS", true);
cache.readyForEvents();
}
public static void doPuts() throws Exception {
final Region<String, String> r = cache.getRegion(region);
Thread t1 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 500; i++) {
r.put("T1_KEY_" + i, "VALUE_" + i);
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 500; i++) {
r.put("T2_KEY_" + i, "VALUE_" + i);
}
}
});
Thread t3 = new Thread(new Runnable() {
public void run() {
for (int i = 0; i < 500; i++) {
r.put("T3_KEY_" + i, "VALUE_" + i);
}
}
});
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
}
public static void resumePuts() {
Region<String, String> r = cache.getRegion(region);
for (int i = 0; i < 100; i++) {
r.put("NEWKEY_" + i, "NEWVALUE_" + i);
}
r.put("last_key", "last_value");
}
public static void waitForLastKey() {
WaitCriterion wc = new WaitCriterion() {
@Override
public boolean done() {
return lastKeyReceived;
}
@Override
public String description() {
return "Did not receive last key.";
}
};
Wait.waitForCriterion(wc, 60 * 1000, 500, true);
}
public static void verifyStats() throws Exception {
CacheClientNotifier ccn = CacheClientNotifier.getInstance();
CacheClientProxy ccp = ccn.getClientProxies().iterator().next();
cache.getLoggerI18n().info(LocalizedStrings.DEBUG, "getQueueSize() " + ccp.getQueueSize());
cache.getLoggerI18n().info(LocalizedStrings.DEBUG,
"getQueueSizeStat() " + ccp.getQueueSizeStat());
cache.getLoggerI18n().info(LocalizedStrings.DEBUG,
"getEventsEnqued() " + ccp.getHARegionQueue().getStatistics().getEventsEnqued());
cache.getLoggerI18n().info(LocalizedStrings.DEBUG,
"getEventsDispatched() " + ccp.getHARegionQueue().getStatistics().getEventsDispatched());
cache.getLoggerI18n().info(LocalizedStrings.DEBUG,
"getEventsRemoved() " + ccp.getHARegionQueue().getStatistics().getEventsRemoved());
cache.getLoggerI18n().info(LocalizedStrings.DEBUG,
"getNumVoidRemovals() " + ccp.getHARegionQueue().getStatistics().getNumVoidRemovals());
assertEquals(ccp.getQueueSize(), ccp.getQueueSizeStat());
}
}