Category Archives: Windows Administration for the DBA

Precision in Date/Time Data Types

There is a general misconception, that is common across many different programming/scripting languages, regarding Date/Time data types, their precision, and how this differs in theory versus practice.  For the sake of this article, I will be discussing the following data types: DATETIME in T-SQL, DATETIME2(7) in T-SQL, and DateTime in C#.NET.

Data Type Precision (Theoretical Precision)

Data Type Theoretical Precision
SQL DATETIME 3.33 ms
SQL DATETIME2(7) 100 ns
C# DateTime 100 ns

When viewing the information presented above, the natural assumption is that each of these data types will give you a unique value at intervals of their precision.  For example, if you were using a SQL DATETIME and queried the time every 1 ms, you would expect to see a unique value roughly once out of every three queries.  The same would be expected of the higher precision data types, should they be queried on relatively similar timelines.

This, unfortunately, is not how Date/Time data types work.  This theoretical precision is representative only of what the data type structure is physically capable of holding, not of what the variable will ever practically contain.

Operating System Precision (Practical Precision)

The important factor that most programmers forget is that every program/query/script is running in the context of some Operating System and these Operating Systems are further running in the context of a processor and RAM.  The processor, while very fast, is consumed with many different actions happening to handle multitasking loads, and thus cannot dedicate a large percentage of its cycles to updating a clock.  The way processors get around this issue is by updating a portion of memory with the current date and time, at some predetermined interval.  The Operating System exposes this data through any one of several counters.  When a program requests the current time from the OS, the OS gives the program the latest reading of the “current” time.  In this way, the counter can be said to be highly, but not perfectly accurate, and highly precise to within some threshold (which we will see next).

When a program requests the current date and time from the operating system, it does so through a built-in function/method.  SQL DATETIME can get its data from GETDATE(), SQL DATETIME2(7) should get its data from SYSTEMDATETIME(), and C# DateTime can get its data from DateTime.Now.  GETDATE() hits an OS counter that has a precision of approximately 15 ms, while SYSTEMDATETIME() and DateTime.Now hit an OS counter that has a precision of approximately 1 ms.

Q.E.D., when you call GETDATE(), you will receive a data value that, while capable of holding a time down to a precision of 3.33 ms, will actually only be precise to about 15 ms (in Windows 2000).  In our previous example of querying every 1 ms, you would only see a unique value roughly once out of every 15 queries, as opposed to the 3 we previously would have assumed.

Demonstration

In order to see the effect we have discussed, the following code will capture the number of times we saw unique values and the average time between unique values.

-- SQL GETDATE
DECLARE @TimeStart DATETIME
DECLARE @Time DATETIME
DECLARE @TimeEnd DATETIME
DECLARE @I INT
DECLARE @Count INT
 
SET @I = 0
SET @Count = 0
SET @TimeStart = GETDATE()
SET @Time = @TimeStart
 
WHILE @I < 10000000
      BEGIN
            SET @TimeEnd = GETDATE()
            IF @TimeEnd != @Time
                  BEGIN
                        SET @Count = @Count + 1
                        SET @Time = @TimeEnd
                  END
            SET @I = @I + 1
      END
PRINT CAST(@Count AS VARCHAR) + ' unique values'
PRINT CAST(DATEDIFF(millisecond, @TimeStart, @TimeEnd) / CAST(@Count AS REAL) AS VARCHAR) + ' milliseconds'
 -- SQL SYSTEMDATETIME
DECLARE @TimeStart DATETIME2
DECLARE @Time DATETIME2
DECLARE @TimeEnd DATETIME2
DECLARE @I INT
DECLARE @Count INT
 
SET @I = 0
SET @Count = 0
SET @TimeStart = SYSDATETIME()
SET @Time = @TimeStart
 
WHILE @I < 10000000
      BEGIN
            SET @TimeEnd = SYSDATETIME()
            IF @TimeEnd != @Time
                  BEGIN
                        SET @Count = @Count + 1
                        SET @Time = @TimeEnd
                  END
            SET @I = @I + 1
      END
 
PRINT CAST(@Count AS VARCHAR) + ' unique values'
PRINT CAST(DATEDIFF(microsecond, @TimeStart, @TimeEnd) / CAST(@Count AS REAL) AS VARCHAR) + ' microseconds'
// C# DateTime
DateTime timeStart;
DateTime time;
DateTime timeEnd = DateTime.Now;
 
int i = 0;
int count = 0;
timeStart = DateTime.Now;
time = timeStart;
 
while(i < 10000000)
{
       timeEnd = DateTime.Now;
       if(timeEnd != time)
       {
              count++;
              time = timeEnd;
       }
       i++;
}
 
Console.WriteLine("{0} unique values",count);
Console.WriteLine("{0} microseconds", ((timeEnd.Ticks - timeStart.Ticks) / count) / (TimeSpan.TicksPerMillisecond / 1000));

From executing these I found the following results:

Data Type Theoretical Precision Practical Precision
SQL DATETIME (on Win2000) 3.33 ms 15.5556 ms
SQL DATETIME (on Win2K3) 3.33 ms 3.33333 ms
SQL DATETIME2(7) 100 ns 1003.71 µs
C# DateTime 100 ns 1000.21 µs

From these results, we can discern a couple things.  First, the counter that SQL Server accesses in Windows 2000 has a practical precision of approximately 15 ms, while the counter that SQL Server accesses in Windows 2003 (and beyond) has a practical precision of approximately 3.33 ms, which is basically the same as the theoretical precision.  Secondly, In both the case of SQL DATETIME2(7) and C# Datetime, the practical precision is 1 ms, which falls woefully short of the 100 ns theoretical precision.

Conclusion

Developers should be aware of what their data types and functions are doing under the surface.  In the case of SQL DATETIME2 and C# DateTime, developers must keep in mind that the true precision of the system date and time retrieved through those data types only has a precision of 1 ms.

Problems with TCP Chimney Offload (TOE)

Over the past few years, several of my servers have had odd intermittent network connection issues where user sessions would “Time-Out” immediately, even when their timeout was set to 30 seconds (or more).  After digging into the problem from multiple angles with my network guys, I stumbled onto an article that showed promise.  I am re-publishing this information here for quick reference and in case someone else has a need to investigate this as a possible issue/solution.  Please see this TechNet entry for more information.

The TCP Offload Engine (TOE) is intended to alleviate problems with high connections by offloading certain workloads onto hardware.  While this is a great idea in theory, in practice it can sometimes cause more problems than solutions.  It is fairly non-interruptive to try turning the TCP Chimney Offload off to test if this is a cause of a strange network connection issue you may be having, so it is worth a try.

First, check to see if you have TCP Chimney Offload enabled:

netsh int tcp show global

A list of global parameters will be returned, look for the one that says, “Chimney Offload State”.  If it is set to anything other than “disabled”, try setting the parameter to “disabled”.  You will need to be in an elevated command prompt to issue the following command:

netsh int tcp set global chimney=disabled

After issuing this command, observe your system to see if you have a change, either for better or worse, with regard to your network connections.  Again, please see this TechNet entry for more information.

Resolving Windows Authentication Annoyances using Klist

In my SQL environment, I manage all of my SQL Server rights by Windows Authentication, based upon Active Directory groups. This allows me to centralize my security administration into groups for specific types of users within my environment. I also get the benefit of my developers not ever accessing my databases with connection strings containing a clear-text password to my databases.

I have had two specific issues arising from managing my security in this way. One of my problems is that this allows my domain administrators to have some level of control over who might have access to a database. I have, on at least two occasions, had an over-zealous admin assign rights to a user “because the user asked for it” without my knowledge. I have already posted my solution to this problem in my post, Monitoring Active Directory Groups for Windows Authentication in SQL Server Using PowerShell.  In this post, I would like to discuss my other issue, propagating changes in Active Directory quickly.

When I first started using Windows Authentication for my SQL Servers, based upon Active Directory groups, I would notice that I would add a user to a group in Active Directory and it would take a long time before the user was actually able to use the rights; sometimes they even had to reboot.  This led to a lot of frustration on both my side and the users’.  It even caused us to jump back to SQL Authentication on some time-sensitive rollouts, because we had no idea why the Active Directory rights were “not working”.  So, after a few embarrassing episodes, I decided to dig into why this was happening.

To me, it made no sense why a Microsoft core enterprise system (SQL Server) talking to another Microsoft core enterprise system (Active Directory) couldn’t figure out that new permissions had been granted to a user’s group.  So, I dug a little into how Kerberos works and found that the group membership is passed in the Kerberos ticket, not just a reference back to Active Directory (which I had thought).  So, this meant that when a ticket was granted to a user’s session, the groups that the particular user was a member of were held until either the ticket was cleared or expired.

It can be useful to see what tickets you have open, which you can do by typing “klist” into a command prompt.  This, however, only lets you see your current tickets.

So, that led me to looking into what actions cause your Kerberos tickets to regenerate.  Remember that little message bubble that shows up in one of your sessions right after you have changed your password?

When you lock and unlock your computer, you are causing Windows to request new Kerberos tickets.  This also explains why rebooting a user’s machine caused the rights to start working, even though it was complete overkill.

Another way to force Windows to request new Kerberos tickets is to run “klist purge” from the command prompt.  This explicitly asks Windows to dump your currently Kerberos tickets and thus, request new ones.

Although this is a simple problem, solving it finally relieved a nagging headache I had experienced from time to time.