Giter Club home page Giter Club logo

eostermueller / load-test-in-a-box Goto Github PK

View Code? Open in Web Editor NEW
14.0 14.0 3.0 3.4 MB

A hands-on, self-directed java performance training platform. An easy to install uber jar with an Angular 13 GUI that orchestrates a load generator (JMeter), SUT with performance defects/fixes, and monitoring from glowroot.org.

License: MIT License

Java 78.70% HTML 2.64% TypeScript 15.35% JavaScript 0.45% Shell 2.08% SCSS 0.61% PowerShell 0.10% Batchfile 0.01% CSS 0.06%
java monitoring performance performance-defects spring-boot

load-test-in-a-box's People

Contributors

devwebcl avatar eostermueller avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

load-test-in-a-box's Issues

glowroot only accessible on localhost

Perhaps instead of exposing this, we should just instead provide instructions so people can expose this on their own.
tcp port 12675

Below are the instructions, taken directly from this glowroot link

By default, the Glowroot UI binds to 127.0.0.1 and is only accessible from localhost. If you wish to access the Glowroot UI from remote machines, change the bind address to 0.0.0.0 under Glowroot UI > Configuration > Web. Alternatively, the bind address can be changed by creating (or editing) admin.json (in the same directory as glowroot.jar), e.g.

{
  "web": {
    "bindAddress": "0.0.0.0"
  }
}

Hide InterruptedException when agent is stopped

The InterruptedException (detail below) is thrown when the agent is stopped with a Ctrl+C in a terminal with the uber jar running.
This is thrown because a health check is in progress...no harm is done since the user is asking for the whole thing to be terminnated with the Ctrl+C, so this exception needs to be hidden.

java.lang.InterruptedException
        at java.lang.ProcessImpl.waitFor(ProcessImpl.java:518)
        at com.github.eostermueller.snail4j.util.OsUtils.executeProcess_mswin(OsUtils.java:136)
        at com.github.eostermueller.snail4j.util.OsUtils.executeProcess(OsUtils.java:160)
        at com.github.eostermueller.snail4j.util.JdkUtils.executeJdkBinCmd(JdkUtils.java:315)
        at com.github.eostermueller.snail4j.util.JdkUtils.getJavaProcesses(JdkUtils.java:279)
        at com.github.eostermueller.snail4j.util.JdkUtils.getPidForProcessNameContains(JdkUtils.java:259)
        at com.github.eostermueller.snail4j.health.AbstractSpringNetworkHealthIndicator.getProcessId(AbstractSpringNetworkHealthIndicator.java:72)
        at com.github.eostermueller.snail4j.health.AbstractTcpHealthIndicator.healthDetail(AbstractTcpHealthIndicator.java:58)
        at com.github.eostermueller.snail4j.health.AbstractTcpHealthIndicator.health(AbstractTcpHealthIndicator.java:41)
        at org.springframework.boot.actuate.health.HealthIndicator.getHealth(HealthIndicator.java:37)
        at org.springframework.boot.actuate.health.HealthEndpointWebExtension.getHealth(HealthEndpointWebExtension.java:85)
        at org.springframework.boot.actuate.health.HealthEndpointWebExtension.getHealth(HealthEndpointWebExtension.java:44)
        at org.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:99)
        at org.springframework.boot.actuate.health.HealthEndpointSupport.getAggregateHealth(HealthEndpointSupport.java:110)
        at org.springframework.boot.actuate.health.HealthEndpointSupport.getContribution(HealthEndpointSupport.java:96)
        at org.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:74)
        at org.springframework.boot.actuate.health.HealthEndpointSupport.getHealth(HealthEndpointSupport.java:61)
        at org.springframework.boot.actuate.health.HealthEndpointWebExtension.health(HealthEndpointWebExtension.java:71)
        at org.springframework.boot.actuate.health.HealthEndpointWebExtension.health(HealthEndpointWebExtension.java:60)
        at sun.reflect.GeneratedMethodAccessor45.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:282)
        at org.springframework.boot.actuate.endpoint.invoke.reflect.ReflectiveOperationInvoker.invoke(ReflectiveOperationInvoker.java:77)
        at org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation.invoke(AbstractDiscoveredOperation.java:60)
        at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$ServletWebOperationAdapter.handle(AbstractWebMvcEndpointHandlerMapping.java:290)
        at org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler.handle(AbstractWebMvcEndpointHandlerMapping.java:373)
        at sun.reflect.GeneratedMethodAccessor44.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:750)
2023-02-05 14:59:09.424  INFO 21976 --- [extShutdownHook] o.s.i.endpoint.EventDrivenConsumer       : Removing {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
2023-02-05 14:59:09.440  INFO 21976 --- [extShutdownHook] o.s.i.channel.PublishSubscribeChannel    : Channel 'tutorial-app.errorChannel' has 0 subscriber(s).
2023-02-05 14:59:09.456  INFO 21976 --- [extShutdownHook] o.s.i.endpoint.EventDrivenConsumer       : stopped bean '_org.springframework.integration.errorLogger'
2023-02-05 14:59:09.487  INFO 21976 --- [extShutdownHook] o.s.s.c.ThreadPoolTaskScheduler          : Shutting down ExecutorService 'taskScheduler'
2023-02-05 14:59:09.519  INFO 21976 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

Need force shutdown of the System Under Test

If the System Under Test (SUT) only partially launches, there's no convenient way to recover.
For example, this shows that the sutApp never started, even though H2 and Wiremock did start.
The cause here? Compilation error in sutApp.
image

Because the SUT startup checkbox is enabled in mid-startup, there's no way to shut down the parts that did start, and no way to complete the startup.
Must open command prompt and manually kill processes.

To implement, create "SUT Force Shutdown" button.
Can't quite decide whether it should be placed somewhere prominently, or somewhat hidden / out of the way, or only displayed when snail4j "detects" it is needed. For example, it would be needed when the SUT doesn't launch within a timeout -- perhaps a 20 second timeout.

SUT does not stop when asked

The attached three threaddumps (nearly identical) were captured after removing the checkbox in the GUI for the SUT....so the SUT should have stopped when removing that check box, but it did not.

Manually, I executed this:
C:\Users\eoste>taskkill /PID:12708 ERROR: The process with PID 12708 could not be terminated. Reason: This process can only be terminated forcefully (with /F option).
Need to compare the attached to threaddumps captured in the easy-to-reproduce scenario.
When thread dumps are captured prior to when it DOES stop correctly.

One possible cause is that running a load test somehow makes it harder to stop.

01.txt
02.txt
03.txt

This gets a little weirder.
After I tried to windows taskkill WITHOUT the force flag, detailed above, I added the force flag and the SUT died.
So I thought, perhaps I just need to add the force flag in OsUtils 'kill' code...but I looked and the force flag is already there.
So it seems that the process died after the code submitted a kill, I submitted a kill from the command line WITHOUT the force, then I resumitted the kill from the command line.

Source code, showing the kill parameter is in place for both linux and Windows:
https://github.com/eostermueller/performanceAnalysisWorkbench/blob/master/backend/src/main/java/com/github/eostermueller/snail4j/OsUtils.java#L218

Need bash scripts in snail4j/build ported to ANT script

Need snail4j/build/*.sh ported to ANT script

Requirements:

  • Need to use 1.10.x version of ant
  • Must work and be tested on mac, linux and Windows.
  • Developer-specific folder locations (those in setenv*.sh) must be specified in a separate file, a properties file sounds reasonable.
  • Need one ant target for each batch file in snail4j/build/*.sh
  • Need additional target as a "ready for build" step. It should indicate whether all of the locations in the properties file exist on the file system, and display all those locations another with some meaningul name. This will be used to test whether the configuration file is setup correctly.

intermittent maven download issues

If you get "curl: (35) schannel: next InitializeSecurityContext failed: Unknown error (0x80092013) - The revocation function was unable to check revocation because the revocation server was offline." from running build/packageSlow.sh, it appears to be intermittent, as the message suggests.
When I re-ran packageSlow.sh, this went away.

Downloading Maven from https://downloads.apache.org/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.zip
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current^M
                                 Dload  Upload   Total   Spent    Left  Speed^M
^M  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0^M  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0^M
curl: (35) schannel: next InitializeSecurityContext failed: Unknown error (0x80092013) - The revocation function was unable to check revocation because the revocation server was offline.^M
CHECKPOINT Just downloaded maven.zip from https://downloads.apache.org/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.zip: -rw-r--r-- 1 eoste 197609 9602303 Apr 19 16:24 /C/Users/eoste/Documents/src/s4j_2020-040-30/snail4j/build/../backend/src/main/resources/apache-maven-3.6.3-bin.zip
-rw-r--r-- 1 eoste 197609 9602303 Apr 19 16:24 /C/Users/eoste/Documents/src/s4j_2020-040-30/snail4j/build/../backend/src/main/resources/apache-maven-3.6.3-bin.zip

Include JVM Arguments in the Workload Key

The "Workload Key" is a json blob of text that defines the current code that's running.
With a little work, JVM parameters (like gc configuration, classloader configuration, other -D parameters) could be added.

When a "Workload Key" contained JVM parameters, there are some important considerations:

  • JVM would need to be restarted, which can take several seconds
  • Must detect JVM parameters not supported by the "currently configured" JDK. For example, if you've specified G1 GC parameters in a 1.6jdk...the JVM might not even start.
  • Which JVMs will users be able to specify parameters for? Of course, the SUT would be the highest priority. But what about JVM running H2? What about the JVM running wiremock?

SUT processes stop correctly on mac but not on ms-win-10

on the Mac, the following two REST services start and stop the 3 processes that make up the SUT (system under test):

http://localhost:8090/snail4j/startSut
http://localhost:8090/snail4j/stopSut

Soon there will be buttons/controls on the UI that invoke these, but they're not ready yet.

I just got finished testing the REST calls on the mac, and both work great. All 3 operating system processes of the SUT dutifully start and stop.

However, the stopSut does not work on ms-win-10.
It looks like the h2 is the only one that stops. The wiremock and the sutApp (aka tjp2) remain. This needs to be fixed so that http://localhost:8090/snail4j/stopSut stops all sut processes, but leaves snail4j/backend-SNAPSHoT-00.01.jar running.

The detail below shows how the start/stop works on the mac.

--Erik

This pom.xml file:

https://github.com/eostermueller/havoc2/blob/master/processManager/pom.xml

uses this plugin:

https://github.com/bazaarvoice/maven-process-plugin

to launch and stop the 3 processes in the SUT.
They're all launch in the maven integration phase.

Then the following test program blocks until a sentinel file is deleted from the file system.

https://github.com/eostermueller/havoc2/blob/master/processManager/src/test/java/com/github/eostermueller/perfgoat/ITBlock.java

But while ITBlock is still alive -- that is the time for snail4j load testing.
When the user needs to restart, perhaps to change JVM parameters, or to just stop, the shail4j/stopSut REST service runs, ITBlock stops, and then all of the SUT processes stop.
The main snail4j process (currently called backend-SNAPSH0T-0.01.jar) will continue to run.

JMeter Load Parameters in the UI

How much load is applied when you lauch the load generator, with the checkbox in the screenshot below? 3t0tt -- 3 threads, zero think time. I chose this because its not too much, not too little. A single thread of load will never catch any multi-threaded defects. 2 threads will sometimes reproduce muti-threaded defects. But in my experience, three threads of load will reproduce multi-threaded defects most of the time, especially when the unit of work being executed is < 10-ish milliseconds.

The main two parameters I'd want are
count of threads to load and test duration.

Would be nice to provide some basic parameters for configuring this. For example, if you're experimenting with larger amounts of parallelism, you'll have to write your own threading code that launches threads. But if you could jack up the number of load generator threads to larger numbers, exploring larger parallelism would be easy/trivial.

Would be nice to have a few basic data entry controls for that just below the "Status: Stopped" for load generation.

image

Need error message for duplicate TCP ports

Screenshot starting snail4j and other programs are listening on snail4j ports!
epic fail.
Must check for dup ports at startup, display error message.
The code should then propose alternative ports (that have already been confirmed are open), and require the user to just click 'ok' or something to use those proposed ports.
...then the newly selected ports should be persisted to snail4j.json config file and the system should then startup as usual.

image

parameterize-able @Load methods

load generators have a gazillion ways to pass data into the code that's being load tested.

Data from .csv files, random number generators, sequences of numbers, the list goes on.
But snail4j can't do this yet. See how I've added parameters to the method names in the below screenshots? That's my ugly shortcut for now.

For example, consider this text from the screenshot: "simulateSynchronizedSlowCode_sleepMilliseconds_1000". Not too tough to imagine that the source has a hard coded parameter of 1000.

Instead this hard coding, the dream is to pass parameters from the snail4j GUI directly into the method parameters of a method annotated with @com.github.eostermueller.snail4j.Load.

As an aside, the classes and method names in this screenshot got there because I added a special method-level annotation to the code I wanted to load. Snail4j uses ClassGraph to query for all classes with that annotation.

Then, snail4j instantiates the annotated class using (logically) prototype scope.
Plain reflection is used to invoke the method using this code, and the parameters from the Snail4j GUI would be specified there.

image

h2 jdbc url bug

Whoops, h2 jdbc url was hardcoded to path on my machine :-(

SUT launch hangs the GUI withi compile error

Current behavior:

  1. Start the uber jar.
  2. make sure the SUT is stopped
  3. introduce a compile error into a .jar file downstream from $HOME/.load-test-in-a-box/sutApp
  4. click the 'start SUT' checkbox.
  5. Wiremock and H2 health meters will turn green, but SUT and glowroot will not. The "System Under Test" launch checkbox will be grayed out with check....and you'll be stuck in this 1/2 started and 1/2 failed state.
  6. The only way out of this 1/2 failed state is to manually kill all the 1/2 started processes or manually download and run this PowerShell script on MS-Windows.
    Ideally, you should be able to delete $HOME/.load-test-in-a-box/deleteMeToStopSnail4jSut.txt but that functionality is not working :-(.

Desired behavior

After step 4 above, the system would somehow detect the compile problem and shutdown everything that was 1/2 started and allow the user to use the GUI to try again.....and also display a message to the end user about what happened -- probably using the "Toast" facility.

UI needs link for glowroot

The snail4j UI needs a link to the locally deployed glowroot, and the tcp port number in the URL should be the one configured in the snail4j.json file, sutAppPort attribute.

When I hit http://localhost:4000/, that expands to this:

http://localhost:4000/transaction/throughput?transaction-type=Web&last=1800000

But the last=1800000 (30 minutes) is too big of a time window.
Need to change that to last=180000 (3 minutes), so immediate changes will be better reflected.

With a larger window like the default 30 minutes, immediate changes take minutes to fully render.

Encrypted Workload Keys

Need a java component that will encrypt and decrypt a json blob of text that is a few 1000 bytes.
I'd really like this to be done using jasypt which is a well-thought-out approach to the whole process.....configuration for names of encryption algorithms, approaches for getting the 'salt', encryption library dependencies, command line tools for encryption/decryption, etc...

A teacher/administrator must be able to specify a secret encryption key at snail4j startup. This key will be used on the java side to decrypt the encrypted text that the user enters into the GUI.

The teacher/administrator must also have a command line facility that encrypts a workload key given that secret key.

Without this enhancement, the snail4j Angular UI shows the class and method name of the running code/workload.
With this enhancement, the end user will paste an encrypted key into the user interface, and snail4j will run the mystery code specified by the encrypted key. The UI will obscure/hide the name of the class/method name.

Detail:

  1. The teacher will use snail4j to create/assemble a plain-text workload key with the desired java processing.
  2. The teacher will use the command line facility to encrypt the plain-text workload key.
  3. The teacher will distribute the encrypted keys to the student in a plain text document, along with documentation on how to use that key with Snail4j. The doc will also "solicit predictions", as detailed below.
  4. The End user will go to a new screen in snail4j, dial in the encrypted key, and the SUT. Once text is pasted into snail4j, but before the user submits the text to the snail4j agent, the GUI will validate whether the key is "good" -- aka, decryptable using the secret key specified at the snail4j command line uber startup.
  5. Once snail4j GUI sends that key to the snail4j java agent, the snail4j java agent will decrypt the encrypted workload key using the "secret key" specified at snailj startup as a command line parameter.
  6. Once decrypted, snail4j will then run the specified workload.
  7. To avoid spoiling the secret, The snail4j UI will hide/obscure the normal GUI

Use Case: Invalid Encrypted Key.

This use case also needs to handle an invalid key.
For example, say the user doesn't copy all the text of the key from the 'doc' given to them by their teacher. Or, say, the teacher gives them a key, and when the snail4j uber jar was launched, it was not given the "secret key", the one that decrypts the text provided by the end user.

Use Case / Background

Snail4j aims to teach java performance to all java developers. I interview java developers a few times a year, and there just aren't enough of them with basic performance skills. But also, our approaches to teaching performance skills aren't cranking out a lot of skilled pepes.

But snail4j is just software, how exactly will it "teach"? In short, it will solicit predictions from the end user.

Here are a few different approaches, where the user is solicited (asked) to make various predictions.

  • Which code will run faster, the code that uses technique A or technique B? Given a few encrypted workload keys, the user is asked say one uses technique A, and which uses technique B.
  • User is asked to use Snail4j to run a particular workload and asked what is taking up most of the time? If the key to specify that workload is opaque/encrypted, the user will be forced to learn how to use tools to discover the problem.
  • User asked what the developer of workload 2 did to make it so much faster than workload 1.

With an encrypted "workload key", the user won't be able to directly inspect the source code, because the class names in the "workload key" will be opaque/encrypted.
This helps force the user to install/learn/use monitoring/observability tools to build an evidence-based story about what's processing, instead of using the source code as a crutch and taking guesses by peeking at the source code.

Here is an example of a clear text workload key that needs to be encrypted:

{
    "useCases": [
      {
        "processingUnits": [
          {
            "description": {
              "en_US": "sleep ms 100"
            },
            "useCaseName": "sleep",
            "selected": true,
            "methodWrapper": {
              "parameters": [],
              "declaringClassName": "com.github.eostermueller.tjp2.misc.SleepDelay",
              "methodName": "simulateSlowCode_sleepMilliseconds_100"
            }
          },
          {
            "description": {
              "en_US": "sleep ms 1"
            },
            "useCaseName": "sleep",
            "selected": false,
            "methodWrapper": {
              "parameters": [],
              "declaringClassName": "com.github.eostermueller.tjp2.misc.SleepDelay",
              "methodName": "simulateSlowCode_sleepMilliseconds_1"
            }
          },
          {
            "description": {
              "en_US": "sync sleep ms 10"
            },
            "useCaseName": "sleep",
            "selected": false,
            "methodWrapper": {
              "parameters": [],
              "declaringClassName": "com.github.eostermueller.tjp2.misc.SleepDelay",
              "methodName": "simulateSynchronizedSlowCode_sleepMilliseconds_10"
            }
          },
          {
            "description": {
              "en_US": "sleep ms 10"
            },
            "useCaseName": "sleep",
            "selected": false,
            "methodWrapper": {
              "parameters": [],
              "declaringClassName": "com.github.eostermueller.tjp2.misc.SleepDelay",
              "methodName": "simulateSlowCode_sleepMilliseconds_10"
            }
          },
          {
            "description": {
              "en_US": "sleep ms 1000"
            },
            "useCaseName": "sleep",
            "selected": false,
            "methodWrapper": {
              "parameters": [],
              "declaringClassName": "com.github.eostermueller.tjp2.misc.SleepDelay",
              "methodName": "simulateSlowCode_sleepMilliseconds_1000"
            }
          },
          {
            "description": {
              "en_US": "sync sleep ms 1000"
            },
            "useCaseName": "sleep",
            "selected": false,
            "methodWrapper": {
              "parameters": [],
              "declaringClassName": "com.github.eostermueller.tjp2.misc.SleepDelay",
              "methodName": "simulateSynchronizedSlowCode_sleepMilliseconds_1000"
            }
          },
          {
            "description": {
              "en_US": "sync sleep ms 1"
            },
            "useCaseName": "sleep",
            "selected": false,
            "methodWrapper": {
              "parameters": [],
              "declaringClassName": "com.github.eostermueller.tjp2.misc.SleepDelay",
              "methodName": "simulateSynchronizedSlowCode_sleepMilliseconds_1"
            }
          },
          {
            "description": {
              "en_US": "sync sleep ms 100"
            },
            "useCaseName": "sleep",
            "selected": false,
            "methodWrapper": {
              "parameters": [],
              "declaringClassName": "com.github.eostermueller.tjp2.misc.SleepDelay",
              "methodName": "simulateSynchronizedSlowCode_sleepMilliseconds_100"
            }
          }
        ],
        "name": "sleep"
      }
    ]
  }

GUI Unit tests to confirm TCP port changes

snail4j uses at least half a dozen port numbers for various connectivity.
To insure snail4j can run on any machine, snail4j must be able to change configuration to use other port numbers without port conflicts.

This enhancement lays out the need for unit tests of the Angular UI that validate that TCP port numbers for a number of processes can be changed, and the SUT startup and load testing functionality still works.

Here are the port numbers that should be tested:
wiremock
h2
SUT JVM
glowroot
jmeter shutdown port.
snail4j agent -- which will be handled slightly different than the above.

EBADPLATFORM while building on win10

For some, running "npm i -f" has fixed the following....but that doesn't fix the problem for me.
The npm i -f suggestion came from here.

[ERROR]
[ERROR] npm ERR! code EBADPLATFORM
[ERROR] npm ERR! notsup Unsupported platform for [email protected]: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})
[ERROR] npm ERR! notsup Valid OS:    darwin
[ERROR] npm ERR! notsup Valid Arch:  any
[ERROR] npm ERR! notsup Actual OS:   win32
[ERROR] npm ERR! notsup Actual Arch: x64
[ERROR]
[ERROR] npm ERR! A complete log of this run can be found in:
[ERROR] npm ERR!     C:\Users\eoste\AppData\Roaming\npm-cache\_logs\2019-11-24T16_48_47_354Z-debug.log

Here is the verbose output from mvn clean install:
out.txt

Need Swagger doc for REST API

Need port health indicators for snail4j agent and glowroot

See the Angular Material slide toggles in the bottom left corner of the screenshot at the bottom of this issue?
Need two more of those.
One for localhost:4000 (glowroot monitoring UI), and another for localhost:8090 (the main snail4j agent, a Spring Boot jar).

But wait! It's a little more complicated than that.
End users need to be able to change those ports, if there is a conflict on their local system.

Step 1

Have a look at this source.
Each indicator should have one Java class that should be in the looks nearly identical to that, and it should be in the same java package.

As seen in the above class, the correct port number to test should be retrieved from the "Configuration" interface.

Step 2: launch glowroot with the port configured in snail4j.

Step 3: Retrieve the "current" port from Spring Boot Snail4j Agent.

image

mac install problem - Cannot run program "cmd.exe"

systemUnderTest.log

Whps. Will likely need ms-win/unix-specific profiles to get around this in processManager/pom.xml:

183             <arguments>
184                                 <argument>cmd.exe</argument>
185                                 <argument>/C</argument>
186                                 <argument>mvn -e -X ${snail4j.maven.offline.passthru}

[INFO] --- process-exec-maven-plugin:0.9:start (sutApp) @ systemUnderTest ---
 84 [INFO] arg: cmd.exe
 85 [INFO] arg: /C
 86 [INFO] arg: mvn -e -X -V -Dmaven.repo.local=/Users/erikostermueller/.snail4j/repository -Dspring-boot.run.fork=true -Dspring-boot.run.jvmArguments    ="-Dsnail4j.wiremock.hostname=localhost -Dsnail4j.h2.port=10675 -Dsnail4j.h2.hostname=localhost -Dsnail4j.wiremock.port=11675 -Dglowroot.agent.por    t=12675 -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=512m" -Dspring-boot.run.agents=/Users/erikostermueller/.snail4j/glowroot/glowroot/glowroot.jar -    Dspring-boot.run.arguments=--server.port=9675 spring-boot:run
 87 [INFO] Full command line: cmd.exe /C mvn -e -X -V -Dmaven.repo.local=/Users/erikostermueller/.snail4j/repository -Dspring-boot.run.fork=true -Dspr    ing-boot.run.jvmArguments="-Dsnail4j.wiremock.hostname=localhost -Dsnail4j.h2.port=10675 -Dsnail4j.h2.hostname=localhost -Dsnail4j.wiremock.port=1    1675 -Dglowroot.agent.port=12675 -Xmx1024m -XX:NewSize=512m -XX:MaxNewSize=512m" -Dspring-boot.run.agents=/Users/erikostermueller/.snail4j/glowroo    t/glowroot/glowroot.jar -Dspring-boot.run.arguments=--server.port=9675 spring-boot:run
 88 [INFO] Starting process: sutApp
 89 [INFO] Using working directory for this process: /Users/erikostermueller/.snail4j/sutApp
 90 [ERROR]
 91 java.lang.RuntimeException: java.io.IOException: Cannot run program "cmd.exe" (in directory "/Users/erikostermueller/.snail4j/sutApp"): error=2, N    o such file or directory
 92     at com.bazaarvoice.maven.plugin.process.ExecProcess.execute (ExecProcess.java:52)

Caused by: java.io.IOException: error=2, No such file or directory
    at java.lang.ProcessImpl.forkAndExec (Native Method)
    at java.lang.ProcessImpl.<init> (ProcessImpl.java:339)
    at java.lang.ProcessImpl.start (ProcessImpl.java:270)
    at java.lang.ProcessBuilder.start (ProcessBuilder.java:1107)
    at java.lang.ProcessBuilder.start (ProcessBuilder.java:1071)
    at com.bazaarvoice.maven.plugin.process.ExecProcess.execute (ExecProcess.java:49)                                                                
    at com.bazaarvoice.maven.plugin.process.ProcessStartMojo.startProcess (ProcessStartMojo.java:44)                                                 
    at com.bazaarvoice.maven.plugin.process.ProcessStartMojo.execute (ProcessStartMojo.java:26)                                                      
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)                                            
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:210)                        

Display PID of SUT in browser UI

Getting to performance data can be painfully slow.....plugging in tools, collecting logs, analyzing graphs.....
To get data quickly, use the command line tools that come with the JDK: jstack, jstat, jcmd, etc....

But to use these you'll need to know the process id (PID) of the SUT (system under test).
The SUT process is com.github.eostermueller.tjp2.PerformanceSandboxApp, and the UI needs to display the PID of this process every time it's restarted.

local repo is not being used

myLogs.zip

When the SUT is launched, maven is downloading files from maven central, which shouldn't be happening. Instead, the required pom/jar files should be in the load-test-in-a-box local maven repo.

Click-to-fail links actually fail in Firefox 108.0.1 64bit

When I left click on a "Click-to-Fail" link in the ngx-markdown panel, the linked "workload" does not display the "toaster" notification indicating success.

Instead, it navigates to a new mostly blank web page with the text "false" in the extreme upper left corner as shown here:
image

Potential Incorrect Memory Calculation

In MacOS and Windows 11, I am getting insufficient memory though I have 16GB RAM and 32GB RAM in macOS and Windows respectively. There could be miscalculation. Could you please double check? Thanks!

image

########################################
########################################
#### 
#### Start of Snail4j Install
#### 
#### 
#### ERROR: Insufficient memory.  Consider adding more RAM or stopping programs to free up existing RAM.  
Expected min available RAM: 3,221,225,472
Actual available RAM: 1,220,919,296

#### INFO:  No JAVA_HOME environment variable found. Will attempt to use [%s] for the SUT JDK
#### INFO:  Number if install issues: 1
#### 
#### 
#### Snail4j Install Failed
#### 
#### 
########################################
########################################

SUT and LG check box problem when browser is reopened

If you start everything (snail4j, SUT, load generator), and then close and reopen the browser window, the health checks do an OK job of re-detecting what's running, as shown here:

image

The slide-toggle's correctly detect the tcp ports are up, and the "Status" text for both the SUT and the Load Generator looks good ("Running" and "Applying Load").

But what's wrong in the above screenshot is that the code isn't marking the two check boxes as being checked. So to fix this issue, code needs to be added at UI startup to test for the SUT and LG status, and add the checks if those systems are up.

could not start system under test

processManager/pom.xml is throwing this exception when launched:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile (default-testCompile) on project systemUnderTest: Fatal error compiling: invalid target release: 1.8 -> [Help 1]

Default Workload

If a user starts the load generator, currently requests enter the tjp2 servlet, do zero work, and return immediately.

Would be nice to have a default workload, something with WELCOME_TO_SNAIL4J in the method name that would show up in the stack traces.

current selection is deselected on workload refresh

Before the workload screen queries snail4j for all the pieces of code that have been annotated with the @load annotation, it zeros out the previously queried code. Makes sense, right? Out with the old, in with the new.

As a side effect, the checkboxes marking the code that is currently selected (if any) to run also get zeroed out (unchecked). :-(

This means the user can no longer tell what is selected(aka running), even though the server side has stored the selection, and load gets applied to the previously selected code.

To fix this bug, once the refresh is done, the current selection must be queried from the server, and the ui must tick all the boxes that the server said were previously selected.

@Load selection across multiple pages is broken

The snail4j angular app has a bug.
If you have multiple "pages" of results in the workload, @load items selected on one page are deselected when moving to a different page, and then back.

Expected behavior:
when you select a @load, that item should remain selected when navigating between multiple pages of results.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.