Thursday, March 14, 2013

SQL Saturday Richmond: Downloads Available

I want to thank everyone involved in the Richmond, VA SQL Saturday this past weekend. It was a great event, and I enjoyed both speaking and meeting with everyone.

For those that may have attended one of my sessions, I wanted to let you know that I’ve uploaded the slide decks and demo materials. Note that any small applications I built were compiled using .NET 4.5 and Visual Studio 2012. If you drop me a note on here or in an email I’ll send you the source code. It really isn’t anything special, except for the one I built for wrecking tempDB. That one is actually sorta cool.Winking smile

Below are the links to where you can download the .zip files:

Thanks again everyone, and I hope to see some of you out at other upcoming events!

Monday, January 21, 2013

3 Reasons to Upgrade SQL Sentry Now

Something that always amazes me no matter how long I have been in technology is the rather large pool of folks that never make it around to upgrading their tools. When it comes to SQL Sentry, we love for you to upgrade for your benefit and ours, and we really work hard to make it easy to do so. So here are some great reasons to upgrade your SQL Sentry environment just as soon as you are able to.

3 – Help Us, Help You

This sounds cheesy, but it is 100% true. When the majority of customers are on the latest version, and you are 1 or 2 major releases behind it will almost always be more difficult to support. Things change, sometimes major things, and having to travel virtually back in time to see how it used to work takes more time and effort. If you want help in the fastest possible time, please upgrade to the latest version by logging onto www.sqlsentry.net/portal.

2 – That Bug You Found Might be Fixed, or That Feature You Wanted Might be Complete

Unfortunately no software is perfect. If it were we might be running from Terminators right now! We take software defects seriously, and we consistently release incremental builds to fix bugs and provide minor improvements. We also take feedback from our customers very seriously, and when you ask for something we will tell you when it is ready. Any new feature or defect is listed in the change list. If you have a question about something on the change list, just email us at support at sqlsentry.net. We are happy to explain in detail any bug fix or feature listed.

1 – You Already Paid for It

New version releases are rolled up under your annual SQL Sentry software support agreement. Besides having full access to our exceptional (and good looking!) support services, this covers all newly released SQL Sentry builds from minor updates to full blown major releases. Even if your maintenance contract expires tomorrow, you can upgrade today!

Honorable Mention – Do it for Kevin!

Every time someone needs support for something that has already been fixed, Kevin Kline (b|t) loses another hair! Think about Kevin’s head the next time you ignore the new version in the Version Checker!

Friday, August 17, 2012

My Perspective: The Top 5 Most Common SQL Server Performance Problems

What Prompted This Post?

As lots of you know already, Kevin Kline (b|t) has joined us here at SQL Sentry. I’ve followed Kevin myself for a good while, so I’m really excited to have him here with us.

One of the first things Kevin asked of me was to list the top 5 technical issues we see while providing customer service for SQL Sentry. For our Client Services team, technical support is far more than just supporting our own software products or managing licenses. Everyone on the team is deeply trained in SQL Server, Windows and Microsoft .NET. In fact, in general, any member trained on our Client Services team should be able to walk into the role of a Windows sysadmin, SQL Server DBA or .NET Application Developer with very little adjustment effort.

When presented with a technical issue or question, we try to see past the surface, and read between the lines. Many technical issues or questions are not rooted in what seems apparent on the surface, and the Client Services team is expected to work to resolve the root problem rather than simply addressing the immediate or obvious symptom.

As a result of this expectation, we do come across a variety of technical issues with both Windows and SQL Server, and I thought that filling Kevin’s request for a "Top 5" would make for an interesting blog post. I’m starting with 5 SQL Server Performance issues, and I’ll have another post later on 5 general technical issues that may or may not have anything to do with SQL Server.

I’ll make this a count down from 5 to 1, with 1 being the most common technical problem we see.

Number 5: Tempdb PAGELATCH Contention

Jonathan Kehayias (b|t) covers this topic very well in a great article on Optimizing tempdb configuration with SQL Server 2012 Extended Events, so I’m not going to attempt to go into it very deeply here, but I will talk about my experience with it.

I’m starting to see this more and more. It is usually with a system making heavy use of tempdb for some type of ETL process. This is especially common if it is an ongoing "real-time" style ETL process. I’m not saying data loads are the cause of this by the way; I’m just relaying my observations.

The symptoms of this can vary, but some things are always the same. High PAGELATCH waits in tempdb and poor performance recorded for processes using tempdb. I’ll typically follow the waits to Top SQL in Performance Advisor, and see lots of queries that use temp tables listed in Top SQL. These queries usually run in milliseconds, and should never be counted among the "Top SQL" for the server. This can have people feeling like these queries are a large part of the problem, but that is not necessarily the case at all. The queries are the victims of the real problem.

Once I’m suspecting this to be the case, I will usually jump to the Disk Activity tab in Performance Advisor to see how tempdb is configured. Most times I actually see the same thing: A busy tempdb with a single data file defined. From here I’ll usually recommend reconfiguring tempdb, and direct them to a resource like Jonathan's article for more information.

Number 4: Expecting Auto Update Statistics to Keep Statistics Updated

The problem here is that the thresholds for triggering auto statistics updates end up being the same in most cases, even for a very large table. Without going into a very deep explanation, the threshold is ~20% of the rows in the table. So on a really big table it takes a lot of data change to trigger an update. Kevin Kline has a nice, easy to follow explanation of this here as well.

The reason this makes the list is that DBAs seem really surprised to find out that the auto update isn’t taking care of things the way the name implies. Then there are also many dbas that believe it should be handled by their maintenance job. Then after looking at the maintenance, they are doing index reorgs most of the time, and that won’t update the statistics either (though a rebuild will). I also want to note here that if you are using the Fragmentation Manager feature in SQL Sentry 7.0 and higher, you can have a running history of when your indexes were reorganized rather than rebuilt. This can help you decide whether the problem you’re seeing could be related to auto update not happening.

The lesson here is really to keep an eye on statistics, and make sure they’re updated regularly, especially on large tables, which are becoming more and more common as time goes on. Another option here can be to use trace flag 2371 to actually change the formula used to trigger the update. The nicest explanation of this option I have found is at this blog post by Juergen Thomas.

Number 3: The CXPACKET Wait Type

I would say that this is hands down the single most common wait type I see on larger SQL Server systems when someone asks me to look into query performance with them.

There is a lot of information out there on how to deal with this, but sadly I still see a lot of people make the initial assumption that the problem should be solved by having either the query or the entire server set MAXDOP to 1. More often than not the problem can be handled by proper indexing or statistics maintenance. It could also be that the plan cached for this query is just not optimal, and you can mark it for recompile using sp_recompile, set recompile at the query level, or just evict the plan using DBCC FREEPROCCACHE with a plan handle. It is best to exhaust these options before deciding to change MAXDOP to 1 because you could be throwing away a lot of processing power without realizing it.

Paul Randal (b|t) has a great survey on his blog here that seems to support what I’m used to seeing as well. In fact, he’s the one who first taught me that MAXDOP 1 is not necessarily the answer to this.

Number 2: Misunderstood “Timeout Expired Prior to the Completion of…”

This one is huge. Outside of some very edge case behavior, there are two basic types of timeouts you *might* deal with for SQL Server. These are connection timeouts and operation (or query) timeouts. In both cases these are values set by the client connecting to the SQL Server. On the server side, there is a remote query timeout setting, but this is the very edge case I mentioned and will leave that for another discussion.

I’m going to focus on operation timeouts, since they are the most common. Operation timeout errors from various software tools might be the most misunderstood situation I come across. The cause of these really boils down to one simple thing though: The client executing the command has set a maximum amount of time that it will wait for the command to complete. If this maximum is reached prior to completion the command is aborted. An error is raised from the client.

Many times the timeout error will induce a panic mode, because the error can look a bit intimidating. The reality is, though, that this is not much different than hitting the stop button in SSMS because the query was taking too long. In fact, it will show up exactly the same in a profiler trace with Error = 2 (Aborted).

So, what does a timeout like this really tell us? It tells us that queries are taking longer than expected. We should go into “performance tuning” mode rather than “something is broken” mode. The error information from the client is really just some good information on where you might start to focus your tuning efforts.

If you receive timeout errors from the SQL Sentry monitoring service, and one of the servers you are monitoring is the source, this is not telling you that SQL Sentry is broken. This is SQL Sentry telling you that this server is experiencing performance issues. Again, it is time for “performance tuning” mode. These errors could be easily consumed internally, and retried later, but this would be doing our customers a huge disservice. We believe that you should know about *any* potential problem on your monitored server, even if it is SQL Sentry encountering the problem.

Incidentally, this is true for SQL Sentry, just as it is for any other system that uses an RDBMS for a repository your SQL Sentry database needs some TLC now and again. Without it you may indeed experience some timeouts from your SQL Sentry client. We spend a lot of time tuning our queries for performance before they ever make it out the door, but proper maintenance will ensure they continue to run as intended.

Number 1: Memory Pressure

This is the big one. As soon as Kevin mentioned wanting this list it’s the first thing that popped into my head. Not only because I see it so often, but also because it is so often mistaken for poor disk performance.

There are lots of caches in SQL Server, but the most well-known is the data cache (aka buffer pool). The easiest way to describe the data cache is that it is the data stored in memory, rather than persisted to disk. Being able to store lots of data in memory long term is desirable because working with data in memory is generally much quicker than having to perform physical IOs.

I could turn this post into a very long discussion on memory pressure in SQL Server at this point, but I promise I will try to avoid that. There is already a ton of information available on this subject, and that is not really the intent of this post. What I will say is that, usually, memory pressure manifests as a few different symptoms. When viewed individually, some of these symptoms can lead you to incorrect, and sometimes costly, conclusions.

The two misleading symptoms are that you may start to see higher than normal latency across the disk subsystem, and you may start to see abnormally high waits related to disk activity. If you look at nothing but these two symptoms, you may come to the conclusion that you need to work on your disk system.

This is why being presented with all relevant metrics on one dashboard is so important. You have to look at the bigger picture, and having the memory-related data available along with the disk activity and waits helps to paint a clearer picture of what is really going on.

Typically what I’ll see (along with the disk waits and disk latency) is a PLE (Page Lifetime Expectancy) that is fairly low for this server. I describe it this way because what is good or bad for this value really “depends”. The larger your buffer cache is, the higher your “critical” threshold will be for PLE. The more data there is to churn in and out of the buffer, the worse off you will be when the “churn” actually happens. Another consideration is NUMA. The way the PLE counter is calculated can cause this value alone to be very misleading when multiple NUMA nodes are involved, as described by Paul Randal in a blog post about Page Life Expectancy isn't what you think... Luckily in SQL Sentry 7.0 and higher, you can actually see where PLE is for the individual NUMA nodes in history mode, which makes this a bit less of a problem.

I’ll usually also see consistently higher lazy writer activity, and SQL Server page faults (SQL Server going to disk). Sometimes I’ll see what I call buffer tearing. It’s basically when the data buffer is up and down frequently creating a jagged (or torn) edge on the history chart in Performance Advisor. Finally, I may also see an abnormally large plan cache reducing available memory for the data cache.

All of these things together spell memory pressure, and there are various ways to deal with them, but the important thing to note is that this is not a disk issue. It’s not saying that your disk system is necessarily wonderful either, but I am saying I wouldn’t call up your SAN guy and order a bunch of new hardware based on this situation. Once you get the memory pressure situation under control, SQL Server will not need to go to disk as much, and the few symptoms related to disk may disappear entirely!

The moral here is really to always consider the full picture of performance, because looking at one thing out of context could severely limit your options for a solution.

Honorable Mention: SQL Server Agent History Retention Settings Unlimited

We see this enough to include it in this list, and I think anyone that uses SQL Server Agent should be aware of it.

In SQL Server Agent Properties, under History, you can adjust retention settings.

image

For some reason, I’ve seen quite a few people set this to unlimited by unchecking both checkboxes. If you do this, and you use Agent jobs frequently, eventually you’re going to run into problems with job history in MSDB, because these tables aren’t really indexed very well. The settings I’m using above are generally fine for most cases, and if you’re using SQL Sentry Event Manager, you’re keeping this information in the SQL Sentry database anyway, so retaining it here is just redundant.

Conclusion

So there are my (current) top 5 most common SQL Server performance issues/topics. For #4 and #5, I actually had to run some numbers to find out what they were, but for the top three, I knew without having to consider it much at all.

Thanks for reading!

Tuesday, June 26, 2012

SQL Agent Alerts & User Defined Errors

Not too long ago I was working with a customer who was having trouble getting a SQL Server Agent alert to fire. At first they thought that Event Manager was misbehaving, but we discovered fairly quickly that the alert itself was never firing at all.

Why Wasn’t The Alert Firing?

With a little more digging I discovered that this was due to the configuration of the alert. The “General” settings were similar to the screenshot below:

image

The key thing to notice here is the “Severity” setting. Severities 0-10 are considered to be informational or to be non-serious, non-system related errors (http://msdn.microsoft.com/en-us/library/ms164086.aspx). These are not logged to the error log by default. Since alerts of type “SQL Server event alert” are only fired based on events that are logged, this is the reason we’re not seeing the alert fire.

So, How DO We Fire The Alert?

Here we were trying to fire the alert based on an error from a RAISERROR statement. The question from this point is, how exactly *can* we get this alert to fire? The answer is straight forward. We need to get the error to log.

This is easily done using the “WITH LOG” option of the RAISERROR statement. The only thing to keep in mind is that for Event Manager to be able to process the alert, we need to raise the error using a valid error number rather than just the generic user error of 50000 that is used by default. For this reason, we need to create a message prior to raising the error, and in my case I’ll remove it afterward. There is probably no need for the removal, but I’m just a little picky about keeping my system data clean. I’m also not building this for a real system, so away it goes as soon as I’m done with it.

Show Me!

A simple script like the following should do the trick:

-- Add my message so that I can have an error number
EXEC sp_addmessage @msgnum = 50025,
              @severity = 9,
              @msgtext = N'This is a user defined error!';
 
-- Use the message number that I just created
-- Sev 9 (http://msdn.microsoft.com/en-us/library/ms164086.aspx)
-- State 0 since we're not getting too fancy here.
RAISERROR (50025, 9, 0) WITH LOG;
 
EXEC sp_dropmessage @msgnum = 50025;

Above we use “sp_addmessage” to add a message for error number 50025. Next I raise the error, with severity 9 and specify that it is to be logged. Finally I just remove the message using “sp_dropmessage”, again because I just want to keep my system data clean.

Note: In SQL Server 2012 you can use the THROW statement to create an error with a custom number without creating a message record as follows:

          THROW 50025, N'This is a user defined error!', 0;

You should remember though that this will always be severity 16, and can’t be used for alerts since only severities 19 through 25 are logged by default as outlined at http://msdn.microsoft.com/en-us/library/aa937483(v=sql.80).aspx, and the THROW statement does not provide an option for logging to the error log.

Now I’ll just run this script a few times, and in the SQL Sentry Event Manager calendar I see exactly what we were hoping for:

image

What Now?

From here I can use the condition and action system in Event Manager to do all sorts of things when this alert is fired. I can run a job, execute a process, email a person or group, execute a TSQL script or just log this to the database.

This is basically how you can get any user defined message to fire an alert that can be monitored by SQL Sentry Event Manager.

Until next time,
-JRH

Friday, November 18, 2011

Customize Your Setup Experience With an MST (sorry, no funny robots…)

An unfortunate, let’s call it a “mishap”, recently had me digging through an MSI (Windows Installer) package to see if I could find the source of the “mishap”, and while this didn’t really have anything to do with SQL Sentry, it did remind me of helping one of our larger customers with automating deployment of their own flavor of setup for SQL Sentry.

I’m going to go through a method you can use to create your own personalized setup experience for a Windows Installer based setup package, but I do want to start with a short disclaimer. There are tools I’m going to talk about that can be used to really back yourself into a corner if you aren’t careful. Before trying any of this at home just make sure you have good backups of your original files, and a nice test environment to use while you work out the details.

What is an MST?

An MST is a transform that can be applied to a Windows Installer setup package (MSI). MSI packages are their own little database, and with the right tools, you can change or manipulate the data in the various tables of the MSI package. Creating a transform lets you apply differences at runtime vs. altering the package itself.

What you need

First, you will obviously need your MSI package. My examples are going to be using the SQL Sentry setup, and you can download that from www.sqlsentry.net/downloads if you have an existing license, or you can sign up for an evaluation at http://www.sqlsentry.net/download-trial/trial.asp. The setup is wrapped in an exe, but that isn’t a problem as we’ll see in a moment.

Next, you will need a tool that will allow you to peer deep into the soul of the MSI and do funny things to it. Microsoft is very good at that sort of thing, and provides us with a tool called Orca with the Windows SDK. You can find information and download links on the MSDN article that covers Orca.exe and its uses here.

Finally, you need a plan. Firing up Orca with your mind set on modifying the developer’s well tested setup package is just not recommended in most cases. There are a few good reasons to do it, and I’m going to be demonstrating one of those, but make sure that you’re going in with a specific goal in mind.

Our Plan

SQL Sentry comes with all of the software bits rolled into one setup package. You can choose to install just the server, just the client or both. Now and again an administrator will distribute the setup to one of their users intending for them to install the client, and they will accidentally install the server as well. It’s not really a problem for them to uninstall the server, but they could prevent this by providing a setup transform that excludes the server by default. That is what I’m going to provide instructions on.

But, this setup is an exe, not an MSI?

That is very true, so we need to extract the MSI. You can do this from the command line using the setup executable itself with the following syntax (I’m using the x64 install for SQL Sentry, the setup file name can vary based on your platform):

C:\> SQLSentrySetup-x64.exe /extract [path]

For me, the command looks like this: SQLSentrySetup-x64.exe /extract “G:\Files\InstallerTransforms”

There may be different ways for different packages to extract an MSI (among other things), but for SQL Sentry this is how it is done. If you are using something else, you may need to check your documentation or ask the vendor.

This will give you a file called “SQLSentrySetup.msi” in [path]. This is the MSI file we’re going to work with in Orca.

What do the insides look like?

Now you want to open the MSI file with Orca. I was able to simply right click the file and select “Edit with Orca”, but you can certainly use Start-> All Programs –> Orca (it has an icon that looks like a tiny Shamu!) and File-> Open if that is not available.

The first thing you might notice is a list of “Tables” on the left:

image

As I mentioned earlier, an MSI package is really just a small database. Most folks reading this are in the database industry, so this is really great. Already you’re dealing with a familiar adversary.

If you take a minute to click through the tables you’ll see they’re lain out in a familiar way with column headers at the top, and rows of values below in a grid.

Now, find the “Feature” table.

SNAGHTML1e90122

This is what we’re interested in. This tables controls the features to be included and their default behavior. You may recognize this as the following screen during a setup:

SNAGHTML7f98e7

Don’t change anything at this point, as that will be changing the base MSI, and we want to apply our change using a transform.

So what about this MST (again, no robots) thing?

Right, so we know how to get into the MSI, but how do we create a transform? That part is pretty easy. With the totally unaltered MSI file open in Orca, look to the menu options at the top. You will see a menu called “Transform”. Select Transform-> New Transform.

The kicker here is that at this point there is really not any visible difference between editing the transform and editing the actual MSI, so just for a sanity check (which I tend to need often) I go to the file menu. The “Save” and “Save As” options should be disabled, and the “Save Transformed As” should be enabled (this can be used to create a transformed version of the MSI if you want).

Now, we can make changes with confidence that we’re working on a transform and not the actual MSI database.

In the feature table, look for the Feature named “Server_x64” with Title “Server Service”. This is the one we want to fiddle with. The Level column tells us whether the feature is included by default. This is documented here. Sometimes folks have the values for this customized, but SQL Sentry does not. The values we can use are as follows:

    • 0: Not listed at all, and not available for install
    • 1: Installed by default (this is what is set already)
    • 2: Not installed by default

I’m going to set this to 2, so that they have it as an option, but they won’t simply install the server by accident because they didn’t know to unselect it:

image

Now I’ll just use the “Transform” menu to “Generate Transform…” to the same folder as my MSI file. I’ll call this one SQLSentrySetup_NoServer.mst.

Something nice in Orca that I should point out is that when you are working in a transform, the values that are different from the original will have a green border around them like this:

image

If you get into a situation where you are creating a very customized setup (as I was with the customer I mentioned earlier) it’s nice to be able to follow that crumb trail of green borders to know where you’ve been messing about.

So how do I use this?

Well, there you are then. You have a setup transform that will do exactly what I said it would. Please come again!

Oh, yes, there might be one thing left to do. I guess we do need a way to actually run the setup package with the transform applied.

That isn’t too difficult either. We just use a command like the following with msiexec:

C:\> msiexec /I [PathToInstaller] TRANSFORMS=[file1;file2;file(n);]

So for me this command looks like this: msiexec /I SQLSentrySetup.msi TRANSFORMS=”SQLSentrySetup_NoServer.mst”. I did not use a full path here simply because I was already running under the context of the location I wanted to work in. If you are just running your command prompt from the default location, you will want to ensure that you provide the full path to where you want to save the MST file. Also, notice that I only have one transform, but you can actually string them together separated by a “;” to apply several different transforms. Information on the command line parameters for msiexec can be found here.

Now, after running this, my setup wizard starts off like normal, but when I get to the component selection screen it defaults to looking like this:

SNAGHTML187d55f

I can pass this out to folks with the MSI and MST out on the network someplace. I can even schedule it for distribution with SMS or something similar.

Conclusion

With a little imagination, and some patience, you can come up with lots of ways to customize the setup package. Again, just remember to keep good backups, and don’t experiment on any system that matters.

If anyone is using SQL Sentry, and has a question about how to customize something specific in the MSI, just ask here or email me at jhall at sqlsentry.net. I’ll do my best to help out.

Until next time,
-JRH

Monday, April 11, 2011

Use Execute Action Parameters for Feeding Events to a Ticketing System

SQL Sentry Event Manager uses a system of conditions and actions for handling events. Basically, you define one or more actions to be taken as the result of a pre-defined condition being met. There are over a hundred pre-defined conditions that range from a SQL Server Agent job failing to a runtime threshold being exceeded for a T-SQL query, to the minimum value threshold for a performance counter being broken. Applicable actions can then be taken once the condition is met. Whether a condition supports a specific action really just depends on whether or not that action makes sense in that context. For example, “Run QuickTrace” is not an action for the “SQL Server: Deadlock” condition, because when a deadlock is detected the event has already occurred, and the QuickTrace would not contain anything relevant.

Two of the actions that are available for just about every action though, are “Execute Process” and “Execute SQL”. These two actions will allow you to execute either a Windows process, or a T-SQL command sequence on a specified server that is watched by SQL Sentry when the associated condition is met. This can be useful for several reasons, but what makes them even better is that ability to pass all of or specific parts of the event data as parameters to be used by that process or T-SQL batch.

A great use for the above feature is in automatic generation of technical incident records for ticketing systems. You can monitor for an event, and have it sent directly to your ticketing system for review by front line technicians providing all the information they need to make decisions without ever touching a keyboard, or even being present for that matter. For the rest of this post, I’ll go over a way to do just that, and hopefully it will spark some good ideas for anyone reading.

I’m going to assume that we’re already monitoring the server with SQL Sentry Event Manager, so from this point we want to choose a good candidate condition. One that is pretty common for a situation like this is “SQL Server: Agent Job Failure.” This condition is met when a SQL Server Agent job that is watched by SQL Sentry Event Manager logs a failure. We’ll enable the “Execute SQL” action for a backup job, as illustrated below.

image
Job, Condition, Action

At this point we’ve got everything enabled, and the SQL Sentry service will attempt to execute a T-SQL command batch whenever this job fails. We do still have something extremely important missing though, and that is what T-SQL to run, and where to run it.

We want to not only call a stored procedure that will create a support ticket, but also pass information from the event that can be used by a technician to locate and resolve the problem. In addition, we want to pass in a custom value of our own that will help the ticketing system classify and prioritize this incident.

The SQL Sentry feature that is going to allow us to do all of this is called “Execute Action Parameters.” You can read our documentation on this feature in our online help here: http://www.sqlsentry.net/help/ug/?System_Parameters.html, but we’re also going to spend some time setting this up and writing some code below.

For the purpose of this post, we’re going to keep it simple. I’ve created a small database called “Ticketing” and added the following table definition:

CREATE TABLE Tickets
(
    TicketID int NOT NULL identity(1,1)
        CONSTRAINT PK_Tickets_TicketID
            PRIMARY KEY CLUSTERED (TicketID),
    Title nvarchar(128) NOT NULL,
    [Description] nvarchar(2000) NOT NULL,
    ClassCD nchar(3) NOT NULL,
    PriorityHint tinyint NOT NULL
)

Obviously, a real ticketing system will require more information, but the basics are here, as well as two columns (ClassCD and PriorityHint) we can use to demonstrate custom user parameters.

I will also use the following stored procedure to populate the Ticketing table:

USE Ticketing;
GO
 
CREATE PROC InsertTicket
    @Title nvarchar(128),
    @Description nvarchar(2000),
    @ClassCD nchar(3),
    @PriorityHint tinyint = 0
AS
    
INSERT INTO [Ticketing].[dbo].[Tickets]
           ([Title]
           ,[Description]
           ,[ClassCD]
           ,[PriorityHint])
     VALUES
           (@Title
           ,@Description
           ,@ClassCD
           ,@PriorityHint)
GO

To this point it’s all very straight forward. We have a data table for storing ticketing data, and a simple stored procedure for populating it.

The interesting part comes when I define the T-SQL to run for my Execute SQL action. Below I will list the raw T-SQL as well as what it should look like in the actions configuration area of the SQL Sentry console. Then I will explain the special elements we see in the code and what they mean.

Entering the T-SQL command for the Execute SQL command is pretty easy. It’s done in the same area of the console application that was used to enable the “Execute SQL” action.

As seen below, you can select which server to execute the command on, and enter the command itself. Remember that only those servers that are monitored by SQL Sentry Event Manager will be listed in the “Server” drop-down.

image

I’m going to select my local server, since that’s really the only one I am monitoring right now, and enter the following for my “T-SQL Command Text”.

EXECUTE Ticketing.dbo.InsertTicket
    @Title = '<%ObjectType%> <%ObjectName%> Failure'
    ,@Description = '<%ObjectType%> <%ObjectName%> has encountered a failure on server [<%ServerName%>]'
    ,@ClassCD = '<%CLASS%>'
    ,@PriorityHint = 2

At first glance, this is just a normal execute statement, but there are some interesting elements within the string literals that go along with event processing in SQL Sentry, and deserve some additional explanation.

First, any time you see the following format <%…%> in this context, it is telling SQL Sentry that we want to pass some part of the event data as a parameter instead of just keeping the literal text. Take for instance “<%ObjectName%>.” Before this command is executed, any instance of “<%ObjectName%>” will be replaced with the name of the object that caused the event. In this case it will be our backup job “Backup User DBs – FULL.” These parameters are all self-explanatory of what information they will inject into the command, except for one. The “<%CLASS%>” parameter is special. The “<%CLASS%>” parameter is what we call a “User Parameter”, and I will go into more detail about that in a bit.

So, given this command, when my backup job fails, I should get a row in the Tickets table telling me that I need to investigate this tragic event. Now I’ll force the job to fail, and we can see it on the SQL Sentry event calendar, and I’ll also list the contents of the Tickets table.

image

Above you can see the job failure and the detail for the steps in the job. This job simply threw an error with the message “CLASS=PRD.” That really doesn’t mean much, but it becomes important when we look at the user parameter in a bit. Now let’s see what we end up with in the Tickets table below:

image

My execute action parameters were replaced by meaningful information about the event. I know what job failed, and what server it failed on, and I can go investigate what happened using my SQL Sentry calendar view.

If this were a database for a real ticketing system, I’m sure several members of the technical staff would have seen it, and it would be classified and prioritized. Notice the “Class CD” which I mean to stand for “Class Code” and “Priority Hint” which I intend to be used to hint at the priority level.

Let’s assume that “PRD” stands for “Production” in a list of codes somewhere, and that “2” is hinting at a priority of 2. You can see how I could send in a custom parameter from my event output, or just a literal value, such as the priority hint.

The error I raised in the job step is where I got the ClassCD from. I could just as easily have done this with a code comment, print statement, or other mechanism since all I need for this to work is for the NAME=VALUE (in this case CLASS=PRD) set to be somewhere in the output of the event. I put it in the error message here because I knew I was going to make the job fail, and it was just quicker. It’s really just a T-SQL RAISERROR statement. I have it listed below:

RAISERROR ('CLASS=PRD', 16, 1);

This is the user parameters feature that I was discussing earlier. I could embed any number of these into the output of an event, and use them to control processing of the specialized event data upon generating the support ticket. All that is required is that NAME=VALUE pair. Your parameter name will be in the following format: <%NAME%>. In my case it was <%CLASS%>.
 
And that pretty much covers it. You can imagine some of the things you can do with this. You can even get really creative, and pass the entire event message in to parse out pieces that we may not have thought about for pre-defined parameters.
 
Now, there is a second action that these parameters can be used with called “Execute Process”. This action can be used to execute a Windows process, but the parameters work the same way, except in that case they would be command line switches. Again you can find the details on usage in our online documentation.
 
I hope that this tutorial sparks some great ideas for SQL Sentry users, and as always the SQL Sentry support team is happy to help with your implementations of SQL Sentry features, so just ping us if you are interested in trying something like this out.
 
Until next time,
-JRH

Tuesday, December 14, 2010

Capture SQL Server Agent WMI Event Alerts with SQL Sentry Event Manager

SQL Sentry Event Manager is able to monitor SQL Server Agent Alerts and react to them out of the box. One limitation with that functionality creeps up when dealing with Agent alerts that are set to fire off of WMI events. While WMI event based alerts aren’t what I would call common place, they do make it easy to do some interesting things with an Agent alert that might normally take a bit more customization to do.
One thing that I know we can alert on using WMI events is when a database is dropped, so to keep the scope of this post small we’ll be using that for our Agent alert.
The first thing you want to do is create the alert using SQL Server Management Studio (SSMS from now on). In the SSMS Object Explorer look for the SQL Server Agent node, then expand it until you see the folder named “Alerts”. Right click the “Alerts” folder and select “New Alert…”. You should get a dialogue similar to the one below:
image
First give the Alert a name. You can call it whatever you want, but try and keep it simple, because we’re going to use this name someplace else later, and the simpler it is, the easier it will be to use. I simply named mine for the purpose it will serve “Database Dropped”.
Under Type select “WMI event alert”. Namespace should default to “\\.\root\Microsoft\SqlServer\ServerEvents\MSSQLSERVER”, but if it doesn’t you want to enter that for the Namespace field. Last, under Query, enter “SELECT * FROM DROP_DATABASE”.
In very short terms, we’re telling SQL Server to run an asynchronous query against the SQL Server WMI Provider that will return a row when someone executes a “DROP DATABASE” command. It’s not my goal here to get too far into the subject of WMI or even the SQL Server WMI Provider for Server Events, but you can find a wealth of information on it at http://msdn.microsoft.com/en-US/library/ms180560(v=SQL.100).aspx.
Now, just click ok, and leave SSMS running. The alert will actually be fired as is, but what we’re going to do is create a custom alert trap that will feed SQL Sentry enough information to display this alert on a calendar and perform actions when conditions are met involving the alert. We’ll come back in a bit, and do one more thing to it, but for now it is configured the way we want.
The way we’re going to have the Agent alert tell SQL Sentry all about the event is with an Agent job. An alert already watched by SQL Sentry uses the SQL Sentry Alert Trap Job that is maintained by SQL Sentry. We’re going to create a special alert trap job, just for our alert, that does some of the same things the default SQL Sentry alert trap does, but provides some different information. The most important piece of information is going to be the name of the Agent alert that this event data is from.
So, back in SSMS I create a new job called “Database Dropped Alert Trap”. I give the job one “Transact-SQL script” step and drop the following code into the "Command field:
set quoted_identifier off

 

begin tran

 

declare @msg varchar(256)

 

set @msg = 'Database [' + '$(ESCAPE_SQUOTE(WMI(DatabaseName)))' + '] was dropped by [' + '$(ESCAPE_SQUOTE(WMI(LoginName)))' + ']  from computer [' + '$(ESCAPE_SQUOTE(WMI(ComputerName)))' + ']'

 

exec msdb.dbo.spTrapAlert_20 '50000', '0','$(ESCAPE_SQUOTE(WMI(DatabaseName)))', @msg

 

update msdb.dbo.SQLSentryAlertLog_20 

set AlertName = 'Database Dropped' 

where ID = @@IDENTITY

 

commit tran
The above code may look a little complicated, but all we’re really doing is grabbing some basic information from the WMI event row. This is provided to us by SQL Server Agent, and is accessed using the bits formatted like “(WMI(XXX)))”. Those have to be escaped a certain way, thus the bits that look like “$(ESCAPE_SQUOTE”. Then we’re using the stored procedure SQL Sentry uses to add the alert event to the alert log table in MSDB. Finally, we update the most recently added row for Alerts giving it the name of our Alert (in my case ‘Database Dropped’), so that the SQL Sentry polling service can identify and associate the object that generated the event data. This all happens inside of a short transaction to make sure it all runs as a singular operation.
Don’t create any schedule for this job. Remember, it will be run as the target for our Agent alert, so no schedule is necessary.
For the next part, we need to perform the steps in order, so I will number them.
1.) Use the SQL Sentry console application to ensure that the new SQL Server Agent Alert (for me it’s named “Database Dropped”) is watched by SQL Sentry Event Manager.
2.) Back in SSMS, right click the agent alert and select properties.
3.) In the alert properties dialogue, select “Response” from the left side panel. If you have done everything to this point, it should look like the image below:
image
4.) In the drop-down under “Execute job” select the Agent job that we created special for this alert:
image
5.) Click ok.
That is all there is to it. Using the code from above for our job step, whenever someone drops a database from this server, the alert should fire and you will get an event in SQL Sentry telling you what database was dropped, what login executed the drop command, and what machine it was executed from. You can use the standard SQL Sentry Event Manager conditions and actions off of this alert, including Send Email and Send Page to be notified proactively.
You can experiment with other WMI events as well such as CREATE or DROP LOGIN. The WMI query is what needs to change, as well as some of the WMI properties gathered in the alert trap job step.
If anyone out there wants some assistance getting this set up, or if you just have questions, head over to forum.sqlsentry.net and post a new thread or just email us at support at sqlsentry.net.
Until next time,
-JRH