Thursday, May 11, 2017

Adventures in SQL Server Licensing

Over the weekend, we upgraded from SQL Server Standard Edition to Enterprise Edition. (We also upgraded from 2008 to 2016, hooray!) There were of course a lot of factors driving this, but one of the more compelling ones was the desire to utilize all the processors on the host machine. SQL Server Standard is limited to 24 logical processors (official source: Compute Capacity Limits by Edition of SQL Server - see the chart near the bottom of the article). SQL Server Enterprise Edition however can use as many processors as the host machine has to offer (see same source). Our host machine has 96 logical processors, so we were only using a quarter of the capacity, so this was an important jump forward.

Imagine our surprise and alarm to come in the morning after the upgrade to see that the server was performing very poorly. We quickly discovered that it was still not using all the processors, and even worse, it had now dropped down to only 20!

After some research, we were able to discover that we had installed the wrong kind of Enterprise Edition! Enterprise Edition comes in two kinds: Server/CAL and Core-based. CAL stands for Client Access Licensing, and is a licensing model that caps how many clients can connect to the server at a time. As you can imagine, this is hard to estimate and significantly limits scalability, so it is not a popular option. Almost everyone goes for Core-based licensing, and that's what we had purchased and needed, but it was not what we had installed. Server/CAL Enterprise Edition is limited to 20 cores (40 if using hyperthreading) [see the footnote to the afore-mentioned chart].

To confirm that this was our issue, we checked the SQL Server logs at when the server started up. We saw this line (emphasis added):
SQL Server detected 4 sockets with 24 cores per socket and 24 logical processors per socket, 96 total logical processors; using 20 logical processors based on SQL Server licensing.
We also checked the edition by running SELECT SERVERPROPERTY('edition') and got Enterprise Edition (64-bit).  This seemed okay, until we realized that we needed Enterprise Edition: Core-based Licensing (64-bit).

We were afraid that we were going to have to reinstall SQL Server, but the fix turned out to be simpler than that. We had to download the right installer, the one for SQL Server 2016 Enterprise Edition Core, and then do an Edition Upgrade from the Maintenance menu. This article has a good walkthrough of how to do it: see the section "UPGRADING SQL SERVER 2008." The screenshots are from an older version of the installer, and it suggests you'll have to enter the product key manually, which you probably won't have to, it will probably be auto-filled. (This MS Docs article gives a more up-to-date and complete procedure, but it doesn't have any accompanying visuals.)

Once we reached the end of the wizard and clicked "Upgrade", the change was very quick, and once the SQL Server service was restarted, it went up to using all 96 cores! We were able to confirm that the edition was now Enterprise Edition: Core-based Licensing (64-bit), and saw the startup message changed to:
SQL Server detected 4 sockets with 24 cores per socket and 24 logical processors per socket, 96 total logical processors; using 96 logical processors based on SQL Server licensing.
It was of course a huge relief to figure this out, but it was hardly a quick process. We were down for almost three hours. So I'm putting this out there in the hopes that other folks won't have to go through the same pain. And it seems like a lot of people have felt this pain - Microsoft clearly needs to do a much better job of labeling and explaining this quirk of SQL Server installation. (Microsoft needs to massively streamline and simplify all SQL Server licensing, but that's a topic for another time.)

So, final take-aways:
  1. When installing SQL Server Enterprise edition, make certain you have the right installer for the license type you have purchased, whether it be Server/CAL or Core-base Licensing.
  2. If you find you have installed the wrong type of SQL Server Enterprise Edition, you can correct the issue by doing an Edition Upgrade from the correct installer.
  3. SQL Server licensing is way too complex.
(Thanks to this DBA.SE answer and this blog post that helped guide us down the right path.)

Saturday, September 17, 2016

4 Things They Don't Teach You About Programming

1. Most of your time will be spent debugging someone else's code, not writing your own.

When you're learning how to program, you write everything from scratch (sometimes to ridiculous lengths). This is good - it's how you learn. But it can set you up for a unrealistic expectations about what real-world programming is like. Most programming involves digging into something someone else wrote years ago and figuring it out because no one really knows anything about it except that it works, but now they need you to change some part of it. It's why programming experts often advise you to code like the person who'll read your code next is a violent psychopath who knows where you live, because one day you will be that person, and you don't want to be driven quite that insane.

See also Orthogonality and the DRY Principle, featuring the Right Honorable Dave Thomas.

2. 90% of programming is converting data from one format to another.

When learning to program, you write Cool Things - binary tree balancers, XML parsers, chess games, even simple databases & operating systems. But when you get a job, most of that stuff is already done, and your job will be to convert one thing into another thing. You gotta convert your objects into database tables. You gotta convert the data coming back from the API into HTML. You gotta convert the values from that ancient file format into something usable. And the list goes on and on.

This doesn't mean that programming is all drudgery! There are lots of interesting, even exciting challenges in this area. It's just not something they cover much in training.

3. Polymorphism isn't that important.

Polymorphism is important: it's one of the most powerful tools you've got in your toolbelt to make software loosely coupled and maintainable. But when you take programming courses they make it seem like programming is the task of building the Unified Class Structure of Everything. In reality, abstract classes and complex inheritance have relatively few uses. Interfaces are the most important aspect of polymorphism, and any programmer worth their salt should know how to use them extremely well. But most stuff beyond that isn't that important.

4. Programming will never stop being a source of wonder.

I've been writing computer programs for 20 years now, and still there are those moments when it works and I go, "no way, I did it!" There's still a bit of magic in it, even after understanding all the math and electricity that's involved, and I've found that that's true for most programmers. It never stops just being cool.

Friday, July 15, 2016

Poking just the wrong spot, or How Two Minor Bugs Generated Some Very Scary Error Messages

So we had a stored procedure that started timing out occasionally in production. We determined the issue was parameter sniffing, so we decided to just add a OPTION RECOMPILE to the query inside it. It was tested, and released.

Upon release however, we starting get an extremely scary-looking error message that was even being entered in the SQL Server log:

SQL Server Assertion: File: <op_ppqte.cpp>, line=12258 Failed Assertion = 'llSkip >= 0'. This error may be timing-related. If the error persists after rerunning the statement, use DBCC CHECKDB to check the database for structural integrity, or restart the server to ensure in-memory data structures are not corrupted.

Naturally we rushed to figure out what was going on. Researching the issue didn't turn up much though - we found a few KB articles that seemed related (like this one and this one), but nothing that described it exactly. As it seemed to be a bug in SQL Server itself, we installed the latest Cumulative Update for SQL Server 2014, but it did not change anything.

As we dug in to try to find a workaround, we discovered there was a different error that had been occurring before we released the change that we had not noticed before:

The offset specified in a OFFSET clause may not be negative.

Now this was easy enough to understand, and we realized that there was indeed a bug in our query. As we worked to reproduce it in our test environments though, we were startled to discover that reproducing it gave us that fatal error! Upon closer inspection of the production error log, we realized that the OFFSET error had disappeared when we added OPTION RECOMPILE.

This led us to believe that the combination of an invalid OFFSET value in a query using OPTION RECOMPILE causes SQL Server to generate a fatal error instead of a specific & useful message. In other words, there's an error in the error handling code.

We fixed the stored procedure to only use valid OFFSET values, kept the OPTION RECOMPILE, tested, and re-deployed. Thankfully, this eliminated all the errors.

This issue has been reported to Microsoft.

Tuesday, June 28, 2016

The Case for Dealing Directly

Update 8/1/2016: After giving this article some further thought, I added another reason.

Suppose you started a new job, and sat down to look over the codebase. As you dug through things, you found that, instead of using public methods and/or properties on classes, they had written a whole reflection framework to reach into objects and modify the private fields. Then, when you asked why they had done something so very peculiar, they responded by saying, "Oh, this way is much better - we don't have to care about the structure of the objects, or waste time writing accessor methods and properties, and we can spend all our time just writing actual code!"

What? No. What?

We could probably spend a whole series of articles explaining why this is a bad approach, but let's just focus on these reasons:
  1. it works against the language's paradigm
  2. it leads to contrivances that don't use the objects very effectively
  3. it makes figuring out the usage and dependencies of objects very difficult
  4. it has security risks
  5. it is more risky to change
  6. it doesn't scale, and the performance could get pretty bad without a lot of ways to optimize it
This is what dynamic SQL is like, and my reaction to it is the same.

Current or former colleagues of mine might quickly note that I have written my fair share of dynamic SQL, and astute readers might note that I have published dynamic SQL on this very blog before. Dynamic SQL, like reflection, is not a bad thing, and has good uses. But it's generally not good for line-of-business applications, for all the reasons listed above.

Dynamic SQL is code generation. When code generation is employed at design (develop) time, it is a very good thing! Intellisense is the most common form of code generation, and it's so fluent & ubiquitous that we barely even think about it anymore. One of the reasons I sing the praises of ReSharper to anyone and everyone who'll listen (and I'm starting to get there with SQL Prompt) is because of the code generation features. They just make life easier, while still giving you the flexibility to write exactly what you need, because you don't have to accept everything they give you.

But code generation at run-time is a different story. Would you trust an application that generates, compiles, and executes code on the fly? That kind of sounds like the behavior of a virus, or at least the work of someone who doesn't yet realize that they'll have to debug that thing somehow. Sure, it's clever, but it's fraught with security, maintainability, reliability, and performance pitfalls.

There are pieces of code that we have to write that are trivial and repetitive, and that's what code generation is for. But the whole reason programmers haven't all been replaced by robots is that writing good code is not a deterministic activity. It requires a fair amount of specialized knowledge. SQL is no different.

Let's consider each one of these points a little more closely.


It works against the paradigm


In other words, just because we can doesn't mean we should. 

To an application, a database is an external resource, like a file system or an API. Interactions to external resources generally need to be coarse-grained, and resources need to be well-encapsulated. This means that while tables are the basic unit of database storage, they are not the basic unit of database interactions. In an ideal application, any discrete action will incur only one database call - e.g., there's only one call when the page loads, and only one when the user clicks the button to update the page. Relational databases are built very unambiguously on the principle that interactions should be atomic so they can be consistent and isolated.

Dynamic SQL works against this paradigm because it wants to deal with the tables as individual units instead of dealing with endpoints (stored procedures) performing atomic operations. The workaround for this is typically to create a transaction in the application code, which often is a performance anti-pattern - it makes the database layer chatty, and holds locks open longer, which leads to blocking & degraded performance. (There are places where in-code transactions make sense, but they are the exception, not the rule.)

It's true that databases are often under-abstracted and have too much repetition, but the solution isn't to go around the database, it's to employ the mechanisms it does have.


It leads to contrivances


I've never met an ORM framework I really liked (and it turns out there are a lot of other engineers who feel the same way; Google it to find out more). You're constantly having to work around them to get things done properly, and the SQL they generate is almost always a classic example of what not to do in a relational database.

Writing effective and appropriate UPDATE, JOIN and WHERE clauses is not a trivial task. Structuring them in the correct manner requires addressing multiple considerations - the impact to/of indexes, the size of the tables involved, the native change tracking features, replication, etc. Dynamic SQL code generators do not provide any abstraction in this regard: they have to get very detailed in order to provide the level of control needed. In other words, they have to re-create the wheel, and in the end their output is no more optimal, and typically less optimal, than writing the SQL directly in stored procedures.


It makes usage discovery difficult


When dynamic SQL generators build statements, the table, view, column, etc. names are coming from diverse locations, and how they are used is often obfuscated. This makes it hard to know which database objects are actually in use. As with any type of code, when it's difficult to tell what's in use and how it's being used, it's difficult to move forward. It significantly hampers development and debugging efforts. Every database I've ever encountered has had many things in it that everyone is 'pretty sure aren't even used anymore', but because discovering dependencies is difficult even under the best of circumstances, nothing is ever done about it. Dynamic SQL's obfuscation of usage just exacerbates this problem, while direct SQL in stored procedures is much more clear and consolidated.


It has security risks


It is often pointed out that the security concerns of dynamic SQL are mitigated by parameterization. This is true, but it's also true that stored procedures mitigate these risks even further. When the SQL code is being constructed on the fly, it still opens up possibilities for injection (and compile errors) that simply don't exist with stored procedures. (Naturally, stored procedures that build dynamic SQL suffer from these same flaws, and in some ways more so - I include them in the ranks of 'dynamic SQL code generators.' Traitors.)

Stored procedures are also a better way to implement the principle of least privilege. When dynamic and/or inline SQL is involved, the application must be given carte blanche to act on the tables. But with stored procedures, the interactions are much more targeted, and (typically) only one object has to have permissions associated to it. It's more maintainable and granular access, two key elements of good security.

These may seem like minor concerns. They are not. Quite frankly, security in every single piece of software on this planet needs to be better - and not just better, but many orders of magnitude better. Good enough security is not good enough.

It is riskier to change


When every operation is being managed by the same code, the risk of changing that code becomes much greater. If a change is made to a dynamic SQL builder for one scenario, it runs the risk of breaking all the other scenarios - in other words, it is harder to keep changes isolated. Say for example you have code that is generating INSERT statements, and you make a change to accommodate one edge case. In doing so, you run the risk of breaking all the INSERT statements in the application. But with stored procedures, a change to one statement has zero effect on similar statements in other procedures. Changes can be kept isolated, the system is more flexible and we can be more confident in our changes.


It doesn't scale


Typically, the only way to optimize dynamic SQL is to pull it out and make it not dynamic. Yes, indexes are always a part of the optimization equation in a relational database, but they're just one tool, and quite frankly they're usually just the first layer. To really address these matters we have to get into the queries themselves, and when we can't directly control the queries, we're at a serious disadvantage.

Also, compilation is not free. Dynamic/inline SQL execution plans can be cached, and stored procedure execution plans aren't necessarily cached, but the former typically aren't and the latter typically are. It's a significant advantage for the DBAs or the DevOps to be able to look up an execution plan by the procedure name, an impossible task with dynamic and inline SQL. Reducing the number of compilations is also a important step in preventing CPU bottlenecks in SQL Server, something that can only be achieved with stored procedures.


(It is not always possible to use stored procedures - after all, some applications have to interact with databases controlled by third parties. But in these scenarios, inline SQL is preferable to dynamic SQL, because it mitigates or avoids most of these discussed pitfalls of dynamic SQL even though it lacks some of the advantages of stored procedures.)

In the end, this can all be boiled down to one simple idea: deal with the database on its own terms. In that reflection framework example in the first paragraph, the coders are effectively trying to treat their class structure as a set of global variables, or a key-value store. Trying to make something act like something it is not only leads to trouble. Relational databases are fundamentally different from imperative object code, and attempts to treat them like they're not are misguided.

Thursday, March 24, 2016

SQL Dependency Hell: User Functions in Computed Columns

SQL Server allows you to create columns that don't contain data, but rather are calculations, hence "computed" columns. For example, say you have a table with an OpenDate and a CloseDate. You could create a OpenDays computed column which returns the value of the function DATEDIFF(DAY, OpenDate, CloseDate). It's useful, logical, and runs fast. (A computed column can just be an expression, for example a column Interest that equals Balance * 0.01, but in this post we're going to focus on columns using functions.) The value of the computed column is by default calculated on demand (e.g., whenever the column is used in a SELECT or WHERE), but can also be marked persisted so the value is actually saved to disk and then updated on recalculation.

This is all well and good for built-in functions. However, when you start using user-defined functions, things can get pretty ugly pretty fast.

Consider this pretty standard user table:

CREATE TABLE [dbo].[Users]
(
 [ID] [int] IDENTITY(1,1) NOT NULL,
 [MembershipUserID] [uniqueidentifier] NOT NULL,
 [UserDomainID] [int] NOT NULL,
 [SecurityLevelID] [int] NULL,
 [FirstName] [varchar](50) NOT NULL,
 [LastName] [varchar](50) NOT NULL,
 [EmailAddress] [varchar](100) NOT NULL,
 [CreationDate] [datetime] NOT NULL,
 [PhoneNumber] [varchar](50) NULL,
 [FaxNumber] [varchar](50) NULL,
 [AddressID] [int] NULL,
 [InvalidEmail] [bit] NULL,
 [ManagerUserID] [int] NULL,
 [HoursWorkedPerWeek] [decimal](5, 2) NULL,
 [LanguageID] [int] NULL
)

Now let's say I want to add a computed column FullName that runs my user-defined function udf_FullName. This function just glues FirstName and LastName together, so it's not doing anything crazy. Adding the computed column is easy:

ALTER TABLE dbo.Users ADD FullName AS dbo.udf_FullName(FirstName, LastName)

Now let's fast-forward three months - the client/business user has decided that it wants all names to listed in "Surname, GivenName" format, so I've got to change udf_FullName. It's a pretty trivial change, so I go in and write my ALTER FUNCTION statement. But when I hit Execute, I get this:

Msg 3729, Level 16, State 3, Procedure udf_FullName, Line 1
Cannot ALTER 'dbo.udf_Fullname' because it is being referenced by object 'Users'.

The function can't be altered because it's being used in a computed column. Hmm. It doesn't really help to consolidate logic if we can't ever change it.

This can be gotten around by dropping the column, modifying the function, and then re-adding the column. If the column value isn't being persisted, this probably won't be a particularly expensive operation. But if there are any views, stored procedures, other functions, etc. that depend on this table and use SCHEMABINDING, you'll quickly run into a cascading chain or updates you have to script. The column will be moved to the end of the table as well, which can cause heartburn for some clients. These are not insurmountable challenges, but they are hassles you just don't need to make for yourself.

Where things can really get ugly is when the function runs a sub-query. I've detailed in a previous post the performance pitfalls of scalar functions running queries, and they become even worse when put into a computed column. A sub-query function used in another query is bad enough, but it's somewhat scope-restricted - it'll only be used in certain circumstances. But with a computed column, the function and its sub-query have the potential to be re-run every single time the table is touched!

Oh, but it gets worse. You can actually get into a scenario where the table and the function have a circular dependency on each other! Consider this function:

CREATE FUNCTION [dbo].[udf_GetUserRoles]
   (@userID int)
RETURNS NVARCHAR(MAX)
WITH SCHEMABINDING
AS
BEGIN
 DECLARE @roleList NVARCHAR(MAX)

 SELECT @roleList = COALESCE(@roleList+',', ' ') + r.RoleName
 FROM dbo.Users u
 INNER JOIN dbo.aspnet_UsersInRoles ur on u.MembershipUserID = ur.UserId
 INNER JOIN dbo.aspnet_Roles r on ur.RoleId = r.RoleId
 WHERE u.ID = @userID

 RETURN @roleList
END

If we ignore the performance, it's not necessarily a bad function. But it's possible to call it from a computed column on the Users table itself!

ALTER TABLE dbo.Users ADD RoleList AS [dbo].[udf_GetUserRoles]([ID])


Fun fact: if you have a database with this setup, and you try to use Microsoft's Generate Scripts tool, it will crash as it gets into an infinite loop of attempted dependency resolution!

Computed columns are nice in theory, but they are difficult to change, especially when functions are involved. For this reason, they should be used sparingly, and they shouldn't use user-defined functions at all.

Wednesday, March 16, 2016

A SQL No-No: Sub-queries in User-Defined Scalar Functions

User-defined functions (UDFs) in SQL Server are a great way to encapsulate and re-use logic. However, they can be mis-used by writing scalar-valued functions that run queries. A thing we sometimes see is a function like this, which runs a query to return a single value:

CREATE FUNCTION [dbo].[udf_GetFeeAmount] 
(
    @AccountID INTEGER
)
RETURNS MONEY
AS
BEGIN

    DECLARE @Result MONEY
    SELECT @Result = NULL

    IF EXISTS(SELECT 1 FROM dbo.Accounts WHERE AccountID = @AccountID AND StatusID IN (1,4,8))
    BEGIN
        SELECT @Result = COALESCE(SUM(Fees.Amount), 0)
        FROM dbo.Fees
        WHERE AccountID = @AccountID
    END

    RETURN @Result

END

The problem with is not necessarily what's been written, but how it gets used. If this was a stored procedure outputting this value or setting an OUTPUT parameter, it'd be great. But say you're going to use this function in a SELECT statement that will return a million rows:

SELECT 
 AccountID,
 dbo.udf_GetFeeAmount(AccountID) AS FeeAmount
FROM dbo.Accounts

The function will be run for each of the 1,000,000 rows, thus generating an additional 1,000,000 queries. Naturally, the performance of such an operation will be miserable. The SQL Server engine can't run the function query in parallel, it has to run it again for each row emitted.

 How then do we encapsulate logic like this in a way that still gets decent performance?

The answer is often a view or a table-valued function (which in many ways is a parameterized view). For example, this scenario could be re-written as this view:

CREATE VIEW [dbo].[vw_AccountFees]
AS
SELECT 
  AccountID,
  ISNULL
  (
    SUM
    (
      CASE WHEN Accounts.StatusID IN (1,4,8) THEN Fees.Amount ELSE 0 END
    )
    , 
    0
  ) AS FeeAmount
FROM dbo.Accounts
  LEFT JOIN dbo.Fees
    ON Accounts.AccountID = Fees.AccountID
GROUP BY 
  Accounts.AccountID

This view is simplistic for the purposes of this example - you could/should expose multiple Accounts columns from this view, so as to avoid the need of JOIN-ing to [dbo].[Accounts] again for other columns.

Writing the query this way allows the database engine to build an execution plan with parallel elements, thus allowing greater throughput and speed, and lower I/O cost.

Now, neither views nor table-valued functions are magic bullets. Table-valued functions don't always work so well in CROSS APPLY queries, and neither approach can save you from a query that's just badly written, or the fact that a table needs better indexing. But they open up optimization and scaling possibilities that querying in scalar functions simply doesn't give you.

The question may be asked: what are scalar-valued user-defined functions good for then? The answer is: for encapsulating non-table-based operations. Say you need to sanitize a column by doing multiple REPLACEs on it, and will need to do this sanitization in multiple places, or even on multiple columns. This would be an excellent candidate for logic that should go into a scalar function. I have written many scalar functions that do date operations that would be tedious to retype all the time, like this:

ALTER FUNCTION [dbo].[udf_GetMonthStart]
(
  @Date DATETIME
)
RETURNS DATE
AS
BEGIN 
   RETURN DATEADD(MONTH, MONTH(@Date)-1, DATEADD(YEAR,YEAR(@Date)-1900,0))
END

Scalar-valued functions are for computing scalars, not for operating on tables, and using them to do so works against the database engine and leads to poor performance.

Tuesday, June 16, 2015

Converting from MSTest to NUnit

Ever since I was converted to the one true gospel of Test-Driven Development in 2008, I have had to convert test projects from MSTest to NUnit and back a few different times. After my last time having to do it, I decided it was time to testify.

The Facts

MSTest and NUnit have different names for the decorations on the test classes & methods:

MSTest Attribute
NUnit Attribute
Purpose
[TestMethod]
[Test]
Identifies of an individual unit test
[TestClass]
[TestFixture]
Identifies of a group of unit tests, all Tests, and Initializations/Clean Ups must appear after this declaration
[ClassInitialize]
[TestFixtureSetUp]
Identifies a method which should be called a single time prior to executing any test in the Test Class/Test Fixture
[ClassCleanup]
[TestFixtureTearDown]
Identifies a method in to be called a single time following the execution of the last test in a TestClass/TestFixture
[TestInitialize]
[SetUp]
Identifies a method to be executed each time before a TestMethod/Test is executed
[TestCleanUp]
[TearDown]
Identifies a method to be executed each time after a TestMethod/Test has executed
[AssemblyInitialize]
 N/A
Identifies a method to be called a single time upon before running any tests in a Test Assembly
[AssemblyCleanUp]
 N/A
Identifies a method to be called a single time upon after running all tests in a Test Assembly

Source: Comparing the MSTest and Nunit Frameworks, by Naysawn Naderi, 1 Feb 2007.
This article contains some information that is no longer accurate, particularly in regards to the NUnit test runner, but this chart is still correct.

There are a few other minor syntactical differences to be aware of:
  1. Methods decorated with [ClassInitialize] in MSTest must have the signature
    public static void MethodName(TestContext context)
    while methods decorated with [TestFixtureSetUp] cannot have any arguments.
  2. MSTest's Assert and NUnit's Assert both have a method IsInstanceOfType(), but the order of parameters is reversed between frameworks.
    MSTest: void IsInstanceOfType(object actual, Type expectedType)
    NUnit:    void IsInstanceOfType(Type expected, object actual)
The Editorial

The debates over whether to use MSTest or NUnit typically revolve around three factors:
  1. Speed
  2. Integration
  3. Ease of use
People are pretty evenly split as to which one is actually faster. Personally I've found that so many factors affect this (the version of Visual Studio being used, the complexity of the tests, the framework being targeted, the version of NUnit) that we have to just call this one a tie.

When it comes to integration, if you're using Resharper, then it's a moot point. Resharper can run MSTest and NUnit tests in the very same test run seamlessly, and with a far superior interface to MSTest. And if you're not using Resharper, well then I just don't know how you do it. I had to go without Resharper for a week at a new job once, and it was like trying to run in waist-deep water. Everything took five times as long, and my confidence in my code was significantly reduced.

MSTest requires at least twice as many keystrokes to write tests than NUnit - everything is more verbose, with no additional benefit (see the difference in method signatures above for a minor example). Plus, NUnit can do this:

[TestCase("ConnectionStringA")]
[TestCase("ConnectionStringB")]
public void CreatePendingPaymentTest(string connectionName)
{
 //setup

 Repository classUnderTest = new Repository(connectionName);
 classUnderTest.CreateRecord();
 
 //assert
}

[TestCase] allows you to run the same test with different settings/inputs.
You can accomplish the same thing with MSTest by writing multiple tests that call the same method with different parameters, but again, more work to get the same result.

So, with speed being equal, integration being a non-issue, and NUnit the easier to use, the verdict is clear: use NUnit. It's available through NuGet when you need to add it into your solution, and you can learn more at NUnit.org. Enjoy!

EDIT (6/18/2015):
The syntactical differences section omitted an item that is now included.