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));
         }
     }