Handling Relational Data in a Distributed Cache

Recorded webinar
By Ron Hussain and Adam J. Keller

In this video webinar learn how to apply your existing data relationships to cache objects for distributed caching.

You can expect to hear about:

  • The data storage model for a relational database and for a distributed cache
  • The goodness of object relational mapping – how to manage data relationships at the object level
  • Mapping 'one-one', 'one-many' and 'many-many' relationships
  • Using NCache features to build relationships between objects to mimic relational data behaviors
  • use of key-based dependency, object query language, groups, group APIs, collections and tags
  • Other important distributed caching features for relational data

I'll talk about having a distributed caching in place. When you have a relational database, you Know, you have some challenges, some performance issues, some scalability issues and then you move transition into using a distributed cache along with the relational database. What are the challenges that you get and how to manage those challenges? So, that's what we have in agenda for today's webinar.

It’s going to be pretty hands-on. I want to show you some code examples. I'll talk about some real examples. I have some examples lined up which I'll actually use to demonstrate this. And, then towards the end I would also have a hands-on portion to go over some basic NCache configurations.

NCache, that's the main distributed caching product. We'll be using that as an example product for this particular webinar. But, overall, it's a general, you know, topic webinar. We have a relational data in the in the database and then you have a distributed cache. How to transition and then start using distributed cache within, you know, the relational data which has relationships and that's a structured Data. So, let's go through this.

What is Scalability?

So, first of all, I'll talk about scalability, the concept of scalability. Scalability is an ability within the application where you can increase the transactional load. Where you can handle more and more requests load out of the application architecture. And, you don't compromise with the Performance, So, if you have a high throughput and low latency, that ability is called scalability. So, you can handle huge amount of requests loads and individual requests performance does not go down. It's same! And, with more resources you can even increase that and linear scalability is an associated term which allows you to actually scale out where you have more or more request load handling capacity introduced into the system by adding more servers. And, in most cases the performance does not go down.

So, if you have low latency and you have a linear improvement in the request load, previously you were handling say 10,000 requests per second or even for five users you have certain amount of latency say a few milliseconds or sub millisecond response times for five users; you should have the same kind of responses, same kind of perform the same kind of latency for five thousand users or fifty thousand users. And, that ability to keep on increasing the user load and their associated requests load, that's called linear scalability.

Which Apps Need Scalability?

So, what are the typical applications that need Scalability,

which-apps-needs-scalability

It is going to be your ASP.NET web applications, Java web applications or even .NET general web applications which are public facing. It could be an e-commerce system, it could be an airline ticketing system, a booking system or it could be a financial service or healthcare service which has a lot of user actually using its public facing. That could be web services WCF or any other communication service which is interacting with some data access layers or is dealing with some front-end applications. But, it may be handling millions of requests at any given point in time. It could be Internet of Things, some back-end devices that can data that some tasks processing for those devices. So, it could have a lot of requests load. Big data processing that's common buzzword these days where we have a lot of small inexpensive compute servers and through distribution of data on multiple servers you actually process huge amount of data loads.

And, similarly, there would be huge model requests load for that particular data. And, then could be any other general server applications and tier applications which may be handling millions of requests, a lot of users, those are the prime candidates for scalability. Those are the applications which need scalability within the architecture.

Scalability Bottleneck

This is a typical diagram. What is the scalability bottleneck?

data-storage-is-bottleneck

So, these are the applications. By design these frameworks are very scalable. ASP.NET web form or WCF form there are options that you can use to scale out these systems. But, they all talk to a back-end database and typically it's a relational database. It could also be an ASP.NET session storage or a mainframe or a file system but that's not what we're covering in today's webinar. Today's webinar is more focused on relational data. So, relational data is a single source. Although you have a very scalable platform right here. You can add more and more servers on this tier. You can scale out by putting a load balancer in front and that load balancer can route requests between different servers. But all these web servers end up talking to a database tier which is not that scalable.

So, that's going to be a source of contention. It is going to be slow to start off and then it cannot scale out. It's a single source there's no option of adding more and more database servers to increase the capacity of request. So, the capacity of request can max out and that can lead to high latency. So, it's a capacity issue for a request handling and it could also end up in to a latency issue so it can choke down the system when you have huge requests load.

The Solution

So, that's the main problem with relational data sources and solution to that is very simple that you start using a distributed cache.

A distributed caching system like NCache which is super-fast because it's in-memory in comparison and then it's linearly scalable. It's not just single server. It's multiple server environment where we have a team of servers joined together into capacity. You pool the memory capacity as well as the transactional capacity and you get a very linearly scalable model out of it. And, NCache is exactly that kind of solution which you can use to handle database scalability issues.

What is In-Memory Distributed Cache?

What is a general in-memory distributed caching system like NCache? What are the characteristics?

Cluster of multiple inexpensive cache servers

It's going to be cluster of multiple inexpensive cache servers which are joined together into a logical Capacity.

So, this is an example of it. You could have two to three caching servers. For NCache, you can use Windows 2008, 2012 or 2016 environment. Only pre-requisite for NCache is .NET 4. And it's a middle tier between your application and the database and it's very scalable as compared to your database tier but it's something which would give you the same kind of scalability that you would get from a speed-up .NET web form or WCF web services web form.

ncache-deployment

Synchronizes cache updates across all cache servers

Synchronizes cache updates across all cache servers so data consistency is built into the protocol. All updates are applied in an automatic manner with an understanding of consistent data view for all the clients which are connected to it.

Linearly scales transactions and memory capacity

It should scale out linearly for transactions as well as from memory capacity you simply add more servers and it shouldn't grow the capacity in response to that. If you have two servers and you add third and fourth server it should essentially double the capacity of that system now that you have double the amount of service. So, that's what NCache offers as well.

Replicates data for reliability

Then replication is another feature for reliability. Any server going down, first of all, it should not have any data loss or downtime. It should be highly available highly reliable system and that's what NCache takes care of.

NCache Deployment

So, having discussed this that you have a scalability issue with relational data sources and distributed caching systems like NCache is a solution to that and that becomes central tier between your applications and the database and your data exists on two places.

ncache-deployment

You have data in the database and then you have data in the distributed cache. So, then NCache also manages some synchronization challenges that that you may have when you have data on two different sources. There's a separate webinar on this topic, but, just to let you know that there are some data access layer providers, there are some change notification dependencies which you can set up.

So, any change in the database can trigger an invalidation or update of items in the cache and similarly any changes or updates that happen in the cache can be applied on the database. So, this can be achieved with the help of our data access layer providers; Read-through and Write-through.

And, then we also have some database change dependencies, record change dependencies which we actually used to ensure 100% synchronization between database and the cache records. But, typically data exists on two different places. You have data in the cache which is usually a subset of the data and then you have actual data in the database. When you migrate data, it is essentially a migration of data. Although you're using these two sources in combination to one another but these are two different sources.

What Data to Cache?

Before we move on, we'll quickly talk about types of data that you can cache it could be a reference data, it could be transactional data.

what-data-to-cache

It's more of a read intensive data that you cache usually it belongs in relational database. It's not that it's not changing it's not 100% static. It's changing data but the frequency of that change is not that great. So, that would categorize as a reference data. And, then we ever transactional data which is changing very frequently. As frequently as in few seconds, few minutes or in some cases that it could be in few milliseconds.

And, then most cached data is relational. It's brought from a relational database and I have already discussed that you have data of master copy in the relational database and then you have a subset of that data, a reference and transactional, brought into the distributed cache. Right!

What is the challenge?

So, what is the challenge? So, let's actually discuss that challenge! When you move your data from a data source into a relation into a distributed cache to achieve more performance more scalability and more reliability to the system. Let's just actually quickly discuss what are the challenges that you see.

the-challenge

It works very well. It improves your performance because it's in memory. it's linearly scalable so it takes care of scalability issues with database. It's not a single point of failure because there are multiple servers and any server can be brought Down. You can bring servers up without any issues. Maintenance becomes a lot easier. Your upgrades become a lot easier. So, in comparison you get a lot of benefits. But, there is one challenge that you need to address and you need to take that into account when you migrate your data or move your data from the database to the distributed cache and start using distributed caching system.

So, distributed cache is a hash table like interface. Right! It's each item is separated with the key and a value, right? So, it's a hash table where items each object in the cache or each item in the cache or a record in the cache is represented with the help of a key. So, it's not a table or it's not a relational data where we have relationships, we have defined schemas, we have entities which have proper relationships among each other. It's going to be a separate key value item representing an object in the cache. So, database has relationships between entities and when you migrate your data or when you move your data from a relational database to a distributed cache, you lose that relationship characteristic of that data.

So, by default you don't have that ability so we can remove this. That's one of the main challenges and then there are some other associated challenges database queries result into a collection of objects. It also results into a data table or data reader. So, dealing with data tables and data readers in a distributed cache is not a good idea, right? so we'll actually go through all these challenges one by one.

Primarily we will discuss how to manage relationships in the distributed cache once you've brought data from a relational data source into the cache how you go about that detail.

Peek Into the NCache API

So, next we'll talk about the caching API.

caching-apis

Now that we have discussed that it is basically a key value store. We have a cache.add where we have a key “mykey” and then we have an object which is any permitted data and permitted serialized object. it could be any customer object, product object, order object. But this is very simple hello world kind of example where we call key value methods using cache.add, cache.update. Similarly, we call cache.get to get the same item back and then we call cache.remove.

Manage Relationships in the Cache

Moving on! So, how to manage relationships in the cache? Now that we've defined this challenge that cache is a hash table like interface it's a key value pair. Value is .NET object and key is a string key which you format and these are independent objects from one another. And, database has relations tables which have relationships with one another. It could be a one-to-one one-to-many and many-to-many relationships between different tables.

First Step: Use Cache Dependency

There are two things that you should consider doing first of all you should consider using the cache dependency and we're primarily focused on key based dependency that's one of the features and I'll show you some code examples around that which helps you keep a track of one-way dependency between the cache items.

use-cache-dependency

So, you could have one parent object and then you could have a dependent object on that particular Object. And, the way it works is that one item depends on another. The main item, the primary object or the parent object; if it goes through a change it gets updated or removed the dependent item automatic is removed from the cache.

A typical example which I have lined up in upcoming slides is list of orders for a certain customer. So, you have a customer A. It has list of orders, say 100 orders, what if that customer is updated or remove from the cache. You don't need those associated orders with that so you might want to do something around those orders collection as well and that's exactly what this dependency is for that you need to have a linkage between these 2 records since these are related in the in the relational database.

Then this dependency can also be cascaded in nature where A depends on B and B depends on C. Any change in the C would trigger an invalidation of B and that would in turn invalidate A as well. So, it could be a cascaded dependency and then could also be multi item dependency as well. One item can be dependent on item A as well as on item B and similarly, one parent item can have multiple child items as well. And, this was a feature initially introduced by ASP.NET cache. It was one of the powerful features that NCache also has. We provide it as a separate cache dependency object and one of the fancy the objects and this is going to be the main focus in this particular webinar.

Second Step: Use Object Relational Mapping

Second step, in order to map your relational data, there's one other challenge that we recommend that you actually should map your domain objects on to your data model, right?

use-object-relational-mapping

So, your domain objects should represent a database table. You should use some kind of O/R mapping, you can also use some O/R mapping tools. It simplifies your programming you can reuse the code once you've classes mapped on to your database tables. You can use even the ORM or O/R mapping tools such as Entity framework and NHibernate as well. These are few popular tools.

The idea here is that you should have classes in the application. The objects, the domain objects in the application. So, your database objects, data tables or data readers are transformed mapped on to the domain objects. So, a customer table should represent a customer class in the application. Similarly, ordered collection or order table should represent order class and the application. And then those are the objects that you deal and store in distributed cache and formulate relationships with the help of Key Dependency.

Handling Relationships Example

Let's take an example!

mapping-domain-objects-to-db

And, we have our database model from Northwind. We have customer Table, we have order table and then we have Product. We have all the column names of the customer we have customer ID, company name, phone, city, state, country, some attributes. Similarly, we have product with Product ID, name, price, units in stock, units on order, reorder level. So, those are some attributes or some columns from the product and then we have order table as well which has Customer ID and Product ID as foreign keys. That's formulating a composite primary key and then we have order-date, ship date and some attributes of orders itself. And if you notice that, there is a one-to-many relationship between customer and order and then there is a one-to-many relationship between product and order. Similarly, we have many-to-one and many-to-one between order and customer and order and product, respectively. So, we'll tackle this particular scenario.

Initially, it was customer product many-to- many relationship which got normalized into two one-to-many relationships. So, this is a normalized data model and we'll use an example of this where we have a class at the main object mapped onto this particular data model. So, we'll talk about primary object in a bit but let me just quickly show you the example here.

mapping-domain-objects-to-database

Notice that the domain objects mapped onto our database model. For this same example, we have a customer class which has customer ID that is going to be used as a primary key and then we have a collection of orders which is representing upon too many relationship with the order class. Similarly, we have a product which has product attributes same as these product ID, name, unit price, right? And then we have orders collection here as well which is representing a one-to-many relationship and on the order side we have the customer ID brought from here product ID brought from here and then we have ordering customer, the customer many-to-one relationship and then we have ordering product so many to one relationship captured as part of the domain object.

So, this is one of the techniques that I'm going to cover in great details in upcoming slides. Let me show you these objects within visual studio as well so this is one of the class.

using Alachisoft.NCache.Web.Caching;
using Alachisoft.NCache.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Alachisoft.NCache.Runtime.Dependencies;
using System.Collections;

namespace Relationships
{
    public class Customer
    {
        public long     CustomerId;
        public string   CompanyName;
        public string   Phone;
        public string   City;
        public string   State;
        public string   Country;

        // one-to-many list of Order objects
        public IList<Order> Orders;
        
        public void CacheCustomer(Cache cache, Customer cust)
        {
            // Let's preserve "orders"
            IList<Order> orders = cust.Orders;

            // Let's now empty "orders" so it doesn't get cached with customer
            cust.Orders = null;

            string custKey = "Customer:CustomerId:" + cust.CustomerId;
            cache.Add(custKey, cust, null,
                                Cache.NoAbsoluteExpiration,
                                Cache.NoSlidingExpiration,
                                CacheItemPriority.Default);

            // Dependency ensures orders is removed if Cust updated/removed
            string[] keys = new string[1];
            keys[0] = custKey;
            CacheDependency dep = new CacheDependency(null, keys);

            string orderskeys = "Customer:Orders:CustomerId:" + cust.CustomerId;
            cache.Add(orderskeys, orders, dep,
                                    Cache.NoAbsoluteExpiration,
                                    Cache.NoSlidingExpiration,
                                    CacheItemPriority.Default);
        }

        public void CacheProducts(Cache cache, double unitPrice, IList<Product> products)
        {
            // Let's cache each product as seperate item. Later
            // we'll search them through OQL
            foreach (Product product in products)
            {
                string productKey = "Product:ProductId:" + product.ProductId;
                cache.Add(productKey, product, null,
                                   Cache.NoAbsoluteExpiration,
                                   Cache.NoSlidingExpiration,
                                   CacheItemPriority.Default);

                cache.GetGroupData("DummyGroup", "DummySubGroup");
                cache.GetByTag(new Alachisoft.NCache.Runtime.Caching.Tag("DummyTag"));
            }
            
        }

        public IList<Product> FindProducts(Cache cache, double unitPrice)
        {
            string query = "SELECT Relationships.Product WHERE this.UnitPrice >= ?";
            Hashtable values = new Hashtable();
            values.Add("UnitPrice", unitPrice);

            ICacheReader products = cache.ExecuteReader(query, values, true);

            IList<Product> prodList = new List<Product>();

            // For simplicity, assume that list is not very large
            while (products.Read())
            {
                prodList.Add((Product)products.GetValue(1));// 1 because on 0 you'll get the Cache Key
            }
 
            return prodList;
        }
    }
}

One thing that I would recommend is that you decorate these classes with serializable tag, right? So, you need to cache them, right? So, these domain objects should be serialized because these are the ones which are going to travel back and forth between your client application and the distributed cache.

So, we have customer class, we have product class, right here. A list of orders and we have order class which has all the attributes shown on the presentation slide as well.

What is Primary Object?

Next, we'll talk about primary object. Now that we've shown some main objects which are mapped on this domain data model. We'll show you some techniques to go over one-to-one one-to-many and many-to-many relationships.

So, first of all, I'll talk about a term which I'll be using in upcoming slides.

what-is-primary-object

Primary object, it's a domain object. It's mapped on to your database. It's a starting point of your application, for example, you have a customer object and then if you need orders you need the customer to start off, right? So, it is the first object that your application fetches and all the other objects which are related to this are going to be brought in relation to this, right?

Another example could be if you're processing the orders right so you might want to fetch orders in that processing unit and then you would like to know about the customer who ordered it to dispatch that particular order, right? So, in that case order becomes your primary object and then it has a many to one relationship or it has a relationship with one of the customers who actually ordered that particular order or all the products within that order. So, it's going to be one way or other but we'll be giving we'll be using a primary object that will cache and then we'll make other objects dependent on that. That's the approach that will follow.

Relationships in Distributed Cache

So, let's actually get started with this. So, first of all, we'll talk about one-to-one and many-to-one relationships within the distributed cache that's the most common scenario, right? So, one option is that you cache related objects with primary object. Now that you've seen our domain objects we have a list of orders as part of the customers. So, if we populate these orders and customer has those orders as part of It, if you store customer as a single object in the cache that has all the orders as part of it, right? So, that would get the job done.

Cache Related Object with Primary Object

So, here's a code example for it.

// cache order along with its OrderingCustomer and OrderedProduct
// but not the "Orders" collection in both of them
public void CacheOrder(Cache cache, Order order)
{

    // We don't want to cache "Orders" from Customers and Product
    order.OrderingCustomer.Orders = null;
    order.OrderedProduct.Orders = null;

    string orderKey = "Order:CustoimerId:" + order.CustomerId
                            + ":ProductId:" + order.ProductId;
    cache.Add(orderKey, order, null,
                        Cache.NoAbsoluteExpiration,
                        Cache.NoSlidingExpiration,
                        CacheItemPriority.Default);
}

We have a cache order, we set the ordering customer within the order to “null.” All right! So, that we don't have a orders also having a reference to customer. It's redundant. This is not a very good programming practice but this is just to enforce. We don't want cache orders from customer and product ID. So, we just want to have one customer added in the cache and then we would just like to have orders as part of that.

So, let's have a look at this, right? So, we set this to null and then we simply store it or if we don't set it to null this can actually have a reference back to that. Similarly, if we have a customer, right? so if we don't set the order to null although this is a separate object but if we just simply store this customer, let me just take you to this, since it has a list of orders, if we simply store this customer as a single object in the cache, it has our orders collection as part of that object. Although I'm setting it null for another example but just to show you this particular case, you can sort one big object and it would have all the related objects as part of that object.

So, you need to start from your domain object, you should have your relationship captured as part of the domain object and you should do that regardless. And, after that you should cache, you should actually have a populated list of orders, list of related object as part of that. So, this is the simplest approach that you can get.

There are some benefits of it that you have one object which is representing everything. But, there are some drawbacks as well. It would be a bigger object in size. Some cases you just need customer but you would end up getting orders as part of that. You'll be dealing with bigger payload. And, you don't have granular orders as separate items in the cache so you would have to deal with collection of orders all the time although you are interested in only one, right? So, that is one approach. That's the starting point.

Cache Related Objects Separately

Second, you cache weighted object as a separate item in the cache.

public void CacheOrder(Cache cache, Order order)
{
    Customer cust = order.OrderingCustomer;
    // Set orders to null so it doesn't get cached with Customer
    cust.Orders = null;

    string custKey = "Customer:CUstomerId:" + cust.CustomerId;
    cache.Add(custKey, cust, null,
                        Cache.NoAbsoluteExpiration,
                        Cache.NoSlidingExpiration,
                        CacheItemPriority.Default);

    // Dependency ensures order is removed if Cust updated/removed
    string[] keys = new string[1];
    keys[0] = custKey;
    CacheDependency dep = new CacheDependency(null, keys);

    string orderKey = "Order:CustomerId:" + order.CustomerId
                            + ":ProductId:" + order.ProductId;
    cache.Add(orderKey, order, dep, 
                        Cache.NoAbsoluteExpiration,
                        Cache.NoSlidingExpiration,
                        CacheItemPriority.Default);
}

An example for that is the order class where we have cache order, right? So, this is the example where we have a customer. First of all, right? And then we're storing customer as a single object you can see that we got the order and then from order we extracted the customer and then we set the orders collection within that customer object to null, right? So, this is something that you should do within the constructor, but that's being done here just to enforce that this customer object does not have this order as part of that. And, then you store this customer as a single object. What we're doing here is that we're creating a customer key which is customer, customer ID, the runtime parameter and then we're storing our customer as a single object in the cache.

Next thing that we're doing is we're creating a cache dependency. So, we talked about two steps that we wanted to do one step was that you map your data model onto your domain object, so your domain objects should represent relational table in the database and then once you plan on caching those, you have primary object in this case it's customer and then we have orders which are related to customer in the one-to-many relationship. You create a dependency between customer and the orders collection with the help of a cache dependency object. You create a cache dependency. This same cache dependency takes two parameters; first one is file, Right? So, it can be dependent on the file name and it can also be dependent on the Key, right? So, we set first parameter to null. So, we don't want it to depend on any files.

There's another feature that within NCache where you can make items dependent on a certain file system some files on your file system. And, then if you use the key based dependency it needs the key of the parent item on which the child item is going to be made dependent upon. And, then we construct the order key of the collection of the order. We have the entire order collection right here which is passed on to this method and then we simply call Cache.Add order.

Now customer and order are related to one another. We have a list of orders from this customer which are represented as a separate object in the cache so whenever you need all the orders of this particular customer you just need to use this key. All you need to do is call, let me just actually use this example right here. I'm sorry! You can call Cache.Get and then you simply pass on order key and this would fetch you this particular order that we constructed within this method, right?

cache-get-key

So, that's what you need in order to fetch all the collection of all the orders of this particular customer at once. But, what if the customer goes through a change? If the customer gets updated the orders collection does not need to stay in the cache. It can either be removed or in some case it can also be updated, Right?

So, that's our approach number two which is more sophisticated in terms of storage, in terms of usability and it also maps two records in our relationship of one-to-many relationship or Many-to-one relationship. It could be other way around as well which we'll cover in a bit where you can have lists of orders and each order can be mapped onto those individual orders can be mapped onto those multiple ID orders can be mapped onto a customer if the order is the primary object for that particular applications code. Now this defines a relationship between customer and orders.

A few more details around cache dependency feature.

ncache-runtime-dependencies

If I go here, its first of all, exposed but with the help of Alachisoft.NCache.Runtime.Dependencies and this is one of the overloads that you use and then you simply use this particular method, right here. And, the behavior of this is in such a way that it allows you to simply track one-way dependency between objects and it could also be cascaded as discussed earlier in the presentation.

One to Many Relationship

Next, we'll talk about one-to-many relationships. Since we talked about one-to-one or many-to-one where we had order and then we had a customer so this is similar to one-to-many in most cases but since it was starting point was order and then we inserted the customer, stored the customer and then we store the collection of orders and then we defined a Many-to-one relationship between orders collection and that customer.

one-many-relationship

Now, one-to-many relationship, that's going to be very similar to what we've discussed. Our first option is that you cache your object collection as part of the primary object, so customer is your primary object, order should be part of that.

Second item is that your related objects are cached separately but individual items in the cache, right?

One to Many – Cache Related Object Collection Separately
public void CacheCustomer(Cache cache, Customer cust)
{
    // Let's preserve "orders"
    IList<Order> orders = cust.Orders;

    // Let's now empty "orders" so it doesn't get cached with customer
    cust.Orders = null;

    string custKey = "Customer:CustomerId:" + cust.CustomerId;
    cache.Add(custKey, cust, null,
                        Cache.NoAbsoluteExpiration,
                        Cache.NoSlidingExpiration,
                        CacheItemPriority.Default);

    // Dependency ensures orders is removed if Cust updated/removed
    string[] keys = new string[1];
    keys[0] = custKey;
    CacheDependency dep = new CacheDependency(null, keys);

    string orderskeys = "Customer:Orders:CustomerId:" + cust.CustomerId;
    cache.Add(orderskeys, orders, dep,
                            Cache.NoAbsoluteExpiration,
                            Cache.NoSlidingExpiration,
                            CacheItemPriority.Default);
}

So, this is collection which is separate, we have orders we got a customer object here one-to-many and customer has orders we got the orders collection out of it and then we will set the customer orders to null so customer is an e object, a primary object, orders collection is a separate object and then you store the customer, store the data cache dependency and store the orders. So, it's going to be a one-to-many the entire collection.

One to Many – Cache Each Object in Related Collection Separately

Second approach is that this collection can also be broken down.

public void CacheCustomer(Cache cache, Customer cust)
{
    // Let's preserve "orders"
    IList<Order> orders = cust.Orders;

    // Let's now empty "orders" so it doesn't get cached with customer
    cust.Orders = null;

    string custKey = "Customer:CustomerId:" + cust.CustomerId;
    cache.Add(custKey, cust, null,
                        Cache.NoAbsoluteExpiration,
                        Cache.NoSlidingExpiration,
                        CacheItemPriority.Default);

    // Dependency ensures orders is removed if Cust updated/removed
    string[] keys = new string[1];
    keys[0] = custKey;
    CacheDependency dep = new CacheDependency(null, keys);

    // Let's cache each order as seperate item but assign it
    // a group so we can fetch all orders for a given customer
    foreach (Order order in orders)
    {
        string orderKey = "Order:CustomerId:" + order.CustomerId
                                + ":ProductId:" + order.ProductId;

        CacheItem cacheItem = new CacheItem(order);
        cacheItem.Group = "Customer:CustomerId:" + cust.CustomerId;
        cacheItem.Dependency = dep;
        cache.Add(orderKey, cacheItem);
    }
}

The orders collection can be separate each item within that collection could be separate item in the cache. So, in this case we would just use the same approach. We will get the customers, will get the orders, will store the customer, will create a dependency of that customer using key dependency and then we'll iterate through all the orders.

Let me just go through cache customer method right here because it's more intuitive here. So, we get the orders collection out of it, we set the customer orders to null so customers is only about customer, store the customer in the cache using cache.Add key, constructed a cache dependency around that a particular key of the customer and then we simply iterate through. There's a loop around here. We should actually iterate through it and this is actually better we iterate through it and then we simply store it at once as individual items in the cache so order has its own key in each order is a separate item in the cache. And, then one other thing that we've done is we've actually grouped these we call cache items add group and customer ID is a group as well. So, we're actually managing a collection inside the cache as well.

Let me see if I have.. there you go actually, for example, we have this right here. We could actually cache these products, that's another example by the way, where we have looped around all the product collection and then we've stored them individually and then we've actually put everything in a group, right?

strategy-2-example

So, this product which is being stored as an item can also be stored like this where we have a ProdCacheItem. My keyboard is playing up so please bear with me! Let's just use this for now and then I'm going to do simply. I can actually add a group. So, this is along this would actually allow me to set a group for this. Let's say dummy group now. If I store this cache item and it could actually have a product as a item for this, Right? So, instead of storing the actual raw object I can even arrange it in a group. I can simply use product cache item. There you go! Right? So, now it is actually being or instead of using the dummy group let's say product group right and when I need to fetch this I can simply use product group and it would fetch me all the items of this collection at once. Although these are being stored individually these are individual Cache.Get calls individual products within the product collection are sort individually but I can arrange them in a group in a collection and then I can fetch them. But nice thing about this is that it's still using a dependency, right? So, it's using a dependency of one customer on those individual items.

So, let's take an example, we have one customer it has hundred orders. So, total we would have one item for customer in the cache, we would have hundred order separately stored as hundred items in the cache and there's a one-way dependency between that one customer and hundred orders. If you remove that one customer out of the cache one hundred orders would invalidate at once. And, now you have a benefit of fetching individual orders, if you need to. So, you can interact with those items individually. One item at a time. You need a certain order you can process it and when you need the entire collection of those orders at once, you can simply call Cache.GetGroupData, and provide the product group and then subgroup could be anything. It could be null even and then you can also use the tag.

For example; the other way of managing this is that you use the product item and you create a tag for it, right? And, then you.. there is a.. yeah! there it is and you can provide a tag which can go something like product tag, right? and then you can associate this as part of that.

strategy-2-example-2

So, this would actually work on the same lines and you can also call get by tag methods and that would take care of all the items at once. It would bring you all the items at once. So, this would give you a more control on your arrangement of data in the cache and then still keeping a one-to-many relationship intact.

So, this takes care of one very particular scenario where we have one-to-many relationship and we have one object added and then we have those items on the many side that collection stored individually items from that collection stored individually in the cache and then you still have a dependency and then you still have a collection type of behavior for that with those related items. So, those items are related to one another, they're forming a collection and then they have our relationship with another object in a one-to-many Formulation. So, this code snippet, very simple intuitive API takes care of all these scenarios. You have one-to-many relationship captured with the help of key dependency. You have arranged those items individually in the cache although but you've still put them in a logical collection of groups or tags and then when you need those items individually you call cache start get, right?

So, one way of getting this item is that you call Cache.Get, right? and use the key which is product key here, right? So, this would fetch you this particular product that's being stored with this particular key, right? And, the other option is that you need all the items within that collection at once so you can use Cache.GetGroupData. So, it can give you a collection behavior and at the same time it can also give you individuals management of those related items.

cache-get-example

So, that should take care of collections and items within the collection and one-to-many at that once.

Many-to-Many Relationships

Next, we have many-to-many relationship.

many-many-relationships

Many-to-many relationships don't exist typically in the domain objects. It's always going to be normalized into two one-to-many relationships in the database as well. As a matter of fact, we had a many-to-many relationship between customer and products. Many-to-many relationships which we got normalized with the help of an intermediary object into two one-to-many relationships. So, we have one-to-many here and many-to-one here between customer order and order to product, respectively. So, that's how you would deal with many-to-many. So, it would end up using either one-to-one many-to-one or one-to-many relationships.

So, this should take care of your many-to-many relationships.

Handling Collections in Distributed Cache

Next is that you had a collection, we've already touched this with the help of our product example but I'll still go through it that you cache into our collection as one item.

handling-collections-in-distributed-cache

For example, you store the product. Let's go through it, right?

public void CacheProducts(Cache cache, double unitPrice, IList<Product> products)
    {
        // cache the entire collection as one item
        string productskey = "Product:UnitPrice:" + unitPrice;
        cache.Add(productskey, products, null,
                               Cache.NoAbsoluteExpiration,
                               Cache.NoSlidingExpiration,
                               CacheItemPriority.Default);
    }

    public IList<Product> FindProducts(Cache cache, double unitPrice)
    {
        string productskey = "Product:UnitPrice:" + unitPrice;
        IList<Product> products = (IList<Product>)cache.Get(productskey);

        return products;
    }

So, you have cache products, so you create a product key and then you have list of products this is being brought here and then you cache them and has a single object. And, like I explained earlier, it is going to work it would just get you the job done and it would work mostly when you need all the items from that list at once. You're not interested in individual items from that list. You're interested in entirely so in the entire store entire list as one item but it will make the object heavier it would not give you support of coding, searching and that's our next topic. Because it's a generic list, I list, it has a product in there but for NCache this is just a list, I list. Right! So, you're not able to pinpoint the object within that list and then attributes and then you don't have ability to search based on those attributes.

So, a more sophisticated way of handling this is to cache each collection item separately. That we covered as part of our earlier example but let's go through it one more time. For example; let's go through cache products one more time and this you simply store them as individual items. Let me find this example for you! OKAY! There it is.

So, first of all, we'll actually store products individually, right? We'll have a key constructed around that for individual products. Nice thing about this is that all individual items of product collection are stored as individual items in the cache so you can fetch them using the key that's the overload for that, the method for that Cache.Get. You can fetch them as a collection as well. That's something that we discussed in great details. One other option is that you also run queries and these SQL like search queries can be applied on the attributes of the objects directly. And, you can only do this if you have stored them individually all the items within the collection are stored as individual items. You map them onto a domain object, product in this case, and the products within a product collection are stored individually as separate items in the cache.

handling-collections-as single

Now you can index the unit price of the product, Product ID and then you can run a query like this. Select product, that's the namespace of the product, where this sets unit price equals to a runtime parameter. And, then you can call Cache.ExecuteReader and that would fetch you all the products which you can iterate through and keep fetching in your application as well. Similarly, you can also say where This.Tag. If you have associated a tag on top of it you can also run queries on that. That's another benefit of tags along with retrieval benefits, performance benefits, it also gives you flexibility from a searching standpoint. And, tags also give you Cache.Get by a tag. It gives you all the APIs, get by all tags get by any tags, so you can actually fetch objects using these tag APIs as well. But, the feature that I wanted to highlight is that you arrange them individually. That's the starting point. You create logical collections using Tag or Groups. So, retrieval is easier. You need individual items you call Cache.Get. Get the items based on the key. You need collections use the group or tag appears and on top of it you can run queries and you can associate, you can actually fetch our items based on a criteria.

In this case it could be unit price. It could be unit price greater than 10 and less than hundred. So, logical operators are supported you could have some aggregation as well, count, sum, there are some sorting order by a group by like operator in operation so it's pretty exciting extensive as far as support is concerned it's similar to SQL it's a subset of SQL queries but it's very flexible in usage and it gets you a lot of ease of use in terms of what items you need. You don't need to work with keys anymore. So, that would only happen if you simply use each collection item is stored separately in the cache. I hope that helps.

Getting Started with NCache

That completes our topic today. Towards the end, I'll just show you five simple steps for getting started with NCache to just go over NCache configurations and then be able to run these API's in a real-life application.

Okay! So, I'm going to quickly get started with this. I have the management tool opened. By the way, we recently released, actually we just released 4.9. So, that's our newest version so you might want to get started with that. So, all you need to do is create a cache, by name of it, choose next, pick a caching topology, partition replica cache is most suitable, async option for replication and here you specify the servers which are going to host this cache. I have demo one and two already installed with NCache.

So, step one is to download and install NCache. Step two is to create a named cache. So, I'm going to go through it, keep everything default because that's not the main scope of discussion today. I'm just going to go over with default values and size of the cache which is on each server. Just set up the basic settings and choose finish.

Step three is that you add a client node. I'll just use my machine. See if I have access to this, yep! All right! So, that's my machine right here. I've added that so step three is complete. All the configurations on the server and in client are complete. I now need to start and test this cache cluster as part of my step 4 and then I'll review this and then use it in actual application. So, that's how simple it is to get started with NCache.

I'll show you some quick counters to show you things in action and then we'll quickly conclude the presentation as well. I'll right-click and choose statistics that would open performance counters and I can also open monitoring tools which is NCache monitor, comes installed with NCache.

performance-counters

So, it's performance counter driven. It gives you server-side performance counters and client side performs counters as well. And, on the client application side I can run this stress testing tool application which again comes installed with NCache. It takes the name since there are configurations done so it would automatically connect to the cache and start simulating the load on my cache cluster. There you go! So, we have request load coming here as well as on to the other server.

Similarly, we have activity being shown on this server as well as clients connected report viewer server and client. And then you can also have your own custom dashboards where you can plug in any of these counters. For example, API logs is a good example. It logs all the requests which are being performed on the cache right now in real time, right?

So, that's one quick example of using these counters from the left main. Like I said, this is just to get you a feel of how the caching looks like. Now I can actually use this in a real-life application. I can use my machine right here and I can simply use basic operation sample which comes installed with NCache. All you need to do is there are different needle folders within samples .NET basic operations, as far as using NCache client side libraries you can use NCache SDK NuGet package. That's the easiest to get all the client-side resources.

The other option is that you actually use Alachisoft.NCache.Runtime and Alachisoft.NCache.Web libraries Yourself. You include those. These are something these are the libraries which come in start with NCache in Microsoft Cache folders. Once you install NCache it would be part of it and then after that I'll quickly show you what you need to do. There you go! So, first thing that you need is add references to these two libraries; runtime and web, include these namespaces which I just highlighted. Web.caching and then from that point onwards this sample is pretty good enough for basic initializing the cache connecting to it, creating an object reading it, deleting it updating it all sort of create, Read, update, delete operations.

So, this is how you initialize the cache. This is the main call, right? It needs the cache name returns a cache handle and then simply call cache or add store everything in a key value pair Cache.Get items and then to update items cache or insert and then you Cache.delete. And, we've already shown you some detailed examples of using key based dependency using tags using groups using SQL like search. So, this should actually give you some details around setting up the environment and then be able to use it in an actual application.

This concludes our presentation.

What to Do Next?

 

Signup for monthly email newsletter to get latest updates.

© Copyright Alachisoft 2002 - . All rights reserved. NCache is a registered trademark of Diyatech Corp.