Native data structures offer conventional methods of storing and retrieving data. Their implementation provides your standalone applications with concurrency. Unfortunately, these data structures are only limited to the threads inside the application process. And with most enterprise applications running on more than one server for scalability, sharing the state of the data structures is paramount. This is where distributed data structures step in.
They extend conventional data structure functionality by enabling multiple processes and application instances to concurrently add, fetch, and remove data while remaining synchronized. Luckily, distributed data structures overcome the limitations of traditional data structures, as they support parallel computation across scalable applications.
Where does NCache fit in?
NCache, as an in-memory caching solution, enhances the functionality that these distributed data structures provide. With NCache as a distributed cache, you can add data to a distributed structure on any server, making it accessible to all applications, processes, OutProc threads, and instances in your system, regardless of where the data is stored.
Furthermore, the data you store in cache servers is valuable, and losing it due to server issues is never an option. This is exactly why NCache allows you to keep replicas of your data through its replicated topologies in the caching server so that if one of the nodes goes down, the replicated data remains accessible, ensuring continuous operations. Moreover, you can also add cache server nodes at runtime for flexibility and scalability.
NCache provides users with the functionality of data replication, partition, and scaling encapsulated as part of the data structures. Therefore, developers using these data structures do not have to worry about the distribution logic inside their .NET applications.
Distributed Data Structures in NCache
NCache provides various data structures to achieve scalability, concurrency, and data integrity in your .NET applications. Let us look at how these data structures enhance your overall application performance.
Distributed List
NCache provides a Distributed List – a native .NET implementation of the IList interface. Since NCache is a .NET-native caching solution, it integrates seamlessly with your .NET applications. The following code snippets demonstrate how to create and populate a Distributed List in NCache.
1 2 3 |
// Create list of Product type IDistributedList<Product> retrievedList = cache.DataTypeManager.GetList<Product>("ProductList"); list.Add(new Product)(){ Name = "Tofu", Category = "Food", UnitPrice = 0.15}); |
The following code removes discontinued products from the previous list.
1 2 3 4 5 |
// Get range of discontinued products to be removed List<Product> discontinuedProducts = FetchDiscontinuedProducts(); // Remove this range from retrievedList int itemsRemoved = retrievedList.RemoveRange(discontinuedProducts); |
Incorporating a Distributed List into everyday applications is easy and can enhance functionality in various scenarios.
Use Case: Shopping cart
In an e-commerce .NET application, NCache’s Distributed List is ideal for managing shopping cart data across multiple servers. By using NCache, each server maintains consistent and updated shopping cart information for every customer. This ensures that any data about added or removed items is always up-to-date and available, improving the overall user experience.
Use Case: Leaderboard
In online gaming or worldwide matches, people are always interested in knowing which team is leading and who’s lagging behind. Information like this can be incorporated in an application that uses NCache’s List Data Structure to store the required data and broadcast it to all interested users.
Distributed Queue
NCache also provides a First-in, First-out (FIFO) implementation known as a Distributed Queue. Queues are used at runtime to store valuable information because of this behavior. The NCache IDistributedQueue interface is easy to understand and implement. Explore the code below to get acquainted with basic queue operations, for instance, creating a queue and adding data to it.
1 2 3 4 5 |
// Create Queue of Candidate type IDistributedQueue<Candidate> queue = cache.DataTypeManager.CreateQueue<Candidate>("CandidateQueue"); // Add candidates to queue queue.Enqueue(new Candidate(){ Name = "John Smith", Position = "Software Engineer" }); |
The following code demonstrates how to dequeue the first item.
1 2 3 4 5 |
// Queue with this key already exists in cache IDistributedQueue<Candidate> retrievedQueue = cache.DataTypeManager.GetQueue<Candidate>("CandidateQueue";); // Remove first item of queue retrievedQueue.Dequeue(); |
You can explore additional queue operations in NCache by referring to the NCache Queue documentation. Below are some use cases of a Distributed Queue to highlight where it can be used and its benefits.
Use Case: Message Queue
In distributed .NET multi-server applications, managing and delivering messages to clients can be challenging due to the lack of server control over each client. A Distributed Queue helps manage this by storing and distributing messages across all cache servers, ensuring consistent client access and high availability by data replication.
Use Case: Sentiment Analysis
In a scenario in which a distributed application processes a high volume of tweets for sentiment analysis, ensuring that each tweet is processed exactly once without duplication is challenging. NCache’s Distributed Queue, lets you effectively manage and process incoming tweets. This ensures that each tweet is enqueued and processed only once across all servers. This prevents duplicate processing and resource wastage while enhancing the efficiency and accuracy of operations, leveraging NCache’s capabilities for high availability and data consistency.
Distributed HashSet
NCache provides an extremely fast and scalable Set implementation called the Distributed HashSet. A HashSet is an unordered where all values are unique. You can create and populate a HashSet in NCache, as shown below.
1 2 3 4 5 6 |
// Create HashSets of int type IDistributedHashSet<int> userSetMonday = cache.DataTypeManager.CreateHashSet<int>("MondayUsers"); // Add user IDs for Monday userSetMonday.Add(1223); userSetMonday.Add(34564); |
The following code sample removes the Monday users from the weekly users HashSet (which have already been added to the cache).
1 2 3 4 5 6 7 8 |
// Get existing HashSets string userIDMondayKey = "UserIDMonday"; string userIDWeeklyKey = "UserIDWeekly"; IDistributedHashSet<int> userSetMonday = cache.DataTypeManager.GetHashSet<int>(userIDMondayKey); IDistributedHashSet<int> usersWeekly = cache.DataTypeManager.GetHashSet<int>(userIDWeeklyKey); // Remove users of Monday from Weekly HashSet var removedItem = usersWeekly.Remove(userSetMonday); |
The following are the application scenarios where performance is improved with Distributed HashSets.
Use Case: IP Address Tracking
As HashSets do not contain any duplicate values, you can use NCache’s Distributed HashSets to keep track of all the website users. For example, in an online bookstore, you can use a Distributed HashSet to record which users have shown interest in specific books or made purchases, ensuring accurate user activity tracking without duplication.
Use Case: Analyzing E-Commerce Sales
You can keep all the information you need inside a HashSet. You can even leverage NCache’s Distributed HashSet to streamline sales data management in e-commerce platforms. By storing unique user IDs as keys and linking them to detailed purchase histories, wishlist items, and other interactions, you can effortlessly track customer behavior and preferences. This structured approach not only simplifies data management but also enhances your ability to perform in-depth analysis and generate actionable insights.
Distributed Dictionary
Distributed Dictionaries provided by NCache, are key-value pairs based on a native .NET implementation of the IDictionary interface. A dictionary holds a value against a certain key and if that value needs to be changed, you need to override it.
You can use IDistributedDictionary in NCache to keep a record of products. The following code sample demonstrates creating a dictionary in the cache with the key ProductDictionary and adding data to it.
1 2 3 4 5 |
// Create dictionary of Product type IDistributedDictionary<string, Product> dictionary = cache.DataTypeManager.CreateDictionary<string, Product>("ProductDictionary"); // Add products dictionary.Add(new Product(){ Name = "Laptop", Category = "Electronics", UnitPrice = 999.99 }); |
The following code sample removes the dictionary entities for the expired products using Remove.
1 2 3 4 5 6 7 8 |
// Get dictionary and show items of dictionary IDistributedDictionary<string, Product> retrievedDictionary = cache.DataTypeManager.GetDictionary<string, Product>("ProductDictionary"); // Create list of keys to remove List<string> keysToRemove = FetchExpiredProducts(); // Number of keys removed is returned int itemsRemoved = retrievedDictionary.Remove(keysToRemove); |
A Distributed Dictionary can be used in multiple cases, as provided below.
Use Case: Storing Encrypted Values in the Cache
NCache’s Distributed Dictionary is ideal for managing cryptographic keys in a distributed environment. By storing encrypted data and its corresponding decryption keys as dictionary entries, you ensure that all servers in your distributed system offer continued access to both the data and the necessary keys, facilitating secure and efficient data retrieval.
Use Case: Storing Login Credentials
In applications spread across multiple servers, using a Distributed Dictionary to store login credentials ensures consistent access to security information. By caching user IDs and passwords as key-value pairs, you enable any server to retrieve and update login data efficiently, ensuring seamless access control and user authentication across your distributed application.
Distributed Counter
NCache also provides a Counter data structure to increment and decrement data values with ease. Moreover, NCache lets you lock data placed in counters to keep data consistent. For your .NET application, the following code helps you understand how to create an ICounter instance, populate it with data, and increment/decrement those values.
1 2 3 4 5 6 7 8 9 10 11 |
// Set initial value of counter long initialValue = 15; // Create counter ICounter counter = cache.DataTypeManager.CreateCounter(key, initialValue); // Increment value long newValue = retrievedCounter.Increment(); // Decrement value newValue = retrievedCounter.Decrement(); |
You can use counters in countless scenarios, as detailed below.
Use Case: Page-View Count
You can easily track web page views per hour or per day, depending on your needs, by using Distributed Counters in NCache. By incrementing the counter in the cache server on each view, you can efficiently aggregate data across multiple servers. Periodic updates to the database ensure accurate long-term records while minimizing database load and improving the performance of your .NET application.
Use Case: Tweets Analysis
When dealing with rapidly changing data, such as tweets, NCache’s Distributed Counters are invaluable. By keeping track of the number of likes and dislikes, comments, etc., NCache enables you to handle high-frequency updates efficiently. This approach reduces your database load by minimizing direct writes, ensuring smooth performance even during sudden spikes in activity.
Conclusion
In single-tier architecture, standard data structures work fine. But when you have multiple servers and high in-demand data in the picture, relying on databases alone can hinder performance. For this, NCache is the ideal solution, offering scalable, in-memory caching that enhances performance and reliability across distributed systems.