 <?xml-stylesheet type="text/css" href="https://www.esdm.co.uk/Data/style/rss1.css" ?> <?xml-stylesheet type="text/xsl" href="https://www.esdm.co.uk/Data/style/rss1.xsl" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
  <channel>
    <title>The knowledge base blog</title>
    <link>https://www.esdm.co.uk/knowledge</link>
    <description />
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>mojoPortal Blog Module</generator>
    <language>en-GB</language>
    <ttl>120</ttl>
    <atom:link href="https://www.esdm.co.uk/Blog/RSS.aspx?p=138~108~38" rel="self" type="application/rss+xml" />
    <itunes:owner />
    <itunes:explicit>no</itunes:explicit>
    <item>
      <title>How to remove .png files recursively from a folder tree without removing .png8 files too</title>
      <description><![CDATA[<p>We have some large GeoWebCache caches of Ordnance Survey map tiles in .png8 format. Unfortunately a few million .png files have crept into the caches too (accidental bad config at some point), duplicating the .png8 tiles and using up a lot more space. I wanted to delete these.</p>

<p>At first I thought "simple:-&nbsp;do a search in Windows Explorer for .png and delete". Ah no, that selects all the .png8 files as well, plus my number of folders and files were far too large.</p>

<p>Then I thought... <code>del /S *.png</code></p>

<p>But no, this deletes the .png8 files as well, grrr.</p>

<p>So I wrote a simple python script, that I will lodge here for future reference. It's Python 2.7, but I imagine would work in 3.* with little or no change. The final print statement probably slows things down, but I wanted to be able to see what was going on.</p>

<pre>
import os
indir = 'M:\\MyVeryLargeCachePath'
for root, dirs, filenames in os.walk(indir):
    for f in filenames:
        if os.path.splitext(f)[1] == '.png':
            os.remove(os.path.join(root, f))
            print('deleted ' + os.path.join(root, f))</pre>

<p>Assuming you have Python installed, simply save this as a file with a .py extension, modify the path and the file extensions to suit, then run it.</p>

<p>This same technique would work for other combinations of file extensions, e.g. to remove *.doc but not *.docx, or to remove *.xls while retaining *.xslx</p>
<br /><a href='https://www.esdm.co.uk/how-to-remove-png-files-recursively-from-a-folder-tree-without-removing-png8-files-too'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/how-to-remove-png-files-recursively-from-a-folder-tree-without-removing-png8-files-too'>...</a>]]></description>
      <link>https://www.esdm.co.uk/how-to-remove-png-files-recursively-from-a-folder-tree-without-removing-png8-files-too</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/how-to-remove-png-files-recursively-from-a-folder-tree-without-removing-png8-files-too</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/how-to-remove-png-files-recursively-from-a-folder-tree-without-removing-png8-files-too</guid>
      <pubDate>Wed, 27 May 2020 09:09:00 GMT</pubDate>
    </item>
    <item>
      <title>Find "Godzillas" in your ArcGIS feature class (by counting vertices)</title>
      <description><![CDATA[<p>To count the number of vertices:</p>

<ul>
	<li>Add a new field to the feature class called, for example, VertexCount</li>
	<li>Use the Calculate Field tool within the attribute table, or in ArcToolbox, with the following Python expression - !shape!.pointcount</li>
	<li>Sort the feature class on the VertexCount field to find the worst offenders. If there is any bad geometry the expression may trip up, which is informative in itself.</li>
	<li>You can now select only the most complex features for generalising.</li>
</ul>

<p>See the following article for more information. I have not tried the dicing method that is discussed later in the article: <a href="http://blogs.esri.com/esri/arcgis/2010/07/23/dicing-godzillas-features-with-too-many-vertices/">http://blogs.esri.com/esri/arcgis/2010/07/23/dicing-godzillas-features-with-too-many-vertices/ </a></p>

<p>&nbsp;</p>
<br /><a href='https://www.esdm.co.uk/find-godzillas-in-your-arcgis-feature-class-by-counting-vertices'>Sylvina Tilbury</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/find-godzillas-in-your-arcgis-feature-class-by-counting-vertices'>...</a>]]></description>
      <link>https://www.esdm.co.uk/find-godzillas-in-your-arcgis-feature-class-by-counting-vertices</link>
      <author>sylvinat@esdm.co.uk (Sylvina Tilbury)</author>
      <comments>https://www.esdm.co.uk/find-godzillas-in-your-arcgis-feature-class-by-counting-vertices</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/find-godzillas-in-your-arcgis-feature-class-by-counting-vertices</guid>
      <pubDate>Wed, 15 May 2013 11:07:00 GMT</pubDate>
    </item>
    <item>
      <title>Tilecache: how to stop tilecache_seed.py bailing out with HTTP 502 errors</title>
      <description><![CDATA[<p>We are using TileCache <a title="http://tilecache.org/" href="http://tilecache.org/">http://tilecache.org/</a> to create caches of Ordnance Survey data of various flavours, for use in high demand web sites. We are tending to pre-seed the caches, then access them directly from disk, as this gives the best overall performance. However building the caches for the larger map scales is a significant task, requiring many days of processing and hundreds of gigabytes of storage.</p> <p>This post addresses a particular problem – the pre-seeding routines failing intermittently with HTTP 502 errors.</p> <p>Pre-seeding is carried out on the command line, issuing a command like this:</p> <p>D:\Websites\UKBaseMap\scripts\tilecache\tilecache-2.11\tilecache_seed.py --bbox=0,0,600000,1300000 OSOpenOSGB 0 10</p> <p>where OSOpenOSGB refers to a config section in tilecache.cfg (which in turn points at the WMS), and “0 10” means process levels 0 to 9.</p> <p>The normal tilecache_seed.py looks like this:</p> <p>================</p><pre class="csharpcode"><span class="rem">#!/usr/bin/env python</span>

<span class="rem"># BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors</span>
<span class="str">""</span><span class="str">"This is intended to be run as a command line tool. See the accompanying
   README file or man page for details."</span><span class="str">""</span>

import TileCache.Client

TileCache.Client.main()</pre>
<p>================
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
</p>
<p>But we have been finding that this fails intermittently with errors being raised, like:</p>
<p><font size="2" face="Courier New">urllib2.HTTPError: HTTP Error 502: Bad Gateway</font><br></p>
<p>We do not know the root cause, but apparently the CGI MapServer WMS is simply failing to respond sometimes, and unfortunately this causes the cache seeding process to stop. We are seeing this at varying intervals, from every 5 minutes to every couple of hours. Enough to completely disrupt the construction of a large cache.</p>
<p>So, in my first foray into Python goodness, I’ve enhanced tilecache_seed.py a little to recover from the failures and continue until the caching finished without an error:</p>
<p>================</p><pre class="csharpcode"><span class="rem">#!/usr/bin/env python</span>

<span class="rem"># BSD Licensed, Copyright (c) 2006-2010 TileCache Contributors</span>
<span class="str">""</span><span class="str">"This is intended to be run as a command line tool. See the accompanying
   README file or man page for details."</span><span class="str">""</span>

<span class="rem"># amended CF 22/2/2012 to resume after errors urllib2.HTTPError: HTTP Error 502: Bad Gateway</span>
import time
import TileCache.Client
i = 1
<span class="kwrd">while</span> i &gt; 0:
    try:
        i = 0
        TileCache.Client.main() <span class="rem"># do the work</span>
    except:
        i = 1
        print <span class="str">'HTTPError occurred - resuming processing...'</span>
        f = open(<span class="str">'errorlog.txt'</span>, <span class="str">'a'</span>) <span class="rem"># open file for appending</span>
        f.write(<span class="str">'HTTP Error occurred at: '</span> + time.strftime(<span class="str">'%x %X'</span>) + <span class="str">'\n'</span>)
        f.close()
        time.sleep( 1 ) <span class="rem"># have a rest to let MapServer recover from whatever was troubling it</span>
        <span class="kwrd">continue</span> <span class="rem"># resume the loop</span>
    <span class="kwrd">else</span>:
        print <span class="str">'Processing completed!'</span> # no error was encountered</pre>
<p>================
<style type="text/css">.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
</style>
</p>
<p>This also logs the occurrence of errors into a log file like this:</p>
<p>HTTP Error occurred at: 02/23/12 23:50:00</p>
<p>The caching now bashes on regardless of intermittent errors. However, best to avoid the –f flag on the command line as this will re-start the job from the beginning and re-create all tiles, therefore may never complete if errors are occurring regularly.</p><br /><a href='https://www.esdm.co.uk/tilecache-how-to-stop-tilecache_seedpy-bailing-out-with-http-502-errors'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/tilecache-how-to-stop-tilecache_seedpy-bailing-out-with-http-502-errors'>...</a>]]></description>
      <link>https://www.esdm.co.uk/tilecache-how-to-stop-tilecache_seedpy-bailing-out-with-http-502-errors</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/tilecache-how-to-stop-tilecache_seedpy-bailing-out-with-http-502-errors</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/tilecache-how-to-stop-tilecache_seedpy-bailing-out-with-http-502-errors</guid>
      <pubDate>Fri, 24 Feb 2012 00:31:05 GMT</pubDate>
    </item>
    <item>
      <title>Serving Ordnance Survey rasters with GeoServer and MapServer - some irks and quirks</title>
      <description><![CDATA[<p>
	I’ve been preparing a <a href="http://tilecache.org/" target="_blank" title="http://tilecache.org/">tilecache</a> of UK Ordnance Survey mapping for a couple of big projects, combining Open Data with licensed Landranger (1:50,000) and Explorer (1:25,000) mapping. The tilecache utility consumes and generates its cache from a WMS of the mapping, and fir this I’ve done most of the work using MapServer. However I’ve hit some stubborn problems with a few areas of mapping, for example this area where I have a blank strip enclosing a tile boundary. The original tiles are fine, but for some reason when render by MapServer we get this blank area.</p>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_3.png"><img alt="Blank strip in Ordnance Survey 50K WMS from MapServer" border="0" height="204" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_4.png" style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="Blank strip in Ordnance Survey 50K WMS from MapServer" width="244" /></a></p>
<p>
	In another couple of areas, the WMS requests to MapServer simply failed with this error sequence:</p>
<p>
	<font face="Courier New" size="1">[Sat Jan 21 09:30:02 2012].785000 msDrawRasterLayerLow(OS50KColourRaster): entering.<br />
	[Sat Jan 21 09:30:02 2012].791000 msResampleGDALToMap in effect: cellsize = 5.000000<br />
	[Sat Jan 21 09:30:02 2012].792000 msDrawGDAL(OS50KColourRaster): using RAW_WINDOW=3509 0 491 652, dst=0,0,491,652<br />
	[Sat Jan 21 09:30:02 2012].792000 msDrawGDAL(): red,green,blue,alpha bands = 1,0,0,0<br />
	[Sat Jan 21 09:30:02 2012].891000 msResampleGDALToMap in effect: cellsize = 5.000000<br />
	[Sat Jan 21 09:30:02 2012].892000 msDrawGDAL(OS50KColourRaster): using RAW_WINDOW=3509 3348 491 652, dst=0,0,491,652<br />
	[Sat Jan 21 09:30:02 2012].892000 msDrawGDAL(): red,green,blue,alpha bands = 1,0,0,0<br />
	[Sat Jan 21 09:30:02 2012].998000 msResampleGDALToMap in effect: cellsize = 5.000000<br />
	[Sat Jan 21 09:30:02 2012].998000 msDrawGDAL(OS50KColourRaster): using RAW_WINDOW=0 0 812 652, dst=0,0,812,652<br />
	[Sat Jan 21 09:30:02 2012].998000 msDrawGDAL(): red,green,blue,alpha bands = 1,0,0,0<br />
	[Sat Jan 21 09:30:03 2012].122000 msResampleGDALToMap in effect: cellsize = 5.000000<br />
	[Sat Jan 21 09:30:03 2012].122000 msDrawGDAL(OS50KColourRaster): using RAW_WINDOW=0 3348 812 652, dst=0,0,812,652<br />
	[Sat Jan 21 09:30:03 2012].122000 msDrawGDAL(): red,green,blue,alpha bands = 1,0,0,0<br />
	[Sat Jan 21 09:30:03 2012].148000 GetBlockRef failed at X block offset 0, Y block offset 499: Unable to access file. GDALDatasetRasterIO() failed: drawGDAL()<br />
	[Sat Jan 21 09:30:03 2012].149000 msDrawMap(): Image handling error. Failed to draw layer named 'OS50KColourRaster'.<br />
	[Sat Jan 21 09:30:03 2012].149000 freeLayer(): freeing layer at 024265B0.</font></p>
<p>
	again the underlying images were apparently fine.</p>
<p>
	And of course this has left gaps in the tile cache.</p>
<p>
	So, in order to overcome these issues I am setting up the WMS using GeoServer to see if that can fill the gaps.</p>
<p>
	My starting point is a folder containing the TIFF images, each with a .TFW world file.</p>
<p>
	Gotchas:</p>
<p>
	There are <a href="http://docs.geoserver.org/stable/en/user/tutorials/image_mosaic_plugin/imagemosaic.html" target="_blank" title="instructions here">instructions for setting up a raster WMS in GeoServer here</a>, but here is my summary with details of where things can go wrong:</p>
<ul>
	<li>
		Create a workspace, in my case “ESDM_UK_BaseMaps” with a namespace of “<a href="https://www.ordnancesurvey.co.uk/" title="https://www.ordnancesurvey.co.uk/">https://www.ordnancesurvey.co.uk/</a>”</li>
	<li>
		Create a store – this will equate with one type of map, in this case the Landranger 1:50,000 colour raster.</li>
	<li>
		Choose ImageMosaic as the type of store</li>
	<li>
		Select the workspace, and give the store a name, in this case “OS50K”</li>
	<li>
		For the connection parameters, enter the folder that contains the images, in my case “file:D:\MapData\OrdnanceSurvey\50KRasterColour”</li>
	<li>
		GeoServer will then automatically scan the folder and create a shapefile index for the tiles (or “granules” as they bizarrely call them). At this point I was confronted with this error message “Could not list layers for this store, an error occurred retrieving them: Unable to acquire a reader for this coverage with format: ImageMosaic”.</li>
</ul>
<p>
	This is a commonly observed error by all accounts with lots of fairly grumpy exchanges among GeoServer users and developers about how to reproduce it and what might be the causes. It seems it can be raised by a raft of different issues. So I returned to a close reading of the instructions and also found <a href="http://www.adrianwalker.org/2010/08/using-ordnance-survey-open-data-street.html" target="_blank" title="Adrian Walker’s blog">Adrian Walker’s useful blog</a> post about configuring OS maps in GeoServer.</p>
<p>
	It turns out I had two problems:</p>
<p>
	First the .TFW extension must be lower case. So in a command prompt run</p>
<p>
	<font face="Courier New" size="2">ren *.TFW *.tfw</font></p>
<p>
	(note that the image file extension does not have to be lower case)</p>
<p>
	Second, GeoServer needs a .prj file for every image (not required by MapServer).</p>
<p>
	These can be generated using a python script (thanks to Adrian Walker again for this tip):</p>
<pre class="csharpcode">
import glob
content = <span class="str">'PROJCS["OSGB 1936 / British National Grid", GEOGCS["OSGB 1936", DATUM["OSGB_1936", SPHEROID["Airy 1830",6377563.396,299.3249646, AUTHORITY["EPSG","7001"]], AUTHORITY["EPSG","6277"]], PRIMEM["Greenwich",0, AUTHORITY["EPSG","8901"]], UNIT["degree",0.01745329251994328, AUTHORITY["EPSG","9122"]], AUTHORITY["EPSG","4277"]], UNIT["metre",1, AUTHORITY["EPSG","9001"]], PROJECTION["Transverse_Mercator"], PARAMETER["latitude_of_origin",49], PARAMETER["central_meridian",-2], PARAMETER["scale_factor",0.9996012717], PARAMETER["false_easting",400000], PARAMETER["false_northing",-100000], AUTHORITY["EPSG","27700"], AXIS["Easting",EAST], AXIS["Northing",NORTH]]'</span>
tifs = glob.glob(<span class="str">'*.tif'</span>) 
<span class="kwrd">for</span> tif <span class="kwrd">in</span> tifs:  
    prj = tif.split(<span class="str">'.'</span>)[0] + <span class="str">'.prj'</span>  
    file = open(prj,<span class="str">'w'</span>)  
    file.writelines(content)  
    file.close()</pre>
<p>
	<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }	</style>
</p>
<p>
	After resolving these issues, it was necessary to return to step 2 above to create the store, but I got the error again! OK, GeoServer had created the index shapefile already, and it was bad due to the missing prj and tfw files; unfortunately GeoServer will not overwrite this file so I had to manually delete the files (the shapefile is named after the store, and comprises seven files with various extensions).&nbsp; After that, creating the store worked fine.</p>
<p>
	Hint: if you examine the .prj file in a text editor and it looks like this, then you need to bin it and start again:</p>
<p>
	<font face="Courier New" size="2">LOCAL_CS["Generic cartesian 2D",<br />
	LOCAL_DATUM["Unknow", 0],<br />
	UNIT["m", 1.0],<br />
	AXIS["x", EAST],<br />
	AXIS["y", NORTH]]</font></p>
<p>
	So on clicking “Save” for the new store, it showed a twirly for a couple of minutes while creating the index shapefile, and then transfers you to the New Layer page, showing the new layer that can be published (it names the layer after the folder containing the images). From there it is a simple matter to complete the configuration of the layer.</p>
<p>
	Note: I think I could have configured a single store one folder higher up, and this would have included each subfolder of TIFFs as a separate layer.</p>
<h3>
	Outcome</h3>
<p>
	Once I had finally got my GeoServer WMS up and running it was time to see whether could fills the gaps in my tilecache…. and I’m very pleased to say yes it could!</p>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_4.png"><img alt="1:50K OS map from GeoServer, with no gap" border="0" height="204" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/image_thumb_5.png" style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="1:50K OS map from GeoServer, with no gap" width="244" /></a></p>
<p>
	I’ve posted separately about performance and image quality from the two servers.</p>
<br /><a href='https://www.esdm.co.uk/ordnance-survey-raster-with-geoserver-and-mapserver-some-irks-and-quirks'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/ordnance-survey-raster-with-geoserver-and-mapserver-some-irks-and-quirks'>...</a>]]></description>
      <link>https://www.esdm.co.uk/ordnance-survey-raster-with-geoserver-and-mapserver-some-irks-and-quirks</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/ordnance-survey-raster-with-geoserver-and-mapserver-some-irks-and-quirks</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/ordnance-survey-raster-with-geoserver-and-mapserver-some-irks-and-quirks</guid>
      <pubDate>Sat, 21 Jan 2012 09:46:00 GMT</pubDate>
    </item>
    <item>
      <title>Breaking a site with Multi-Mechanize</title>
      <description><![CDATA[<p>In my previous post I <a title="Trying out Multi-Mechanize web performance and load testing framework" href="trying-out-multi-mechanize-web-performance-and-load-testing-framework" target="_blank">got started with Multi-Mechanize</a> and established that it is a useful tool for load-testing web sites.&nbsp; Next I tried it on a real site that we are currently building – the new “Norfolk Heritage Explorer”. This is a mojoPortal site with exeGesIS plug-in components that fetch data from the “HBSMR Gateway” web service. This is the bit I wanted to test for performance under load.</p> <p>I ran the test against a single page in the new site that loads a record from the web service, with a query on the record ID.</p> <p><a href="http://nhe.no-ip.org/glossary?uid=TNF2069">http://nhe.no-ip.org/glossary?uid=TNF2069</a></p> <p>I didn’t mention in the previous post that you need to watch the “errors” figure. You can see this in the console during the test run, and also at the top of the results html. The first time I ran the test, I got a huge error rate – something like 5000 errors out of 6000 transactions. Therefore I lowered the number of threads to 4, ramping up over 100 seconds.</p> <p>This time I got 474 errors out of 627 transactions, and an interesting pattern emerged:</p><b>transactions:</b> 627<br><b>errors:</b> 474<br><b>run time:</b> 100 secs<br><b>rampup:</b> 100 secs<br><br><b>test start:</b> 2011-12-28 10:39:26<br><b>test finish:</b> 2011-12-28 10:41:04<br><br><b>time-series interval:</b> 10 secs<br><br> <h4>Timer Summary (secs)</h4> <table> <tbody> <tr> <th>count</th> <th>min</th> <th>avg</th> <th>80pct</th> <th>90pct</th> <th>95pct</th> <th>max</th> <th>stdev</th></tr> <tr> <td>623</td> <td>0.170</td> <td>0.473</td> <td>1.022</td> <td>1.300</td> <td>1.580</td> <td>3.174</td> <td>0.521</td></tr></tbody></table> <h4>Interval Details (secs)</h4> <table> <tbody> <tr> <th>interval</th> <th>count</th> <th>rate</th> <th>min</th> <th>avg</th> <th>80pct</th> <th>90pct</th> <th>95pct</th> <th>max</th> <th>stdev</th></tr> <tr> <td>1</td> <td>19</td> <td>1.90</td> <td>0.900</td> <td>1.045</td> <td>1.150</td> <td>1.150</td> <td>1.431</td> <td>1.431</td> <td>0.132</td></tr> <tr> <td>2</td> <td>18</td> <td>1.80</td> <td>1.050</td> <td>1.164</td> <td>1.249</td> <td>1.290</td> <td>1.570</td> <td>1.570</td> <td>0.128</td></tr> <tr> <td>3</td> <td>18</td> <td>1.80</td> <td>1.010</td> <td>1.095</td> <td>1.130</td> <td>1.180</td> <td>1.180</td> <td>1.180</td> <td>0.050</td></tr> <tr> <td>4</td> <td>19</td> <td>1.90</td> <td>1.010</td> <td>1.067</td> <td>1.100</td> <td>1.120</td> <td>1.150</td> <td>1.150</td> <td>0.037</td></tr> <tr> <td>5</td> <td>20</td> <td>2.00</td> <td>0.940</td> <td>0.983</td> <td>1.010</td> <td>1.030</td> <td>1.050</td> <td>1.050</td> <td>0.030</td></tr> <tr> <td>6</td> <td>24</td> <td>2.40</td> <td>1.332</td> <td>1.641</td> <td>1.820</td> <td>1.940</td> <td>1.970</td> <td>2.010</td> <td>0.194</td></tr> <tr> <td>7</td> <td>25</td> <td>2.50</td> <td>1.280</td> <td>1.588</td> <td>1.780</td> <td>1.800</td> <td>1.850</td> <td>1.990</td> <td>0.181</td></tr> <tr> <td>8</td> <td>137</td> <td>13.70</td> <td>0.170</td> <td>0.316</td> <td>0.200</td> <td>0.480</td> <td>1.440</td> <td>2.600</td> <td>0.420</td></tr> <tr> <td>9</td> <td>192</td> <td>19.20</td> <td>0.170</td> <td>0.193</td> <td>0.190</td> <td>0.200</td> <td>0.200</td> <td>0.530</td> <td>0.035</td></tr> <tr> <td>10</td> <td>151</td> <td>15.10</td> <td>0.170</td> <td>0.231</td> <td>0.200</td> <td>0.200</td> <td>0.204</td> <td>3.174</td> <td>0.343</td></tr></tbody></table> <h4>Graphs</h4> <h6>Response Time: 10 sec time-series</h6><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times_intervals.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="Load_Homepage_response_times_intervals" border="0" alt="Load_Homepage_response_times_intervals" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times_intervals_thumb.png" width="244" height="103"></a> <h6>Response Time: raw data (all points)</h6><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="Load_Homepage_response_times" border="0" alt="Load_Homepage_response_times" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times_thumb.png" width="244" height="103"></a> <h6>Throughput: 10 sec time-series</h6><a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_throughput.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="Load_Homepage_throughput" border="0" alt="Load_Homepage_throughput" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_throughput_thumb.png" width="244" height="103"></a> <p>I think what’s happening here is:</p> <p>First 50 seconds we get consistent response times around 1 second with no errors. Not sure why no change at 20-30 seconds transition, because I would have expected M-M to add a virtual user at this point.</p> <p>After 50 seconds, we clearly have more virtual users, and the site slows down with average responses 2.25 seconds. </p> <p>At 75 seconds (presumably 3 virtual users), the response times become much faster… eh? Well because I was watching the console output I knew this is also when the errors starting happening – pity M-M’s analysis doesn’t reveal this.&nbsp; I also “manually” uses the site during this period, and found that mojoPortal was returning it’s standard “something’s bust” page (and doing so very rapidly!).</p> <p>Examination of the mojoPortal system log showed that our plug-in component that talks to the web service was throwing an unhandled exception, along the lines that the root XML element was missing in the response from the HBSMR Gateway web service. This is turn was causing mojoPortal to output it’s standard error page.</p> <blockquote> <p>2011-12-28 10:41:16,125 ERROR 82.31.27.48 - en-US - /glossary?uid=TNF2069 - mojoPortal.Web.Global -&nbsp; Referrer(none) useragent Mozilla/5.0 Compatible <br>System.Xml.XmlException: Root element is missing.<br>&nbsp;&nbsp; at System.Xml.XmlTextReaderImpl.Throw(Exception e)<br>&nbsp;&nbsp; at System.Xml.XmlTextReaderImpl.ParseDocumentContent()<br>&nbsp;&nbsp; at System.Xml.XmlTextReaderImpl.Read()<br>&nbsp;&nbsp; at System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace)<br>&nbsp;&nbsp; at System.Xml.XmlDocument.Load(XmlReader reader)<br>&nbsp;&nbsp; at System.Xml.XmlDocument.LoadXml(String xml)<br>&nbsp;&nbsp; at ESDMmojoPortal.NHE.Business.GatewayResult.get_BBox()</p></blockquote> <p>I’ll need to investigate this further, but it suggests the HBSMR Gateway web service was returning an error response or perhaps empty XML when put under this load. This has to be improved of course, because it can come under very heavy load when used from the national Heritage Gateway site as well as applications like the Norfolk Heritage Explorer.</p> <p>So Multi-Mechanize has been incredibly useful in highlighting a weakness that could otherwise have rumbled on causing occasional problems for months.</p> <p>However, note that at first glance the graphs look as though the site was performing better as the load increased, because erring transactions were included into the analysis undifferentiated. In this case it was extremely obvious this was not really the case, because once the page started giving errors it basically stopped working at all; but this could be far less clear-cur, and the verdict has to be to remember to look at the number of errors first, and if there are any then bear this in mind in any interpretation of the figures and graphs. It would be useful if M-M could represent the errors in the results analysis, perhaps including them into the tabular stats, and offering graphs with the errors filtered out.</p><br /><a href='https://www.esdm.co.uk/breaking-a-site-with-multi-mechanize'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/breaking-a-site-with-multi-mechanize'>...</a>]]></description>
      <link>https://www.esdm.co.uk/breaking-a-site-with-multi-mechanize</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/breaking-a-site-with-multi-mechanize</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/breaking-a-site-with-multi-mechanize</guid>
      <pubDate>Wed, 28 Dec 2011 11:33:57 GMT</pubDate>
    </item>
    <item>
      <title>Trying out Multi-Mechanize web performance and load testing framework</title>
      <description><![CDATA[<p>
	Information and download from <strike><a href="http://code.google.com/p/multi-mechanize/" title="http://code.google.com/p/multi-mechanize/">http://code.google.com/p/multi-mechanize/</a></strike></p>
<p>
	UPDATE 14th March 2012: now moved to <a href="http://testutils.org/multi-mechanize"><font color="#0066cc">http://testutils.org/multi-mechanize</font></a></p>
<blockquote>
	<p>
		Multi-Mechanize is an open source framework for API performance and load testing. It allows you to run simultaneous python scripts to generate load (synthetic transactions) against a web site or API/service.&nbsp;</p>
	<p>
		In your scripts, you have the convenience of mechanize along with the power of the full Python programming language at your disposal. You programmatically create test scripts to simulate virtual user activity. Your scripts will then generate HTTP requests to intelligently navigate a web site or send requests to a web service.</p>
	<p>
		…</p>
	<p>
		You should be proficient with Python, HTTP, and performance/load testing to use multi-mechanize successfully.</p>
</blockquote>
<h3>
	Installing Multi-Mechanize</h3>
<p>
	First time around I got this horribly wrong, probably through not being proficient with…. actually my problem was installing Python 3.2 for Windows 64 bit; it turns out Multi-Mechanize only works with Python 2.6 and 2.7, and if you install the wrong version you get loads of syntax error messages; so I had to start again. Here is the correct sequence:</p>
<ol>
	<li>
		First install Python 2.7<br />
		<a href="http://www.python.org/download/releases/2.7.2/" title="http://www.python.org/download/releases/2.7.2/">http://www.python.org/download/releases/2.7.2/</a>&nbsp;<br />
		specifically<br />
		<a href="http://www.python.org/ftp/python/2.7.2/python-2.7.2.amd64.msi">Windows X86-64 MSI Installer (2.7.2)</a> <a href="http://www.python.org/download/releases/2.7.2/#id5">[1]</a><a href="http://www.python.org/download/releases/2.7.2/python-2.7.2.amd64.msi.asc">(sig)</a></li>
	<li>
		To install mechanize, download<br />
		<a href="http://pypi.python.org/pypi/distribute#distribute-setup-py" title="http://pypi.python.org/pypi/distribute#distribute-setup-py">http://pypi.python.org/pypi/distribute#distribute-setup-py</a>&nbsp;<br />
		and run&nbsp;<br />
		C:\Python27\python C:\blah\Downloads\distribute_setup.py<br />
		then<br />
		C:\Python27\scripts\easy_install mechanize</li>
	<li>
		Install Numpy from <a href="http://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy" title="http://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy">http://www.lfd.uci.edu/~gohlke/pythonlibs/#numpy</a>&nbsp;<br />
		specifically <a href="http://www.lfd.uci.edu/~gohlke/pythonlibs/">numpy-MKL-1.6.1.win-amd64-py2.7.‌exe</a> [8.7 MB] [Python 2.7] [64 bit] [Oct 29, 2011]</li>
	<li>
		Then install Matplotlib from <a href="http://www.lfd.uci.edu/~gohlke/pythonlibs/#matplotlib" title="http://www.lfd.uci.edu/~gohlke/pythonlibs/#matplotlib">http://www.lfd.uci.edu/~gohlke/pythonlibs/#matplotlib</a>&nbsp;<br />
		specifically <a href="http://www.lfd.uci.edu/~gohlke/pythonlibs/">matplotlib-1.1.0.win-amd64-py2.7.‌exe</a> [4.4 MB] [Python 2.7] [64 bit] [Oct 06, 2011]</li>
	<li>
		Finally download Multi-Mechanize<br />
		<a href="http://code.google.com/p/multi-mechanize/downloads/list" title="http://code.google.com/p/multi-mechanize/downloads/list">http://code.google.com/p/multi-mechanize/downloads/list</a><br />
		and copy the unzipped folder somewhere (no installation).</li>
</ol>
<h3>
	Using Multi-Mechanize</h3>
<p>
	It’s pretty simple. Use a command like this to start a session:</p>
<p>
	&gt;c:\python27\python multi-mechanize.py testMySite</p>
<p>
	This refers to a “project” (in this case “testMySite”) which is a folder under \projects under your Multi-Mechanize folder.&nbsp; To create a project I just copied and adapted the “default_project” that came in the download.</p>
<p>
	In the project folder there is a config.cfg text file that specifies some global settings and some details about what scripts to run on and how many simultaneous sessions to run. There is some sparse information on the config settings here: <a href="http://code.google.com/p/multi-mechanize/wiki/ConfigFile" title="http://code.google.com/p/multi-mechanize/wiki/ConfigFile">http://code.google.com/p/multi-mechanize/wiki/ConfigFile</a></p>
<p>
	You can specify any number of “user groups”, each representing one sequence of actions on your web site/service (e.g. loading the home page, or executing a search). Each user group is associated with a python script defining the actions. Each user group has a number of threads, i.e. virtual users attempting the sequence of operations.</p>
<p>
	TODO: insert some information about the actual Python script - how to perform simple and more complex operations on a web site.</p>
<p>
	The global settings include how long to run the test for, the time series interval for results analysis, and how long to “rampup” the number of users, presumably from one up to the maximum number of threads specified in the user groups. The ramp-up is to see within one test how the site performs under low and then progressively higher loads.</p>
<p>
	The results of the test are output into a folder below the project, with a time-stamped name like \results\results_2011.12.27_16.32.07</p>
<p>
	This contains the raw results as CSV, plus some graphs as PNG images, and an HTML page that brings the numbers and images together into a report.</p>
<h3>
	Example tests</h3>
<p>
	I ran simple tests on the following sites:</p>
<ul>
	<li>
		<a href="http://www.esdm.co.uk">www.esdm.co.uk</a> (main exeGesIS web site built in DotNetNuke and running on one of our older Telehouse servers)</li>
	<li>
		<a href="http://www.esdm.no-ip.co.uk">www.esdm.no-ip.co.uk</a> (draft replacement exeGesIS web site built in mojoPortal and running on our newer Telehouse hosting stack)</li>
	<li>
		<a href="http://www.breconfans.org.uk">www.breconfans.org.uk</a> (a site I run at home, using mojoPortal on Arvixe hosting)</li>
	<li>
		<a href="http://www.wikipedia.org">www.wikipedia.org</a> (a high availability global site as a control)</li>
</ul>
<p>
	The test script simply loaded the home page and checked for some text on the page. I tested with two user groups undertaking the same actions, with 50 virtual users in each, ramping up throughout the 5 minute test period.</p>
<h3>
	www.esdm.co.uk</h3>
<h4>
	Timer Summary (secs)</h4>
<table>
	<tbody>
		<tr>
			<th>
				count</th>
			<th>
				min</th>
			<th>
				avg</th>
			<th>
				80pct</th>
			<th>
				90pct</th>
			<th>
				95pct</th>
			<th>
				max</th>
			<th>
				stdev</th>
		</tr>
		<tr>
			<td>
				1088</td>
			<td>
				0.480</td>
			<td>
				12.897</td>
			<td>
				19.550</td>
			<td>
				23.866</td>
			<td>
				28.932</td>
			<td>
				88.588</td>
			<td>
				9.897</td>
		</tr>
	</tbody>
</table>
<h4>
	Interval Details (secs)</h4>
<table>
	<tbody>
		<tr>
			<th>
				interval</th>
			<th>
				count</th>
			<th>
				rate</th>
			<th>
				min</th>
			<th>
				avg</th>
			<th>
				80pct</th>
			<th>
				90pct</th>
			<th>
				95pct</th>
			<th>
				max</th>
			<th>
				stdev</th>
		</tr>
		<tr>
			<td>
				1</td>
			<td>
				105</td>
			<td>
				3.50</td>
			<td>
				0.480</td>
			<td>
				1.613</td>
			<td>
				2.120</td>
			<td>
				2.290</td>
			<td>
				2.630</td>
			<td>
				4.190</td>
			<td>
				0.711</td>
		</tr>
		<tr>
			<td>
				2</td>
			<td>
				82</td>
			<td>
				2.73</td>
			<td>
				1.992</td>
			<td>
				4.856</td>
			<td>
				6.574</td>
			<td>
				8.324</td>
			<td>
				9.834</td>
			<td>
				11.624</td>
			<td>
				2.251</td>
		</tr>
		<tr>
			<td>
				3</td>
			<td>
				101</td>
			<td>
				3.37</td>
			<td>
				3.000</td>
			<td>
				6.962</td>
			<td>
				9.360</td>
			<td>
				11.960</td>
			<td>
				13.194</td>
			<td>
				14.610</td>
			<td>
				3.095</td>
		</tr>
		<tr>
			<td>
				4</td>
			<td>
				110</td>
			<td>
				3.67</td>
			<td>
				2.830</td>
			<td>
				9.231</td>
			<td>
				12.650</td>
			<td>
				17.330</td>
			<td>
				18.070</td>
			<td>
				30.890</td>
			<td>
				4.900</td>
		</tr>
		<tr>
			<td>
				5</td>
			<td>
				89</td>
			<td>
				2.97</td>
			<td>
				4.964</td>
			<td>
				13.425</td>
			<td>
				17.822</td>
			<td>
				20.912</td>
			<td>
				22.222</td>
			<td>
				28.932</td>
			<td>
				5.095</td>
		</tr>
		<tr>
			<td>
				6</td>
			<td>
				114</td>
			<td>
				3.80</td>
			<td>
				4.912</td>
			<td>
				14.126</td>
			<td>
				18.792</td>
			<td>
				22.286</td>
			<td>
				24.210</td>
			<td>
				48.924</td>
			<td>
				7.163</td>
		</tr>
		<tr>
			<td>
				7</td>
			<td>
				117</td>
			<td>
				3.90</td>
			<td>
				4.670</td>
			<td>
				14.342</td>
			<td>
				18.290</td>
			<td>
				20.986</td>
			<td>
				23.958</td>
			<td>
				58.470</td>
			<td>
				6.999</td>
		</tr>
		<tr>
			<td>
				8</td>
			<td>
				92</td>
			<td>
				3.07</td>
			<td>
				5.402</td>
			<td>
				19.430</td>
			<td>
				24.054</td>
			<td>
				28.328</td>
			<td>
				31.194</td>
			<td>
				51.860</td>
			<td>
				7.733</td>
		</tr>
		<tr>
			<td>
				9</td>
			<td>
				163</td>
			<td>
				5.43</td>
			<td>
				2.650</td>
			<td>
				17.628</td>
			<td>
				25.356</td>
			<td>
				31.604</td>
			<td>
				34.338</td>
			<td>
				88.588</td>
			<td>
				12.654</td>
		</tr>
		<tr>
			<td>
				10</td>
			<td>
				115</td>
			<td>
				3.83</td>
			<td>
				6.500</td>
			<td>
				22.619</td>
			<td>
				27.201</td>
			<td>
				33.947</td>
			<td>
				39.892</td>
			<td>
				86.551</td>
			<td>
				11.353</td>
		</tr>
	</tbody>
</table>
<h4>
	Graphs</h4>
<h6>
	Response Time: 30 sec time-series</h6>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times_intervals%5B6%5D.png"><img alt="Load_Homepage_response_times_intervals[6]" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times_intervals%5B6%5D_thumb.png" style="border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Load_Homepage_response_times_intervals[6]" width="244" /></a></p>
<h6>
	Response Time: raw data (all points)</h6>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times%5B6%5D.png"><img alt="Load_Homepage_response_times[6]" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times%5B6%5D_thumb.png" style="border-width: 0px; margin: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Load_Homepage_response_times[6]" width="244" /></a></p>
<h6>
	Throughput: 30 sec time-series</h6>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_throughput%5B6%5D.png"><img alt="Load_Homepage_throughput[6]" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_throughput%5B6%5D_thumb.png" style="border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Load_Homepage_throughput[6]" width="244" /></a></p>
<h3>
	www.esdm.no-ip.co.uk</h3>
<h4>
	Timer Summary (secs)</h4>
<table>
	<tbody>
		<tr>
			<th>
				count</th>
			<th>
				min</th>
			<th>
				avg</th>
			<th>
				80pct</th>
			<th>
				90pct</th>
			<th>
				95pct</th>
			<th>
				max</th>
			<th>
				stdev</th>
		</tr>
		<tr>
			<td>
				5105</td>
			<td>
				0.210</td>
			<td>
				2.914</td>
			<td>
				4.410</td>
			<td>
				5.954</td>
			<td>
				7.190</td>
			<td>
				27.443</td>
			<td>
				2.285</td>
		</tr>
	</tbody>
</table>
<h4>
	Interval Details (secs)</h4>
<table>
	<tbody>
		<tr>
			<th>
				interval</th>
			<th>
				count</th>
			<th>
				rate</th>
			<th>
				min</th>
			<th>
				avg</th>
			<th>
				80pct</th>
			<th>
				90pct</th>
			<th>
				95pct</th>
			<th>
				max</th>
			<th>
				stdev</th>
		</tr>
		<tr>
			<td>
				1</td>
			<td>
				518</td>
			<td>
				17.27</td>
			<td>
				0.210</td>
			<td>
				0.347</td>
			<td>
				0.410</td>
			<td>
				0.470</td>
			<td>
				0.520</td>
			<td>
				1.210</td>
			<td>
				0.119</td>
		</tr>
		<tr>
			<td>
				2</td>
			<td>
				599</td>
			<td>
				19.97</td>
			<td>
				0.300</td>
			<td>
				0.790</td>
			<td>
				1.110</td>
			<td>
				1.250</td>
			<td>
				1.320</td>
			<td>
				2.380</td>
			<td>
				0.322</td>
		</tr>
		<tr>
			<td>
				3</td>
			<td>
				473</td>
			<td>
				15.77</td>
			<td>
				0.960</td>
			<td>
				1.596</td>
			<td>
				1.920</td>
			<td>
				2.030</td>
			<td>
				2.332</td>
			<td>
				3.550</td>
			<td>
				0.399</td>
		</tr>
		<tr>
			<td>
				4</td>
			<td>
				528</td>
			<td>
				17.60</td>
			<td>
				1.020</td>
			<td>
				2.068</td>
			<td>
				2.410</td>
			<td>
				2.590</td>
			<td>
				2.742</td>
			<td>
				5.602</td>
			<td>
				0.505</td>
		</tr>
		<tr>
			<td>
				5</td>
			<td>
				504</td>
			<td>
				16.80</td>
			<td>
				1.250</td>
			<td>
				2.586</td>
			<td>
				3.020</td>
			<td>
				3.360</td>
			<td>
				3.550</td>
			<td>
				6.170</td>
			<td>
				0.713</td>
		</tr>
		<tr>
			<td>
				6</td>
			<td>
				461</td>
			<td>
				15.37</td>
			<td>
				2.314</td>
			<td>
				3.532</td>
			<td>
				4.204</td>
			<td>
				5.482</td>
			<td>
				6.124</td>
			<td>
				9.872</td>
			<td>
				1.253</td>
		</tr>
		<tr>
			<td>
				7</td>
			<td>
				406</td>
			<td>
				13.53</td>
			<td>
				2.670</td>
			<td>
				4.808</td>
			<td>
				6.440</td>
			<td>
				7.470</td>
			<td>
				8.340</td>
			<td>
				17.990</td>
			<td>
				2.107</td>
		</tr>
		<tr>
			<td>
				8</td>
			<td>
				556</td>
			<td>
				18.53</td>
			<td>
				2.080</td>
			<td>
				4.031</td>
			<td>
				5.340</td>
			<td>
				6.560</td>
			<td>
				8.332</td>
			<td>
				18.562</td>
			<td>
				2.157</td>
		</tr>
		<tr>
			<td>
				9</td>
			<td>
				506</td>
			<td>
				16.87</td>
			<td>
				1.960</td>
			<td>
				5.030</td>
			<td>
				6.570</td>
			<td>
				8.010</td>
			<td>
				9.734</td>
			<td>
				25.216</td>
			<td>
				2.685</td>
		</tr>
		<tr>
			<td>
				10</td>
			<td>
				554</td>
			<td>
				18.47</td>
			<td>
				2.050</td>
			<td>
				4.880</td>
			<td>
				6.540</td>
			<td>
				8.082</td>
			<td>
				8.882</td>
			<td>
				27.443</td>
			<td>
				2.432</td>
		</tr>
	</tbody>
</table>
<h4>
	Graphs</h4>
<h6>
	Response Time: 30 sec time-series</h6>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times_intervals%5B4%5D.png"><img alt="Load_Homepage_response_times_intervals[4]" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times_intervals%5B4%5D_thumb.png" style="border-width: 0px; margin: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Load_Homepage_response_times_intervals[4]" width="244" /></a></p>
<h6>
	Response Time: raw data (all points)</h6>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times%5B4%5D.png"><img alt="Load_Homepage_response_times[4]" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times%5B4%5D_thumb.png" style="border-width: 0px; margin: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Load_Homepage_response_times[4]" width="244" /></a></p>
<h6>
	Throughput: 30 sec time-series</h6>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_throughput%5B4%5D.png"><img alt="Load_Homepage_throughput[4]" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_throughput%5B4%5D_thumb.png" style="border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Load_Homepage_throughput[4]" width="244" /></a></p>
<h3>
	www.breconfans.org.uk</h3>
<h4>
	Timer Summary (secs)</h4>
<table>
	<tbody>
		<tr>
			<th>
				count</th>
			<th>
				min</th>
			<th>
				avg</th>
			<th>
				80pct</th>
			<th>
				90pct</th>
			<th>
				95pct</th>
			<th>
				max</th>
			<th>
				stdev</th>
		</tr>
		<tr>
			<td>
				6710</td>
			<td>
				0.780</td>
			<td>
				2.218</td>
			<td>
				2.970</td>
			<td>
				4.620</td>
			<td>
				5.590</td>
			<td>
				16.120</td>
			<td>
				1.655</td>
		</tr>
	</tbody>
</table>
<h4>
	Interval Details (secs)</h4>
<table>
	<tbody>
		<tr>
			<th>
				interval</th>
			<th>
				count</th>
			<th>
				rate</th>
			<th>
				min</th>
			<th>
				avg</th>
			<th>
				80pct</th>
			<th>
				90pct</th>
			<th>
				95pct</th>
			<th>
				max</th>
			<th>
				stdev</th>
		</tr>
		<tr>
			<td>
				1</td>
			<td>
				220</td>
			<td>
				7.33</td>
			<td>
				0.780</td>
			<td>
				0.830</td>
			<td>
				0.850</td>
			<td>
				0.880</td>
			<td>
				0.890</td>
			<td>
				0.990</td>
			<td>
				0.031</td>
		</tr>
		<tr>
			<td>
				2</td>
			<td>
				500</td>
			<td>
				16.67</td>
			<td>
				0.790</td>
			<td>
				0.959</td>
			<td>
				1.020</td>
			<td>
				1.144</td>
			<td>
				1.420</td>
			<td>
				2.180</td>
			<td>
				0.216</td>
		</tr>
		<tr>
			<td>
				3</td>
			<td>
				703</td>
			<td>
				23.43</td>
			<td>
				0.874</td>
			<td>
				1.111</td>
			<td>
				1.200</td>
			<td>
				1.280</td>
			<td>
				1.400</td>
			<td>
				1.720</td>
			<td>
				0.153</td>
		</tr>
		<tr>
			<td>
				4</td>
			<td>
				761</td>
			<td>
				25.37</td>
			<td>
				0.980</td>
			<td>
				1.416</td>
			<td>
				1.620</td>
			<td>
				2.060</td>
			<td>
				2.120</td>
			<td>
				2.490</td>
			<td>
				0.356</td>
		</tr>
		<tr>
			<td>
				5</td>
			<td>
				857</td>
			<td>
				28.57</td>
			<td>
				1.080</td>
			<td>
				1.611</td>
			<td>
				1.810</td>
			<td>
				2.072</td>
			<td>
				2.430</td>
			<td>
				6.206</td>
			<td>
				0.556</td>
		</tr>
		<tr>
			<td>
				6</td>
			<td>
				982</td>
			<td>
				32.73</td>
			<td>
				0.940</td>
			<td>
				1.699</td>
			<td>
				1.720</td>
			<td>
				2.730</td>
			<td>
				3.240</td>
			<td>
				10.488</td>
			<td>
				0.778</td>
		</tr>
		<tr>
			<td>
				7</td>
			<td>
				628</td>
			<td>
				20.93</td>
			<td>
				1.250</td>
			<td>
				2.986</td>
			<td>
				4.340</td>
			<td>
				5.030</td>
			<td>
				5.600</td>
			<td>
				11.762</td>
			<td>
				1.473</td>
		</tr>
		<tr>
			<td>
				8</td>
			<td>
				693</td>
			<td>
				23.10</td>
			<td>
				1.450</td>
			<td>
				3.333</td>
			<td>
				4.870</td>
			<td>
				5.680</td>
			<td>
				7.000</td>
			<td>
				15.868</td>
			<td>
				1.905</td>
		</tr>
		<tr>
			<td>
				9</td>
			<td>
				663</td>
			<td>
				22.10</td>
			<td>
				1.680</td>
			<td>
				3.794</td>
			<td>
				5.150</td>
			<td>
				6.300</td>
			<td>
				7.600</td>
			<td>
				14.934</td>
			<td>
				1.986</td>
		</tr>
		<tr>
			<td>
				10</td>
			<td>
				703</td>
			<td>
				23.43</td>
			<td>
				1.660</td>
			<td>
				3.714</td>
			<td>
				5.390</td>
			<td>
				6.910</td>
			<td>
				7.900</td>
			<td>
				16.120</td>
			<td>
				2.171</td>
		</tr>
	</tbody>
</table>
<h4>
	Graphs</h4>
<h6>
	Response Time: 30 sec time-series</h6>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times_intervals%5B8%5D.png"><img alt="Load_Homepage_response_times_intervals[8]" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times_intervals%5B8%5D_thumb.png" style="border-width: 0px; margin: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Load_Homepage_response_times_intervals[8]" width="244" /></a></p>
<h6>
	Response Time: raw data (all points)</h6>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times%5B8%5D.png"><img alt="Load_Homepage_response_times[8]" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_response_times%5B8%5D_thumb.png" style="border-width: 0px; margin: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Load_Homepage_response_times[8]" width="244" /></a></p>
<h6>
	Throughput: 30 sec time-series</h6>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_throughput%5B8%5D.png"><img alt="Load_Homepage_throughput[8]" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_homepage_throughput%5B8%5D_thumb.png" style="border-width: 0px; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Load_Homepage_throughput[8]" width="244" /></a></p>
<h3>
	www.wikipedia.org</h3>
<h4>
	Timer Summary (secs)</h4>
<table>
	<tbody>
		<tr>
			<th>
				count</th>
			<th>
				min</th>
			<th>
				avg</th>
			<th>
				80pct</th>
			<th>
				90pct</th>
			<th>
				95pct</th>
			<th>
				max</th>
			<th>
				stdev</th>
		</tr>
		<tr>
			<td>
				2748</td>
			<td>
				0.340</td>
			<td>
				4.880</td>
			<td>
				6.811</td>
			<td>
				9.970</td>
			<td>
				14.132</td>
			<td>
				98.597</td>
			<td>
				5.963</td>
		</tr>
	</tbody>
</table>
<h4>
	Interval Details (secs)</h4>
<table>
	<tbody>
		<tr>
			<th>
				interval</th>
			<th>
				count</th>
			<th>
				rate</th>
			<th>
				min</th>
			<th>
				avg</th>
			<th>
				80pct</th>
			<th>
				90pct</th>
			<th>
				95pct</th>
			<th>
				max</th>
			<th>
				stdev</th>
		</tr>
		<tr>
			<td>
				1</td>
			<td>
				278</td>
			<td>
				9.27</td>
			<td>
				0.340</td>
			<td>
				0.643</td>
			<td>
				0.780</td>
			<td>
				0.930</td>
			<td>
				0.970</td>
			<td>
				2.990</td>
			<td>
				0.264</td>
		</tr>
		<tr>
			<td>
				2</td>
			<td>
				269</td>
			<td>
				8.97</td>
			<td>
				0.570</td>
			<td>
				1.689</td>
			<td>
				2.240</td>
			<td>
				2.484</td>
			<td>
				3.030</td>
			<td>
				5.304</td>
			<td>
				0.796</td>
		</tr>
		<tr>
			<td>
				3</td>
			<td>
				239</td>
			<td>
				7.97</td>
			<td>
				1.944</td>
			<td>
				3.265</td>
			<td>
				4.040</td>
			<td>
				4.716</td>
			<td>
				5.542</td>
			<td>
				7.652</td>
			<td>
				1.096</td>
		</tr>
		<tr>
			<td>
				4</td>
			<td>
				303</td>
			<td>
				10.10</td>
			<td>
				0.750</td>
			<td>
				3.182</td>
			<td>
				4.310</td>
			<td>
				5.482</td>
			<td>
				7.074</td>
			<td>
				18.540</td>
			<td>
				2.366</td>
		</tr>
		<tr>
			<td>
				5</td>
			<td>
				293</td>
			<td>
				9.77</td>
			<td>
				0.880</td>
			<td>
				4.424</td>
			<td>
				6.282</td>
			<td>
				7.574</td>
			<td>
				9.214</td>
			<td>
				26.812</td>
			<td>
				3.156</td>
		</tr>
		<tr>
			<td>
				6</td>
			<td>
				297</td>
			<td>
				9.90</td>
			<td>
				0.960</td>
			<td>
				5.225</td>
			<td>
				7.410</td>
			<td>
				10.330</td>
			<td>
				14.050</td>
			<td>
				29.854</td>
			<td>
				4.526</td>
		</tr>
		<tr>
			<td>
				7</td>
			<td>
				290</td>
			<td>
				9.67</td>
			<td>
				0.830</td>
			<td>
				6.111</td>
			<td>
				8.510</td>
			<td>
				12.400</td>
			<td>
				15.470</td>
			<td>
				43.068</td>
			<td>
				5.177</td>
		</tr>
		<tr>
			<td>
				8</td>
			<td>
				252</td>
			<td>
				8.40</td>
			<td>
				0.930</td>
			<td>
				7.125</td>
			<td>
				9.380</td>
			<td>
				13.642</td>
			<td>
				21.808</td>
			<td>
				62.750</td>
			<td>
				6.843</td>
		</tr>
		<tr>
			<td>
				9</td>
			<td>
				278</td>
			<td>
				9.27</td>
			<td>
				0.870</td>
			<td>
				8.470</td>
			<td>
				10.430</td>
			<td>
				15.573</td>
			<td>
				23.071</td>
			<td>
				98.597</td>
			<td>
				10.443</td>
		</tr>
		<tr>
			<td>
				10</td>
			<td>
				249</td>
			<td>
				8.30</td>
			<td>
				1.170</td>
			<td>
				9.083</td>
			<td>
				12.550</td>
			<td>
				17.946</td>
			<td>
				23.132</td>
			<td>
				75.871</td>
			<td>
				8.477</td>
		</tr>
	</tbody>
</table>
<h4>
	Graphs</h4>
<h6>
	Response Time: 30 sec time-series</h6>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_front_page_response_times_intervals.png"><img alt="Load_Front_Page_response_times_intervals" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_front_page_response_times_intervals_thumb.png" style="margin: 0px; border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Load_Front_Page_response_times_intervals" width="244" /></a></p>
<h6>
	Response Time: raw data (all points)</h6>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_front_page_response_times.png"><img alt="Load_Front_Page_response_times" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_front_page_response_times_thumb.png" style="margin: 0px; border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Load_Front_Page_response_times" width="244" /></a></p>
<h6>
	Throughput: 30 sec time-series</h6>
<p>
	<a href="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_front_page_throughput.png"><img alt="Load_Front_Page_throughput" border="0" height="103" src="https://www.esdm.co.uk/Data/Sites/1/media/wlw/load_front_page_throughput_thumb.png" style="border: 0px currentColor; padding-top: 0px; padding-right: 0px; padding-left: 0px; display: inline; background-image: none;" title="Load_Front_Page_throughput" width="244" /></a></p>
<p>
	&nbsp;</p>
<h3>
	Analysis</h3>
<p>
	I am no expert in load testing, so I do not pretend to be able to analyse the results in any meaningful way. Looking to learn though…!</p>
<p>
	But bringing together the most significant statistics from these tests…</p>
<table border="1" cellpadding="2" cellspacing="0" width="574">
	<tbody>
		<tr>
			<td valign="top" width="130">
				Measurement</td>
			<td valign="top" width="116">
				esdm.co.uk</td>
			<td valign="top" width="113">
				esdm.no-ip.co.uk</td>
			<td valign="top" width="131">
				breconfans.org</td>
			<td valign="top" width="82">
				www.wikipedia.org</td>
		</tr>
		<tr>
			<td valign="top" width="130">
				CMS</td>
			<td valign="top" width="116">
				DNN</td>
			<td valign="top" width="113">
				mojoPortal</td>
			<td valign="top" width="131">
				mojoPortal</td>
			<td valign="top" width="82">
				n/a</td>
		</tr>
		<tr>
			<td valign="top" width="130">
				Platform</td>
			<td valign="top" width="116">
				exeGesIS Telehouse hosting.<br />
				Windows Server 2003 + SQL Server 2000</td>
			<td valign="top" width="113">
				exeGesIS Telehouse hosting.<br />
				Windows Server 2008 + SQL Server 2008 R2</td>
			<td valign="top" width="131">
				Arvixe hosting.<br />
				Windows Server 2008 + SQL Server 2008 Express</td>
			<td valign="top" width="82">
				n/a</td>
		</tr>
		<tr>
			<td valign="top" width="130">
				Successful requests</td>
			<td valign="top" width="116">
				1088</td>
			<td valign="top" width="113">
				5105</td>
			<td valign="top" width="131">
				6710</td>
			<td valign="top" width="82">
				2748</td>
		</tr>
		<tr>
			<td valign="top" width="130">
				Minimum response time</td>
			<td valign="top" width="116">
				0.480</td>
			<td valign="top" width="113">
				0.210</td>
			<td valign="top" width="131">
				0.780</td>
			<td valign="top" width="82">
				0.340</td>
		</tr>
		<tr>
			<td valign="top" width="130">
				Average response time</td>
			<td valign="top" width="116">
				12.897</td>
			<td valign="top" width="113">
				2.914</td>
			<td valign="top" width="132">
				2.218</td>
			<td valign="top" width="82">
				4.880</td>
		</tr>
		<tr>
			<td valign="top" width="130">
				95pct response time</td>
			<td valign="top" width="115">
				28.932</td>
			<td valign="top" width="113">
				7.190</td>
			<td valign="top" width="132">
				5.590</td>
			<td valign="top" width="82">
				14.132</td>
		</tr>
		<tr>
			<td valign="top" width="130">
				Average requests / sec</td>
			<td valign="top" width="118">
				3.63</td>
			<td valign="top" width="118">
				17.02</td>
			<td valign="top" width="139">
				22.37</td>
			<td valign="top" width="104">
				9.16</td>
		</tr>
	</tbody>
</table>
<p>
	These identical tests put the web sites under heavy load, particularly after the first minute or so. It is clear that the two mojoPortal sites dramatically out-perform our old esdm.co.uk, with average response times of 2.9 and 2.2 seconds compared with 12.8 over the full test period. The reasons for this are far less clear though.&nbsp; esdm.co.uk and esdm.no-ip.co.uk have about the same amount of content, but this is not really a fair comparison between DotNetNuke and mojoPortal, because esdm.no-ip.co.uk is running on faster servers. Also, we are running the latest version of mojoPortal (2.3.7.5) compared to an ancient version of DNN.</p>
<p>
	Interestingly the minimum response time was 0.48 for esdm.co.uk, significantly faster than the 0.78 seconds for breconfans.org.uk, perhaps because the latter is on US hosting meaning a longer round-trip regardless of other factors; esdm.no-ip.co.uk gave 0.21 seconds minimum, perhaps demonstrating the inherent responsiveness of our new hosting stack.</p>
<p>
	I was surprised that breconfans.org came out better under load than esdm.no-ip.co.uk, but I suspect this is because the latter is a much larger site, with hundreds of pages (and so a much larger mojoPortal database) compared to about 25 pages for breconfans.</p>
<p>
	Testing wikipedia was an afterthought, and of course it is not quite the same because it is always under heavy load; nonetheless it was reassuring to find that my mojoPortal sites performed better, and that the wikipedia performance also degraded through the test as more virtual users were added (though this does make me wonder whether some of the performance degradation is client-side, e.g. inherent bottle-neck in processing http requests in Windows?).</p>
<h3>
	Conclusion</h3>
<p>
	Multi-Mechanize was simple to set up and use (admittedly in a limited manner). It quickly demonstrates what kinds of load web sites can sustain before degrading.</p>
<p>
	More advanced configuration of the test scripts, e.g. to perform form-based operations, should be reasonably easy for our developers. This should give real power for load-testing our web applications.</p>
<p>
	&nbsp;</p>
<p>
	Aside: I found when running the longer tests with lots of threads (e.g. 100) that the routine would not complete after the allotted time, and would stick saying “waiting for all requests to finish”.&nbsp; Usually it would complete after a minute or two extra waiting. If not, no outputs were produced, but killing the python.exe process <em>sometimes</em> caused the output files to be created. This issue has been encountered by others, with no apparent resolution (e.g. <a href="http://groups.google.com/group/multi-mechanize/browse_thread/thread/706c30203c568d61" title="http://groups.google.com/group/multi-mechanize/browse_thread/thread/706c30203c568d61">http://groups.google.com/group/multi-mechanize/browse_thread/thread/706c30203c568d61</a>)</p>
<br /><a href='https://www.esdm.co.uk/trying-out-multi-mechanize-web-performance-and-load-testing-framework'>Crispin Flower</a>&nbsp;&nbsp;<a href='https://www.esdm.co.uk/trying-out-multi-mechanize-web-performance-and-load-testing-framework'>...</a>]]></description>
      <link>https://www.esdm.co.uk/trying-out-multi-mechanize-web-performance-and-load-testing-framework</link>
      <author>crispin.flower@idoxgroup.com (Crispin Flower)</author>
      <comments>https://www.esdm.co.uk/trying-out-multi-mechanize-web-performance-and-load-testing-framework</comments>
      <guid isPermaLink="true">https://www.esdm.co.uk/trying-out-multi-mechanize-web-performance-and-load-testing-framework</guid>
      <pubDate>Tue, 27 Dec 2011 18:46:00 GMT</pubDate>
    </item>
  </channel>
</rss>