Posts Tagged ‘IdeaPipe’

September 24th, 2008

How NOT To Optimize LINQ Statements

About a month ago I was experimenting with different ways to optimize my LINQ queries against the IdeaPipe database, in order to improve the read times. I wanted to improve the read times because our new Facebook Application was being launched and I anticipated an increase in our traffic to the server, which is used to host IdeaPipe and the Facebook Application component.

Whenever I am trying to optimize SQL queries I fire up SQL Server Profiler and take a look at how the queries are performing. This helps me identify queries that are taking a longer time to execute and probably need to be looked at or re-thought. One of the queries that I identified as needing improvement was the following LINQ query:

Old Query

from i in source
join xgl in GroupIdeaLinks on i.IdeaId equals xgl.IdeaId into groupLinksGroup
from gl in groupLinksGroup.DefaultIfEmpty()
let visibility = (gl == null ? 'O' : gl.Group.VisibilityPermission)
let groupId = (gl == null ? -1 : gl.GroupId)
where visibility == 'O' || GroupMembers.Count(m => m.GroupId == groupId && m.IsApproved && m.UserId == userId && (visibility == 'G' || (m.RoleId & (int)Role.Manager) == 0)) == 1
select i;

That produced this monster of a SQL statement:

SELECT [t5].[IdeaId], [t5].[CategoryId], [t5].[UserId], [t5].[IsPopular], [t5].[PopularOn], [t5].[Title], [t5].[SafeDescription], [t5].[Description], [t5].[Path], [t5].[Score], [t5].[Rank], [t5].[ExternalLink], [t5].[VideoLink], [t5].[BumpUpCount], [t5].[BumpDownCount], [t5].[TotalBumpCount], [t5].[TotalCommentCount], [t5].[CreatedOn], [t5].[rowversion]
FROM (
    SELECT [t4].[IdeaId], [t4].[CategoryId], [t4].[UserId], [t4].[IsPopular], [t4].[PopularOn], [t4].[Title], [t4].[SafeDescription], [t4].[Description], [t4].[Path], [t4].[Score], [t4].[Rank], [t4].[ExternalLink], [t4].[VideoLink], [t4].[BumpUpCount], [t4].[BumpDownCount], [t4].[TotalBumpCount], [t4].[TotalCommentCount], [t4].[CreatedOn], [t4].[rowversion], [t4].[value],
        (CASE
            WHEN [t4].[test] IS NULL THEN @p1
            ELSE [t4].[GroupId]
         END) AS [value2]
    FROM (
        SELECT [t0].[IdeaId], [t0].[CategoryId], [t0].[UserId], [t0].[IsPopular], [t0].[PopularOn], [t0].[Title], [t0].[SafeDescription], [t0].[Description], [t0].[Path], [t0].[Score], [t0].[Rank], [t0].[ExternalLink], [t0].[VideoLink], [t0].[BumpUpCount], [t0].[BumpDownCount], [t0].[TotalBumpCount], [t0].[TotalCommentCount], [t0].[CreatedOn], [t0].[rowversion], [t2].[test], [t2].[GroupId],
            (CASE
                WHEN [t2].[test] IS NULL THEN @p0
                ELSE CONVERT(NChar(1),[t3].[VisibilityPermission])
             END) AS [value]
        FROM [Ideas].[Ideas] AS [t0]
        LEFT OUTER JOIN (
            SELECT 1 AS [test], [t1].[GroupId], [t1].[IdeaId]
            FROM [Groups].[GroupIdeaLink] AS [t1]
            ) AS [t2] ON [t0].[IdeaId] = [t2].[IdeaId]
        INNER JOIN [Groups].[Groups] AS [t3] ON [t3].[GroupId] = [t2].[GroupId]
        ) AS [t4]
    ) AS [t5]
WHERE (UNICODE([t5].[value]) = @p2) OR (((
    SELECT COUNT(*)
    FROM (
        SELECT
            (CASE
                WHEN ([t6].[GroupId] = [t5].[value2]) AND ([t6].[IsApproved] = 1) AND (([t6].[UserId]) = @p3) AND ((UNICODE([t5].[value]) = @p4) OR (([t6].[RoleId] & @p5) = @p6)) THEN 1
                WHEN NOT (([t6].[GroupId] = [t5].[value2]) AND ([t6].[IsApproved] = 1) AND (([t6].[UserId]) = @p3) AND ((UNICODE([t5].[value]) = @p4) OR (([t6].[RoleId] & @p5) = @p6))) THEN 0
                ELSE NULL
             END) AS [value]
        FROM [Groups].[GroupMembers] AS [t6]
        ) AS [t7]
    WHERE [t7].[value] = 1
    )) = @p7)

So I started playing around with the LINQ statement until I reduced the size of my SQL query significantly. The following is the result of that optimization:

New Query

var groupMembership = (from gm in GroupMembers
                       let visibility = gm.Group.VisibilityPermission
                       where visibility == 'O' || (gm.IsApproved && gm.UserId == userId && (visibility == 'G' || (gm.RoleId & (int)Role.Manager) == 0))
                       select gm.GroupId).Distinct();

from i in Ideas
join xgl in GroupIdeaLinks on i.IdeaId equals xgl.IdeaId into groupLinksGroup
from gl in groupLinksGroup.DefaultIfEmpty()
where gl == null || groupMembership.Contains(gl.GroupId)
select i;

Which outputs the following SQL query:

SELECT [t0].[IdeaId], [t0].[CategoryId], [t0].[UserId], [t0].[IsPopular], [t0].[PopularOn], [t0].[Title], [t0].[SafeDescription], [t0].[Description], [t0].[Path], [t0].[Score], [t0].[Rank], [t0].[ExternalLink], [t0].[VideoLink], [t0].[BumpUpCount], [t0].[BumpDownCount], [t0].[TotalBumpCount], [t0].[TotalCommentCount], [t0].[CreatedOn], [t0].[rowversion]
FROM [Ideas].[Ideas] AS [t0]
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t1].[GroupId], [t1].[IdeaId]
    FROM [Groups].[GroupIdeaLink] AS [t1]
    ) AS [t2] ON [t0].[IdeaId] = [t2].[IdeaId]
WHERE ([t2].[test] IS NULL) OR (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM (
        SELECT DISTINCT [t3].[GroupId]
        FROM [Groups].[GroupMembers] AS [t3]
        INNER JOIN [Groups].[Groups] AS [t4] ON [t4].[GroupId] = [t3].[GroupId]
        WHERE (UNICODE([t4].[VisibilityPermission]) = @p0) OR (([t3].[IsApproved] = 1) AND (([t3].[UserId]) = @p1) AND ((UNICODE([t4].[VisibilityPermission]) = @p2) OR (([t3].[RoleId] & @p3) = @p4)))
        ) AS [t5]
    WHERE [t5].[GroupId] = [t2].[GroupId]
    ))

As you can tell the second query is much more compact and it does the exact same thing as the first query. I was pretty proud of my self and riding high on my genius, until this happened:

Well this didn’t really happen, but you get the point. I quickly came down from my high when I tested the performance of the new query in SQL Server Profiler, and received these results from the two queries:

  • OLD QUERY (Reads 130, Durration 5)
  • NEW QUERY (Reads 218, Durration 28)

That is right my optimization increased the number of times SQL has to read the table by 68% and time it takes to execute by 460%. So I reversed all my changes and learned a lesson on how not to optimize a LINQ statement.

The moral of the story is you probably don’t need to optimize your SQL query through LINQ, just keep it simple and optimize your LINQ statement and leave the rest up to the professionals at Microsoft who created the LINQ to SQL expression query generator.

Tags: , , ,

Posted in C#, How To, SQL | kick it on DotNetKicks.com | Bookmark | View blog reactions | 7 Comments »

July 16th, 2008

Review of NDepend

When I was asked to review NDepend I didn’t want to do just another review.  Because there are plenty of great reviews and I would just be adding to the noise.  So I am going to use this time to give a personal story of how NDepend has helped me restructure some code I have been working on.

As you are probably aware I have been working on a start up, called IdeaPipe, we have been going full steam ahead since January 2008 to get IdeaPipe to the point where it is today.  Going at that break neck speed for almost 6+ months has left some raw spots in the code, that I have been putting off.  Because lets face it even though they are not optimal, they are returning the correct results.  Just like a rough draft for a book it is better to get all the way through it and get a good picture of the entire story as a whole, and go back and rework the spots that were created inorder to advance the storeline.  The same is true for software.

In large software projects you sometimes forget where these rough spots were and why you actually implimented something in a certain way.  Luckily for us we have tools like NDepend that provide base metrics telling you were you need to focus your work. 

One of the features that I loved the most in NDepend was this CQL (Code Query Lanauge) that allows you to run SQL like queries against your assemblies.  So if you wanted to find all the private variables that didn’t follow the rule of starting with “_” that is as easy as running a SELECT query in the command window.  The CQL is by far the coolest software feature that I have seen in a while.

Overall I give NDepend a thumbs up.  I do have one request for the good folks at NDepend and that is to intigrate all the windows in to Visual Studio to provide a seemless experience for us developers.

Tags: , ,

Posted in Programming, Review | kick it on DotNetKicks.com | Bookmark | View blog reactions | 2 Comments »

June 6th, 2008

MVC + Facebook == Wonderful Development Platform

Just recently I started experimenting with the ASP.NET MVC Framework and the Facebook Development Platform, it has been a very bumpy road, but I have ironed out some major issues that I would like to share with you today. I will start with a little history of what I am trying to do. For about a month and a half I have had one of my IdeaPipe interns, Dimitry, experimenting with creating a FBML (Facebook Meta Language) Application with MVC. MVC is an ideal platform for FBML because with MVC you have total control over your markup which is needed to have a lean FBML application. I am not going to go in to the differences of developing an FBML vs IFrame Facebook Application, because that information is easily found with a Google Search. What I am going to talk about is the hurdles I overcame and the custom software I had to develop to get MVC working smoothly with Facebook.

In my last post on the subject I was using the Facebook Developer Toolkit, however because of various implementation problems that were at the foundation of the software when working with MVC, I moved to Facebook.NET which is a object based model for implementing the Facebook Session instead of an inheritance model. What you will need in order to get started is:

One of the problems I ran into was creating a Facebook Session from my Action Method. To remedy this issue I created a FacebookAttribute that is an ActionFilterAttribute and a FacebookWebSession based off of the work done on Facebook.NET.

FacebookAttribute

The FacebookAttribute is added to your Action Methods and will look like the following:

[Facebook(ApplicationName = "IdeaPipe")]
public ActionResult SomeAction(FacebookService facebookService, FacebookSession facebookSession, int myOtherVariables)
{ ... }

As you can see the FacebookAttribute just attaches to the Action Method and you just have to specify your ApplicationName that you want to instantiate. The FacebookAttribute also passes in a FacebookService and FacebookSession object for use in your method. The other keys get set in your Web.Config as any standard Facebook.NET application would.

<facebook>
    <application name="IdeaPipe" apiKey="1234" secret="5678" type="GlobalApplication" />
</facebook>

The magic behind this attribute is pretty simple.

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
	FacebookApplicationSettings settings = FacebookSection.GetApplication(ApplicationName);

	ApplicationKey = ApplicationKey ?? settings.ApiKey;
	Secret = Secret ?? settings.Secret;

	FacebookWebSession session = new FacebookWebSession(ApplicationKey, Secret);
	session.Initialize(HttpContext.Current);

	FacebookService service = new FacebookService(session);

	if (filterContext.ActionParameters.ContainsKey(ActionParameterFacebookSession))
		filterContext.ActionParameters[ActionParameterFacebookSession] = session;

	if (filterContext.ActionParameters.ContainsKey(ActionParameterFacebookService))
		filterContext.ActionParameters[ActionParameterFacebookService] = service;
}

Download: FacebookAttribute.cs

FacebookWebSession

The FacebookWebSession was developed out of necessity because the only other FacebookSession objects in Facebook.NET are strongly tied to a WebForms Control that couldn’t be created as easily as I did in the FacebookAttribute. I am going to fore go the source code since much of this is a copy, paste, and rearrange from the Facebook.NET source. Plus much of it is just boring if-then-else statements that go on for awhile and just do a technical setup from the query string fields.

Download: FacebookWebSession.cs

FacebookSection

This is the file that I had to change the one method from internal to public so that I could get the information contained in the Web.Config configuration for my application. Note this file will not be necessary in the future if my changes get accepted in to the Facebook.NET source tree.

This file replaces the \Web\Configuration\FacebookSection.cs of the Facebook.NET source.

Download: FacebookSection.cs
Download: Facebook.NET Binaries For MVC

So that is all that you should need in order to start working with Facebook Applications in MVC. Note that it is still a good idea to include the FacebookApplication control on your pages because it is still needed. The primary goal of the source code above was to allow the use the the FacebookSession in the Action methods. If you have any questions please post them below.

Tags: , ,

Posted in ASP.NET, C#, How To, Programming | kick it on DotNetKicks.com | Bookmark | View blog reactions | 11 Comments »

June 5th, 2008

Give Your ASP.NET Applications Velocity

Scaling ASP.NET Application just got easier with a new technology that Microsoft has just released that they have dubbed codename “Velocity”. This product is still in the early stages of development, but it is meant as a direct competitor against memcached. If you are not familiar with memcached, here is how it is described in Wikipedia:

memcached (pronunciation: mem-cache-dee) is a general-purpose distributed memory caching system that was originally developed by Danga Interactive for LiveJournal, but is now used by many other sites. It is often used to speed up dynamic database-driven websites by caching data and objects in memory to reduce the number of times the database must be read. Memcached is distributed under a permissive free software license.

So basically it allows you to create a distributed memory cache across your server farm, that allows any computer in the server farm to access the data in the cache. So there is no more issues with storing session data on server farms, or worrying about setting up common SQL stores of temporary data. This is also very practical for reducing database stress on Web 2.0 sites, many of the top Web 2.0 sites use this to reduce reads on the database.   The biggest user of memcached to date is Facebook.  This diagram below gives a basic idea of how distributed caching works.

Diagram of Velocity

There have been many Open Source projects for getting memcached working on C#, and most have been pretty successful, but because memcached was designed for the UNIX environment, porting memcached to a Windows Service has always lagged behind the actual releases of the required libraries to get memcached working. Enter Velocity, as the Velocity team describes:

Velocity is intended to provide distributed caching (in memory) for all .NET applications – from enterprise scale to web-scale. We believe that there are many applications that need a distributed caching mechanism, and that there is, therefore, a need for distributed caching as a core part of the .NET platform. We expect to have more integrated support for this functionality with other parts of the .NET platform in our upcoming releases.

There is also a pretty nice Velocity writeup on MSDN that goes in depth about how Velocity works as well as providing some basic code examples on how to get data into and out of your Velocity Cache. The current set of features looks pretty nice, and I can’t wait for Velocity to become more stable so I can introduce it in to the IdeaPipe mix.

Here is a breif overview of the Current Features:

  1. Support for different cache types, partitioned and local
  2. Support for different client types, simple and routing
  3. Load Balancing & Dynamic Scaling
  4. ASP.Net Integration, currently there is only a Session Provider
  5. Key and Tag based Access

And Beyond

  1. Availability – support for Failover when machines go down
  2. Replicated Cache – another cache type
  3. Embedded Topology – run the cache embedded within you application instead of as a cache service
  4. Notifications – Get notified when a object in the cache is updated
  5. Consistency Models – Support for both weak and strong consistency when doing reads/writes
  6. Native client access to the cache service (E.g – PHP, C++ etc)
  7. Manageability & Administration

Tags: , , , , ,

Posted in ASP.NET, C#, Review | kick it on DotNetKicks.com | Bookmark | View blog reactions | 6 Comments »