TAG | RBU
When setting up monitoring jobs, there are often predictable time periods in which you want to change the behavior of a script or prevent it from running at all, without having to manually stop/start the monitoring job each time. For instance, you might want to prevent errors and alert emails during routine maintenance windows, or perhaps you’re only interested in site performance on weekdays during regular business hours. We’ve come up with solutions to a few common situations that will help jump start your scripts.
Overview
At the core of all these examples is a small configuration object that sits at the top of the script for quick changes, and a single function that returns true or false depending on whether or not the current time is within a blackout period. The following is some pseudo-code outlining one way in which you might choose to implement these examples in your own scripts. Don’t forget to add in the appropriate maintenance object and isInMaintenance() function from the examples below to make this work.
// The configuration object for the maintenance window var maintenance = {...} // The maintenance function (plug in the appropriate function) var isInMaintenance = function() { ... } var selenium = browserMob.openBrowser(); if(!browserMob.isValidation() && isInMaintenance()) { // Active maintenance window browserMob.log("Is in maintenance!"); } else { // Run normal script browserMob.beginTransaction(); browserMob.beginStep("Step 1"); selenium.open("http://google.com/search?q=website+monitoring"); ... browserMob.endStep(); browserMob.endTransaction(); }
Web Page Check
The most straight forward way to check for a maintenance window is to have Selenium load a web page and look for an element that’s only present during scheduled down time. You’ll want to be careful not to choose something that is also present when the site is really in trouble or you will never get your alerts!
var maintenance = { check: { url: "http://example.com/home", element: "//img[@title=\"maintenance\"]" } } var isInMaintenance = function() { if(maintenance.check && maintenance.check.url != null && maintenance.check.element != null) { selenium.open(maintenance.url); return selenium.isElementPresent(maintenance.element); } return false; }
Multipe Date Ranges
One of the most common requests we get is for a way to handle scheduled maintenance windows. These windows tend to be one-off date ranges decided well in advance of the actual event. Given a set of start/end dates, you could prevent monitoring errors and email alerts during the blackout periods. When setting the dates, don’t forget to include the time zone abbreviation in the argument string or the script will use the local time of the machine it’s running on.
var maintenance = { dates: [ {start: new Date("05/12/2010 23:00 PDT"), end: new Date("05/13/2010 02:30 PDT")}, {start: new Date("05/24/2010 23:00 PDT"), end: new Date("05/25/2010 02:30 PDT")} ] } var isInMaintenance = function() { var now = new Date(), nowTime = now.getTime(), maintenanceWindows = []; // Check for maintenance date ranges if(maintenance.dates && maintenance.dates.length) { maintenanceWindows = maintenance.dates; } for(var i in maintenanceWindows) { var m = maintenanceWindows[i]; if(nowTime >= m.start.getTime() && nowTime < m.end.getTime()) { return true; } } return false; }
Days of the Week
Here’s an example for blacking out specific days of the week. This will allow you to create a script that, for example, monitors on Mondays, Wednesdays, and Fridays, or only on business days. Simply provide a time zone offset, which is the number of minutes your time zone deviates from coordinated universal time (a.k.a. UTC, GMT, Zulu time), and the days of the week you don’t want to run the script (from 0-6, where 0 is Sunday, 1 is Monday, etc.).
var maintenance = { timezoneOffset: 240, // UTC offset in minutes (eg: NYC (GMT-4) is 4 * 60 days: [0, 6] // Sunday = 0, Saturday = 6 } var isInMaintenance = function(){ var now = new Date(), nowTime = now.getTime(), timezoneOffset = maintenance.timezoneOffset || 0, localTimezoneOffset = now.getTimezoneOffset(), timezoneDiff = localTimezoneOffset - timezoneOffset; // Check for full blackout days (such as weekends) if(maintenance.days != null && maintenance.days.length) { var targetDate = new Date(nowTime + (timezoneDiff * 60 * 1000)), targetDay = targetDate.getDay(); for(var i in maintenance.days) { if(maintenance.days[i] == targetDay) { return true; } } } return false; }
Time of Day
This example shows how you can have a daily blackout range. This code will parse the start/end strings into a daily time range adjusted to the target time zone. Use this if you only want to monitor during banker’s hours, or if you want to prevent a script from running while your database is backing up.
var maintenance = { timezoneOffset: 240, // UTC offset in minutes (eg: NYC (GMT-4) is 4 * 60 hours: {start: "23:30", end: "07:30"} } var isInMaintenance = function(startTime, endTime, offset) { var offset = maintenance.timezoneOffset || 0; var now = new Date(); var nowTime = now.getTime(); var maintenanceWindows = []; var toUtc = function(localTime) { var chunks = localTime.toString().split(":"); var h = parseInt(chunks[0], 10); var m = parseInt(chunks[1], 10) || 0; var d = new Date(); d.setHours(h); d.setMinutes(m + offset); return d.getHours() + ":" + d.getMinutes(); } var sH, eH, sM, eM; startTime = toUtc(maintenance.hours.start); var chunks = startTime.toString().split(":"); sH = parseInt(chunks[0], 10); sM = parseInt(chunks[1], 10) || 0; endTime = toUtc(maintenance.hours.end); chunks = endTime.toString().split(":"); eH = parseInt(chunks[0], 10); eM = parseInt(chunks[1], 10) || 0; var startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), sH, sM, 0, 0); var endDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), eH, eM, 0, 0); if(startDate.getTime() <= endDate.getTime()) { // Single range mid-day maintenanceWindows.push({start: startDate, end: endDate}); } else { // Date wraps, which means two blackout ranges in a single day var morning = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0), midnight = new Date(morning.getTime() + (24 * 60 * 60 * 1000)); maintenanceWindows.push({ start: morning, end: endDate }); maintenanceWindows.push({ start: startDate, end: midnight }); } for(var i in maintenanceWindows) { var m = maintenanceWindows[i]; if(nowTime >= m.start.getTime() && nowTime < m.end.getTime()) { return true; } } return false; }
Now, a few of you might have noticed that the above examples are compatible with one another; you can mix-and-match them as you see fit. Just combine the various maintenance object fields into one config object, combine the contents of all the maintenance methods you want to use (just watch not to overwrite the variable declarations), and you can create something that fits just about any situation.
downtime · javascript · maintenance · maintenance windows · Monitoring · RBU · Scripting · scripts
7
FAQ: What’s the difference between a Real Browser User and a Virtual User?
No comments · Posted by Patrick Lightbody in FAQ
At BrowserMob we have two different levels of load testing support: one for Real Browser Users (RBUs) and one for Virtual Users (VUs). Often it’s not immediately clear what the difference is and why you’d use one over the other.
RBU Benefits
The high level difference is this: an 100 RBU test means that there will be 100 browsers in the cloud all loading your site and interacting with it, while a 100 VU test means that there will be 100 “threads” issuing HTTP GET and POST requests to your website, but not actually running a browser. This means that RBUs:
- Check functionality – they don’t just make HTTP requests, they validate if the application is actually working as an end-user would expect it to.
- Provide better error reporting – because there is an actual browser on a desktop running, we can take a screenshot to show you what the failure looked like.
- Are more realistic – today’s browsers load up to 15 objects at a time and have very unique HTTP request patterns that are almost impossible to simulate without a real browser.
- Use a wide range of IP addresses – because RBUs need a lot of resources, we allocate between 1 and 6 browsers per IP.
- Can measure real user wait time – sometimes the UI can’t be used until multiple overlapping AJAX calls are finished. Only a real browser can tell you when the UI is ready for interaction.
RBU Cost
When it comes to load testing website applications, RBUs are superior in almost every way. However, with all that realism and IP address distribution comes a massive hardware requirement: for every RBU in a test we allocate a full CPU core and 1.5GB+ of RAM. That means that a 100 RBU test requires at least 100 CPU cores and 150GB of RAM.
Compared to traditional load testing, which only requires half a CPU core or less and 256MB of RAM, it’s very different. This resource-hungry nature of RBUs means that they cost 10X more than our Virtual User service. That means a 100 RBU test will cost the same as a 1000 VU test. As such, depending on your budget, it may make sense to use a mixture of RBUs and VUs.
Scripting Effort
While RBUs are more realistic but cost a bit more, it also helps to understand the level of effort to write and maintain RBY and VU scripts. Remember that RBU scripts interact at the application level, while VU scripts interact at the protocol level.
Consider this RBU script:
var selenium = browserMob.openBrowser(); selenium.open("http://google.com/"); selenium.type("q", "BrowserMob"); selenium.click("btnG"); selenium.waitForPageToLoad(30000); if (!selenium.isTextPresent("BrowserMob")) { throw "BrowserMob text not found"; }
Written as a VU script, each and every HTTP request would need to be scripted:
var c = browserMob.openHttpClient(); // get the home page and verify we got a 200 OK c.get("http://www.google.com/", 200); c.get("http://www.google.com/intl/en_ALL/images/logo.gif"); c.get("http://www.google.com/extern_js/f/.../Y3ut8jJ-OXk.js"); c.get("http://www.google.com/extern_chrome/be5ffa94030d2d34.js"); c.get("http://clients1.google.com/generate_204"); c.get("http://www.google.com/images/nav_logo7.png"); c.get("http://www.google.com/csi?v=3&s=webhp&action=&e=...&rt=..."); // simulate the AJAX event for the Google auto-complete // also verify a 200 OK response c.get("http://clients1.google.com/complete/search?hl=en&client=hp&q=BrowserMob&cp=27", 200); // simulate searching // also verify a 200 OK response var resp = c.get("http://www.google.com/search?hl=en&source=hp&q=BrowserMob&btnG=...", 200); // get the other requests caused by the new page load c.get("http://www.google.com/images/nav_logo7.png"); c.get("http://id.google.com/verify/EAAAAECIWFsnXJixiQogbBCZlzA.gif"); c.get("http://www.google.com/csi?v=3&s=web&action=&ei=slYdS_-MGpXKsAPngsX8BA&e=..."); c.get("http://clients1.google.com/generate_204"); // finally, validate the browsermob was in the main HTTP request if (resp.getBody().indexOf("BrowserMob") == -1) { throw "BrowserMob text not found"; }
When to Use Virtual Users
As you can imagine, writing and maintaining VU scripts is a lot more work. However, that doesn’t mean VUs are always a bad idea. In fact, they are great for:
- Placing a large amount of low-cost “dumb” load on a website. For example, when trying to saturate simple hits again a home page.
- When testing a web service, such as REST or SOAP, that can’t be directly tested with a web browser.
- When budget constraints require VUs
As a general rule of thumb, if your own hourly cost to write a VU script is more than it would be to quickly run an RBU test, you should probably go with an RBU. However, if it’s an extremely large scale test, VUs may be worth considering for a good portion of the load.
Don’t Forget!
Finally, don’t forget that there is no reason you can’t run two load tests at the same time:
- A smaller RBU test that lets you measure what the real user experience is like
- A larger VU test that hammers at the site in a cost-effective way
This technique allows you to stay within your budget without giving up entirely on the benefits of RBU scripts.
