TFS Alternative to Cloaking – shorten build base path

I was facing yet another [ProjectName] “contains more than the allowed 259 characters. Type or select a shorter path.”

I have learned how to cloak these entries when they are not needed for the build I am doing, and we have also resorted to shortening the path names of some so that we can include them. But our base folders were pretty long so I decided to try and change them. I googled for the $(SourceDir) entry and found a couple of helpful articles:

tfs – How do I change the build directory that MSBuild uses under Team Foundation Build? – Stack Overflow

TFS Top Tip #16 – Limit What you Build – Martin Woodward

From these I was able to change the entries in the config and Build Agent settings that would help me to shorten the path to something that would stop the madness of cloaking and avoid the above error (until we go above the limit again).

Here is what I did (this is all VS and TFS 2008):

I went to the Team Explorer in Visual Studio and right clicked on Builds and selected

I then inside the the manage selected my (only) build agent and got the window below:

Changing this from e:\teambuild\projects (which was a decent naming convention) to the shortened E:\Bld saved us 15 characters which solved a bunch of path length issues.

In addition, I changed the default “Sources” in the file

c:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies\tfsbuildservice.exe.config

from <add key=”SourcesSubdirectory” value=”Sources” />

to <add key=”SourcesSubdirectory” value=”Src” />

saving me an additional 4 characters for a full 19 characters of shortening.

This seems very silly in a world where we are trying to be more descriptive and I hope that MS will eventually let us do as we please with our file and folders, but it saved us some hassle and maybe it will save you some too.

Worker Thread Class

In an article by Juval Löwy, he does a great job of describing and providing an example of a worker thread class. Since many of us are not yet able to pick up .Net 4, we do not yet have the threading beauty that apparently awaits us there. So I adopted this worker thread and made a few changes that fill a few gaps for me. The listing is below along with comments where I made changes to Juval’s original work.

 /// <summary>
 /// Thread mgmt wrapper class - base code taken from devx.com article by Juval Löwy
 /// Added mutex and exception string to allow exception info to be stored and passed out from thread.
 /// Also added ability to pass in delegate that is run as the worker thread function, and some
 /// additional ways to construct.
 /// Added a mutex and wrapped it in a contained class to pass into the worker so that they can monitor
 /// the EndLoop value. I also added a passthrough object to this class so that the worker can take in
 /// an object when it starts and pass one back out when it is done.
 /// Also found a race condition for a test that Juval was doing on the IsAlive when it asserted that
 /// the handle could not be same value as isalive, but when function is done and closing,
 /// this value could be set right at the end and isalive not yet unset. I changed it to a definite
 /// test - that the handle is set when isalive is false. This must be true, since the thread has to exit
 /// that way (unless someone crawls in and kills it which this wrapper is supposed to allow you to avoid
 /// completely).
 /// </summary>
public class WorkerThread : IDisposable
 {
 /// <summary>
 /// Thread function passing ref to Thread Objects so we can:
 /// 1. check to see if we're done, and
 /// 2. Pass in or out values to / from the thread.
 /// </summary>
 public delegate void ThreadFunc(ThreadObjects threadObjects);

 /// <summary>The <see cref="ThreadFunc"/> delegate.</summary>
 protected ThreadFunc threadFunc = null;

 /// <summary>The <see cref="ManualResetEvent"/> m_WorkerStartedEvent - that is reset when the worker thread starts its task.</summary>
 protected ManualResetEvent m_WorkerStartedEvent;
 /// <summary>ManualResetEvent m_WorkerDoneEvent - reset when the worker thread has completed its task.</summary>
 protected ManualResetEvent m_WorkerDoneEvent;
 /// <summary>The <see cref="ManualResetEvent"/> m_WorkerExceptionEvent - reset when an exception occurs</summary>
 protected ManualResetEvent m_WorkerExceptionEvent;

 /// <summary>The System thread that we are using to implement our worker thread</summary>
 protected Thread m_ThreadObj;

 /// <summary>holds the objects used to initialize, communicate with, and pull data back from the worker thread.</summary>
 protected ThreadObjects m_mutexedObjects;

 /// <summary>
 /// This object is passed into the worker so it can monitor when to stop,
 /// and to get / set the passthrough object
 /// </summary>
 public class ThreadObjects
 {
 /// <summary>if true tells thread to stop processing (must be processed by the worker thread user code)</summary>
 protected bool _mEndLoop;
 /// <summary>controls access to m_endloop</summary>
 protected Mutex _endLoopMutex;

 /// <summary>used to pass data into and out of the worker thread</summary>
 protected object _mPassThrough;
 /// <summary>controls access to the passthrough data</summary>
 protected Mutex _mPassThroughMutex;

 /// <summary>
 /// Initializes a new instance of the <see cref="ThreadObjects"/> class.
 /// Creates the mutexes.
 /// </summary>
 public ThreadObjects()
 {
 _endLoopMutex = new Mutex();
 _mPassThroughMutex = new Mutex();
 }
 /// <summary>
 /// Gets or sets a value indicating whether the worker thread should stop processing.
 /// This is initially set false and then set to true by the caller to tell the worker
 /// thread to stop. The worker thread function should check this value periodically so
 /// that the thread can be shutdown gracefully.
 /// </summary>
 /// <value><c>true</c> if processing should stop; otherwise, <c>false</c>.</value>
 public bool EndLoop
 {
 set
 {
 _endLoopMutex.WaitOne();
 _mEndLoop = value;
 _endLoopMutex.ReleaseMutex();
 }
 get
 {
 bool result = false;
 _endLoopMutex.WaitOne();
 result = _mEndLoop;
 _endLoopMutex.ReleaseMutex();
 return result;
 }
 }

 /// <summary>
 /// Gets or sets an object value that the worker thread can use or set.
 /// It is recommended that this only be set before starting the thread by the caller
 /// and that it be checked by the caller only after the thread is complete. Otherwise the
 /// values are possibly unreliable and require careful management.
 /// For instance, this could be used to hold an array of strings indicating the status of
 /// a thread that cyclically performs a task. The Thread could keep it updated and
 /// delete old info out of it when it has new and keep just the last 20 events in there
 /// so a status monitor could check out whats happening from time to time without
 /// forcing the thread to stop and restart.
 /// </summary>
 /// <value>Any value that the caller / worker wishes to pass through.</value>
 public object PassThrough
 {
 set
 {
 _mPassThroughMutex.WaitOne();
 _mPassThrough = value;
 _mPassThroughMutex.ReleaseMutex();
 }
 get
 {
 object result = "";
 _mPassThroughMutex.WaitOne();
 result = _mPassThrough;
 _mPassThroughMutex.ReleaseMutex();
 return result;
 }
 }

 internal void Close()
 {
 _endLoopMutex.Close();
 _mPassThroughMutex.Close();
 }

 private string _name = "";
 /// <summary>Gets or sets the name of the thread.</summary>
 public string Name { get { return _name; }  set { _name = value; } }
 }

 /// <summary>
 /// Gets or sets a value indicating whether the worker thread should stop processing.
 /// This is initially set false and then set to true by the caller to tell the worker
 /// thread to stop. The worker thread function should check this value periodically so
 /// that the thread can be shutdown gracefully.
 /// </summary>
 /// <value><c>true</c> if processing should stop; otherwise, <c>false</c>.</value>
 public bool EndLoop
 {
 get { return m_mutexedObjects.EndLoop; }
 set { m_mutexedObjects.EndLoop = value; }
 }

 /// <summary>
 /// Gets or sets an object value that the worker thread can use or set.
 /// It is recommended that this only be set before starting the thread by the caller
 /// and that it be checked by the caller only after the thread is complete. Otherwise the
 /// values are possibly unreliable and require careful management.
 /// For instance, this could be used to hold an array of strings indicating the status of
 /// a thread that cyclically performs a task. The Thread could keep it updated and
 /// delete old info out of it when it has new and keep just the last 20 events in there
 /// so a status monitor could check out whats happening from time to time without
 /// forcing the thread to stop and restart.
 /// </summary>
 /// <value>Any value that the caller / worker wishes to pass through.</value>
 public object PassThrough
 {
 get { return m_mutexedObjects.PassThrough; }
 set { m_mutexedObjects.PassThrough = value; }
 }

 /// <summary>
 /// Gets the ThreadState of the thread.
 /// </summary>
 /// <value>The <see cref="System.Threading.ThreadState"/> of the underlying thread.</value>
 public ThreadState State { get { return m_ThreadObj.ThreadState; } }

 /// <summary>Holds the exception details (if any) when an exception is generated in the thread so
 /// that it can be picked up by the thread that started this worker.</summary>
 protected string m_ExceptionInfo;

 /// <summary>controls access to the exception info</summary>
 protected Mutex m_ExceptionInfoMutex;

 /// <summary>Gets the thread being managed.</summary>
 public Thread Thread{get{return m_ThreadObj;}}

 /// <summary>
 /// Gets or sets the exception info. A holding place for any exception that occurs in the thread.
 /// It will contain whatever string the worker function puts in it (i.e. message, stack, etc.).
 /// Generally this should be an empty string unless there is an exception, thus allowing the
 /// caller to be able to determine if a thread processed successfully.
 /// </summary>
 /// <value>The exception info, or an empty string if there was no exception.</value>
 public string ExceptionInfo
 {
 set
 {
 m_ExceptionInfoMutex.WaitOne();
 m_ExceptionInfo = value;
 m_ExceptionInfoMutex.ReleaseMutex();
 }
 get
 {
 string result = "";
 m_ExceptionInfoMutex.WaitOne();
 result = m_ExceptionInfo;
 m_ExceptionInfoMutex.ReleaseMutex();
 return result;
 }
 }

 /// <summary>
 /// Gets the generated thread name using the namespace and the current system
 /// ticks. It does not need to be unique, but we make it so in order to tell
 /// threads apart (since they were not explicitly named).
 /// </summary>
 /// <value>The name of the generated thread.</value>
 static string GenThreadName { get { return typeof(WorkerThread).FullName + DateTime.Now.Ticks.ToString(); } }

 /// <summary>
 /// Initializes a new instance of the <see cref="WorkerThread"/> class
 /// using a <see cref="ThreadFunc"/> delegate and a thread name.
 /// </summary>
 /// <param name="threadFuncIn"><see cref="ThreadFunc"/> delegate
 /// passes an object that allows the worker function to get
 /// input and/or set a result, and to monitor if the caller
 /// has requested that the worker stop processing.
 /// </param>
 /// <param name="threadName">Name of the thread. If this is made meaningful, then
 /// it makes debugging easier, so it is recommended that it be set accordingly.</param>
 public WorkerThread(ThreadFunc threadFuncIn, string threadName)
 {
 m_mutexedObjects = new ThreadObjects();
 m_mutexedObjects.Name = threadName;
 m_mutexedObjects.EndLoop = false;
 m_ThreadObj = null;
 m_ExceptionInfo = "";
 m_ExceptionInfoMutex = new Mutex();
 m_WorkerStartedEvent = new ManualResetEvent(false);
 m_WorkerDoneEvent = new ManualResetEvent(false);
 m_WorkerExceptionEvent = new ManualResetEvent(false);
 threadFunc = threadFuncIn;
 ThreadStart threadStart = new ThreadStart(Worker);
 m_ThreadObj = new Thread(threadStart);
 m_ThreadObj.Name = threadName;
 }

 /// <summary>
 /// Initializes a new instance of the <see cref="WorkerThread"/> class
 /// using a <see cref="ThreadFunc"/> delegate. The thread name is generated
 /// using <see cref="GenThreadName"/> which grabs the full name of the WorkerThread type and
 /// appends the system clock ticks.
 /// </summary>
 /// <param name="threadFunc"><see cref="ThreadFunc"/> delegate
 /// passes an object that allows the worker function to get
 /// input and/or set a result, and to monitor if the caller
 /// has requested that the worker stop processing.
 /// </param>
 public WorkerThread(ThreadFunc threadFunc) : this(threadFunc, GenThreadName)
 {
 }

 /// <summary>
 /// Initializes a new instance of the <see cref="WorkerThread"/> class.
 /// </summary>
 /// <param name="autoStart">if set to <c>true</c> if the thread should start
 /// as soon as it is set up. The thread name is generated
 /// using <see cref="GenThreadName"/> which grabs the full
 /// name of the WorkerThread type and appends the system clock ticks.</param>
 /// <param name="threadFunc"><see cref="ThreadFunc"/> delegate
 /// passes an object that allows the worker function to get
 /// input and/or set a result, and to monitor if the caller
 /// has requested that the worker stop processing.
 /// </param>
 public WorkerThread(bool autoStart, ThreadFunc threadFunc)
 : this(autoStart, threadFunc, GenThreadName)
 {
 }

 /// <summary>
 /// Initializes a new instance of the <see cref="WorkerThread"/> class.
 /// </summary>
 /// <param name="autoStart">if set to <c>true</c> if the thread should start
 /// as soon as it is set up. The thread name is generated
 /// using <see cref="GenThreadName"/> which grabs the full
 /// name of the WorkerThread type and appends the system clock ticks.</param>
 /// <param name="threadFunc"><see cref="ThreadFunc"/> delegate
 /// passes an object that allows the worker function to get
 /// input and/or set a result, and to monitor if the caller
 /// has requested that the worker stop processing.
 /// </param>
 /// <param name="threadName">Name of the thread. If this is made meaningful, then
 /// it makes debugging easier, so it is recommended that it be set accordingly.</param>
 public WorkerThread(bool autoStart, ThreadFunc threadFunc, string threadName)
 : this(threadFunc, threadName)
 {
 if (autoStart)
 {
 Start();
 }
 }

 /// <summary>
 /// This <see cref="WaitHandle"/> is used to indicate that the thread worker has started.
 /// It is set at the beginning of the thread function inside the WorkerThread class, and
 /// verified in the IsAlive, that if the thread is dead, that this handle got set.
 /// If not, an exception is thrown.
 /// See also <seealso cref="WorkerDoneHandle"/> and <seealso cref="WorkerExceptionHandle"/>
 /// </summary>
 /// <value>The handle.</value>
 public WaitHandle WorkerStartedHandle
 {
 get
 {
 return m_WorkerStartedEvent;
 }
 }

 /// <summary>
 /// This <see cref="WaitHandle"/> is used to indicate that the thread worker has finished.
 /// It is set at the end of the thread function inside the WorkerThread class, and
 /// verified in the IsAlive, that if the thread is dead, that this handle got set.
 /// If not an exception is thrown.
 /// See also <seealso cref="WorkerStartedHandle"/> and <seealso cref="WorkerExceptionHandle"/>
 /// </summary>
 /// <value>The handle.</value>
 public WaitHandle WorkerDoneHandle
 {
 get
 {
 return m_WorkerDoneEvent;
 }
 }

 /// <summary>
 /// This <see cref="WaitHandle"/> is used to indicate that the thread worker terminated in an exception state.
 /// It is set if an exception is caught in the wrapper worker function.
 /// See also <seealso cref="WorkerStartedHandle"/> and <seealso cref="WorkerDoneHandle"/>
 /// </summary>
 /// <value>The handle.</value>
 public WaitHandle WorkerExceptionHandle
 {
 get
 {
 return m_WorkerExceptionEvent;
 }
 }

 /// <summary>
 /// Starts the thread after checking that it is currently instantiated and
 /// is not alive. This is called automatically if autostart is set in the
 /// call to the constructor.
 /// </summary>
 public void Start()
 {
 if(m_ThreadObj == null)
 throw new NullReferenceException("Start called on null thread object.");

 if(m_ThreadObj.IsAlive)
 throw new ThreadStateException("Start called on thread that is already running.");

 m_ThreadObj.Start();
 }

 /// <summary>
 /// If object is being disposed, this function will ensure that the thread is killed.
 /// Moved the close handles to this function from Kill, because we may still want to use these
 /// values after killing the thread to check on what happened.
 /// </summary>
 public void Dispose()
 {
 Kill();
 m_mutexedObjects.Close();
 m_WorkerStartedEvent.Close();
 m_WorkerDoneEvent.Close();
 m_WorkerExceptionEvent.Close();
 }

 /// <summary>
 /// Kills this thread. First it checks to see if we have a thread and if it is currently
 /// alive. If not, the function returns without doing anything. Otherwise, EndLoop is set,
 /// and it waits using a Join for the thread to die. If the thread won't die after the allotted time,
 /// then the thread is aborted. THe called thread should never require this of the caller, but
 /// in some cases, since we are calling code that is unmanaged and outside of our control,
 /// we may have threads that are hung. Thus the thread should have resource freeing coded into
 /// the catch handler of the thread function.
 /// </summary>
 public void Kill(TimeSpan timeout)
 {
 //Kill is called on client thread - must use cached object
 if (m_ThreadObj == null) // thread is not there - leave it
 return;

 if (IsAlive == false)
 return;

 m_mutexedObjects.EndLoop = true;
 //Wait for thread to die
 Join(timeout);

 if (m_ThreadObj.IsAlive == true)
 {    // this thread refuses to die and we gave it 2 minutes - abort.
 m_ThreadObj.Abort(); // send abort Exception
 }
 }

 /// <summary>
 /// Kills this thread. First it checks to see if we have a thread and if it is currently
 /// alive. If not, the function returns without doing anything. Otherwise, EndLoop is set,
 /// and it waits using a Join for the thread to die.
 /// </summary>
 public void Kill()
 {
 Kill(new TimeSpan(0, 2, 0));
 }

 /// <summary>
 /// Joins thread - if thread is already dead, it returns, otherwise it waits on the thread to die.
 /// </summary>
 public void Join()
 {
 if (m_ThreadObj == null) // thread is not there - leave it
 return;

 if (IsAlive == false)
 return;

 Debug.Assert(Thread.CurrentThread.GetHashCode() !=
 m_ThreadObj.GetHashCode());

 m_ThreadObj.Join();
 }

 /// <summary>
 /// Joins with the specified milliseconds timeout.
 /// </summary>
 /// <param name="millisecondsTimeout">The milliseconds timeout.</param>
 /// <returns></returns>
 public bool Join(int millisecondsTimeout)
 {
 TimeSpan timeout = TimeSpan.FromMilliseconds(millisecondsTimeout);

 return Join(timeout);
 }

 /// <summary>
 /// Joins with the specified timeout.
 /// </summary>
 /// <param name="timeout">The timeout.</param>
 /// <returns></returns>
 public bool Join(TimeSpan timeout)
 {
 if (m_ThreadObj == null) // thread is not there - leave it
 return true;

 if (IsAlive == false)
 return true;

 if (Thread.CurrentThread.GetHashCode() == m_ThreadObj.GetHashCode())
 throw new Exception("Attempting to Join a thread that has the same identity as the current thread.");

 return m_ThreadObj.Join(timeout);
 }

 /// <summary>
 /// Gets or sets the name of the thread.
 /// </summary>
 /// <value>The name of the thread.</value>
 public string Name { get { return m_ThreadObj.Name; } set { m_ThreadObj.Name = value; } }

 /// <summary>
 /// Gets a value indicating whether the worker thread is alive.
 /// </summary>
 /// <value><c>true</c> if the worker thread is alive; otherwise, <c>false</c>.</value>
 public bool IsAlive
 {
 get
 {
 Debug.Assert(m_ThreadObj != null);
 bool isAlive = m_ThreadObj.IsAlive;
 return isAlive;
 }
 }

 /// <summary>
 /// Worker runs the function that was passed in wrapped by an exception handler.
 /// If the function being called has any issues the exception is caught
 /// and the exception message is put into a string along with the thread name,
 /// call stack and any inner exceptions for access by the caller / creator of the thread.
 /// </summary>
 protected void Worker()
 {
 try
 {
 if (threadFunc != null)
 {
 m_WorkerStartedEvent.Set(); // indicate that we have started
 threadFunc.Invoke(m_mutexedObjects);
 }
 else
 throw new NullReferenceException("WorkerThread function expected.");
 }
 catch(Exception ex)
 {
 string exInfo = ex.Message;
 ExceptionInfo = Thread.CurrentThread.Name + " exception:" + exInfo;
 m_WorkerExceptionEvent.Set();
 }
 finally
 {
 m_WorkerDoneEvent.Set();
 }
 }
 }

Email with attachment from byte array

How to Send Email with Attachment using ASP.NET 2.0 and C#

This article tells how to send an email with an attachment. I made a few modifications to make it work because I had to attach a byte array that came from a database. So this is how you do that:


void SendEmail()
{
List<String> recipientList = GetEmailAddressList();

SmtpClient client = new SmtpClient(GetSMTPServer(), GetSMTPPort());

MailAddress from = new MailAddress(ConfigurationManager.AppSettings["EmailContacts.From"]);

 if (recipientList.Count == 0)
 {
 AddError("no email addresses found.");
 return;
 }

 MailMessage message = new MailMessage();
 message.From = from;
 foreach (string to in recipientList)
 {
 message.To.Add(to);
 }

 //create the message
 message.Body = "A new file is attached for your review.";

 message.BodyEncoding = Encoding.UTF8;

 // Get pdf binary data and save the data to a memory stream
 System.IO.MemoryStream ms = new System.IO.MemoryStream(GetPdf());

 // Create the attachment from a stream. Be sure to name the data with a file and
 // media type that is respective of the data
 message.Attachments.Add(new Attachment(ms, "myFile.pdf", "application/pdf"));

 message.Subject = "New File for Review";

 //send it
 client.Send(message);
}

Tony Sneed’s Blog -> Michael C. Kennedy’s Weblog -> Facebook C# SDK

This:

Using Open Source? Get NuGet. | Tony Sneed’s Blog

has a reference to this:

Michael C. Kennedy’s Weblog – 11 Killer Open Source Projects I Found with NuGet

which has a reference to this:

Facebook C# SDK

which looks extermely cool and useful. More as I dig in.

Thanks Tony!

 

Nicely written post extending the MSDN audio CD writing sample to make it actually write CDs.

Once I have implemented this, I will update with feedback.

C# Audio CD Writer for Windows XP – Sichbo Interactive

 

Using Open Source? Get NuGet. | Tony Sneed’s Blog

 

Iterating Through Databases on Sql Server

This is a way to find all of the databases matching a particular name on a SQL Server and process something in those databases.

USE master
GO

set ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

– iterate through DBs
DECLARE @DbNames TABLE (
rowNum int identity (1,1),
dbname sysname NOT NULL )

INSERT INTO @DbNames
SELECT name
FROM sys.databases
WHERE state=0 AND user_access=0 and has_dbaccess(name) = 1
AND [name] like ‘NameToMatch%’
ORDER BY [name]

DECLARE @EndCount int;
SELECT @EndCount = count(*) FROM @DbNames

DECLARE @RowCounter int;
SELECT @RowCounter = 1;

DECLARE @DbName varchar(20);
DECLARE @sql varchar(2000);

WHILE (@RowCounter <= @EndCount)
BEGIN
SELECT @DbName = dbname FROM @DbNames WHERE @RowCounter = rowNum;
SELECT @sql =
‘USE ‘ + @DbName — do something here…
EXEC (@sql)
–PRINT @sql
SELECT @RowCounter = @RowCounter + 1
END

Pleasant Things Work Better – UX Magazine

Pleasant Things Work Better | UX Magazine

“There’s a vast difference between making a design that doesn’t suck and creating one that people will love to use.”

As usual, decent read on UX…

Mom and Dad

“A mother’s love for her child is like nothing else in the world. It knows no law, no pity, it dares all things and crushes down remorselessly all that stands in its path.”

– Agatha Christie

“My father gave me the greatest gift anyone could give another person, he believed in me.”

- Jim Valvano

I am thankful for my mom and dad. They have believed in me, sometimes much more than I was worthy of at the time. They have valued me and shown love. I am so glad to have them. It is never without some regret that I think about the distance between us now that I have moved my family to Texas, but it is not something I can undo. But I miss them, and I know our kids miss them.

My mom has always had our back and while usually somewhat quiet, I have seen her in action as the momma bear and the quote above does her complete justice. Do not stand between her and her kids! Once when one of my sisters had mono and was sluggish in school, my mom came in to visit one particularly outspoken teacher who had given my sis a hard time about keeping up. My usually quiet mom stood up to this lady who was never soft-spoken, and the teacher backed down. Don’t mess with my mom when it comes to us kids!

When my Kathy got sick, mom was right there and provided help along with many others to get us through. Our kids found a second home in their house next-door to us. Sometimes you don’t think clearly enough about what you have until it’s gone…

Now family is more spread out than ever, and we have to work with video chat and only get home once every year or two. It is hard. I still feel her care for me even from 1700 miles.

Dad and I had a few good moments growing up, though neither of us would say it was great relationally. My dad was an angry man, angry from childhood junk, from his own choices, and I think he felt stuck. But he has in later years been working his way out of that and the edge of it has gone at least to a greater extent. I am grateful for this, as it has given us a chance to grow something. It has been in our later years that we have started to be friends and find common ground and conversation. But from day one, I knew he believed in me. He would brag on me to others saying I could do stuff that I’m not sure I could really do, but he was proud.

He worked for GE for years and was one of their best machinists. He taught me about working hard and that sometimes the best way to do something had to be discovered by your own effort. He is the only man I know who rode a motorcycle with a homemade steel cage side car all the way through the winter through snow / rain / etc. That got him on the evening news.

He rejected the status quo in his work, and worked to be the best. I do the same and my brain works a lot like his in that way. It is a genetic gift that has served me well. He proved that a dinner prayer could be heartfelt and 10 seconds long at the same time. His poems range from funny to serious, tho’ always not far from tongue in cheek, and always with a point. My not-so-well developed poetry and songwriting are apples close to the tree, though I could invest better in those talents.

Mom, Dad, I am thankful to have parents that feel the way you do about me and my family. I wish we were not so far. May God give us better and more frequent opportunities to be together.

“Once you eliminate the impossible…

“Once you eliminate the impossible, whatever remains, no matter how improbable, must be the truth.” – Arthur Conan Doyle, Sr. (Scottish writer, creator of the detective Sherlock Holmes, 1859-1930)

Doyle was on to something. Something that I should know by now and yet it evades me is to methodically search out a thing sometimes when the pressure is on.

We recently had a run-in with a SQL script that was not behaving as it should. But we didn’t know that, what we knew is that records had disappeared. No trace that we could see immediately.

We looked for them and thought maybe there was some sort of deletion done intentionally by users. Nope. Corruption? Not likely – Other functions and data were fine. Then we looked at the most obvious script that could have caused the issue. It looked like there were improvements that could be made to the script, however, it looked impossible that it could actually perform the delete we were suspicious of without making a copy of them prior to the delete. This was a correct assumption, however, I continued to try and prove it wrong.

The simpler and more logical approach would have been to get a list of all of the scripts that would ever do a delete and see if any of them had the possibility of causing the issue. I had actually suggested this and then got stuck on this one.

The problem turned out to be a companion script that usually only copied out records to archive them after they were 90 days old. It has been in production for years running just as it does today. It can’t be that…?

Well the script happened to convert a date after subtracting 90 days from it into a string. This would be all well and fine if the string was in year/month/day order, but it was instead in mm/dd/yyyy order. So when it came to comparing to see if a record was old enough to be archived, and it compared the month of January 4th, 2011 (01/04/2011) to October 7th, 2010 (10/07/2010), it actually only looked at the first character to determine that the date starting with “01″ was less than the one starting with “10″. So it archived the record, causing us a bit of a panic.

Doyle also said: “There is nothing as deceptive as an obvious fact.” Likewise, this eluded us. I spent a lot of time trying to see if a long shot was the trouble, when all along it was an obvious fact sitting right beside me.

It is worth it to take a breath and assess an emergency situation before running headlong into what you think may be a solution. A thorough assessment would have saved us an evening of trouble. Hopefully I remember this next time.

How Accountants Kill Innovation

This article shows the value in taking small steps (sort of like agile) toward a project while continuing to assess its value can lead to great rewards rather than trying to assess all of the value up front prior to doing any investment.

A good, short read.

How Accountants Kill Innovation

Follow

Get every new post delivered to your Inbox.