Category Archives: C#

Go, Go, Golang!

WHAT!

 

Yep, you read that right. I’m building tools in Go.

I can hear you now…. But Wes you are a SQL Server guy, a WINDOWS guy! What about C#? What about Powershell? What about ANY THING ELSE???

Making Decisions

Sometimes you make choices, not based on what you want to use but what you must use when building your tool set.

Here was my decision making path I hope it isn’t too complex.

It must run on everything from Windows Server 2003 through Windows 2012 R2 where I don’t have total control over the OS or what is installed on it.

That was it. I had no clue it would be so difficult to accomplish. Let me run down several candidates.

C#/.Net/mono

This is my default go to stack. I have been writing in C# for quite a while and I’m fairly comfortable with the language and love the amount of third party libraries and tooling around it. Oh and ReSharper. Microsoft is also pushing hard to open up C# and all aspects of .Net going so far as buying Xamarin, the developers behind mono. All of that is great! There is still one HUGE downside with the .Net framework. You have to have the .Net framework.

But Wes, the framework has been shipping for YEARS and is already installed on probably 90% of all the servers you are going to be using your tools on! You are absolutely correct, which version though? That is a horrible problem to solve. Pretty much the only way I found to solve it was to only use the 2.0 framework. That meant a HUGE part of the improvements to the .Net framework was now unavailable. Oh yeah, ever try to file a bug report to a third party library developer about a bug that only effects the 2.0 framework? You end up building “new” code and now maintaining legacy code from third parties.

Not all is lost! You can compile a “native app” but that can only target Windows 10, unless you use mono. With mono It is possible to build a stand alone self contained executable for Windows.

I’m saved!

Well…. The up side is I get the new shiny bits but mono doesn’t implement the full .Net framework. Also it is hit or miss on third party libraries and if they will also work with mono. Oh and the build process is right out of the 90’s. If all of that wasn’t enough there are possible licensing issues with embedding the mono run time in your application vs. linking to it as a dynamic library.

Powershell

See Above.

No seriously, it suffers from the same kind of problems. I am back to building scripts that target Powershell V1 and hoping security issues don’t blow up in my face.

Ruby/Python

I don’t want the Ruby or Python folks to flatten my tires, I’m not saying they are the same weakly typed, single-threaded interpreted language. Python doesn’t have Rails. ZING!

All kidding aside, I personally like Python over Ruby but that again is a personal preference. Honestly though I am more old school and really love strictly typed languages.

Both have TONS of third party libraries, that run on Linux or OSX. Windows is a bit thinner though. They both have ways of rapping up their respective run time into an object that looks like a standalone executable. There are several companies running both Ruby and Python on Windows and ship software installers to end users. I know it can be done. I also know it is kinda painful too.

Other Contenders

I didn’t stop there!

I looked again at Nodejs which has tooling to build command line applications. Having built a couple of applications using Nodejs and having no love for the “sideways mountain” that is the async call back pattern of Javascript I gave it a pass.
Lua, another favorite language of mine mostly due to is simplicity and ability to write and then read that same code again later.
Open Pascal/Delphi was given a cursory look but it just doesn’t have the critical mass it once had.
C++ is awesome if you want to crash both your program and the computer. I dare you to figure out what part of the 1000 page specification you aren’t going to use to maintain some level of productivity.
C has a huge draw for me but again it is a double-barrel shotgun but one barrel points backwards.

Finally, Go

I eventually found my way to Googles’ Go language. I had poked around with it several years ago but it was a pain to compile for Windows. Fast forward to 2016 and now Go compiles easily on Windows. Most third party packages are written in Go which means it generally compiles on Windows with zero changes. These executables are statically linked that means executables that will probably be larger than some but come with the bonus of not having to carry around all the run time and libraries or hope they are installed on the machine.
Go, as a language, is simple. It isn’t as terse as say K but it is much closer to C and Python than C# or C++ is.

Go is absolutely opinionated. There is the way I like to write code then there is the Go way. They don’t always agree. You want generics? NOT IN MY LANGUAGE YOU DON’T! Declare a variable and then don’t use it? No compile for you! Want to pick your own way of structuring your source code files? That ain’t going to fly.

These are all choices specifically made to satisfy writing LOTS of code worked on by LOTS of people, a Google’s worth so to speak.

So, why does this matter to me and the hand full of people that may actually have to work on my code?

More than you think. There are suggestions, fences, guide rails and walls in the Go ecosystem. They are there to help prevent bugs and improve productivity at scale. I have worked with a lot of programming languages over the years. My favorite ones allowed me to build stuff quickly and hand it to someone else knowing they could also get up to speed quickly. To that end I am finding that I agree with some of the guiding principles of Go.

Simplicity, safety and readability are paramount.
Minimal design: There is one way to write a piece of code.
It’s about expressing algorithms, not the type system.
Tooling is as important as the language.
Build times shouldn’t take over night.
Compiles naively across all platforms.
Things of interest should be easy; even if that means not everything is possible.

That means I have to give up some control. It also means Go may not be the best choice in all situations. To me Go is starting to feel like T-SQL, it isn’t horribly complicated yet tremendously powerful. I’ll be writing about the good and the bad around Go, or golang if you like, soon. I’m only in my first two weeks with it now.

Building Flat File Connectors Dynamically For SSIS

Building Connectors Is Crap in SSIS – Updated

What else can I say. I finally broke when I had to build a flat file connector with 258 columns that needed to be imported into a staging database. 258 columns… I almost had a stroke. Not only is it mind numbing it’s also error prone. I do dabble in c# so I build a little tool to automate this for me.

Introducing SSISConnectionBuilder

SSISConnectionBuilder is a simple command line tool to ease the burden of building flat file connectors for SSIS. It is WAY WAY alpha but I am working on cleaning up the code. You can pick it up here on codeplex.

I have only tested this on Windows 8/7 64 bit. If you get an error about cannot find DLL you need to install the client development stuff from the SQL Server installer.

You need to generate an excel spreadsheet with four columns. Column,Type,Precision,Scale.

The program loops through the sheet and kicks out an SSIS dtsx file with a single flat file connector defined.

You can choose a delimiter, package name and set the csv file name for the connector. The csv file name doesn’t have to be valid. If you know the csv file will be unicode you need to pass the -u or you will have errors with your connector with the error column being ntext instead of text.
Command line options:
-s, –schemafile =VALUE Your excel schema definition file.
-d, –delimiter=VALUE The column separator you wish to use, usually a comma or pipe.
-p, –packagename=VALUE Name of the dtsx file that will have your connection in.
-c, –csvfilename=VALUE Name of the csv file that your connection will use.
-u, –unicode csv file is Unicode.
-?, -h, –help show this message and exit

Update 7/23/2013

Removed the dependencies on the SSIS SDK the current release doesn’t require any external dll’s to run!

Lan Sleep/Wake Up Tool

Lazy Is The Mother Of Invention

I write little tools from time to time that I need to manage my personal lab. Since my machines are in their own powered and air conditioned closet it isn’t very convenient to get up, open the closet, get slammed by the noise and turn a machine on. And, I don’t want to leave them on all the time if I’m not using them. I’m no Paul Randal (blog|twitter) but I do use between 4000 and 5000 kilowatt hours a month.

Enter Innovation!

So, since I couldn’t find a simple tool to handle this I wrote one! LabWakeUp is pretty easy to use.

Don’t blink, you might miss it

I looks for a text file called servers.txt. This file should have one server name per line. The program loops through them and tries to get a MAC address via system call (a.k.a P/Invoke) to the windows dll that handles ARP calls. It then writes these to another text file called serverMACs.txt that has, you guessed it, a server name and a MAC address if one could be found. You can manually put entries in this file if you like. I personally don’t like hunting around for MAC addresses then typing them into anything if I don’t have to. You only need to fill in the windows user name and password if you want to wake a machine up.

They Are Undocumented Features!

When you start it up nothing is displayed while it goes through the servers.txt file. Again, lazy strikes, I may fix it later it is just annoying and not a “bug”.
In the serverMACs.txt if a server isn’t found it may be a bogus MAC address. This is most defiantly a bug and will be addressed.
The serverMACs.txt isn’t updated if a new MAC is found. You have to delete the entry and let it rediscover it.

Improvements Are Coming… Eventually!

Asyncronus multi-threaded MAC discovery. This will grey out a portion of the tool but make things more tolerable.
Discover MAC on new entries only. So, if there aren’t any new names in servers.txt we won’t do the MAC lookup.
Update MAC entries. Yep, just update the MAC entries that we know about

Finally…

If you end up using this tool let me know! I’d like to think that some of the things I build are useful to others too.

Fast File Copy With Managed Code: UBCopy update!

If you have been following my trials with working with C# and files you know it hasn’t been a bed of roses. I ran into a roadblock when I was trying to build a high performance file copy tool in the CLR. I eventually found a solution. It works but it isn’t very clean. So, I did a little more work, removed a broken bit and simplified another bit to make it a little more stable. I’m not done yet. I know there are a couple of places I can clean the code up a bit more and a way to speed things up a bit more at the cost of using more memory, but that is for another blog post.

Logging, to the rescue

The first major change was adding a logging facility to the UBCopy program via log4net. I had stayed away from adding it fearing to much overhead but after some tweaking it seems pretty darn fast. This allowed me to do some tight timings and log debug information to a log file to track long running copies without much fuss. It also allows me to turn off all console messaging since this is used in an automated fashion if something fails I’ve got the log file to look at.

Please don’t lock my file

There are a few places where the file target file being written to could potentially be locked. Right now I have to create the file and set the file length then close it before opening it for unbuffered writes. I haven’t done anything about this grab yet. The second one is when I am writing the last block to the file. Since you have to write in page aligned multiples I was closing the file, reopening it in a buffered mode and flushing the last partial buffer to disk. I haven’t fixed these up yet but I am working on a solution. I haven’t encountered a problem yet where the file gets locked during this process but it is still a potential threat that must be dealt with. Even though the file stream is seekable and writeable you can’t issue a setlength command on it. I plan on looking deeper into the filestream code and see what they are doing when you issue a setlength. If I can eliminate the open,close and open routine it will speed things up a bit and keep anyone from grabbing my file handle.
Here’s and example of what I’m talking about, after the set file size is done there is a very small window of opportunity here.

//open output file set length to prevent growth and file fragmentation and close it.
            //We do this to prevent file fragmentation and make the write as fast as possible.
            try
            {
                _outfile = new FileStream(_outputfile, FileMode.Create, FileAccess.Write, FileShare.None, 8,
                                          FileOptions.WriteThrough);
                //set file size to minimum of one buffer to cut down on fragmentation
                _outfile.SetLength(_infilesize > CopyBufferSize ? _infilesize : CopyBufferSize);
                _outfile.Close();
                _outfile.Dispose();
            }
            catch (Exception e)
            {
                throw;
            }
            //open file for write unbuffered
            try
            {
                _outfile = new FileStream(_outputfile, FileMode.Open, FileAccess.Write, FileShare.None, 8,
                                          FileOptions.WriteThrough | FileFlagNoBuffering);

            }
            catch (Exception e)
            {
                throw;
            }

Unbuffered, mostly

Since unbuffered writes prevent you from writing data that isn’t memory page aligned (usually 4096), you get an error when writing to the file. To get around this all writes are the same size period, this really only effects the last write which may have a partially full buffer. It will write down the full buffer, including invalid data. Luckily, an easy fix is to reset the end of file pointer back to the correct location and everything is happy again. This allows me to eliminate an additional bit of code. here is the old code.

//open file for write buffered We do this so we can write the tail of the file
//it is a cludge but hey you get what you get in C#
outfile = new FileStream(outputfile, FileMode.Open, FileAccess.Write, FileShare.None, 8,
             FileOptions.WriteThrough);
//go to the right position in the file
outfile.Seek(infilesize - bytesRead1, 0);
//flush the last buffer syncronus and buffered.
outfile.Write(Buffer1, 0, bytesRead1);

And now the new, faster and better way!

//close the file handle that was using unbuffered and write through and move the EOF pointer.
Log.Debug("Close Write File Unbuffered");
_outfile.Close();
_outfile.Dispose();

try
{
    if (IsDebugEnabled)
    {
        Log.Debug("Open File Set Length");
    }
    _outfile = new FileStream(_outputfile, FileMode.Open, FileAccess.Write, FileShare.None, 8,
                                FileOptions.WriteThrough);
    _outfile.SetLength(_infilesize);
    _outfile.Close();
    _outfile.Dispose();
}
catch (Exception e)
{
    if (IsDebugEnabled)
    {
        Log.Debug("Failed to open for write set length");
        Log.Debug(e);
    }
    throw;
}

Buffered or Unbuffered

If the file was smaller than the size of the buffer I was just copying the file buffered. This was a check and route to a diffrent code path for copying files. Not a huge deal but just another place for problems to creep up over time. So, I removed it when I changed the way unbuffered multi-threaded writes were happening I was able to get rid of the buffered copy routine all together. Now, it will initially set the file size to a single buffer. Write the data and reset the file pointer to the original size.

You want a MOVE but get a COPY

There was also a bug that on small files UBCopy wouldn’t do a move just a copy in some cases. There was an error in the code that prevented the delete of the source file after the copy to the new location due to a lock that was being held by the program. Not the end of the world, just wasn’t working like it should 100% of the time.

So, here is the new AsyncUnbuffCopy routine. Calling it is super easy. If you want to use the code as-is you will need to download log4net and include it in your project.

//
// AsyncUnbuffCopy.cs
//
// Authors:
//  Wesley D. Brown <wes@planetarydb.com>
//
// Copyright (C) 2010 SQLServerIO (http://www.SQLServerIO.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using log4net;

namespace UBCopy
{
    internal class AsyncUnbuffCopy
    {
        private static readonly ILog Log = LogManager.GetLogger(typeof(AsyncUnbuffCopy));
        private static readonly bool IsDebugEnabled = Log.IsDebugEnabled;

        //file names
        private static string _inputfile;
        private static string _outputfile;

        //checksum holders
        private static string _infilechecksum;
        private static string _outfilechecksum;

        //show write progress
        private static bool _reportprogress;

        //cursor position
        private static int _origRow;
        private static int _origCol;

        //number of chunks to copy
        private static int _numchunks;

        //track read state and read failed state
        private static bool _readfailed;

        //syncronization object
        private static readonly object Locker1 = new object();

        //buffer size
        public static int CopyBufferSize;
        private static long _infilesize;

        //buffer read
        public static byte[] Buffer1;
        private static int _bytesRead1;

        //buffer overlap
        public static byte[] Buffer2;
        private static bool _buffer2Dirty;
        private static int _bytesRead2;

        //buffer write
        public static byte[] Buffer3;

        //total bytes read
        private static long _totalbytesread;
        private static long _totalbyteswritten;

        //filestreams
        private static FileStream _infile;
        private static FileStream _outfile;

        //secret sauce for unbuffered IO
        const FileOptions FileFlagNoBuffering = (FileOptions)0x20000000;

        private static void AsyncReadFile()
        {
            //open input file
            try
            {
                _infile = new FileStream(_inputfile, FileMode.Open, FileAccess.Read, FileShare.None,
CopyBufferSize, FileFlagNoBuffering);
            }
            catch (Exception e)
            {
                if (IsDebugEnabled)
                {
                    Log.Debug("Failed to open for read");
                    Log.Debug(e);
                }
                throw;
            }
            //if we have data read it
            while (_totalbytesread < _infilesize)
            {
                if (IsDebugEnabled)
                {
                    Log.Debug("Read _buffer2Dirty    : " + _buffer2Dirty);
                }
                _bytesRead1 = _infile.Read(Buffer1, 0, CopyBufferSize);
                Monitor.Enter(Locker1);
                try
                {
                    while (_buffer2Dirty) Monitor.Wait(Locker1);
                    Buffer.BlockCopy(Buffer1, 0, Buffer2, 0, _bytesRead1);
                    _buffer2Dirty = true;
                    _bytesRead2 = _bytesRead1;
                    _totalbytesread = _totalbytesread + _bytesRead1;
                    Monitor.PulseAll(Locker1);
                    if (IsDebugEnabled)
                    {

                        Log.Debug("Read       : " + _totalbytesread);
                    }
                }
                catch (Exception e)
                {
                    Log.Fatal("Read Failed.");
                    Log.Fatal(e);
                    _readfailed = true;
                    throw;
                }
                finally { Monitor.Exit(Locker1); }
            }
            //clean up open handle
            _infile.Close();
            _infile.Dispose();
        }

        private static void AsyncWriteFile()
        {
            //open output file set length to prevent growth and file fragmentation and close it.
            //We do this to prevent file fragmentation and make the write as fast as possible.
            try
            {
                if (IsDebugEnabled)
                {
                    Log.Debug("Open File Set Length");
                }
                _outfile = new FileStream(_outputfile, FileMode.Create, FileAccess.Write, FileShare.None, 8,
                                          FileOptions.WriteThrough);

                //set file size to minimum of one buffer to cut down on fragmentation
                _outfile.SetLength(_infilesize > CopyBufferSize ? _infilesize : CopyBufferSize);

                _outfile.Close();
                _outfile.Dispose();
            }
            catch (Exception e)
            {
                Log.Fatal("Failed to open for write set length");
                Log.Fatal(e);
                throw;
            }

            //open file for write unbuffered
            try
            {
                if (IsDebugEnabled)
                {
                    Log.Debug("Open File Write Unbuffered");
                }
                _outfile = new FileStream(_outputfile, FileMode.Open, FileAccess.Write, FileShare.None, 8,
                                          FileOptions.WriteThrough | FileFlagNoBuffering);

            }
            catch (Exception e)
            {
                Log.Fatal("Failed to open for write unbuffered");
                Log.Fatal(e);
                throw;
            }

            var pctinc = 0.0;
            var progress = pctinc;

            //progress stuff
            if (_reportprogress)
            {
                if (IsDebugEnabled)
                {
                    Log.Debug("Report Progress : True");
                }
                pctinc = 100.00 / _numchunks;
            }
            if (IsDebugEnabled)
            {
                Log.Debug("While Write _totalbyteswritten          : " + _totalbyteswritten);
                Log.Debug("While Write _infilesize - CopyBufferSize: " + (_infilesize - CopyBufferSize));
            }
            while ((_totalbyteswritten < _infilesize) && !_readfailed)
            {
                if (IsDebugEnabled)
                {
                    Log.Debug("Write Unbuffered _buffer2Dirty    : " + _buffer2Dirty);
                }
                lock (Locker1)
                {
                    if (IsDebugEnabled)
                    {
                        Log.Debug("Write Unbuffered Lock");
                    }
                    while (!_buffer2Dirty) Monitor.Wait(Locker1);
                    if (IsDebugEnabled)
                    {
                        Log.Debug("Write Unbuffered _buffer2Dirty    : " + _buffer2Dirty);
                    }
                    Buffer.BlockCopy(Buffer2, 0, Buffer3, 0, _bytesRead2);
                    _buffer2Dirty = false;
                    if (IsDebugEnabled)
                    {
                        Log.Debug("Write Unbuffered _buffer2Dirty    : " + _buffer2Dirty);
                    }
                    _totalbyteswritten = _totalbyteswritten + CopyBufferSize;
                    if (IsDebugEnabled)
                    {
                        Log.Debug("Written Unbuffered : " + _totalbyteswritten);
                    }
                    Monitor.PulseAll(Locker1);
                    //fancy dan in place percent update on each write.

                    if (_reportprogress && !IsDebugEnabled)
                    {
                        Console.SetCursorPosition(_origCol, _origRow);
                        if (progress < 101 - pctinc)
                        {
                            progress = progress + pctinc;
                            Console.Write("%{0}", Math.Round(progress, 0));
                        }
                    }
                }
                try
                {
                    _outfile.Write(Buffer3, 0, CopyBufferSize);
                }
                catch (Exception e)
                {
                    Log.Fatal("Write Unbuffered Failed");
                    Log.Fatal(e);
                    throw;
                }
            }

            //close the file handle that was using unbuffered and write through
            Log.Debug("Close Write File Unbuffered");
            _outfile.Close();
            _outfile.Dispose();

            try
            {
                if (IsDebugEnabled)
                {
                    Log.Debug("Open File Set Length");
                }
                _outfile = new FileStream(_outputfile, FileMode.Open, FileAccess.Write, FileShare.None, 8,
                                          FileOptions.WriteThrough);
                _outfile.SetLength(_infilesize);
                _outfile.Close();
                _outfile.Dispose();
            }
            catch (Exception e)
            {
                if (IsDebugEnabled)
                {
                    Log.Debug("Failed to open for write set length");
                    Log.Debug(e);
                }
                throw;
            }
        }

        public static int AsyncCopyFileUnbuffered(string inputfile, string outputfile, bool overwrite,
bool movefile, bool checksum, int buffersize, bool reportprogress)
        {
            if (IsDebugEnabled)
            {
                Log.Debug("inputfile      : " + inputfile);
                Log.Debug("outputfile     : " + outputfile);
                Log.Debug("overwrite      : " + overwrite);
                Log.Debug("movefile       : " + movefile);
                Log.Debug("checksum       : " + checksum);
                Log.Debug("buffersize     : " + buffersize);
                Log.Debug("reportprogress : " + reportprogress);
            }
            //report write progress
            _reportprogress = reportprogress;

            //set file name globals
            _inputfile = inputfile;
            _outputfile = outputfile;

            //setup single buffer size, remember this will be x3.
            CopyBufferSize = buffersize * 1024 * 1024;

            //buffer read
            Buffer1 = new byte[CopyBufferSize];

            //buffer overlap
            Buffer2 = new byte[CopyBufferSize];

            //buffer write
            Buffer3 = new byte[CopyBufferSize];

            //clear all flags and handles
            _totalbytesread = 0;
            _totalbyteswritten = 0;
            _bytesRead1 = 0;
            _buffer2Dirty = false;

            //if the overwrite flag is set to false check to see if the file is there.
            if (File.Exists(outputfile) && !overwrite)
            {
                if (IsDebugEnabled)
                {
                    Log.Debug("Destination File Exists!");
                }
                Console.WriteLine("Destination File Exists!");
                return 0;
            }

            //create the directory if it doesn't exist
            if (!Directory.Exists(outputfile))
            {
                try
                {
                    // ReSharper disable AssignNullToNotNullAttribute
                    Directory.CreateDirectory(Path.GetDirectoryName(outputfile));
                    // ReSharper restore AssignNullToNotNullAttribute
                }
                catch (Exception e)
                {
                    Log.Fatal("Create Directory Failed.");
                    Log.Fatal(e);
                    Console.WriteLine("Create Directory Failed.");
                    Console.WriteLine(e.Message);
                    throw;
                }
            }

            //get input file size for later use
            var inputFileInfo = new FileInfo(_inputfile);
            _infilesize = inputFileInfo.Length;

            //get number of buffer sized chunks used to correctly display percent complete.
            _numchunks = (int)((_infilesize / CopyBufferSize) <= 0 ? (_infilesize / CopyBufferSize) : 1);

            if (IsDebugEnabled)
            {
                Log.Debug("File Copy Started");
            }
            Console.WriteLine("File Copy Started");

            //create read thread and start it.
            var readfile = new Thread(AsyncReadFile) { Name = "ReadThread", IsBackground = true };
            readfile.Start();

            if (IsDebugEnabled)
            {
                //debug show if we are an even multiple of the file size
                Log.Debug("Number of Chunks: " + _numchunks);
            }

            //create write thread and start it.
            var writefile = new Thread(AsyncWriteFile) { Name = "WriteThread", IsBackground = true };
            writefile.Start();

            if (_reportprogress)
            {
                //set fancy curor position
                _origRow = Console.CursorTop;
                _origCol = Console.CursorLeft;
            }

            //wait for threads to finish
            readfile.Join();
            writefile.Join();

            //leave a blank line for the progress indicator
            if (_reportprogress)
                Console.WriteLine();

            if (IsDebugEnabled)
            {
                Log.Debug("File Copy Done");
            }

            Console.WriteLine("File Copy Done");

            if (checksum)
            {
                if (IsDebugEnabled)
                {
                    Log.Debug("Checksum Source File Started");
                }
                Console.WriteLine("Checksum Source File Started");
                //create checksum read file thread and start it.
                var checksumreadfile = new Thread(GetMD5HashFromInputFile) {
Name = "checksumreadfile", IsBackground = true };
                checksumreadfile.Start();

                if (IsDebugEnabled)
                {
                    Log.Debug("Checksum Destination File Started");
                }
                Console.WriteLine("Checksum Destination File Started");
                //create checksum write file thread and start it.
                var checksumwritefile = new Thread(GetMD5HashFromOutputFile) {
Name = "checksumwritefile", IsBackground = true };
                checksumwritefile.Start();

                //hang out until the checksums are done.
                checksumreadfile.Join();
                checksumwritefile.Join();

                if (_infilechecksum.Equals(_outfilechecksum))
                {
                    if (IsDebugEnabled)
                    {
                        Log.Debug("Checksum Verified");
                    }
                    Console.WriteLine("Checksum Verified");
                }
                else
                {
                    if (IsDebugEnabled)
                    {
                        Log.Debug("Checksum Failed");
                        Log.DebugFormat("Input File Checksum : {0}", _infilechecksum);
                        Log.DebugFormat("Output File Checksum: {0}", _outfilechecksum);
                    }
                    Console.WriteLine("Checksum Failed");
                    Console.WriteLine("Input File Checksum : {0}", _infilechecksum);
                    Console.WriteLine("Output File Checksum: {0}", _outfilechecksum);
                }
            }

            if (movefile && File.Exists(inputfile) && File.Exists(outputfile))
                try
                {
                    File.Delete(inputfile);
                }
                catch (IOException ioex)
                {
                    if (IsDebugEnabled)
                    {
                        Log.Error("File in use or locked cannot move file.");
                        Log.Error(ioex);
                    }
                    Console.WriteLine("File in use or locked");
                    Console.WriteLine(ioex.Message);
                }
                catch (Exception ex)
                {
                    if (IsDebugEnabled)
                    {
                        Log.Error("File Failed to Delete");
                        Log.Error(ex);
                    }
                    Console.WriteLine("File Failed to Delete");
                    Console.WriteLine(ex.Message);
                }
            return 1;
        }

        //hash input file
        public static void GetMD5HashFromInputFile()
        {
            var fs = new FileStream(_inputfile, FileMode.Open, FileAccess.Read, FileShare.None,
CopyBufferSize);
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] retVal = md5.ComputeHash(fs);
            fs.Close();

            var sb = new StringBuilder();
            for (var i = 0; i < retVal.Length; i++)
            {
                sb.Append(retVal[i].ToString("x2"));
            }
            _infilechecksum = sb.ToString();
        }

        //hash output file
        public static void GetMD5HashFromOutputFile()
        {
            var fs = new FileStream(_outputfile, FileMode.Open, FileAccess.Read, FileShare.None,
CopyBufferSize);
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] retVal = md5.ComputeHash(fs);
            fs.Close();

            var sb = new StringBuilder();
            for (var i = 0; i < retVal.Length; i++)
            {
                sb.Append(retVal[i].ToString("x2"));
            }
            _outfilechecksum = sb.ToString();
        }
    }
}

A New Year, Time To Get My Learn On!

altMy friend, and newly minted MVP, Jen McCown (blog|twitter) is hosting  T-SQL Tuesday this month. I normally don’t do the T-SQL Tuesday, but this is a post about resolutions so I am resolving to do more of them in the new year. I’ve always considered myself a core engine kind of guy. Focused on things like I/O, on disk structures and the optimizer. As SQL Server grows it has become harder and harder to keep up with the Joneses’ (Steve?). Like every other DBA that earned their salt in the mines of 6.0 through 2000 I’ve got a ton of tools I’ve written myself to monitor every aspect of SQL Server. I’ve also not kept up as well with other technologies like replication that have an impact on my day to day life at work. So, with all that in mind this is my list of technical things to “get good at” this year.

SQL Server Stuff

1. Partitioning
I’ve worked on and off with partitioning since SQL Server 2000 and have a solid grasp of how it works and what the benefits are, just not at a deep enough level. I found myself telling someone that was just flat wrong a few weeks ago about partitioning in SQL Server 2008 R2. It wasn’t a huge deal but if I’m spreading misinformation I’m doing my community and myself harm.

2. Change Data Capture
Again, I’m using a throw back from the good old days. Using triggers to capture change data and insert that into history tables. It’s time to get rid of this performance robber and move to something a tad more modern. I sat in a couple of sessions at PASS and think if I can rap my head around it we could see a large improvement in performance.

3. Replication
I feel pretty good about my replication skills but it is becoming very important to our infrastructure and I need to make sure that I’m not making assumptions that aren’t true anymore and that I’ve configured my environment as best as I can. I sat in on Kendal Van Dyke’s (blog|twitter) replication at PASS and came away with a couple of things that I need to do and some additional things to research.

4. Analysis Services Administration
We are moving full boar into the BI stack. It isn’t my job to write MDX but I have to make sure that the performance from an infrastructure level is good and that we are prepared to recover if the need arises.

.Net Programming Stuff

1.LINQ
I do some stuff with LINQ including LINQ to SQL but I need to get better at it. Just trying to keep my mediocre c# skills from falling off completely.

2.Parallel Programming in .Net
I write a ton of multi-threaded stuff and have built up my own threading frameworks over the years. Microsoft is making a big push to make parallel programming easier. With PLINQ and the new Async framework that is in development right now. It isn’t a part of my core job skills but it is something I enjoy and use quite a bit.

3.Entity Framework
I don’t have to like it but it is everywhere I turn these days. I do get questions as well on how to optimize the database and without knowing exactly what is going on under the covers it limits my ability to troubleshoot issues.

90 Degrees From Center

1. Get Better With Python
I do more than Microsoft stuff and still enjoy learning new things. I recently picked up on Python and look to keep growing my skillset with 2.7 and 3.x lines. It is a fun language to program in. If you dig on easy to read and easy to write Python has that in spades.

2. Pick Up Lisp again
You heard me. Lisp the grandfather of modern functional languages. I worked with Lisp about a million years ago it seems. Apparently, it is back in vogue with several dialects floating around and quite a robust community supporting them. I can get a bit religious at times but passionate people make for a culture of innovation.