Thursday, March 17, 2011

Memcached on Windows.

As you may know Memcached is a distributed caching system that is used by major website out there (facebook et.al) and there are many articles out in the wild that handle this matter on windows, I will try and put in my experience and my lessons learnt. As you are aware -  memcached is not exactly meant to run on windows and thus we have to put up with ports of the original software that my not exactly be the best to run on a production environment. I started out with the http://code.jellycan.com/memcached/ build. It was kind of old since the last time it was updated was in November 2009. It worked for a while but I could not run it with a degree of surety on production, I figured it may possibly have had some optimization done on it and some bugs lurking in there that might have been resolved. So I decided to do some bit of research in an attempt to get the latest and greatest release of memcached for windows. I stumbled upon Membase this is one of the best Memcached like services, it is basically in two parts
1.    The Membase Server is a NoSQL database. That is basically a distributed key-value data store; it is designed and optimized for the data management needs of interactive web applications, so it allows the data layer to scale out just like the web application logic tier – simply by adding more commodity servers.
2.    Memcached is part of Membase Server, transparently caching data in front of its key-value database. You can configure Membase Server to provide only Memcached services and for there for skip the use of the NoSQL database.
I decided to use the later only as what I needed was the caching framework only, and this worked for a while and worked really well coupled with the nice intuitive administration portal that comes with the install – it was exactly what I was looking for...until I attempted to throw in a large dataset for caching and it failed, We quickly diagnosed that the issue was that all memcached services have a max of 1024kb size for items that need to be stored in them – this was not acceptable as we had started implementing a caching collections instead of individual items policy for our work. Apparently there is an argument/flag passed at start-up that instructs the memcached service to start accepting items that are larger than 1mb .
memcached.exe -I
But this particular flag was not available(availed) in the memcached service on the Membase distribution. So I was regrettably back on the road, looking for an build that was new and that had capability of accepting the argument for our work.
After sometime I came across an article on how to build the memcached service on windows and to this I said a big Hallelujah!! I executed steps outlined below to do my own build.
1.    Installed mysysgit. This in addition to installing git, it also installed a compiler capable of building a 32 bit version of memcached on windows. Since apparently Microsoft does not supply a C99 compliant C compiler…go figure.
2.    Installed libevent 2.0.10 (latest at the time) on my machine by executing the following commands (from within your msysgit-shell).
$ cd /tmp
$ tar xfz libevent-2.0.10-stable.tar.gz
$ cd libevent-2.0.10-stable
$ ./configure --prefix=/usr/local
$ make all
$ make install  
3.    Built memcached by checking out and compiling the source code
$ git clone git://github.com/trondn/memcached.git
$ git checkout -t origin/engine
$ make -f win32/Makefile.mingw

I run it by executing 
        $ ./memcached.exe -E ./libs/default_engine.s
Then tested it using telnet to port 11211 and issue the "stats" command to verify that it works! On the deployment environment I just included pthreadGC2.dll from the msysgit distribution and the libs/default_engine.so in the folder I wanted memcached to run on.


Now building a x64 bit release was not as easy and I gave up! 

Wednesday, March 16, 2011

Killing IE6

I am just done watching episode 94 of Ping on channel 9 and heard that Microsoft is doing all it can to make IE6 lose its market share (loved the catchphrase – “friends don't let friends use IE6”), Its a fairly belated attempt seeing as companies like Google have already joined the armada of companies that do not support IE6. To web developers, this is definitely a great thing and means that we will decreasingly stop creating cascading style sheets that is specifically meant to run on IE6 and below and endless hours of javascript debugging.
This means no more png alpha transparency fixes, no more....

.box {
   background: #00f; /* all browsers including Mac IE */
   *background: #f00; /* IE 7 and below */
   _background/**/: #0f0; /* IE 5.0 */
   _background:/**/ #f62; /* IE 5.5 only */
   _background/**/:/**/ #f61; /* IE 6 only */
   padding: 7px;
   color: #fff;
}
nonsense in CSS!! Welcome to the beginning of a standardized Internet!! –DO I HAVE A WITNESS?

On the flipside (for Microsoft), using the latest stats, it seems the loss of IE6 market share does not translate to the gain in market share of IE7+ browsers as Google Chrome appears to be growing almost at the rate IE is losing ground. (At this point I drop my well thought of pun of the week and the only reason I wrote this post!!)
Microsoft – be careful what you wish for because it may just CHROME true.

P.S there is a website out there that does real time countdown of IE6 Market share its called IE6Countdown that complements a bunch of websites that want IE6 dead they are I Dropped IE6 , IE6 Update and IE6 No more

Tuesday, March 15, 2011

Preventing Lazy and comment-less source code checking in subversion

I come from a background of using Visual Source safe as a tool for source control and had never really needed to setup a source control infrastructure prior to my assignment in Pakistan. So when I landed the job, I quickly downloaded and installed the Collabnet build of subversion. Then decided that like any software development team worth its salt, I would have to create software coding practices and procedures and one of which was the use of source control and thus formulated rules around checking in source code such as No check ins without comments, All comments must make sense blah blah blah. Later during my analysis of the check ins I realised that my team members were going against rules and were checking in code without comments or just lazy comments like “fixed” or “done” or “checked in” at which point I came to a crossroad – do I lay down the law at them for doing this or do I find an automated way of resolving this matter and I decided the later had a more long lasting effect – plus it stretched us, which is good. So I did my research and discovered that it was not direct provided (not a config change) but was possible by using hooks.
I created a file that had a bunch of excluded words as mentioned above saved it in c:\svn\excludedwords.txt and then created a pre-commit batch file that was supposed to be run prior to committing the change(duh!). The exact filename was Pre-commit.bat that was placed in the Hooks folder on SVN
The contents of the batch file were
rem Make sure that the log message contains some text.
set REPOS=%1
set TXN=%2

"C:\svn\SVNlook.exe" log -t %TXN% %REPOS% | FindStr [a-zA-Z0-9]
IF %ERRORLEVEL% EQU 0 GOTO OK
echo Your commit has been blocked because you didn't provide a log message  1>&2
echo Please write a log message describing the purpose of your changes and 1>&2
echo then try committing again. -- Thank you 1>&2
exit 1

:OK
rem Check if comment is in list of reserved words to not be used..


the above is meant to check for empty comments and would echo out correct error messages.

"C:\svn\SVNlook.exe" log -t %TXN% %REPOS% >comment
setlocal enabledelayedexpansion
Set SEPARATOR=
set COMMENT=
for /f "delims=" %%a in (comment) do ( 
    set currentline=%%a
    set COMMENT=!COMMENT!%SEPARATOR%!currentline!
)

FIND "%COMMENT%" "c:\svn\excludedwords.txt">Null
If %ERRORLEVEL% EQU 1 goto OK2

:Fail
echo Your commit has been blocked because the single word comment you provided is not allowed 1>&2
echo Line is -%COMMENT%- 1>&2
echo Please write a proper log message describing the purpose of your changes and 1>&2
echo then try committing again. -- Thank you 1>&2
exit 1

:OK2
rem Check that the author of this commit has the rights to perform
rem the commit on the files and directories being modified.
rem commit-access-control.pl "$REPOS" "$TXN" commit-access-control.cfg || exit 1

rem All checks passed, so allow the commit.
exit 0

The above was used to check if the comment was a single word or found in my excluded list of words.

So I quietly sneaked in the change and waited for responses, I got a few moans but explained later that it was important to follow rules.

Sunday, March 13, 2011

10000 apps milestone and other interesting stats

Its been reported that the Microsoft market place has hit the 10000 apps milestone sometime yesterday and was at 10162 at the time i went to press - Kudos (even after locking out a huge number of potential developers)
windows phone 7 applications

windows phone 7 applications

windows phone 7 applications

windows phone 7 applications

windows phone 7 applications

windows phone 7 applications

the above charts should update automatically.

you can get the html for the above at http://www.windowsphoneapplist.com/stats/

Friday, March 11, 2011

WP7 spared at Pwn2Own Hackathon

At the annual Pwn2Own hackathon, IPhone and Blackberry failed as Android and WP7 were left standing as the hackers withdrew at the last minute citing to legal and other hindrances.

Wednesday, March 9, 2011

The Global Publisher Program

As I had ranted before - Microsoft does not allow developers from Africa and some areas of the middle east and Asia to register as developers for WP7, this was a short sighted bummer coz it affects the platform as the assumption that those regions do not have developers is silly and gives android an upper hand (p.s. I have already started running Eclipse on my laptop, reading the professorial android application development and a cheap android tablet)
If you are thinking of a rich market for mobile/smart phone look no further than these places.
Anyway - a couple of days ago there was some movement  in that front as now you can register with a third party service called Yalla Apps that will publish your apps for you and take 20% of the earnings (after Microsoft's 30%) so you get 80% of what you would normally receive. You have to pay $99 for 100 credits, each app submission costs 25 credits wether it's a free or paid app, and development device unlocking costs 50 credits per device and is accomplished using remote desktop access from Yalla Apps. Its not the best suited solution but its the best we have for now.

Monday, March 7, 2011

Way Forward

Today represents the last day of my work in Pakistan for the foreseeable future and the end of the contract I have been on for almost an year, I still don't have anything solid to fall on. So the tomorrow is the first day of the rest of my life and consequently it will really be the first time in my life that I have been in between jobs and nothing to peg my employment insecurities on. I am kinda looking forward to it...how will I react? How will I cope, will I will be freaked out, I hope I will be able to cut it - nothing seems to appeal to me just as yet. A couple of questions lurk at the back of my mind....
  1. Will I go back to international consulting?- Maybe - subject to opportunities,
  2. Will I go back to local support staff status - unlikely (but I have a couple of mouths to feed so its not out of the case - but it will be quite a sellout) ,
  3. Will I go back to the private sector? - I don't Know - but I don't think so, slaving away for someone else,
  4. Will I attempt to do something on my own? - I hope so!! Thus What will I do? I need the muses to inspire!! (this can be linked up with No. 1 above)
so, the rest of my life is a canvas waiting for me to paint it....let the games begin.

Database related Issues experienced using EF 4.0 and Transaction scope objects.

As I had blogged previously, we have just gone live in certain places in Pakistan with the Computerized Electoral Roll System , a project I have been consulting on in the last couple of months. Conseqently yours truly yesterday (07th March 2011) traveled to Osama bin laden’s backyard – Peshawar in Khyber-Pakhtoonkhwa for a go live test, demo, walk through all wrapped up in one that failed coz the links that connect the provinces’ to the capital were down and we had to settle for the use of a development copy and database for all of the above.

But running towards this go live we had experienced some problems. This is an attempt at logging them and documenting them of all and sundry – most of this is not new nor am I the first to resolve them but I will show you how I resolved them.

The first problem I run into months ago while using transactions was that I seemingly could not execute an insert, update or delete sp (imported function) when in the transaction context.

e.g.

var datactx = ContextHelper<CERSEntities>.GetCurrentContext();

using (var scope = new TransactionScope())

{

List<VOTER> updatedvoters = new List<VOTER>();

foreach (var voter in voterEntity)

{

datactx.UpdateVoters(Param1,Param2,...);

}

scope.Complete();

}

Was causing issues, until I discovered that EF 4.0 was silly and that it somehow expected a

insertedvoter.FirstOrDefault();

After each call to the store procedure more info on this can be found here.

Secondly we run into a problem in that we had been developing had been on SQL Server 2008 but the target database was SQL 2005 - this posed two issues, first a more obvious issue we received a message that

"Type datetime2 is not a defined system type"

Datetime2 is not available in SQL 2005 so I quickly redid the EDMX, and changed its version by opening the edmx file using a text editor and changing the provider manifest to 2008

<edmx:Edmx Version="2.0" xmlns:edmx="http://schemas.microsoft.com/ado/2008/10/edmx">

<edmx:Runtime>

<edmx:StorageModels>

<Schema Namespace="CERSModel.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2008" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">

Saved it change it to ProviderManifestToken="2005" and recompiled DAL

The second problem was more subtle and only happened in certain screens i.e. the transactions we were running were now becoming distributed. We traced it to the TransactionScope object. It was now all over suddenly escalating transactions to distributed transactions. I have been using the TransactionScope ever since I discovered it, because it’s a nice neat way of providing a neat way to do transactions while also giving your code the ability to reuse ambient transactions (i.e. transactions already created by caller methods) while supporting method level transactions just in case the method is called by a method that does not have a transaction.

But because we were not spanning database nor using multiple connection strings – the obvious things that cause normal transactions to escalate into distributed transactions, this was problematic we later dug up the reason as being that the issue was happening because the target db server was a SQL 2005. This posed a problem that would either need to

1. Upgrade the target database server to 2008 – the cost implications and the time did not make it easy on us (we were quite tempted to run the prod database of 80 million voters on SQL Server 2008 development edition).

2. Replicate all the code that was being used in methods that used ambient transactions – this proved to be quite untidy not to mention unprofessional.

3. Create a customized transaction scope object that would be used to do exactly what the TransactionScope object does but in the context of our use case.

I thought the latter was neater and would be a nice challenge and had a couple of hours to kill so I decided to do my own transaction scoping mechanism by – this is how I did it.

1. I created an extension method that would be attached on a data context and this was it

public class OurStaggeredDBTransaction : MarshalByRefObject, IDbTransaction

{

private const string TransactionCountKey = "TransactionCount";

public IDbTransaction Transaction { get; set; }

private bool CommitCalled = false;

public OurStaggeredDBTransaction(IDbTransaction originaltrans)

{

Transaction = originaltrans;

}

private void ResetCount()

{

HttpContext httpContext = HttpContext.Current;

if (httpContext != null)

{

string contextTypeKey = TransactionCountKey + typeof(OurStaggeredDBTransaction).Name;

if (httpContext.Items[contextTypeKey] != null)

{

httpContext.Items[contextTypeKey] = 0;

return;

}

else

{

throw new ApplicationException("There is no Nested Transaction in Http Context available");

}

}

throw new ApplicationException("There is no Http Context available");

}

public void IncreaseCount()

{

CommitCalled = false;

HttpContext httpContext = HttpContext.Current;

if (httpContext != null)

{

string contextTypeKey = TransactionCountKey + typeof(OurStaggeredDBTransaction).Name;

if (httpContext.Items[contextTypeKey] != null)

{

httpContext.Items[contextTypeKey] = ((int)httpContext.Items[contextTypeKey]) + 1;

}

else

{

httpContext.Items.Add(contextTypeKey, 1);

}

return ;

}

throw new ApplicationException("There is no Http Context available");

}

private void DeductCount()

{

HttpContext httpContext = HttpContext.Current;

if (httpContext != null)

{

string contextTypeKey = TransactionCountKey + typeof(OurStaggeredDBTransaction).Name;

if (httpContext.Items[contextTypeKey] != null)

{

httpContext.Items[contextTypeKey] = ((int)httpContext.Items[contextTypeKey]) - 1;

return;

}

else

{

throw new ApplicationException("There is no Nested Transaction in Http Context available");

}

}

throw new ApplicationException("There is no Http Context available");

}

private int GetCount()

{

HttpContext httpContext = HttpContext.Current;

if (httpContext != null)

{

string contextTypeKey = TransactionCountKey + typeof(OurStaggeredDBTransaction).Name;

if (httpContext.Items[contextTypeKey] != null)

{

return (int)httpContext.Items[contextTypeKey];

}

else

{

throw new ApplicationException("There is no Nested Transaction in Http Context available");

}

}

throw new ApplicationException("There is no Http Context available");

}

public void Commit()

{

CommitCalled = true;

if (GetCount() > 1)

{

DeductCount();

}

else

{

Transaction.Commit();

}

}

public IDbConnection Connection

{

get { return Transaction.Connection; }

}

public IsolationLevel IsolationLevel

{

get { return Transaction.IsolationLevel; }

}

public void Rollback()

{

CommitCalled = false;

ResetCount();

Transaction.Rollback();

}

public void Dispose()

{

if (CommitCalled && GetCount() >0)

{

//do nothing

//Transaction.Dispose();

CommitCalled = false;

}

else

{

Transaction.Dispose();

}

}

}

public static OurStaggeredDBTransaction BeginTransaction(this CERSEntities objcontext)

{

HttpContext httpContext = HttpContext.Current;

if (httpContext != null)

{

string contextTypeKey = ObjectContextKey + typeof(CERSEntities).Name;

if (httpContext.Items[contextTypeKey] == null)

{

if (objcontext.Connection.State != System.Data.ConnectionState.Open)

objcontext.Connection.Open();

httpContext.Items.Add(contextTypeKey, new OurStaggeredDBTransaction(objcontext.Connection.BeginTransaction()));

}

//increase the count for each time requested

((OurStaggeredDBTransaction)httpContext.Items[contextTypeKey]).IncreaseCount();

return (OurStaggeredDBTransaction)httpContext.Items[contextTypeKey];

}

throw new ApplicationException("There is no Http Context available");

}

2. Created an extension method that could be used on a datacontext object (CERSEntities in our case)

public static OurStaggeredDBTransaction BeginTransaction(this CERSEntities objcontext)

{

HttpContext httpContext = HttpContext.Current;

if (httpContext != null)

{

string contextTypeKey = ObjectContextKey + typeof(CERSEntities).Name;

if (httpContext.Items[contextTypeKey] == null)

{

if (objcontext.Connection.State != System.Data.ConnectionState.Open)

objcontext.Connection.Open();

httpContext.Items.Add(contextTypeKey, new OurStaggeredDBTransaction(objcontext.Connection.BeginTransaction()));

}

//increase the count for each time requested

((OurStaggeredDBTransaction)httpContext.Items[contextTypeKey]).IncreaseCount();

return (OurStaggeredDBTransaction)httpContext.Items[contextTypeKey];

}

throw new ApplicationException("There is no Http Context available");

}

3. Changed all transaction scope objects to be as follows

using (var scope = datactx.BeginTransaction())

{

....................

scope.Commit();

}

This worked like a charm, as I reused the same transaction and kept count of callers and failure in one resulted in failure in all but success in on would just reduce the count of callers.

It could do with some more work, but it seems to work as of now – which I am happy with. Feel free to correct, critic the above code.

I will be on my way to Nairobi in a couple of hours….looking forward to holding my wife and baby boy in my hands!!!