Appium Load Balancer
Table of Contents
Overview
Appium Load Balancer, also known as AppiumLB, is HeadSpin’s solution to more efficient device management when testing with Appium and Selenium tests. Identifying and tracking each individual test target, particularly when you want your tests to cover a variety of geographical regions and device SKUs, can be time-consuming and frustrating. AppiumLB does not function like a traditional load balancer program handling incoming network traffic; rather, it simulates that same balancing logic to API commands and device UDIDs or WebDriver URLs to cut down on time and effort spent on device or host management.
AppiumLB is a single, generic Web Driver URL granting access to any HeadSpin devices available to you. It functions somewhat like a proxy server in that all API commands will touch AppiumLB before touching your device hosts, and AppiumLB will select devices at random that meet your test criteria. Test devices are specified through the new <code class="dcode">headspin:selector</code> capability, using the selector syntax as described in the Selectors DSL docs. AppiumLB automatically picks the proper devices and start a new Appium session with that device's <code class="dcode">udid</code>, <code class="dcode">headspin:appiumVersion</code> and the original <code class="dcode">headspin:selector</code> capabilities. AppiumLB also automatically picks a proper browser device with <code class="dcode">browserName</code>, <code class="dcode">browserVersion</code> and the original <code class="dcode">headspin:selector</code> capabilities for a Selenium session. Using AppiumLB, your testing experience will have minimal incidents of tests failing due to offline or unresponsive devices, reserved devices that are unavailable, or user error in identifying devices. Every request, barring user error in its design, will gain some kind of response and cut down on test troubleshooting. Please note that, as described in the Capabilities section below, you can specify a particular device using the <code class="dcode">udid</code> capability.
Using AppiumLB
To use AppiumLB, set your Web Driver URL as <code class="dcode">https://appium-dev.headspin.io/v0/your-api-token/wd/hub</code> and make sure that the <code class="dcode">headspin:selector</code> capability reflects target devices for your test. The Web Driver URL is available as Load Balanced Driver URL in the automation configuration.
AppiumLB will look for a device or devices matching the selectors, lock the device for automation and start an Appium or a Selenium session against the device's host server. It then returns a response containing the <code class="dcode">sessionId</code> of the newly created session, where you can send Appium or Selenium commands.
Retry and avoid previously failed devices
AppiumLB will retry establishing a new session against available devices in the device pool up to the value of <code class="dcode">appium:newCommandTimeout</code> (or <code class="dcode">headspin:newCommandTimeout</code>) before responding to the client with a timeout error. As part of the new session request, if <code class="dcode">headspin:waitForDeviceOnlineTimeout</code> is specified, AppiumLB will wait for a device to be available up to this value. AppiumLB will keep the http connection between AppiumLB and a selected Pbox alive without communication for up to the <code class="dcode">newCommandTimeout</code> minus the time taken to acquire the selected device. I.e., <code class="dcode">newCommandTimeout</code> is the max time that a new session request will be allowed to take, including time finding an appropriate device. The client should always keep the client-side HTTP request timeout to a value higher than <code class="dcode">appium:newCommandTimeout</code> (or <code class="dcode">headspin:newCommandTimeout</code>), plus a few buffer seconds such as 30 additional seconds, to ensure that it will receive timeout errors. If the client-side HTTP request timeout is lower, the client script will disconnect and it will not be possible to handle timeout-related errors. <code class="dcode">headspin:ignoreFailedDevice</code>, when set to False, does nothing; when set to True, this capability will ignore failed-connection devices when attempting to secure an available device (note that by default this value is set to True). As an example of AppiumLB securing an available device, if a given device is unlocked around 20 seconds before the request to find a free device is issued, and the new session request takes 150 seconds against the free device, then the request could take 170 seconds in total.
Each retry will avoid previously failed devices if the cause is a device-side issue to make the session creation reliable. For instance, if a device fails due to an unexpected disconnection in the new session request, AppiumLB will avoid the device for selection for the next 10 minutes. AppiumLB can be configured to skip this avoidance behavior with the capability <code class="dcode">headspin:ignoreFailedDevice</code>. Please note that at this time there is no action that can be taken to manually force-stop new session request timeout.
If Appium fails to find an available device in your device pool with the given <code class="dcode">appium:udid</code> or <code class="dcode">headspin:selector</code>, it will respond with an error message that starts with No matched devices were found. It indicates the given udid or selector syntax was wrong or the syntax was correct but no matched devices existed in your device pool.
If AppiumLB fails to find an available device in your device pool, it will respond with an error message that starts with No available good condition devices ... "No available devices" indicates devices are offline, are already locked by a user, must be reserved by you, or failed against a new session request. AppiumLB will respond with an error message if the last retry fails in a new session request. <code class="dcode">headspin:retryNewSessionFailure</code> and <code class="dcode">headspin:waitForDeviceOnlineTimeout</code> help you to control the retry rule.
If no devices are available in the device pool after excluding previously failed devices then AppiumLB will select a device from the device pool without considering the previously failed devices. This behavior helps reduce obstacles in the case of your device pool containing few devices for the given selector(s). The selected device may manifest an error due to its previously failed state.
The error previously failed devices only indicates devices for which AppiumLB failed to create a new session due to a problem on the device. Errors due to other causes (such as invalid Appium capabilities) do not cause devices to be marked as "previously failed".
<code class="dcode">message</code> key in the error response could include past error messages up to 10 latest cases to help error investigation.
AppiumLB Capabilities
In addition to the capabilities in Appium Capabilities docs and Selenium Capabilities docs, AppiumLB also support these capabilities:
AppiumLB will restrict target devices if the given capabilities includes <code class="dcode">platformName</code>. If the capability specifies <code class="dcode">Android</code>, AppiumLB will choose a device from Android devices. The capability is necessary for Appium, but not for Selenium. <code class="dcode">browserName</code> and <code class="dcode">browserVersion</code> capabilities will be part of selector for Selenium to detect a browser device.
Examples
Appium
- Python
- Ruby Core
Selenium
- Python
- Ruby
Resolving Errors
AppiumLB returns standard errors by Appium instances on proxy servers, and also the following:
Advanced Usage
Increasing Network Efficiency With directConnect Capabilities
With AppiumLB, a request goes through an additional AppiumLB server and therefore takes a small amount of additional network time, usually of about 5-10 seconds but potentially up to a minute (see this issue Pro for more details). If this presents a problem, the <code class="dcode">directConnect</code> capabilities can be used to speed up network time. To use this, your Appium client must support the <code class="dcode">directConnect</code> capabilities and the <code class="dcode">directConnect</code> capability must be set to <code class="dcode">true</code>. Currently only limited clients support this capbility. Please work with your internal IT departments and HeadSpin contacts to determine whether directConnect is a possibility for your testing environment.
A successful request with <code class="dcode">directConnect</code> made to AppiumLB will receive a <code class="dcode">create session</code> response, with information about the session through the <code class="dcode">directConnect</code> capabilities. The capabilities contain hostname, port and path to the actual Appium server with the devices selected for the tests. They are:
With this information, your Appium client can be configured to communicate with the test device directly.
Example Requests
Below are example requests sent with <code class="dcode">directConnect</code>.
Python
● Python client <code class="dcode">0.39+</code>
- documentation
- <code class="dcode">direct_connection</code> is enabled by default.
Ruby
● ruby_lib <code class="dcode">10.0.0+</code>
● ruby_lib_core <code class="dcode">3.0.1+</code>
- documentation
- <code class="dcode">direct_connect</code> is enabled by default.
Java
● Java client <code class="dcode">8.2.1+</code>
- Disabled by default
JavaScript (webdriverio)
● webdriverio <code class="dcode">v7.16.14+</code>
- enableDirectConnect option configures the availability.
- <code class="dcode">enableDirectConnect</code> is enabled by default.
Example Responses
Below are some example responses as W3C or MJOSNWP with a session creation command.
W3C create session response
MJSONWP create session response
Selenium Grid and AppiumLB
Selenium Grid (hub) and AppiumLB behave similarly.
Selenium Grid has a hub and nodes. A node is a Selenium driver or an Appium server hosting a device. The hub handles a new session request to a proper node in order to establish a session following the capabilities. The node responds to the new session request with a session ID. Then, the hub proxies requests that have the same session ID to the proper node. This helps clients handle various nodes behind the hub since they need to know only one WebDriver URL through the hub. Clients do not need to know each WebDriver URL behind the hub for each separate Selenium driver and Appium server.
The Selenium Grid hub and node behavior and relationships are similar to AppiumLB and available devices in your device pool. AppiumLB picks a device up from your device pool in a new session request. It has the ability to wait for a device to be available using the <code class="dcode">waitForDeviceOnlineTimeout</code> capability. Once the new session request succeeds, commands for the same session ID can reach the device until the session is expired. One unique feature in AppiumLB is its directConnect capabilities to reduce network latency between the client and the device after a new session request. This feature allows clients to communicate with each PBox directly after a new session request. With direct connect, only the first new session request may have additional network latency due to routing through the load balancer.
A client needs to keep the HTTP connection to get a response by Selenium Grid or AppiumLB. A new session request tends to have a longer timeout than other commands since it includes some device setup processes. Client read timeouts before getting the response for a new session request can cause a new session creation failure. <code class="dcode">newSessionWaitTimeout</code> in Selenium Grid is similar to <code class="dcode">waitForDeviceOnlineTimeout</code> in AppiumLB to attempt to find an available node or device to coordinate the new session request.