Archive

Archive for the ‘Distribued Cache’ Category

Wishlist for Velocity (CPT4?)

June 4, 2009 Leave a comment

I went to Rob‘s presentation on Velocity tonight, it’s a great presentation and a lot of information shared with audience.

I prepare a wishlist for Velocity (CPT4 or next beta release) and hope the Velocity team can take my wishlist into consideration.

  1. A new method: bool DataCache.ContainsRegion(string region).
    • This method checks if the region exists in the named cache or not.
    • See my previous post for details.
  2. Update the method DataCache.GetObjectsByAnyTag(List<DataCacheTag> tags, string region) so the keys of returned cache items are unique.
    • Make sure the cache key is not contained in the keys, then add the item into the collection.
    • See my previous post for example.
  3. Make sure the default region name is not arbitratry.
    • DataCache.GetCacheItem Method – Gets a DataCacheItem object to retrieve all information associated with your cached object in the cluster.
    • DataCache.GetCacheItem (String) -> Gets a DataCacheItem object to retrieve all information associated with your cached object in the cluster.
    • DataCache.GetCacheItem (String, String) -> Gets a DataCacheItem object to retrieve all information associated with your cached object in the cluster. For objects stored in regions.
    • When using the DataCache.GetCacheItem (String) method (without region specified), the return object’s property -RegionName – is kind of arbitrary.
  4. A new commad for administrators to create region in PowerShell.
    • New-CacheRegion [-CacheName] <CacheName> [-RegionName] <RegionName>
    • Example 1:
      • New-CacheRegion -CacheName Products -RegionName MyRegion -> this creates a region “MyRegion” in the named cache “Products”.
    • Example 2:
      • New-CacheRegion -RegionName MyRegion -> this creates a region “MyRegion” in the default cache “default”

Velocity (CTP3) – DataCache.GetObjectsByAnyTag Method

June 1, 2009 1 comment

I was writing some code to experiment the Tag search in Velocity CTP3 and I found there is some small bug in one of the get objects by tag methods – DataCache.GetObjectsByAnyTag.

The definition of the method of DataCache.GetObjectsByAnyTag is:

Gets an enumerable list of all cached objects in the specified region that have any of the same tags in common.

For example I have 3 cache items:

Item 1: key = “111″, content = “aaa”, tag = “a”

Item 2: key = “222″, content = “bbb”, tag = “b”

Item 3: key = “333″, content = “ccc”, tag = “a,b,c”

Here are some scenarios:
(1) If I search with Tag “a” via DataCache.GetObjectsByAnyTag(<a>, “my_region”), I get:
Item 1: key = “111″, content = “aaa”, tag = “a”
Item 3: key = “333″, content = “ccc”, tag = “a,b,c”
(2) If I search with Tag “b” via DataCache.GetObjectsByAnyTag(<b>, “my_region”), I get:
Item 2: key = “222″, content = “bbb”, tag = “b”
(3) But if I search with Tags “a,b” via DataCache.GetObjectsByAnyTag(<a, b>, “my_region”), I get:
Item 1: key = “111″, content = “aaa”, tag = “a”
Item 3: key = “333″, content = “ccc”, tag = “a,b,c”
Item 3: key = “333″, content = “ccc”, tag = “a,b,c”
Item 2: key = “222″, content = “bbb”, tag = “b”

Here are some scenarios:

(1) If I search with Tag “a” via DataCache.GetObjectsByAnyTag(<a>, “my_region”), I get:

Item 1: key = “111″, content = “aaa”, tag = “a”

Item 3: key = “333″, content = “ccc”, tag = “a,b,c”

(2) If I search with Tag “b” via DataCache.GetObjectsByAnyTag(<b>, “my_region”), I get:

Item 2: key = “222″, content = “bbb”, tag = “b”

Item 3: key = “333″, content = “ccc”, tag = “a,b,c”

(3) But if I search with Tags “a,b” via DataCache.GetObjectsByAnyTag(<a, b>, “my_region”), I get:

Item 1: key = “111″, content = “aaa”, tag = “a”

Item 2: key = “222″, content = “bbb”, tag = “b”

Item 3: key = “333″, content = “ccc”, tag = “a,b,c”

Item 3: key = “333″, content = “ccc”, tag = “a,b,c”

In scenario (3), the item 3 has been added twice because its tags are “a,b,c” and I search by <a,b> (Any Tag).

I believe this method - DataCache.GetObjectsByAnyTag - should check if the cache key is contained in the collection, if not, then add it to the collection – unless the velocity team want the developers do the checking of duplicated key in the caller program.

I will list my code and some screenshots.

The first file is CacheWorker.cs:

using System;

using Microsoft.Data.Caching;

namespace VelocityTest

{

public class CacheWorker

{

public const string DEFAULT_CACHE_REGION = “Default_Cache_Region”;

private static object _synclock = new object();

private static CacheWorker _cacheWorker = null;

private static DataCacheFactory _cacheFactory = null;

private static DataCache _defaultCache = null;

private static DataCache _eventCache = null;

private CacheWorker()

{

}

public static CacheWorker Instance

{

get

{

lock (_synclock)

{

try

{

if (_cacheWorker == null)

{

_cacheWorker = new CacheWorker();

// we use configuration file

_cacheFactory = new DataCacheFactory();

// default cache

_defaultCache = _cacheFactory.GetDefaultCache();

_defaultCache.RemoveRegion(DEFAULT_CACHE_REGION);

_defaultCache.CreateRegion(DEFAULT_CACHE_REGION, true);

// names cache

_eventCache = _cacheFactory.GetCache(“Event2″);

}

return _cacheWorker;

}

catch (DataCacheException ex)

{

return null;

}

}

}

}

public DataCacheFactory CacheFactory

{

get { return _cacheFactory; }

}

public DataCache DefaultCache

{

get { return _defaultCache; }

}

public DataCache EventCache

{

get { return _eventCache; }

}

public DataCacheItemVersion AddCache(string key, object value, TimeSpan? timeout)

{

DataCacheItemVersion version = null;

if (timeout.HasValue)

{

version = _defaultCache.Put(key, value, timeout.Value);

}

else

{

version = _defaultCache.Put(key, value);

}

return version;

}

}

}

The second file is Default3.aspx page:

<%@ Page Language=”C#” AutoEventWireup=”true” CodeBehind=”Default3.aspx.cs” Inherits=”VelocityTest.Default3″ %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;

<html xmlns=”http://www.w3.org/1999/xhtml&#8221; >

<head runat=”server”>

<title>Velocity – Tag Example</title>

</head>

<body>

<form id=”form1″ runat=”server”>

<p>

<h3>Velocity – Tag Example</h3>

<table border=”0″ cellpadding=”2″>

<tr>

<td><b>Cache Key</b></td>

<td>

<asp:TextBox ID=”txtCacheKey” runat=”server” />

<asp:RequiredFieldValidator ID=”vadCacheKey” runat=”server” ControlToValidate=”txtCacheKey” ErrorMessage=”* required” Display=”Dynamic” />

</td>

</tr>

<tr>

<td><b>Cache Content</b></td>

<td>

<asp:TextBox ID=”txtCacheContent” runat=”server” />

<asp:RequiredFieldValidator ID=”vadCacheContent” runat=”server” ControlToValidate=”txtCacheContent” ErrorMessage=”* required” Display=”Dynamic” />

</td>

</tr>

<tr>

<td><b>Cache Tags</b></td>

<td><asp:TextBox ID=”txtCacheTag” runat=”server” />(Tags are seperated by comma)</td>

</tr>

<tr>

<td>&nbsp;</td>

<td>

<asp:Button ID=”btnCancel” runat=”server” Text=”Cancel” CausesValidation=”false” />

<asp:Button ID=”btnSave” runat=”server” Text=”Save” />

</td>

</tr>

</table>

</p>

<hr />

<p>

<table border=”0″ cellpadding=”2″>

<tr>

<td><b>Search Method:</b></td>

<td>

<asp:RadioButtonList ID=”radSearchMethodList” runat=”server” RepeatDirection=”Horizontal”>

<asp:ListItem Text=”No Tag” Value=”NoTag” />

<asp:ListItem Text=”All Tags” Value=”AllTags” />

<asp:ListItem Text=”Any Tag” Value=”AnyTag” />

<asp:ListItem Text=”One Tag” Value=”OneTag” />

</asp:RadioButtonList>

</td>

</tr>

<tr>

<td><b>Search Tags:</b></td>

<td><asp:TextBox ID=”txtSearchTag” runat=”server” />(Tags are seperated by comma)</td>

</tr>

<tr>

<td>&nbsp;</td>

<td>

<asp:Button ID=”btnClearSearch” runat=”server” Text=”Clear” CausesValidation=”false” />

<asp:Button ID=”btnSearch” runat=”server” Text=”Search” CausesValidation=”false” />

</td>

</tr>

</table>

</p>

<hr />

<p>

<asp:GridView ID=”gvCache” runat=”server”

AutoGenerateColumns=”false”

EmptyDataText=”No cache data”>

<Columns>

<asp:TemplateField HeaderText=”Key” ShowHeader=”true”>

<ItemTemplate>

<asp:Label ID=”lblCacheKey” runat=”server” />

</ItemTemplate>

</asp:TemplateField>

<asp:TemplateField HeaderText=”Content” ShowHeader=”true”>

<ItemTemplate>

<asp:Label ID=”lblCacheContent” runat=”server” />

</ItemTemplate>

</asp:TemplateField>

<asp:TemplateField HeaderText=”Tags” ShowHeader=”true”>

<ItemTemplate>

<asp:Label ID=”lblCacheTag” runat=”server” />

</ItemTemplate>

</asp:TemplateField>

</Columns>

</asp:GridView>

</p>

</form>

</body>

</html>

The third file is Default3.aspx.cs code-behind:


using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Text;

using Microsoft.Data.Caching;

namespace VelocityTest

{

[Serializable]

public class MyCacheItem

{

public string Key { get; set; }

public string Content { get; set; }

public List<DataCacheTag> Tags { get; set; }

public MyCacheItem(string key, string conent)

{

Key = key;

Content = conent;

Tags = new List<DataCacheTag>();

}

public MyCacheItem(string key, string conent, string[] tags)

{

Key = key;

Content = conent;

Tags = new List<DataCacheTag>();

foreach (string tag in tags)

{

Tags.Add(new DataCacheTag(tag));

}

}

public string[] GetTags()

{

string[] tags = null;

if (Tags != null && Tags.Count > 0)

{

tags = new string[Tags.Count];

int i = 0;

foreach (DataCacheTag tag in Tags)

{

tags[i++] = tag.ToString();

}

}

return tags;

}

}

public enum TagSearchMethod

{

NoTag,

AllTags,

AnyTag,

OneTag

}

public partial class Default3 : System.Web.UI.Page

{

protected const string DEFAULT_DELIMITER = “,”;

protected DataCacheFactory _factory = CacheWorker.Instance.CacheFactory;

protected DataCache _cache = CacheWorker.Instance.DefaultCache;

protected void Page_Load(object sender, EventArgs e)

{

btnSave.Click += new EventHandler(btnSave_Click);

btnCancel.Click += new EventHandler(btnCancel_Click);

btnClearSearch.Click += new EventHandler(btnClearSearch_Click);

btnSearch.Click += new EventHandler(btnSearch_Click);

gvCache.RowDataBound += new GridViewRowEventHandler(gvCache_RowDataBound);

gvCache.DataSource = GetCacheDataSource(TagSearchMethod.NoTag, null);

gvCache.DataBind();

}

protected List<MyCacheItem> GetCacheDataSource(TagSearchMethod method, List<DataCacheTag> tags)

{

List<MyCacheItem> items = new List<MyCacheItem>();

IEnumerable<KeyValuePair<string, Object>> cachePairs = null;

if (method == TagSearchMethod.NoTag)

{

cachePairs = _cache.GetObjectsInRegion(CacheWorker.DEFAULT_CACHE_REGION);

}

else if (method == TagSearchMethod.AllTags)

{

cachePairs = _cache.GetObjectsByAllTags(tags, CacheWorker.DEFAULT_CACHE_REGION);

}

else if (method == TagSearchMethod.AnyTag)

{

cachePairs = _cache.GetObjectsByAnyTag(tags, CacheWorker.DEFAULT_CACHE_REGION);

}

else if (method == TagSearchMethod.OneTag)

{

cachePairs = _cache.GetObjectsByTag(tags[0], CacheWorker.DEFAULT_CACHE_REGION);

}

if (cachePairs != null)

{

foreach (KeyValuePair<string, object> item in cachePairs)

{

items.Add((MyCacheItem)item.Value);

}

}

return items;

}

protected void gvCache_RowDataBound(object sender, GridViewRowEventArgs e)

{

if (e.Row.RowType == DataControlRowType.DataRow)

{

MyCacheItem data = e.Row.DataItem as MyCacheItem;

if (data != null)

{

Label lblCacheKey = e.Row.FindControl(“lblCacheKey”) as Label;

if (lblCacheKey != null)

{

lblCacheKey.Text = data.Key;

}

Label lblCacheContent = e.Row.FindControl(“lblCacheContent”) as Label;

if (lblCacheContent != null)

{

lblCacheContent.Text = data.Content;

}

Label lblCacheTag = e.Row.FindControl(“lblCacheTag”) as Label;

if (lblCacheTag != null)

{

if (data.Tags != null)

{

lblCacheTag.Text = String.Join(“,”, data.GetTags());

}

else

{

lblCacheTag.Text = “&nbsp;”;

}

}

}

}

}

protected void btnSearch_Click(object sender, EventArgs e)

{

if (radSearchMethodList.SelectedItem != null)

{

TagSearchMethod method = (TagSearchMethod)Enum.Parse(typeof(TagSearchMethod), radSearchMethodList.SelectedValue);

List<DataCacheTag> tags = GetTags(txtSearchTag.Text.Trim());

gvCache.DataSource = GetCacheDataSource(method, tags);

}

else

{

gvCache.DataSource = GetCacheDataSource(TagSearchMethod.NoTag, null);

}

gvCache.DataBind();

}

protected void btnClearSearch_Click(object sender, EventArgs e)

{

Response.Redirect(Request.RawUrl);

}

protected void btnCancel_Click(object sender, EventArgs e)

{

Response.Redirect(Request.RawUrl);

}

protected void btnSave_Click(object sender, EventArgs e)

{

if (!String.IsNullOrEmpty(txtCacheKey.Text.Trim()) &&

!String.IsNullOrEmpty(txtCacheContent.Text.Trim()))

{

MyCacheItem item = null;

string[] tags = null;

if (!String.IsNullOrEmpty(txtCacheTag.Text.Trim()))

{

tags = (string[])txtCacheTag.Text.Trim().Split(DEFAULT_DELIMITER.ToCharArray());

}

if (tags != null && tags.Length > 0)

{

item = new MyCacheItem(txtCacheKey.Text.Trim(), txtCacheContent.Text.Trim(), tags);

}

else

{

item = new MyCacheItem(txtCacheKey.Text.Trim(), txtCacheContent.Text.Trim());

}

_cache.Put(item.Key, item, item.Tags, CacheWorker.DEFAULT_CACHE_REGION);

}

gvCache.DataSource = GetCacheDataSource(TagSearchMethod.NoTag, null);

gvCache.DataBind();

}

protected List<DataCacheTag> GetTags(string tagString)

{

List<DataCacheTag> tags = new List<DataCacheTag>();

string[] items = tagString.Split(DEFAULT_DELIMITER.ToCharArray());

foreach (string item in items)

{

tags.Add(new DataCacheTag(item));

}

return tags;

}

}

}

The following are screenshots.

This screenshot has nothing is cache.

ScreenShot001

This screenshot has 3 cache items.

ScreenShot002

This screenshot is search by AllTags <a>.

ScreenShot003

This screenshot is search by AllTags <a, b>.

ScreenShot004

This screenshot is search by AnyTag <a>.

ScreenShot005

This screenshot is search by AnyTag <b>.

ScreenShot006

This screenshot is search by Tag <a, b> – there are 4 cache items!

ScreenShot007

Velocity (CTP3) – How do I know if a cache region is created?

June 1, 2009 1 comment

In Velocity (CTP3) we can create, remove, and clean a cache region programmatically by calling the following methods:

  • DataCache.CreateRegion
  • DataCache.RemoveRegion
  • DataCache.ClearRegion

But there is no method to indicate if a cache region is created or not; so if I create a region which has been created already, an exception is thrown with the error code DataCacheErrorCode.RegionAlreadyExsits.

So far the walkaround is remove the cache region before creating it:

_defaultCache.RemoveRegion(DEFAULT_CACHE_REGION);

_defaultCache.CreateRegion(DEFAULT_CACHE_REGION, true);

But this is not a good approach; so I hope the Velocity team will add the feature in the next release to check if a cache region is created.

Use Velocity (CTP3) as Session State Provider

May 22, 2009 2 comments

The blog shows how to configure web.config to use Velocity as session state provider, but it only works for CTP2.

I did some experiment and found out the following configuration in web.config will work in Velocity CTP3:

<sessionState mode=Custom customProvider=SessionStoreProvider>

<providers>

<add name=SessionStoreProvider type=Microsoft.Data.Caching.DataCacheSessionStoreProvider, ClientLibrary />

</providers>

</sessionState>


Notification callback in Velocity CTP3

May 17, 2009 Leave a comment

This is an example of how to make the Notification callback work in Velocity CTP3.

Here is my web.config – the cache is a routing client:

<?xml version=1.0?>

<configuration>

<configSections>

<!– required to read the <dataCacheClient> element –>

<section name=dataCacheClient

type=Microsoft.Data.Caching.DataCacheClientSection,CacheBaseLibrary

allowLocation=true

allowDefinition=Everywhere/>

<!– required to read the <fabric> element, when present –>

<section name=fabric

type=System.Data.Fabric.Common.ConfigFile,FabricCommon

allowLocation=true

allowDefinition=Everywhere/>

<!– routing client–>

<dataCacheClient deployment=routing>

<clientNotification pollInterval=300 />

<!– cache host(s) –>

<hosts>

<host

name=VIRTUALB-UJT42H

cachePort=22233

cacheHostName=DistributedCacheService/>

</hosts>

</dataCacheClient>

</configuration>

Here is the cache cluster configuration via the command Export-CacheClusterConfig:

<?xml version=1.0 encoding=utf-8?>

<configuration>

<configSections>

<section name=dataCache type=Microsoft.Data.Caching.DataCacheSection, CacheBaseLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91 />

</configSections>

<dataCache cluster=Velocity_Cluster_1 size=Small>

<caches>

<cache type=partitioned consistency=strong name=default>

<policy>

<eviction type=lru />

<expiration defaultTTL=10 isExpirable=true />

<serverNotification isEnabled=true />

</policy>

</cache>

<cache type=partitioned consistency=strong name=default_2>

<policy>

<eviction type=lru />

<expiration defaultTTL=10 isExpirable=true />

<serverNotification isEnabled=true />

</policy>

</cache>

</caches>

<hosts>

<host clusterPort=22234 hostId=1825498580 size=1024 quorumHost=true

name=VIRTUALB-UJT42H cacheHostName=DistributedCacheService

cachePort=22233 />

</hosts>

<advancedProperties>

<partitionStoreConnectionSettings providerName=System.Data.SqlClient

connectionString=Data Source=VIRTUALB-UJT42H\SQLEXPRESS;Initial Catalog=Velocity_Shared_1;User Id=Not_sa;Password=ForYourEyesOnly;

leadHostManagement=false />

</advancedProperties>

</dataCache>

</configuration>

Here is the code of CacheWorker.cs (kind of helper class with singleton pattern):

using System;

using Microsoft.Data.Caching;

namespace VelocityTest

{

public class CacheWorker

{

private static object _synclock = new object();

private static CacheWorker _cacheWorker = null;

private static DataCacheFactory _cacheFactory = null;

private static DataCache _defaultCache = null;

private CacheWorker()

{

}

public static CacheWorker Instance

{

get

{

lock (_synclock)

{

try

{

if (_cacheWorker == null)

{

_cacheWorker = new CacheWorker();

// we use configuration file

_cacheFactory = new DataCacheFactory();

// default cache

_defaultCache = _cacheFactory.GetDefaultCache();

}

return _cacheWorker;

}

catch (DataCacheException ex)

{

return null;

}

}

}

}

public DataCacheFactory CacheFactory

{

get { return _cacheFactory; }

}

public DataCache DefaultCache

{

get { return _defaultCache; }

}

}

}

Here is the partial code-behind of the test page:

public partial class _Default : System.Web.UI.Page

{

public const string CACHE_KEY = “{8768036E-2B99-4563-8BA5-C2D07B5B6023}”;

protected static DataCacheFactory _factory = CacheWorker.Instance.CacheFactory;

protected static DataCache _cache = CacheWorker.Instance.DefaultCache;

protected void Page_Load(object sender, EventArgs e)

{

if (!Page.IsPostBack)

{

DataCacheOperation filter = DataCacheOperation.AddItem | DataCacheOperation.RemoveItem | DataCacheOperation.ReplaceItem | DataCacheOperation.ClearRegion | DataCacheOperation.CreateRegion | DataCacheOperation.RemoveRegion;

DataCacheNotificationDescriptor dn = _cache.AddCacheLevelCallback(filter, OnCacheNotificationReceived);

SetCache(MyPerson, true);

ShowCache(GetCache());

}

btnUpdate.Click += new EventHandler(btnUpdate_Click);

btnRefresh.Click += new EventHandler(btnRefresh_Click);

btnRemove.Click += new EventHandler(btnRemove_Click);

btnGet.Click += new EventHandler(btnGet_Click);

btnAnotherPage.Click += new EventHandler(btnAnotherPage_Click);

}

// other code is here….


public void OnCacheNotificationReceived(string cacheName, string regionName, string key, DataCacheItemVersion version, DataCacheOperation cacheOperation, DataCacheNotificationDescriptor nd)

{

StringBuilder sb = new StringBuilder();

sb.Append(“cacheName: “ + cacheName + Environment.NewLine);

sb.Append(“regionName: “ + regionName + Environment.NewLine);

sb.Append(“key: “ + key + Environment.NewLine);

sb.Append(“cacheOperation: “ + cacheOperation.ToString() + Environment.NewLine);

txtMessageBox.Text = sb.ToString();

}

}

Basically the configuration has the Notification enabled and set the client as routing client. The callback is fired when ever there is a change in cache item or region.

Follow

Get every new post delivered to your Inbox.