You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by db...@apache.org on 2019/05/26 00:12:53 UTC
[tomee] 01/05: Playing with tooling to measure startup across all
TomEE versions
This is an automated email from the ASF dual-hosted git repository.
dblevins pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomee.git
commit 5c3464b6d3011041c0721604826c65c85ce5ac36
Author: David Blevins <da...@gmail.com>
AuthorDate: Sat May 25 00:44:41 2019 -0700
Playing with tooling to measure startup across all TomEE versions
---
itests/startup/pom.xml | 113 +++++
.../apache/tomee/itest/startup/StartupTest.java | 512 +++++++++++++++++++++
.../org/apache/tomee/server/composer/TomEE.java | 61 ++-
3 files changed, 682 insertions(+), 4 deletions(-)
diff --git a/itests/startup/pom.xml b/itests/startup/pom.xml
new file mode 100644
index 0000000..e09117d
--- /dev/null
+++ b/itests/startup/pom.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>itests</artifactId>
+ <groupId>org.apache.tomee</groupId>
+ <version>8.0.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.tomee.itests</groupId>
+ <artifactId>startup</artifactId>
+ <packaging>jar</packaging>
+ <name>OpenEJB :: iTests :: Server Composer</name>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <version>${project.version}</version>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.tomee</groupId>
+ <artifactId>tomee-server-composer</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tomee</groupId>
+ <artifactId>apache-tomee</artifactId>
+ <version>8.0.0-SNAPSHOT</version>
+ <type>tar.gz</type>
+ <classifier>microprofile</classifier>
+ <exclusions>
+ <exclusion>
+ <groupId>*</groupId>
+ <artifactId>*</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tomee</groupId>
+ <artifactId>javaee-api</artifactId>
+ <version>8.0</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.microprofile.jwt</groupId>
+ <artifactId>microprofile-jwt-auth-api</artifactId>
+ <version>1.1</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.nimbusds</groupId>
+ <artifactId>nimbus-jose-jwt</artifactId>
+ <version>4.23</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.tomee</groupId>
+ <artifactId>openejb-cxf-rs</artifactId>
+ <version>${tomee.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-math3</artifactId>
+ <version>3.6.1</version>
+ </dependency>
+ <dependency>
+ <groupId>com.github.tomas-langer</groupId>
+ <artifactId>chalk</artifactId>
+ <version>1.0.2</version>
+ </dependency>
+ <dependency>
+ <groupId>com.github.tomas-langer.cli</groupId>
+ <artifactId>cli-progress</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ </dependencies>
+</project>
+
diff --git a/itests/startup/src/test/java/org/apache/tomee/itest/startup/StartupTest.java b/itests/startup/src/test/java/org/apache/tomee/itest/startup/StartupTest.java
new file mode 100644
index 0000000..5880bf3
--- /dev/null
+++ b/itests/startup/src/test/java/org/apache/tomee/itest/startup/StartupTest.java
@@ -0,0 +1,512 @@
+/*
+ * 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.tomee.itest.startup;
+
+import com.github.tomaslanger.chalk.Ansi;
+import org.apache.cxf.feature.LoggingFeature;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.johnzon.jaxrs.JohnzonProvider;
+import org.apache.openejb.loader.Files;
+import org.apache.openejb.loader.IO;
+import org.apache.openejb.math.stat.descriptive.SynchronizedDescriptiveStatistics;
+import org.apache.openejb.monitoring.Managed;
+import org.apache.openejb.util.Join;
+import org.apache.tomee.server.composer.Archive;
+import org.apache.tomee.server.composer.TomEE;
+import org.junit.Test;
+
+import javax.ejb.Singleton;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.URI;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static java.util.Collections.singletonList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class StartupTest {
+
+ private final File downloads = new File("/tmp/downloads");
+
+ public StartupTest() {
+ if (!downloads.exists()) assertTrue(downloads.mkdir());
+ }
+
+ @Test
+ public void runFour() throws Exception {
+ int nameWidth = 20;
+
+ final File file = new File("/tmp/downloads/apache-tomee-8.0.0-M1-plus.tar.gz");
+ final String name = file.getName().replace(".tar.gz", "");
+ nameWidth = Math.max(nameWidth, name.length());
+
+ final long start = System.nanoTime();
+ final TomEE tomee = TomEE.from(file)
+ .out(INGORED())
+ .err(INGORED())
+ .add("webapps/speed.war", new File("/tmp/downloads/speed.war"))
+ .build();
+
+ final String address = tomee.toURI().resolve("/speed").toURL().toExternalForm();
+ final WebClient webClient = WebClient.create(address);
+
+ {// valid token
+ final Response response = webClient.reset()
+ .path("/color/")
+ .header("Content-Type", "application/json")
+ .get();
+ assertEquals(200, response.getStatus());
+ }
+
+ final int i = 1;
+ final long extractedTime = toMillis(tomee.getStats().getExtracted());
+ final long startupTime = toMillis(tomee.getStats().getStartup());
+ final long executionTime = toMillis(System.nanoTime() - start);
+
+ final int unit = 100;
+ final int n = (int) (executionTime / unit);
+ final char[] bar = new char[n];
+
+ int j = 0;
+
+ for (int k = 0; k * unit < extractedTime && j < bar.length; k++, j++) bar[j] = 'x';
+ for (int k = 0; k * unit < startupTime && j < bar.length; k++, j++) bar[j] = 's';
+ for (; j < bar.length; j++) bar[j] = 'o';
+
+ final String executionBar = new String(bar).replace("\0", "x");
+
+ System.out.printf("%-" + nameWidth + "s %4s %6s %6s %6s %s%n",
+ name,
+ i,
+ extractedTime,
+ startupTime,
+ executionTime,
+ executionBar
+ );
+ }
+
+
+ private static long toMillis(final long time) {
+ return TimeUnit.NANOSECONDS.toMillis(time);
+ }
+
+ private static PrintStream INGORED() {
+ return new PrintStream(new OutputStream() {
+ @Override
+ public void write(final int b) throws IOException {
+ }
+ });
+ }
+
+ public void progress2() throws Exception {
+ run(() -> true,
+ new Line("one", 1),
+ new Line("three", 3),
+ new Line("five", 5),
+ new Line("seven", 7),
+ new Line("eleven", 11)
+ );
+ }
+
+ private static void run(final Supplier<Boolean> proceed, Supplier<String>... items) {
+ run(proceed, Arrays.asList(items));
+ }
+
+ private static void run(final Supplier<Boolean> proceed, final List<Supplier<String>> items) {
+ for (final Supplier<String> item : items) System.out.println();
+
+ while (proceed.get()) {
+
+ final long start = System.currentTimeMillis();
+
+ System.out.print(Ansi.cursorUp(items.size() + 1));
+
+ for (final Supplier<String> item : items) {
+ System.out.print(Ansi.cursorDown());
+ System.out.print(Ansi.eraseLine());
+ System.out.printf("\r%s", item.get());
+ }
+
+ System.out.print(Ansi.cursorDown());
+ System.out.printf("\r");
+
+ final long elapsed = System.currentTimeMillis() - start;
+ if (elapsed < 500) {
+ try {
+ Thread.sleep(500 - elapsed);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ Thread.interrupted();
+ }
+ }
+ }
+ }
+
+ // @Test
+ public void progress() throws Exception {
+// System.out.println();
+
+ System.out.println();
+ System.out.println();
+ System.out.println();
+ System.out.println();
+ for (int i = 0; i <= 100; i++) {
+ System.out.print(Ansi.cursorUp(5));
+
+ int v = i;
+ if (i % 3 == 0) v *= 100;
+ if (i % 10 == 0) v *= 1000;
+ if (i % 21 == 0) v *= 1000000;
+ System.out.print(Ansi.cursorDown());
+ System.out.print(Ansi.eraseLine());
+ System.out.printf("\rone %s", v + 5);
+
+ System.out.print(Ansi.cursorDown());
+ System.out.print(Ansi.eraseLine());
+ System.out.printf("\rtwo %s", v + 1);
+
+ System.out.print(Ansi.cursorDown());
+ System.out.print(Ansi.eraseLine());
+ System.out.printf("\rthree %s", v + 9);
+
+ System.out.print(Ansi.cursorDown());
+ System.out.print(Ansi.eraseLine());
+ System.out.printf("\rfour %s", v + 15);
+
+ System.out.print(Ansi.cursorDown());
+ System.out.printf("\r");
+
+ Thread.sleep(500);
+ }
+ }
+
+ @Test
+ public void get() throws Exception {
+ getCachedTomeeTarGzs()
+ .stream()
+ .map(this::getDownloaded)
+ .map(File::getAbsolutePath)
+ .forEach(System.out::println);
+
+ }
+
+ private List<URI> getCachedTomeeTarGzs() {
+ final File index = new File(downloads, "index.txt");
+ if (index.exists()) {
+ return Stream.of(slurp(index).split("\n"))
+ .map(URI::create)
+ .collect(Collectors.toList());
+ }
+
+ final List<URI> uris = getAllTomeeTarGzs();
+
+ try {
+ IO.copy(IO.read(Join.join("\n", uris)), index);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+
+ return uris;
+ }
+
+ private File getDownloaded(final URI uri) {
+ final File file = getFile(uri);
+
+ if (file.exists()) return file;
+
+ return download(uri);
+ }
+
+ private File download(final URI uri) {
+ final WebClient client = WebClient.create(uri.toASCIIString());
+ final Response response = client.get();
+ assertEquals(200, response.getStatus());
+
+ final InputStream entity = (InputStream) response.getEntity();
+ final BufferedInputStream buffer = new BufferedInputStream(entity);
+
+ Files.mkdir(downloads);
+
+ final File file = getFile(uri);
+
+ System.out.printf("Downloading: %s%n", file.getAbsolutePath());
+
+ try {
+ IO.copy(buffer, file);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ return file;
+ }
+
+ private File getFile(final URI uri) {
+ return new File(downloads, new File(uri.getPath()).getName());
+ }
+
+ private List<URI> getAllTomeeTarGzs() {
+ final URI uri = URI.create("http://archive.apache.org/dist/");
+ return (List<URI>) Stream.of("openejb/", "tomee/")
+ .map(uri::resolve)
+ .map(this::getVersions)
+ .flatMap(Collection::stream)
+ .map(this::getTarGzs)
+ .flatMap(Collection::stream)
+ .collect(Collectors.toList());
+ }
+
+ private String slurp(final File index) {
+ try {
+ return IO.slurp(index);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private List<URI> getVersions(final URI uri) {
+ final WebClient client = WebClient.create(uri.toASCIIString());
+ final String html = client.get(String.class);
+ return (List<URI>) Stream.of(html.split("\n"))
+ .filter(s -> s.contains("alt=\"[DIR]\""))
+ .map(s -> s.replaceAll(".*a href=\"([^\"]+)\".*", "$1"))
+ .map(uri::resolve)
+ .collect(Collectors.toList());
+ }
+
+ private List<URI> getTarGzs(final URI uri) {
+ final WebClient client = WebClient.create(uri.toASCIIString());
+ final String html = client.get(String.class);
+
+ return (List<URI>) Stream.of(html.split("[<>]"))
+ .filter(s -> s.contains("tar.gz\""))
+ .filter(s -> s.contains("apache-tomee"))
+ .map(s -> s.replace("a href=\"", ""))
+ .map(s -> s.replace("\"", ""))
+ .map(uri::resolve)
+ .collect(Collectors.toList());
+ }
+
+ public void test1024() throws Exception {
+ final File appJar = Archive.archive()
+ .add(StartupTest.class)
+ .add(ColorService.class)
+ .asJar();
+
+ final TomEE tomee = TomEE.microprofile()
+ .add("webapps/test/WEB-INF/beans.xml", "")
+ .add("webapps/test/WEB-INF/lib/app.jar", appJar)
+ .build();
+
+ final WebClient webClient = createWebClient(tomee.toURI().resolve("/test").toURL());
+
+ {// valid token
+ final Response response = webClient.reset()
+ .path("/movies")
+ .header("Content-Type", "application/json")
+ .get();
+ assertEquals(200, response.getStatus());
+ }
+ }
+
+ private static WebClient createWebClient(final URL base) {
+ return WebClient.create(base.toExternalForm(), singletonList(new JohnzonProvider<>()),
+ singletonList(new LoggingFeature()), null);
+ }
+
+ @Path("/movies")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Singleton
+ public static class ColorService {
+
+ @GET
+ public String getAllMovies() {
+ return "good";
+ }
+ }
+
+ public static class Dist {
+
+ }
+
+ public static class Stat {
+ private final AtomicLong count = new AtomicLong();
+ private final SynchronizedDescriptiveStatistics samples;
+ private final String name;
+
+ public Stat(final String name) {
+ this.samples = new SynchronizedDescriptiveStatistics(2000);
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Managed
+ public void setSampleSize(final int i) {
+ samples.setWindowSize(i);
+ }
+
+ @Managed
+ public int getSampleSize() {
+ return samples.getWindowSize();
+ }
+
+ @Managed
+ public long getCount() {
+ return count.get();
+ }
+
+ @Managed
+ public double getPercentile99() {
+ return samples.getPercentile(99.0);
+ }
+
+ @Managed
+ public double getPercentile90() {
+ return samples.getPercentile(90.0);
+ }
+
+ @Managed
+ public double getPercentile75() {
+ return samples.getPercentile(75.0);
+ }
+
+ @Managed
+ public double getPercentile50() {
+ return samples.getPercentile(50.0);
+ }
+
+ @Managed
+ public double getPercentile25() {
+ return samples.getPercentile(25.0);
+ }
+
+ @Managed
+ public double getPercentile10() {
+ return samples.getPercentile(10.0);
+ }
+
+ @Managed
+ public double getPercentile01() {
+ return samples.getPercentile(1.0);
+ }
+
+ @Managed
+ public double getStandardDeviation() {
+ return samples.getStandardDeviation();
+ }
+
+ @Managed
+ public double getMean() {
+ return samples.getMean();
+ }
+
+ @Managed
+ public double getVariance() {
+ return samples.getVariance();
+ }
+
+ @Managed
+ public double getGeometricMean() {
+ return samples.getGeometricMean();
+ }
+
+ @Managed
+ public double getSkewness() {
+ return samples.getSkewness();
+ }
+
+ @Managed
+ public double getKurtosis() {
+ return samples.getKurtosis();
+ }
+
+ @Managed
+ public double getMax() {
+ return samples.getMax();
+ }
+
+ @Managed
+ public double getMin() {
+ return samples.getMin();
+ }
+
+ @Managed
+ public double getSum() {
+ return samples.getSum();
+ }
+
+ @Managed
+ public double getSumsq() {
+ return samples.getSumsq();
+ }
+
+ @Managed
+ public double[] sortedValues() {
+ return samples.getSortedValues();
+ }
+
+ @Managed
+ public double[] values() {
+ return samples.getValues();
+ }
+
+ public void record(final long time) {
+ count.incrementAndGet();
+ samples.addValue(time);
+ }
+
+ }
+
+ private static class Line implements Supplier<String> {
+ private final String prefix;
+ private final int mod;
+ private int i;
+
+ public Line(final String prefix, final int mod) {
+ this.prefix = prefix;
+ this.mod = mod;
+ }
+
+ @Override
+ public String get() {
+ i += mod;
+ return prefix + " " + i;
+ }
+ }
+}
diff --git a/itests/tomee-server-composer/src/main/java/org/apache/tomee/server/composer/TomEE.java b/itests/tomee-server-composer/src/main/java/org/apache/tomee/server/composer/TomEE.java
index 2461603..2d0dc1e 100644
--- a/itests/tomee-server-composer/src/main/java/org/apache/tomee/server/composer/TomEE.java
+++ b/itests/tomee-server-composer/src/main/java/org/apache/tomee/server/composer/TomEE.java
@@ -25,6 +25,7 @@ import org.tomitribe.util.hash.XxHash64;
import java.io.File;
import java.io.IOException;
+import java.io.PrintStream;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
@@ -36,14 +37,16 @@ import java.util.stream.Stream;
public class TomEE {
+ private final Stats stats;
private final File home;
private final int port;
private final Process process;
- private TomEE(final File home, final int port, final Process process) {
+ private TomEE(final File home, final int port, final Process process, final Stats stats) {
this.home = home;
this.port = port;
this.process = process;
+ this.stats = stats;
}
public URI toURI() {
@@ -90,17 +93,61 @@ public class TomEE {
return of("org.apache.tomee:apache-tomee:tar.gz:webprofile:" + Version.VERSION);
}
+ public Stats getStats() {
+ return stats;
+ }
+
+ public static class Stats {
+ private final long extracted;
+ private final long startup;
+
+ public Stats(final long extracted, final long startup) {
+ this.extracted = extracted;
+ this.startup = startup;
+ }
+
+ public long getExtracted() {
+ return extracted;
+ }
+
+ public long getStartup() {
+ return startup;
+ }
+ }
+
public static Builder of(final String mavenCoordinates) throws Exception {
return new Builder(mavenCoordinates);
}
+ public static Builder from(final File mavenCoordinates) throws Exception {
+ return new Builder(mavenCoordinates);
+ }
+
public static class Builder extends ServerBuilder<Builder> {
+ private PrintStream err = System.err;
+ private PrintStream out = System.out;
+
public Builder(final String mavenCoordinates) throws IOException {
super(mavenCoordinates);
filter(Excludes::webapps);
}
+ public Builder(final File mavenCoordinates) throws IOException {
+ super(mavenCoordinates);
+ filter(Excludes::webapps);
+ }
+
+ public Builder err(PrintStream err) {
+ this.err = err;
+ return this;
+ }
+
+ public Builder out(PrintStream out) {
+ this.out = out;
+ return this;
+ }
+
public Builder update() {
home(this::update);
return this;
@@ -143,10 +190,14 @@ public class TomEE {
final File tmpdir = cleanOnExit.clean(Files.tmpdir());
final File home;
+ final long extracted;
{ // extract the server
home = new File(tmpdir, "server");
Files.mkdir(home);
+
+ final long start = System.nanoTime();
TarGzs.untargz(archive, home, true, filter);
+ extracted = System.nanoTime() - start;
}
{ // make scripts executable
@@ -182,6 +233,7 @@ public class TomEE {
if (list) Files.visit(tmpdir, TomEE::print);
+ final long start = System.nanoTime();
final Process process = cleanOnExit.clean(builder.start());
final CountDownLatch startup = new CountDownLatch(1);
@@ -198,8 +250,8 @@ public class TomEE {
watch.accept(errorStream);
}
- final Future<Pipe> stout = Pipe.pipe(inputStream.get(), System.out);
- final Future<Pipe> sterr = Pipe.pipe(errorStream.get(), System.err);
+ final Future<Pipe> stout = Pipe.pipe(inputStream.get(), out);
+ final Future<Pipe> sterr = Pipe.pipe(errorStream.get(), err);
try {
if (!startup.await(await.getTime(), await.getUnit())) {
@@ -208,8 +260,9 @@ public class TomEE {
} catch (InterruptedException e) {
throw new StartupFailedException(e);
}
+ final long startTime = System.nanoTime() - start;
- return new TomEE(home, http, process);
+ return new TomEE(home, http, process, new Stats(extracted, startTime));
}
}