Wednesday, 12 August 2015

The joys of Azure Site and Database Migration

Many Many moons ago... In a coastal town far away... I ran an internet café. Three C's Internet Café (Which stood for Coffee, Cocktails & Computers. Nobody got it, even though it was written in foot high letters across the front of the building!) Three C's had a web site (www.threecs.net) which had many visitors!
I sold the internet café in 2010, they renamed it Karma Café and carried on in their own way. The website however continued, partly because I was lazy, partly because it still had many many visitors!
I decided today to migrate it off the old Rackspace hosting to Azure, this should be easy... or so I thought.
The Three C's website is very old-fashioned, a frames site using classic ASP to a little SQL Server Express database (migrated from Access) with a few JavaScript tricks and ASP hacks. Webmatrix let me migrate the site easily enough (whilst not telling me it was ignoring some of the web.config settings!) then came the joy of migrating the database.
As with all proper sites, the database server backs itself up every night (I have a series of very useful SQL Express automated backup scripts.) Normally to migrate servers I would restore one of these scripts to the server, reconnect the users and everything would be good.
Failure #1 - You cant restore a SQL database backup to Azure (bummer)
OK, try connecting from the the server using SQL Management Studio, success! Ah, but half the menu items are missing (mostly around data exporting and backups) and of course the version on my web server is SQL 2008 R2 Express, you need 2012 to get to all the toys.
Failure #2 - You need SQL Server 2012 to use all the toys.
OK, I have SQL 2012 on my local PC, restore the database locally an deploy/export it from there. Easy... Except it wont deploy, SQL Azure must have a clustered index on all tables, well it is more efficient, but it wasn't a requirement with on-prem SQL Server.
Failure #3 - You need clustered indexes on all tables with SQL Azure
This actually took me a while to solve, many solutions presented themselves.
  • Exporting the data instead (same clustered index problem)
  • Creating the tables with scripts, edited to have clustered indexes
Failure #4 - you cant have disk references in your scripts (Azure doesn't have disks in the same way as on-prem SQL Server)
  • Create the tables with scripts, modifying the Primary Keys to be clustered in scripting, then export the data with identities switched off
Failure #5 - Importing data can muck up your referential integrity (Which I guessed, I was hoping that switching off identities would help... nope!)
Inevitably I had been a good boy and used proper referential integrity. Yes this complicates the database, no I didn't want to switch it off.
  • Fix the database locally (making clustered indexes) then use the SQL 2012 Deployment wizard
Failure #6 - Exporting from access creates some read only columns (my own fault)
  • Delete the Access specific fields
Failure #7 - SQL Azure doesn't like Extended properties

It would appear that the combination of upgrading from Access to SQL Server, and then updating whenever a new version came out (in combination to masochistic tendencies) creates many extended properties on a database, in many different places, all of which need deleting :(
I found a query online that would create another set of queries to delete the extended properties:

/* This script will generate calls to sp_dropextendedproperty for every extended property that exists in your database. Actually, a caveat: I don't promise that it will catch each and every extended property that exists, but I'm confident it will catch most of them! It is based on this: http://blog.hongens.nl/2010/02/25/drop-all-extended-properties-in-a-mssql-database/ by Angelo Hongens. Also had lots of help from this: http://www.sqlservercentral.com/articles/Metadata/72609/ by Adam Aspin Adam actually provides a script at that link to do something very similar but when I ran it I got an error: Msg 468, Level 16, State 9, Line 78 Cannot resolve the collation conflict between "Latin1_General_100_CS_AS" and "Latin1_General_CI_AS" in the equal to operation. So I put together this version instead. Use at your own risk. Jamie Thomson 2012-03-25 */ /*Are there any extended properties? Let's take a look*/ select *,OBJECT_NAME(major_id) from sys.extended_properties xp /*Now let's generate sp_dropextendedproperty statements for all of them.*/ --tables set nocount on; select 'EXEC sp_dropextendedproperty @name = '''+xp.name+''' ,@level0type = ''schema'' ,@level0name = ''' + object_schema_name(xp.major_id) + ''' ,@level1type = ''table'' ,@level1name = ''' + object_name(xp.major_id) + '''' from sys.extended_properties xp join sys.tables t on xp.major_id = t.object_id where xp.class_desc = 'OBJECT_OR_COLUMN' and xp.minor_id = 0 union --columns select 'EXEC sp_dropextendedproperty @name = '''+sys.extended_properties.name+''' ,@level0type = ''schema'' ,@level0name = ''' + object_schema_name(extended_properties.major_id) + ''' ,@level1type = ''table'' ,@level1name = ''' + object_name(extended_properties.major_id) + ''' ,@level2type = ''column'' ,@level2name = ''' + columns.name + '''' from sys.extended_properties join sys.columns on columns.object_id = extended_properties.major_id and columns.column_id = extended_properties.minor_id where extended_properties.class_desc = 'OBJECT_OR_COLUMN' and extended_properties.minor_id > 0 union --check constraints select 'EXEC sp_dropextendedproperty @name = '''+xp.name+''' ,@level0type = ''schema'' ,@level0name = ''' + object_schema_name(xp.major_id) + ''' ,@level1type = ''table'' ,@level1name = ''' + object_name(cc.parent_object_id) + ''' ,@level2type = ''constraint'' ,@level2name = ''' + cc.name + '''' from sys.extended_properties xp join sys.check_constraints cc on xp.major_id = cc.object_id union --check constraints select 'EXEC sp_dropextendedproperty @name = '''+xp.name+''' ,@level0type = ''schema'' ,@level0name = ''' + object_schema_name(xp.major_id) + ''' ,@level1type = ''table'' ,@level1name = ''' + object_name(cc.parent_object_id) + ''' ,@level2type = ''constraint'' ,@level2name = ''' + cc.name + '''' from sys.extended_properties xp join sys.default_constraints cc on xp.major_id = cc.object_id union --views select 'EXEC sp_dropextendedproperty @name = '''+xp.name+''' ,@level0type = ''schema'' ,@level0name = ''' + object_schema_name(xp.major_id) + ''' ,@level1type = ''view'' ,@level1name = ''' + object_name(xp.major_id) + '''' from sys.extended_properties xp join sys.views t on xp.major_id = t.object_id where xp.class_desc = 'OBJECT_OR_COLUMN' and xp.minor_id = 0 union --sprocs select 'EXEC sp_dropextendedproperty @name = '''+xp.name+''' ,@level0type = ''schema'' ,@level0name = ''' + object_schema_name(xp.major_id) + ''' ,@level1type = ''procedure'' ,@level1name = ''' + object_name(xp.major_id) + '''' from sys.extended_properties xp join sys.procedures t on xp.major_id = t.object_id where xp.class_desc = 'OBJECT_OR_COLUMN' and xp.minor_id = 0 union --FKs select 'EXEC sp_dropextendedproperty @name = '''+xp.name+''' ,@level0type = ''schema'' ,@level0name = ''' + object_schema_name(xp.major_id) + ''' ,@level1type = ''table'' ,@level1name = ''' + object_name(cc.parent_object_id) + ''' ,@level2type = ''constraint'' ,@level2name = ''' + cc.name + '''' from sys.extended_properties xp join sys.foreign_keys cc on xp.major_id = cc.object_id union --PKs SELECT 'EXEC sys.sp_dropextendedproperty @level0type = N''SCHEMA'', @level0name = [' + SCH.name + '], @level1type = ''TABLE'', @level1name = [' + TBL.name + '] , @level2type = ''CONSTRAINT'', @level2name = [' + SKC.name + '] ,@name = ''' + REPLACE(CAST(SEP.name AS NVARCHAR(300)),'''','''''') + '''' FROM sys.tables TBL INNER JOIN sys.schemas SCH ON TBL.schema_id = SCH.schema_id INNER JOIN sys.extended_properties SEP INNER JOIN sys.key_constraints SKC ON SEP.major_id = SKC.object_id ON TBL.object_id = SKC.parent_object_id WHERE SKC.type_desc = N'PRIMARY_KEY_CONSTRAINT' union --Table triggers SELECT 'EXEC sys.sp_dropextendedproperty @level0type = N''SCHEMA'', @level0name = [' + SCH.name + '], @level1type = ''TABLE'', @level1name = [' + TBL.name + '] , @level2type = ''TRIGGER'', @level2name = [' + TRG.name + '] ,@name = ''' + REPLACE(CAST(SEP.name AS NVARCHAR(300)),'''','''''') + '''' FROM sys.tables TBL INNER JOIN sys.triggers TRG ON TBL.object_id = TRG.parent_id INNER JOIN sys.extended_properties SEP ON TRG.object_id = SEP.major_id INNER JOIN sys.schemas SCH ON TBL.schema_id = SCH.schema_id union --UDF params SELECT 'EXEC sys.sp_dropextendedproperty @level0type = N''SCHEMA'', @level0name = [' + SCH.name + '], @level1type = ''FUNCTION'', @level1name = [' + OBJ.name + '] , @level2type = ''PARAMETER'', @level2name = [' + PRM.name + '] ,@name = ''' + REPLACE(CAST(SEP.name AS NVARCHAR(300)),'''','''''') + '''' FROM sys.extended_properties SEP INNER JOIN sys.objects OBJ ON SEP.major_id = OBJ.object_id INNER JOIN sys.schemas SCH ON OBJ.schema_id = SCH.schema_id INNER JOIN sys.parameters PRM ON SEP.major_id = PRM.object_id AND SEP.minor_id = PRM.parameter_id WHERE SEP.class_desc = N'PARAMETER' AND OBJ.type IN ('FN', 'IF', 'TF') union --sp params SELECT 'EXEC sys.sp_dropextendedproperty @level0type = N''SCHEMA'', @level0name = [' + SCH.name + '], @level1type = ''PROCEDURE'', @level1name = [' + SPR.name + '] , @level2type = ''PARAMETER'', @level2name = [' + PRM.name + '] ,@name = ''' + REPLACE(CAST(SEP.name AS NVARCHAR(300)),'''','''''') + '''' FROM sys.extended_properties SEP INNER JOIN sys.procedures SPR ON SEP.major_id = SPR.object_id INNER JOIN sys.schemas SCH ON SPR.schema_id = SCH.schema_id INNER JOIN sys.parameters PRM ON SEP.major_id = PRM.object_id AND SEP.minor_id = PRM.parameter_id WHERE SEP.class_desc = N'PARAMETER' union --DB SELECT 'EXEC sys.sp_dropextendedproperty @name = ''' + REPLACE(CAST(SEP.name AS NVARCHAR(300)),'''','''''') + '''' FROM sys.extended_properties SEP WHERE class_desc = N'DATABASE' union --schema SELECT 'EXEC sys.sp_dropextendedproperty @level0type = N''SCHEMA'', @level0name = [' + SCH.name + '] ,@name = ''' + REPLACE(CAST(SEP.name AS NVARCHAR(300)),'''','''''') + '''' FROM sys.extended_properties SEP INNER JOIN sys.schemas SCH ON SEP.major_id = SCH.schema_id WHERE SEP.class_desc = N'SCHEMA' union --DATABASE_FILE SELECT 'EXEC sys.sp_dropextendedproperty @level0type = N''FILEGROUP'', @level0name = [' + DSP.name + '], @level1type = ''LOGICAL FILE NAME'', @level1name = ' + DBF.name + ' ,@name = ''' + REPLACE(CAST(SEP.name AS NVARCHAR(300)),'''','''''') + '''' FROM sys.extended_properties SEP INNER JOIN sys.database_files DBF ON SEP.major_id = DBF.file_id INNER JOIN sys.data_spaces DSP ON DBF.data_space_id = DSP.data_space_id WHERE SEP.class_desc = N'DATABASE_FILE' union --filegroup SELECT 'EXEC sys.sp_dropextendedproperty @level0type = N''FILEGROUP'', @level0name = [' + DSP.name + '] ,@name = ''' + REPLACE(CAST(SEP.name AS NVARCHAR(300)),'''','''''') + '''' FROM sys.extended_properties SEP INNER JOIN sys.data_spaces DSP ON SEP.major_id = DSP.data_space_id WHERE DSP.type_desc = 'ROWS_FILEGROUP'

It returns a series of rows that you simply copy and paste into another query window then run, which deletes all the extended properties.

Finally, run the deployment wizard - preferably with your fingers crossed!

It does run, I now have my cocktails database installed on a free instance of SQL Azure, and it is very quick! It was also very painful, I probably would have been better off re-writing from scratch and importing the data as cut and paste for this small data-set. But I've done it now, I've recorded it here and I can now do it again... Right up until Microsoft change their minds on how it works!

Wednesday, 25 February 2015

Numeric characters in circles

Having searched the web for ages (and found lots of conflicting posts) I decided to document the [ALT] key sequences for alphanumeric character enclosed in a circle:

9312
9313
9314
9315
9316
9317
9318
9319
9320
9321
9322
9323
9324
9325
9326
9327
9328
9329
9330
9331
9332
9333
9334
9335
9336
9337
9338
9339
9340
9341
9342
9343
9344
9345
9346
9347
9348
9349
9350
9351
9352
9353
9354
9355
9356
9357
9358
9359
9360
9361
9362
9363
9364
9365
9366
9367
9368
9369
9370
9371
9372
9373
9374
9375
9376
9377
9378
9379
9380
9381
9382
9383
9384
9385
9386
9387
9388
9389
9390
9391
9392
9393
9394
9395
9396
9397
9398
9399
9400
9401
9402
9403
9404
9405
9406
9407
9408
9409
9410
9411
9412
9413
9414
9415
9416
9417
9418
9419
9420
9421
9422
9423
9424
9425
9426
9427
9428
9429
9430
9431
9432
9433
9434
9435
9436
9437
9438
9439
9440
9441
9442
9443
9444
9445
9446
9447
9448
9449
9450
9451
9452
9453
9454
9455
9456
9457
9458
9459
9460
9461
9462
9463
9464
9465
9466
9467
9468
9469
9470
9471

Thursday, 31 January 2013

JQuery Sliders, Galleries and SEO

I've always had a dislike for Flash based galleries and sliders. They are awful for SEO, dire for mobile devices and a pure PITA for normal browsing as you wait for the dreaded rotating icon as they load to show you an image that would have been there in half the time!

Thankfully the death knell for Flash started well over 3 years ago and now with the advent of tablet devices, notably the iPad, we hardly see any at all. In its place we find a plethora of JQuery based galleries and sliders.

JQuery widgets were once the domain of the particularly geeky (says he writing a tech blog!) with the whole case sensitive commands and obscure, code only access that has still meant that the odd Flash Gallery got used occasionally. But easy to use libraries are now emerging; the kind of thing where you can simply put your images on a page, surround them with a <div> and call a single line of code. Hey Presto! a working gallery. To the point where even the defaults are good enough!

I have been playing with a nice JQuery gallery called Galleria. It's reasonably pretty, free (in vanilla form) and has a nice series of options (not free but good value!) that are quite worth while. It's flexible enough that I've tweaked it to be a slider as well without having to re-invent the wheel and uses a reasonable css model that you can override quite easily. Not only that they have gone for all the latest HTML5 and CSS3 trickery so its fast and kept legacy browser compatibility. Oh and it's touch compliant for mobile and tablet use!

If you dig inside the code it's really quite a clever piece of coding, with nice calls you can make after its loaded to play with the functionality and with enough documentation to get you started. I'm glad I didn't have to write it!

Now for the big question, does it have any SEO value? It's a really good question, because it is naturally assumed that JQuery is parsed by the search engine spiders. The problem is that may be true for specific JavaScript tricks and redirects, but it doesn't work for content and it doesn't count towards the ranking of your page or images. What spiders really like is ordinary HTML with ordinary Tags; like <img> or <a> There is a nice article about it Image SEO that describes what gets indexed quite well.

Modern JQuery Galleries have nice solutions to this though. As with Galleria, all you need do is use ordinary image tags on your page <img src="MyHouse.jpg"> and the JQuery will pick them up and reformat the page to make it look like a gallery. This means you can assemble your images as you would for any ordinary page and populate it as you need to for SEO.

As a quick Summary this is what most of the spiders understand. (there is a lot more, but this is the simple version!)

  • <img
    The spider now knows its an image!
  • src="http://www.mysite.com/images/MyHouse.jpg"
    This is where the image is, using a full path makes life easier for the spider (and quicker)
    The usual caveats with friendly urls apply, the easier it is the more (little) marks you get!
  • alt="This is a picture of my house in Devon, it's beautiful in the sunny weather"
    Really important bit! This gives the picture context and allows good indexing and relevance. Don't just use a list of keywords, you get more marks for something that makes sense, remember the spider needs to be quick, so Keep It SimpleS. Look to get a simple sentence structure with context and include small words that won't get indexed (it's the easiest way to check if it's a real sentence)
  • title="My House"
    Apparently this isn't used to rank you result, but does help in the same way that a page title does in ordinary search results to give your image context where they may be hundreds of others.
  • >
    Don't forget to close the tag!
At the time of writing, nothing else counts in the image tag. You may find your JQuery Widget lets you use other tags as well, typically data-description or data-something which are valid HTML but are not indexed, they often control internal linking and order, so are worth doing if you want to take the time.

I will agree with the developers <img> tags with loads of attributes doesn't make for nice code, i.e.: 
<img src='images/WP_000039.jpg' data-title='Dog Washing' title='Dog Washing' data-description='This is actually our own Dalmatian, she doesn&#39;t like the bath so she is excellent practice!' alt='Dog Washing - This is actually our own Dalmatian, she doesn&#39;t like the bath so she is excellent practice!' data-thumb='images/T_WP_000039.jpg'>
JSON is a far nicer way to represent the data. 
{
link: '/seo',
title: 'Dog Washing'
description: '<H2>Dog Washing</H2>
<p>This is actually our own Dalmatian, she doesn&#39;t like the bath so she is excellent practice!<p>',
image: 'images/WP_000039.jpg',
thumb: 'images/T_WP_000039.jpg',
},
But the search engines can't read it, so avoid it, unless you specifically don't want it read by the spider. Which is often the case if it's a slider that just repeats what is further down the page.

If you do it right you will be pleasantly surprised at how fast your images appear in Google, Bing et-al. 

I've been pestering my wife to sort out her Dog Grooming Business, eventually I gave up and did a totally vanilla site and within a week the images in her little gallery started appearing in obscure Dog Grooming Searches, that's brownie points worth earning!

Wednesday, 5 December 2012

The Perils of Duplicate Content

Currently one of the most frequent questions I am asked is - Does duplicate content really matter? Just for a change I can give a definitive answer - As far as Google is concerned at the moment; Yes!

As with all these questions what is really meant is does this matter to Google and we can see from Matt Cutts blog and the Google Webmasters Blog that it's not just a myth, You will be pushed down the rankings for doing it.

Google actually identify duplicate content down to short quotes, if you don't link it to the originators site as a quote you *will* be marked down and the content will have a lower ranking in the Search Engine Results Page (SERP.) THere is a short YouTube Video about it.

Overnight this has destroyed the concept of article "syndication" where a journalist or copywriter would write an article one and send it to multiple publications, being paid for each one. I have seen writers weep, on stage as they have recanted this. I feel for them, but that's how the internet has changed life (I was fed up with reading the same article in different motoring magazines anyway!)

The problem with all of this is that it is retrospective. All those old news articles on your high authority site? check them as they can suddenly be marking you down. Those datasheets that just said what was on the main article? The old ideas that if you keep saying the same thing over and over on different pages in your site to just to drive up the rankings? Now we all knew it was 'cheating' now you get penalised for it.

A really common mistake I am coming across on even large sites is page replication across domains and URL's. I thought this was obvious and people knew, but I will spell it out... Having www.mysite.com and www.mysite.com/home as the same page is duplicate content. I have tested it, having removed the page and redirecting all the incorrect links and watched the site climb up the search rankings over the next few *days* This really seems to make a difference!

The other really simple mistake is buying all the domain names that relate to you and pointing them at your site. www.mysite.com having the same content as www.mysite.co.uk *is* duplicate content! It seems really obvious, because it is! but if you must have multiple domains redirect them, better still make them relevant to the country/business type. Best advice, if the domain doesn't relate to you, don't buy it. You may not want www.mysite.xxx as a porn site, but knowing that xxx is blocked as porn, do you want it pointing to your home page? Leave it alone!

If you are worried, get somebody that knows what they are doing to look at it. Just fixing the stupids will make a remarkable difference!

Thursday, 20 September 2012

Fun with Windows 8 Preview

I've spent the last few days playing with the Windows 8 Consumer Preview. Indeed I am using it now to post this entry.

I am using an Asus EP121 as a test machine:
  • 64GB hard drive
  • Additional 64GB SD Card
  • Bluetooth Keyboard (essentially a Microsoft 6000 keyboard re-branded by Asus)
  • Microsoft Arc Touch Wireless Mouse (with RF dongle)
First impressions were very good, I had more issues backing up my existing Windows 7 installation than I did installing a fresh version (Windows backup really needs to be left on it's own... Don't try and do anything whilst it's working, just be very, very patient!)

The installation itself worked flawlessly without the need for a mouse or keyboard, it picked up the touchscreen and configured itself perfectly. Several minutes later I was faced with a fresh looking 'Metro' interface, clean and uncluttered with a nice new desktop behind all using what looks like a cleaner font and nice new background images that fade in and out.

However beyond the skin things get more difficult. I needed to join my home domain; normally I do this by right clicking the computer entry on the start menu and choosing properties. But there is no start menu in Windows 8... OK, system in the control panel, um control panel is on the start menu. Choosing settings from the Charms menu (slide finger in from the right of the screen) produces no sign of the control panel. Eventually I cheated (Windows key + R -> run menu -> type control) and hey presto the control panel appears and I could choose system to join the domain. I have found since that the charms menu is context sensitive, so that when you are in desktop mode (traditional windows interface) the settings button takes you to a sub menu that does have the control panel. It is somewhat less than intuitive!

Windows 8 joined the domain fine, as usual all the homegroup stuff stops working. Many of the sample applications seem to use aspects of homegroup and library functionality mixed in with live/Xbox sign in, much of which doesn't work properly at the time of writing.

Lets see how it progresses!

Friday, 3 February 2012

What to Index

After another brainstorm today on search performance we discovered an interesting anomaly on how one of our competitors submit pages to Google. Its quite clever the way Yell submit pages, essentially client pages are not submitted to search engines in the traditional manner, the keyword phrases that link to the clients are; leading to very high keyword density for their chosen phrases. It's actually quite cunning.  I'm going to implement it over the next few weeks as an additional sitemap. If it works I'll document it properly!

We had a little bit of a mess up with one of our suppliers that came to a head this week. Softcat were chosen to supply some tablet PC's and laptops. To be fair I was a picky bastard insisting on a Samsung Tablet PC which we ordered about 3 weeks ago. It was very difficult to source 3 weeks ago, now of course it's on ebuyer with next day delivery and all the accessories included. Needless to say when we received the pc from softcat we checked the contents of the box to find that there was no keyboard or stand, they were optional extras that were not available in this country. Annoying... we could have bought it with everything included and got it a week earlier from Ebuyer... hmmmm time to return it and buy it from someone else!

Contacting softcat we found that as we had opened the box we could not return it. Despite literally not even touching the PC! The sales rep was insistent that we could not return the PC, even though we had actually ordered 2 slate PC's, plus a 17" HP laptop and needed a further 3 laptops and 2 more slate PC's; probably £8K worth of kit. Nope, tough. You open the package, you own it. Guess what we did? sent everything except the opened box back. Sorry, that is shit customer service, I'll go to Ebuyer and use my credit card thank you.

One problem... I managed to upset our assistant Systems Admin in the process (Laura) it wasn't her fault, I did pester her and it all got got pretty confusing and I got pretty annoyed at softcat, I expect it rubbed off. Damn it. So I have some crawing to do. In summary, Softcat you are useless and your customer support is shit. You have cost me £1000 and I've upset Laura which I really didn't want to do :(

Thursday, 2 February 2012

Fanatical Day Out

I went to visit Rackspace at their UK office today, ostensibly to discuss new hosting options and provide due diligence for our current solution. It was a genuinely interesting trip; I thoroughly recommend a visit if you get the opportunity. We discussed at length different virtualisation options with Bruce who is one of their pre sales technical support guys. Again I ended up learning a thing or two about oracle, VM-Ware and SAN utilisation. Its all about the IOPS!

We were shown around the offices by Patrick Williams whom I have dealt with at Rackspace for about 3 years now, we have sparred over pricing every time; some say haggling, some say refinement of solution architecture!

The Rackspace offices themselves are pretty cool. 700 people in one office and every one a smiling face (despite our bad taste in suits!) and Rackspace themselves are not only fanatical about their client service and support, but also about their employee support.

After visiting Rackspace we visited Titan internet (now part of the Iomart group) who have provided hosting for part of our services for a few years now. It was unfortunate that the monitoring system went down during the day, locking itself up whilst still responding to ping requests. No real hassle as during the day a our intensive glassfish consoles give the developers far greater granularity, but annoying none the less as our SMS and email alerts were not online whilst we were away from the office. I spotted it fairly early as we didn't get the normal 7:30 alert as the indexes were rebuilt, but it's a manual process that needs a sysadmin to start it. Somewhat of a highlight that we are awfully dependent on the skill of our internal personnel, something we really need to automate so we can concentrate on innovating rather than managing what we already have!