4

「L2」キャッシュを行うためにEFプロバイダーラッパーをうまく採用しました: https://efwrappers.codeplex.com/

この設計では、複数の "キャッシュ プロバイダー" (インメモリ、asp.net キャッシュ) がサポートされています。ここで、共有キャッシュを使用して、これをクラスターに持ち込む必要があります。AppFabric をキャッシュ プロバイダーとして使用する方法を示す Julie Lerman の記事を見つけました: http://msdn.microsoft.com/en-us/magazine/hh394143.aspx

コードは MSDN の記事から引用されていますが、設計が壊れているようです。私は AppFabric の専門家ではありませんが、3 つの問題が見られます。そのうちの 1 つは、キャッシュ API の根本的に間違った使用です。他の誰かが AppFabric キャッシュ プロバイダーを使用していますか? Julie から提供されたコード例に問題がある人はいますか?

//This class was based on the VelocityCacheAdapter clas
//MSDN Magazine Sept 2011 Data Points Column Sample Code
//(Julie Lerman)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using EFCachingProvider.Caching;
using Microsoft.ApplicationServer.Caching;

namespace EFAppFabricCacheAdapter
{

  public class AppFabricCache : ICache
  {
    private DataCache _cache;

    public AppFabricCache(DataCache cache)
    {
      _cache = cache;
    }

    public bool GetItem(string key, out object value)
    {
      key = GetCacheKey(key);
      value = _cache.Get(key);

      return value != null;
    }

    //!!!ISSUE #1: Minor issue: No try/catch around the call to _cache.Put. 
    // The event that separate workers try to update the same entry will cause
    // exceptions. 
    //!!!ISSUE #2: The query is cached without a region. 
    // The dependent sets are cached in regions. 
    //This works well when you are invalidating a specific item but 
    //it fails when you invalidate sets, all related items to the set are not removed

    public void PutItem(string key, object value,
      IEnumerable<string> dependentEntitySets, 
      TimeSpan slidingExpiration, DateTime absoluteExpiration)
    {
      key = GetCacheKey(key);
      _cache.Put(key, value, absoluteExpiration - DateTime.Now, 
        dependentEntitySets.Select(c => new DataCacheTag(c)).ToList());

      foreach (var dep in dependentEntitySets)
      {
        CreateRegionIfNeeded(dep);
        _cache.Put(key, " ", dep);
      }

    }

//!!!ISSUE #2 again: say i have a customer and orders. 
//If I invalidate the order set then any related customer/order 
//query should be removed. This just clears regions but doesnt remove the actual
//queries. This design will leave stale objects in cache
//!!!ISSUE #3: minor issue, but calling _cache.GetObjectsInRegion and iterating will 
// cause unnecessary I/O. Changing this to calling region.Clear() will 
// be more efficient.
    public void InvalidateSets(IEnumerable<string> entitySets)
    {
      // Go through the list of objects in each of the set. 
      foreach (var dep in entitySets)
      {
        foreach (var val in _cache.GetObjectsInRegion(dep))
        {
          _cache.Remove(val.Key);
        }
      }
    }

    public void InvalidateItem(string key)
    {
      key = GetCacheKey(key);

      DataCacheItem item = _cache.GetCacheItem(key);
      _cache.Remove(key);

      foreach (var tag in item.Tags)
      {
        _cache.Remove(key, tag.ToString());
      }
    }

    //creates a hash of the query to store as the key  
    private static string GetCacheKey(string query)
    {
      byte[] bytes = Encoding.UTF8.GetBytes(query);
      string hashString = Convert
        .ToBase64String(MD5.Create().ComputeHash(bytes));
      return hashString;
    }

    private void CreateRegionIfNeeded(string regionName)
    {
      try
      {
        _cache.CreateRegion(regionName);
      }
      catch (DataCacheException de)
      {
        if (de.ErrorCode != DataCacheErrorCode.RegionAlreadyExists)
        {
          throw;
        }
      }
    }
  }
}
4

0 に答える 0