Table of Contents

Filtering Objects in a PMI Operation

In addition to the Invoke method on the Cache class, the Scaleout.Client library provides Invoke extension methods that work on a LINQ IQueryable, allowing you to use a Where clause to filter the objects evaluated by a PMI operation.

Procedure

  1. Add a using Scaleout.Client.QuerySupport directive to the top of a C# source file to gain access to query extension methods and attributes.

  2. Mark a property on the cached type with a [SossIndex] attribute to index it in the ScaleOut service.

    • Once a property is indexed, the ScaleOut LINQ provider can use it in a Where clause.
    • Multiple properties in a class can be indexed.
    • Pass HighPriorityHashable into the attribute's constructor to improve performance. Up to 8 properties in a class can be added to the service's hashed indexes.
    class InventoryItem
    {
        public int ItemId { get; set; }
    
        [SossIndex(HashIndexPriority.HighPriorityHashable)]
        public DateTime ExpirationDate { get; set; }
    
        public string Status { get; set; }
    }
    
  3. Indexes in the ScaleOut service are updated when objects are added or updated in a cache.

    var conn = GridConnection.Connect("bootstrapGateways=localhost:721");
    var cache = new CacheBuilder<int, InventoryItem>("inventory cache", conn).Build();
    
    // Add sample items to service:
    cache.Add(key: 1, new InventoryItem { ItemId = 1, 
                                          ExpirationDate = DateTime.Now.AddMonths(-5) });
    cache.Add(key: 2, new InventoryItem { ItemId = 2,
                                          ExpirationDate = DateTime.Now.AddMonths(5) });
    
  4. Use the QueryObjects() method to create a LINQ expression and execute an invoke operation against a filtered set of objects.

    The parameters to the invoke operation are:

    • operationId: The name of a registered invocation handler that is running on the ScaleOut farm.
    • param:: A parameter value, serialized to byte array, that can be passed to invocation handlers derived from ForEachWithParam or ReduceWithParam. Use null for PMI operations that do not take a parameter.
    • invokeTimeout: (Optional) The maximum allowed time for the PMI operation to complete. Use null or TimeSpan.Zero to wait indefinitely.
    • invocationGrid: (Optional) If a ScaleOut Invocation Grid is used to deploy/host invocation handler code, its name can be specified here. This sample does not use invocation grids.
    var response = cache.QueryObjects()
                        .Where(item => item.ExpirationDate <= DateTime.Now)
                        .Invoke("SetStatusExpired", param: null);
    
  5. Check whether the PMI operation succeeded or failed.

    • If an unhandled exception is thrown from an invocation handler, error data will be sent back and made available to the Invoke client through the ErrorData property. The representation and encoding of the error will vary depending on the library that is used to handle the PMI operation; if the handler is implemented using the Scaleout.Client library, the error will be the full "ToString()" text representation of the exception, encoded as UTF-8.

PMI Handler Code

Before running the client code above, a PMI handler application must be deployed to all ScaleOut hosts to handle the "SetStatusExpired" operation.

class SetExpired : ForEach<int, InventoryItem>
{
    public override void Evaluate(int key, OperationContext<int, InventoryItem> context)
    {
        var response = context.Cache.Read(key);

        if (response.Result == ServerResult.Retrieved)
        {
            // Modify item and update it in the ScaleOut service:
            InventoryItem item = response.Value;
            if (item.Status != "Expired")
            {
                item.Status = "Expired";
                context.Cache.Update(key, item);
            }
        }
    }

    // Entry point into the server-side PMI handler
    static void Main(string[] args)
    {
        // Connect to the cache that stores inventory items.
        var conn = GridConnection.Connect("bootstrapGateways=localhost:721");
        var cacheBuilder = new CacheBuilder<int, InventoryItem>("inventory cache", conn);
        var cache = cacheBuilder.Build();

        // Register the handler with the cache and give it a name.
        ServiceEvents.SetInvokeHandler(cache,
                                       "SetStatusExpired",
                                       new SetExpired());

        // Run indefinitely to handle invocation requests.
        Console.ReadLine();
    }
}