This is an automated email from the ASF dual-hosted git repository. bschuchardt pushed a commit to branch feature/GEODE-7852healthMonitor in repository https://gitbox.apache.org/repos/asf/geode.git
commit 376df4cc7abc6db38b4b298f8af6f3c53baedf15 Author: Bruce Schuchardt <[email protected]> AuthorDate: Tue Apr 14 10:55:56 2020 -0700 GEODE-7852: test ClientHealthMonitor functionality behind a SNI gateway This ensures that a server sitting behind an SNI gateway detects the loss of a client and cleans up after it. In this case the test detects that the server has closed CQs created by the non-durable client. Since test code is not accessible in the Docker container that's running the server I've enhanced the StatArchiveReader to be able to report the values of a statistic and have enabled statistics recording in the server. --- .../client/sni/ClientSNICQAcceptanceTest.java | 34 +++++- .../client/sni/geode-config/gemfire.properties | 3 +- .../internal/statistics/StatArchiveReader.java | 114 +++++++++++++++++++-- 3 files changed, 140 insertions(+), 11 deletions(-) diff --git a/geode-assembly/src/acceptanceTest/java/org/apache/geode/client/sni/ClientSNICQAcceptanceTest.java b/geode-assembly/src/acceptanceTest/java/org/apache/geode/client/sni/ClientSNICQAcceptanceTest.java index d422807..33a4320 100644 --- a/geode-assembly/src/acceptanceTest/java/org/apache/geode/client/sni/ClientSNICQAcceptanceTest.java +++ b/geode-assembly/src/acceptanceTest/java/org/apache/geode/client/sni/ClientSNICQAcceptanceTest.java @@ -22,6 +22,7 @@ import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_ import static org.apache.geode.distributed.ConfigurationProperties.SSL_REQUIRE_AUTHENTICATION; import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE; import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE_PASSWORD; +import static org.apache.geode.test.awaitility.GeodeAwaitility.await; import static org.apache.geode.test.util.ResourceUtils.createTempFileFromResource; import static org.assertj.core.api.Assertions.assertThat; @@ -31,6 +32,7 @@ import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import com.palantir.docker.compose.DockerComposeRule; +import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; @@ -42,6 +44,7 @@ import org.apache.geode.cache.Region; import org.apache.geode.cache.client.ClientCache; import org.apache.geode.cache.client.ClientCacheFactory; import org.apache.geode.cache.client.ClientRegionShortcut; +import org.apache.geode.cache.client.internal.PoolImpl; import org.apache.geode.cache.client.proxy.ProxySocketFactories; import org.apache.geode.cache.query.CqAttributes; import org.apache.geode.cache.query.CqAttributesFactory; @@ -72,6 +75,7 @@ public class ClientSNICQAcceptanceTest { AtomicInteger eventCreateCounter = new AtomicInteger(0); AtomicInteger eventUpdateCounter = new AtomicInteger(0); + private ClientCache cache; class SNICQListener implements CqListener { @@ -107,9 +111,18 @@ public class ClientSNICQAcceptanceTest { } + @After + public void after() throws Exception { + String output = + docker.exec(options("-T"), "geode", arguments("cat", "server-dolores/server-dolores.log")); + System.out.println("Server log file--------------------------------\n" + output); + if (cache != null) { + cache.close(); + } + } + @Test - public void performSimpleCQOverSNIProxy() - throws CqException, CqExistsException, RegionNotFoundException { + public void performSimpleCQOverSNIProxy() throws Exception { Properties gemFireProps = new Properties(); gemFireProps.setProperty(SSL_ENABLED_COMPONENTS, "all"); gemFireProps.setProperty(SSL_KEYSTORE_TYPE, "jks"); @@ -123,7 +136,7 @@ public class ClientSNICQAcceptanceTest { .container("haproxy") .port(15443) .getExternalPort(); - ClientCache cache = new ClientCacheFactory(gemFireProps) + cache = new ClientCacheFactory(gemFireProps) .addPoolLocator("locator-maeve", 10334) .setPoolSocketFactory(ProxySocketFactories.sni("localhost", proxyPort)) @@ -152,6 +165,21 @@ public class ClientSNICQAcceptanceTest { assertThat(eventUpdateCounter.get()).isEqualTo(62); + // verify that the server cleans up when the client connection to the gateway is destroyed + ((PoolImpl) cache.getDefaultPool()).killPrimaryEndpoint(); + // since we can't run code in the server let's grab the CQ statistics and verify that + // the CQ has been closed. StatArchiveReader has a main() that we can use to get a printout + // of stat values + await().untilAsserted(() -> { + String stats = docker.exec(options("-T"), "geode", + arguments("java", "-cp", "/geode/lib/geode-dependencies.jar", + "org.apache.geode.internal.statistics.StatArchiveReader", + "stat", "server-dolores/statArchive.gfs", "CqServiceStats.numCqsClosed")); + System.out.println("stats from server are :" + stats); + // the stat should transition from zero to one at some point + assertThat(stats).contains("0.0 1.0"); + }); + } public void updateRegion(Region<String, Integer> region) { diff --git a/geode-assembly/src/acceptanceTest/resources/org/apache/geode/client/sni/geode-config/gemfire.properties b/geode-assembly/src/acceptanceTest/resources/org/apache/geode/client/sni/geode-config/gemfire.properties index 213cabe..1f13fb0 100644 --- a/geode-assembly/src/acceptanceTest/resources/org/apache/geode/client/sni/geode-config/gemfire.properties +++ b/geode-assembly/src/acceptanceTest/resources/org/apache/geode/client/sni/geode-config/gemfire.properties @@ -15,4 +15,5 @@ # limitations under the License. # -#empty +statistic-sampling-enabled=true +statistic-archive-file=statArchive.gfs diff --git a/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveReader.java b/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveReader.java index 25670c3..a62e311 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveReader.java +++ b/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveReader.java @@ -210,21 +210,121 @@ public class StatArchiveReader implements StatArchiveFormat, AutoCloseable { } } + private static class SingleStatRawStatSpec implements StatSpec { + + private final String archive; + private final String statType; + private final String statName; + + SingleStatRawStatSpec(String archive, String typeAndStat) { + this.archive = archive; + String[] parts = typeAndStat.split("\\.", 0); + this.statType = parts[0]; + this.statName = parts[1]; + } + + @Override + public boolean archiveMatches(File archive) { + return true; // this.archive.equalsIgnoreCase(archive.getName()); + } + + @Override + public boolean typeMatches(String typeName) { + return this.statType.equalsIgnoreCase(typeName); + } + + @Override + public boolean statMatches(String statName) { + return this.statName.equalsIgnoreCase(statName); + } + + @Override + public boolean instanceMatches(String textId, long numericId) { + return true; + } + + @Override + public int getCombineType() { + return StatSpec.NONE; + } + } + + private static void printStatValue(StatArchiveReader.StatValue v, long startTime, long endTime, + boolean nofilter, boolean persec, boolean persample, boolean prunezeros, boolean details) { + v = v.createTrimmed(startTime, endTime); + if (nofilter) { + v.setFilter(StatArchiveReader.StatValue.FILTER_NONE); + } else if (persec) { + v.setFilter(StatArchiveReader.StatValue.FILTER_PERSEC); + } else if (persample) { + v.setFilter(StatArchiveReader.StatValue.FILTER_PERSAMPLE); + } + if (prunezeros) { + if (v.getSnapshotsMinimum() == 0.0 && v.getSnapshotsMaximum() == 0.0) { + return; + } + } + System.out.println(" " + v.toString()); + if (details) { + System.out.print(" values="); + double[] snapshots = v.getSnapshots(); + for (int i = 0; i < snapshots.length; i++) { + System.out.print(' '); + System.out.print(snapshots[i]); + } + System.out.println(); + String desc = v.getDescriptor().getDescription(); + if (desc != null && desc.length() > 0) { + System.out.println(" " + desc); + } + } + } + + /** * Simple utility to read and dump statistic archive. */ public static void main(String args[]) throws IOException { String archiveName = null; + final StatArchiveReader reader; if (args.length > 1) { - System.err.println("Usage: [archiveName]"); - ExitCode.FATAL.doSystemExit(); - } else if (args.length == 1) { - archiveName = args[0]; + if (!args[0].equals("stat") || args.length > 3) { + System.err.println("Usage: stat archiveName statType.statName"); + ExitCode.FATAL.doSystemExit(); + } + archiveName = args[1]; + String statSpec = args[2]; + if (!statSpec.contains(".")) { + throw new IllegalArgumentException( + "stat spec '" + statSpec + "' is malformed - use StatType.statName"); + } + File archiveFile = new File(archiveName); + if (!archiveFile.exists()) { + throw new IllegalArgumentException("archive file does not exist: " + archiveName); + } + if (!archiveFile.canRead()) { + throw new IllegalArgumentException("archive file exists but is unreadable: " + archiveName); + } + File[] archives = new File[] {archiveFile}; + SingleStatRawStatSpec[] filters = + new SingleStatRawStatSpec[] {new SingleStatRawStatSpec(archiveName, args[2])}; + reader = new StatArchiveReader(archives, filters, false); + final StatValue[] statValues = reader.matchSpec(filters[0]); + System.out.println(statSpec + " matched " + statValues.length + " stats..."); + for (StatValue value : statValues) { + printStatValue(value, -1, -1, true, false, false, false, true); + } + System.out.println(""); + System.out.flush(); } else { - archiveName = "statArchive.gfs"; + if (args.length == 1) { + archiveName = args[0]; + } else { + archiveName = "statArchive.gfs"; + } + reader = new StatArchiveReader(archiveName); + System.out.println("DEBUG: memory used = " + reader.getMemoryUsed()); } - StatArchiveReader reader = new StatArchiveReader(archiveName); - System.out.println("DEBUG: memory used = " + reader.getMemoryUsed()); reader.close(); }
