Saturday, June 30, 2007

Basket Pipeline Performance

We're nearing the end of development for one of the projects I'm on right now. This site utilized some AJAX and these functions needed to be snappy to really work, so I started looking into tuning performance. After profiling, I found that by far the site's biggest bottleneck was the basket pipeline.

Like most e-commerce sites, this one has a shopping basket summary on every page to show the user how many items they have in their basket, and the basket's subtotal. We were running the basket pipeline before rendering this summary to make sure that the total was up to date, which means that the pipeline was running at least once per page. It also ran additional times if any items were added to the cart during that request. As it turned out, even running it once was our bottleneck by a long shot.

This information led us to a new approach:
  • Whenever the basket is modified (e.g., when an item is added), the basket pipeline will be run and the basket saved.
  • The basket summary will use the totals saved from the last time the pipeline was run. These totals may be stale if any item's price has changed since it was added to the cart.
  • The user will be required to review their cart before checking out. This page will run the basket pipeline again to make sure that the user is being given the most up-to-date prices.
This approach means that the summary might have a stale total if prices change after items are added to the basket, but we considered this acceptable since they are required to review their basket before checking out. Max Akbar also validated this approach, and based on his response I think this is how we are expected to handle these summary controls.

Saturday, June 23, 2007

Targeted Ads Based on User Profile

I recently had to prepare a demonstration of Commerce Server, to include targeted ads. Marketing is one area that I'm not familiar with, and the documentation is a little sparse, so I wanted to share the solution to the major snag I hit: targeting based on the current user's profile. I based the demo on a site that was already displaying ads, and so will assume here that this is your starting point as well. If not, I think you will find enough documentation to get to that point.

First, you'll need to modify one of your regular ads to be targeted:
  1. Add the UserObject targeting context. For the user profile to be available for your targeting expressions, the context needs to be added in the marketing manager. Open the Marketing Manager, and choose the "Expressions" view in the left pane. Then click "Set Targeting Profiles" in the task list, and add the User Object (or whatever profile object represents your user profile) if it is not already present in the right-hand pane.
  2. Create your expression. Click "Create new target expression" in the list of tasks. Choose a name for your expression (123 area code), and build the expression ("UserObject.Telephone Number, Begins with, 123"). Click OK when you're done.
  3. Add the targeting expression to your ad. Open the ad you would like to target, and disapprove it if it is already approved. On the targeting tab, click Add in the "Targeting Expressions" section, and choose your new targeting expression. If you check "Create a Local Copy of this Global Expression", it will make a copy of the expression rather than link to the one you choose. This means that changes to the expression in the future won't affect your ad.
  4. Choose a targeting Action. You just added a condition to your ad. Now you need to specify whether your ad is only included where that condition is true ("Require"), or never shown if it's true ("Exclude"). These are just the simple choices; check MSDN for the full details.
  5. Save, Approve, and close the ad.
Now that the ad is configured, you may need to make some changes to make it appear correctly on your site. You should already have some code written that uses a ContentSelector object to grab a few ads to display. Remember when you had to make sure that the UserObject was available for creating your targeting expression? You have to do essentially the same thing for the ContentSelector. You might think that it will use the CommerceContext.UserId or ComemrceContext.UserProfile to determine the current user, but it does not; you have to tell it which user profile to use by adding it to the Profiles collection. You should end up with something like this:

There you have it. Your targeted ad should now be worked into the set of ads returned by the ContentSelector based on the current user's profile. One more thing though, just in case: tracing. There is a trace mode for the ContentSelector, which you can use to see into the content selection process a bit. Its messages are quite cryptic, but it was useful for me to help troubleshoot why certain ads weren't showing. Here's some example code that will drop the trace messages into the page (borrowed from some Commerce Server boot camp training materials!):

ContentSelector cso = adSelector;
Response.Write(Environment.NewLine + Environment.NewLine + "<hr>");
Response.Write(Environment.NewLine + "<h2>cso Values</h2>");
Response.Write(Environment.NewLine + "ItemsRequested - '" + cso.ItemsRequested + "'<br/>");
Response.Write(Environment.NewLine + "Name - '" + cso.Name + "'<br/>");
Response.Write(Environment.NewLine + "PageHistory - '" + cso.PageHistory + "'<br/>");
Response.Write(Environment.NewLine + "Size - '" + cso.Size + "'<br/>");

Response.Write(Environment.NewLine + Environment.NewLine + "<h2>TraceMessages</h2>");
Response.Write(Environment.NewLine + "<table border=1>");
int i = 0;
foreach (StringCollection strcol in cso.TraceMessages)
{
Response.Write(Environment.NewLine + Environment.NewLine + "<tr><td>Item" + i + "</td><td>");
foreach (string s in strcol)
{
Response.Write(Environment.NewLine + s + "<br/>");
}
Response.Write(Environment.NewLine + "</td></tr>");
i++;
}
Response.Write(Environment.NewLine + "</table>");