From 38c2ab8d71052474ae61d6cf3627d99840b7c66f Mon Sep 17 00:00:00 2001
From: Erik Lumme <erik@vaadin.com>
Date: Tue, 12 Sep 2017 15:36:15 +0300
Subject: Migrate LoadTestingWithGatling

---
 .../articles/LoadTestingWithGatling.asciidoc       | 267 +++++++++++++++++++++
 1 file changed, 267 insertions(+)
 create mode 100644 documentation/articles/LoadTestingWithGatling.asciidoc

(limited to 'documentation/articles/LoadTestingWithGatling.asciidoc')

diff --git a/documentation/articles/LoadTestingWithGatling.asciidoc b/documentation/articles/LoadTestingWithGatling.asciidoc
new file mode 100644
index 0000000000..ec5bc80525
--- /dev/null
+++ b/documentation/articles/LoadTestingWithGatling.asciidoc
@@ -0,0 +1,267 @@
+[[loading-testing-with-gatling]]
+Load testing with Gatling
+-------------------------
+
+http://gatling.io[Gatling] is a powerful tool for load testing. Compared
+to WebDriver/Selenium/TestBench, it doesn't render the actual content,
+but just simulates messages clients send to the server. The server
+doesn't know if the test tool actually does something with the
+responses, so it gives you perfectly valid numbers for your applications
+performance - on the server side. It scales very well, so you don' t
+need huge army of nodes to bombard your application under test. It can
+be used with Vaadin as such, but with these tips you hopefully get
+started easier.
+
+[[vaadin-tips-to-make-tests-more-stable-and-easier-to-create]]
+Vaadin tips to make tests more stable and easier to create
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Gatling works with Vaadin "out of the box", but there are some obstacles
+you might face if you don't understand a bit how Vaadin communication
+works. E.g. plain recordings from standard Vaadin apps will not work
+properly.
+
+The communication that Vaadin's "thin client" in browser does with the
+server side has couple of checks that it does to improve robustness and
+security. One can simulate these with Gatling as well, by e.g.
+https://github.com/mstahv/v-quiz/blob/master/src/test/scala/loadtest/WebSocketVaadinSimulation.scala#L84[reading the XSRF preventation key into a variable] and passing the value
+https://github.com/mstahv/v-quiz/blob/master/src/test/scala/loadtest/WebSocketVaadinSimulation.scala#L95[in
+each response]. However, these setting can be disabled during load
+testing to make it easier to write and maintain your application. The
+effect for the scalability, when disabling or configuring these, should
+be negligible. Feel free to do these, but also remember to remove these
+"hacks" when building your production war file. Consider e.g. using
+separate maven profile and inject different parameters with it.
+
+[[disabling-xsrf-presentation-key]]
+Disabling XSRF presentation key
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The XSRF preventation can be disabled with following servlet parameter
+(or similar servlet 3 style parameter). NOTE, do not leave this for
+public apps in production.
+
+[source,xml]
+....
+<context-param>
+  <param-name>disable-xsrf-protection</param-name>
+  <param-value>true</param-value>
+</context-param>
+....
+
+[[disabling-syncid-happens-with-similar-parameter]]
+Disabling syncId happens with similar parameter
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+[source,xml]
+....
+<context-param>
+  <param-name>syncIdCheck</param-name>
+  <param-value>false</param-value>
+</context-param>
+....
+
+If you want to do the above with Java Servlet 3.0 annotations, use the
+following:
+
+[source,java]
+....
+initParams = {
+  @WebInitParam(name = "disable-xsrf-protection", value = "true"),
+  @WebInitParam(name = "syncIdCheck", value = "false")}
+....
+
+[[using-debug-ids-in-communication]]
+Using debug ids in communication
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you want to minimize the effort needed for maintaining your
+scalability tests, you probably want to do a small hack to the Vaadin
+communication mechanism. Normally Vaadin uses a incrementing session
+wide identifier to connect components to their "client side
+counterparts". Thus, if you add a one single component to your login
+screen, the whole load test simulation might be broken.
+
+You can set "id" for each component, but in recent Vaadin versions this
+id is no more used in the communication, but only assigned to
+client dom. This can still be enforced with a specially crafted
+extension to VaadinServlet. An implementation for the "competing tool" JMeter can be
+found at <<jmeter-vaadin-servlet-extension>>. This implementation works for Gatling users
+as well. Note that, it is suggested to do this only for load testing, and NOT
+for the production.
+
+[[ignoring-obsolete-static-file-requests]]
+Ignoring "obsolete" static file requests
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+One of the most simplest and cheapest method to improve your apps
+scalability is to serve static files form a separate server or from a
+CDN provider. Thus it might make sense to leave loading those files away
+from your test script. If you do the script manually, just don't add
+requests for static files (js/css/images/...). If you recorded you test
+case, just remove these form the script. Check out the example project
+that only uses the required files.
+
+[[testing-with-websockets]]
+Testing with WebSockets
+~~~~~~~~~~~~~~~~~~~~~~~
+
+If your want to load test your application with the most advanced
+communication channel, WebSockets, you can do that with Gatling as well.
+Using the recorder in this case doesn't work, but handcrafting the test
+case isn't that hard once you get started. The example app has a branch
+with WebSocket test case. With WebSocket communication it might also be
+handy to disable xsrf preventation and the so called "syncid".
+
+First two request are just normal http requests. The first gets the
+"host page" and also the initial state request is done with normal XHR.
+The difference to normal Vaadin communication is that it is to be sent
+to "/PUSH" address.
+
+After the initial state request you start to use the special WebSocket
+API in Gatling. There are lot of things to keep in mind with this
+fundamentally totally different kind of communication mechanism. Check
+out Gatling's generic websocket help for basic info.
+
+When you start handcrafting the WebSocket simulation, the easiest tool
+is probably Chrome's dev tools. With that you can open normal browser
+session and "sniff" the traffic that is sent to the server and also the
+messages that are received. An easy option is just to copy paste the
+payloads and possibly add some verification to ensure proper answers are
+received. The websocket example is built with special variable to work
+without disabling xsrf verification.
+
+If you are using random input in your load tests, something that is
+highly suggested for realistic numbers, you might end up in small
+problems. The message format, by Atmosphere, has a weird number and "|"
+in front of each message. That number tells the message length and it
+must really match the real message length. Create a simple helper
+function to calculate that if your input data length varies.
+
+[source,javascript]
+....
+import io.gatling.core.session._
+import io.gatling.core.session.el._
+
+def atmoMessage(message: Expression[String]) = message.map(m => m.length + '|' + m)
+
+.sendText(atmoMessage("SomeMessage"))
+....
+
+If (and when) you probably want to close the websocket connection
+cleanly, you need to notify the server with an extra xhr with a
+identifier given by the atmosphere framework. The key is the first
+message that the server sends when you connect to it. 
+
+Check out this script for
+https://github.com/mstahv/v-quiz/blob/master/src/test/scala/loadtest/WebSocketVaadinSimulation.scala[an
+example using WebSocket] communication. It also saves XSRF preventation
+key to variable, so it don't need it to be disabled from the server.
+
+[[configuring-gatling-to-the-web-app-build]]
+Configuring Gatling to the Web app build
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is a good habit to keep your tests in the same projects as your
+actual application. Then you can easily verify your application still
+scales, after you have for example written a cryptic SQL query.
+
+Even better if you can make a Gatling script to be executed during
+builds to make. Gatling has http://gatling.io/docs/current/extensions/maven_plugin/[a
+maven plugin] that can do exactly this thing.
+https://github.com/mstahv/gatling-vaadin-example[The example project
+setup] executes a test during basic "mvn install". With similar setup in
+a real project, your CI server most likely saves results stored under
+target directory. This way it is easy to check it out afterwards how the
+performance of your application has evolved during its development.
+
+[[jmeter-vaadin-servlet-extension]]
+JMeter Vaadin Servlet extension
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The implementation referred to in <<using-debug-ids-in-communication>>
+
+[source,java]
+....
+package com.example.vaadin7jmeterservlet;
+
+import com.vaadin.server.ClientConnector;
+import com.vaadin.server.DeploymentConfiguration;
+import com.vaadin.server.ServiceException;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.VaadinService;
+import com.vaadin.server.VaadinServlet;
+import com.vaadin.server.VaadinServletService;
+import com.vaadin.server.VaadinSession;
+import com.vaadin.ui.Component;
+
+/**
+ * @author Marcus Hellberg (marcus@vaadin.com)
+ *  Further modified by Johannes Tuikkala (johannes@vaadin.com)
+ */
+public class JMeterServlet extends VaadinServlet {
+  private static final long serialVersionUID = 898354532369443197L;
+
+  public JMeterServlet() {
+    System.setProperty(getPackageName() + "." + "disable-xsrf-protection",
+        "true");
+  }
+
+  @Override
+  protected VaadinServletService createServletService(
+          DeploymentConfiguration deploymentConfiguration)
+          throws ServiceException {
+      JMeterService service = new JMeterService(this, deploymentConfiguration);
+      service.init();
+
+      return service;
+  }
+
+  private String getPackageName() {
+      String pkgName;
+      final Package pkg = this.getClass().getPackage();
+      if (pkg != null) {
+        pkgName = pkg.getName();
+      } else {
+        final String className = this.getClass().getName();
+        pkgName = new String(className.toCharArray(), 0,
+            className.lastIndexOf('.'));
+      }
+      return pkgName;
+  }
+
+  public static class JMeterService extends VaadinServletService {
+      private static final long serialVersionUID = -5874716650679865909L;
+
+      public JMeterService(VaadinServlet servlet,
+              DeploymentConfiguration deploymentConfiguration)
+              throws ServiceException {
+        super(servlet, deploymentConfiguration);
+      }
+
+      @Override
+      protected VaadinSession createVaadinSession(VaadinRequest request)
+              throws ServiceException {
+        return new JMeterSession(this);
+      }
+  }
+
+  public static class JMeterSession extends VaadinSession {
+    private static final long serialVersionUID = 4596901275146146127L;
+
+    public JMeterSession(VaadinService service) {
+      super(service);
+    }
+
+    @Override
+    public String createConnectorId(ClientConnector connector) {
+      if (connector instanceof Component) {
+        Component component = (Component) connector;
+        return component.getId() == null ? super
+            .createConnectorId(connector) : component.getId();
+      }
+      return super.createConnectorId(connector);
+    }
+  }
+}
+....
-- 
cgit v1.2.3

