Taswar Bhatti
The synonyms of software simplicity
elastic search

For the past year I have been evaluating and working and even presented ElasticSearch, and I thought it would be good to showcase a series of article on ElasticSearch for .NET Developers. What it brings to the table when developing a software solution. I also did a talk on ElasticSearch at Montreal DevTeach, if you are interested in my slides feel free to view them on slideshare or my blog.

Without further adieu, lets get started and lets look at what ElasticSearch really is.

First off, ElasticSearch some consider it as ELK Stack but for new branding they have been trying to call themselves Elastic Stack rather, although the ELK has been stuck with many people and google searches, but we from here on we will call it just Elastic Stack.

So what does the Elastic Stack consist of you may wonder?
Basically the Elastic Stack consist of ElasticSearch, Logstash and Kibana. Lets go through them individually so that we can understand what each component does and brings to a software solution.

ElasticSearch

ElasticSearch

ElasticSearch


This is the core main search engine or store that you use for storing your data, it is build in Java. It stores documents in json format and uses Lucene to index it, elastic search provides and builds metadata upon the index that was created by Lucene (Note: Lucene is build in Java, there is also a port of Lucene to .NET called NLucene)

Some people may think that ElasticSearch is a database that we store data into like mysql, postgres or mssql, but I would say Elastic is not really a database since there is no db file and does not have relationships like SQL. Its more like a NOSQL solution but not quite like mongodb either. The best thing to describe it, I would say is think of it as a Search Engine where you store documents in. I know its confusing at first but don’t worry it will come clear later or once you start playing around with it.

Logstash

logstash

logstash


Logstash is another module/component/service. You can use logstash without using ElasticSearch, the main functionality of Logstash is to get some input, filter it and output it somewhere, again the output does not need to be ElasticSearch but usually it is. An example of logstash could be I have IIS logs or Apache Logs I need to input them into logstash, and I would like to geo tag each of the IP address and store them into ElasticSearch or some database. Main idea of Logstash is (INPUT -> FILTER -> OUTPUT) simple. One more thing to note is Logstash is build with JRuby on the JVM and there are tones of open source plugins for Logstash that one can download, even to anonymized the data or encrypt etc before outputting the data.

Kibana

kibana

kibana


Kibana is the graphical user interface for ElasticSearch, it is used analyzing your data and for creating charts from ElasticSearch data. It is quite powerful, one can slice and dice many kinds of charts using Kibana.
Kibana is build with node.js and its a single page app (SPA) application.

Beats

beats

beats


Beats are basically light weight shippers of data. There are many types of beats, eg. filebeat is used for shipping file data (e.g apache.log) to ElasticSearch or Logstash. Winlogbeat allows one to ship windows events to ElasticSearch or Logstash, check out the beats offered by Elastic; you can also write your own beat using the ibbeat library, and not to mention that beats are actually written in GoLang. If you are interested in using Golang with VSCode check out the channel 9 video I did for golang and vscode.

So here we sum up the main components of Elastic Stack, I will go through each component individually in upcoming blog post, going through install process to configuration.

satazureday

I had the opportunity to speak at satazureday Azure Saturday here in Ottawa last week, and went through the topic of Azure Key Vault. I also had a co-presenter to share the talk with; an upcoming public speaker Petrica Mihai. He created most of the slides and the demo code in C# 🙂
You can view the code at https://github.com/mihaipetri/AzureKeyVaultNet

In any case if you are interested here are the slides on Azure Key Vault.

And the transcript:

  1. Azure Key Vault • What are we trying to solve with KeyVault?
    • Let’s step back and look at a Cloud Design Pattern
    • External Configuration Pattern
  2. External Configuration Pattern
  3. Typical Application
  4. Storing Configuration in file
  5. Multiple application
  6. External Configuration Pattern
    • Helps move configuration information out of the application deployment
    • his pattern can provide for easier management and control of configuration data
    • For sharing configuration data across applications and other application instances
  7. Problems
    • Configuration becomes part of deployment
    • Multiple applications share the same configuration
    • Hard to have access control over the configuration
  8. External Configuration Pattern
  9. When to use the pattern
    • When you have shared configuration, multiple application
    • You want to manage configuration centrally by DevOps
    • Provide audit for each configuration
  10. When not to use
    • When you only have a single application there is no need to use this pattern it will make things more complex
  11. Cloud Solution Offerings
    • Azure KeyVault (Today’sTalk)
    • Vault by Hashicorp
    • AWS KMS
    • Keywhiz
  12. What is Azure Key Vault ?
    • Safe1guard cryptographic keys and secrets used by cloud applications and services
    • Use hardware security modules (HSMs)
    • Simplify and automate tasks for SSL/TLS certificates
  13. Gemalto / SafeNet – Hardware Security Module
  14. How Azure Key Vault can help you ?
    • Customers can import their own keys into Azure, and manage them
    • Keys are stored in a vault and invoked by URI when needed
    • KeyVault performs cryptographic operations on behalf of the application
    • The application does not see the customers’ keys
    • KeyVault is designed so that Microsoft does not see or extract your keys • Near real-time logging of key usage
  15. Bring Your Own Key (BYOK)
  16. Create a Key Vault New-AzureRmKeyVault -VaultName ‘MihaiKeyVault’ -ResourceGroupName ‘MihaiResourceGroup’ -Location ‘Canada East’
  17. Objects, identifiers, and versioning
    • Objects stored in Azure KeyVault (keys, secrets, certificates) retain versions whenever a new instance of an object is created, and each version has a unique identifier and URL
    • https://{keyvault-name}.vault.azure.net/{object-type}/{object- name}/{object-version}
  18. Azure Key Vault keys
    • Cryptographic keys in Azure KeyVault are represented as JSONWeb Key [JWK] objects
    • RSA: A 2048-bit RSA key.This is a “soft” key, which is processed in software by KeyVault but is stored encrypted at rest using a system key that is in an HSM
    • RSA-HSM: An RSA key that is processed in an HSM
    • https://myvault.vault.azure.net/keys/mykey/abcdea84815e4ca8bc19c f8eb943ee88
  19. Create a Key Vault key $key = Add-AzureKeyVaultKey -VaultName ‘MihaiKeyVault’ -Name ‘MihaiFirstKey’ -Destination ‘Software’
  20. Azure Key Vault secrets
    • Secrets are octet sequences with a maximum size of 25k bytes each
    • The Azure KeyVault service does not provide any semantics for secrets; it accepts the data, encrypts and stores it, returning a secret identifier, “id”, that may be used to retrieve the secret
    • https://myvault.vault.azure.net/secrets/mysecret/abcdea54614e4ca7 ge14cf2eb943ab23
    • Create a Key Vault secret $secret = Set-AzureKeyVaultSecret -VaultName ‘MihaiKeyVault’ -Name ‘SQLPassword’ -SecretValue $secretvalue
    • Azure Key Vault certificates
      • Import/generate existing certificates, self-signed or Enroll from Public Certificate Authority (DigiCert, GlobalSign andWoSign)
      • When a KeyVault certificate is created, an addressable key and secret are also created with the same name
      • https://myvault.vault.azure.net/certificates/mycertificate/abcdea848 15e4ca8bc19cf8eb943bb45
    • Create a Key Vault certificate
    • Secure your Key Vault
      • Access to a key vault is controlled through two separate interfaces: management plane and data plane
      • Authentication establishes the identity of the caller
      • Authorization determines what operations the caller is allowed to perform
      • For authentication both management plane and data plane use Azure Active Directory
      • For authorization, management plane uses role-based access control (RBAC) while data plane uses key vault access policy
    • Access Control
      • Access Control based on Azure AD
      • Access assigned at theVault level
      • – permissions to keys
      • – permissions to secrets
      • Authentication against AzureAD
      • – application ID and key
      • – application ID and certificate
    • Azure Managed Service Identity (MSI)
      • Manage the credentials that need to be in your code for authenticating to cloud services
      • Azure KeyVault provides a way to securely store credentials and other keys and secrets, but your code needs to authenticate to Key Vault to retrieve them
      • Managed Service Identity (MSI) makes solving this problem simpler by giving Azure services an automatically managed identity in Azure Active Directory (Azure AD)
      • You can use this identity to authenticate to any service that supports AzureAD authentication, including KeyVault, without having any credentials in your code

      Azure Key Vault Logging

      • Monitor how and when your key vaults are accessed, and by whom
      • Save information in an Azure storage account that you provide
      • Use standard Azure access control methods to secure your logs by restricting who can access them
      • Delete logs that you no longer want to keep in your storage account
    • Azure Key Vault Pricing • Operations (Standard or Premium) $0.030 per 10000 operations
      • Advanced Operations (Standard or Premium) $0.150 per 10000 operations
      • Certificate Renewals (Standard or Premium) $3.00 per renewal
      • Hardware Security Module Protected Keys (Premium only) $1.00 per key
    • Azure Key Vault DEMO
      • Create KeyVault, Secrets, Keys and Certificates
      • Create AzureAD Application
      • Consuming Secrets and Keys https://azurekeyvaultnet.azurewebsites.net – live demo
      • https://github.com/mihaipetri/AzureKeyVaultNet – demo code
Taswar Bhatti - Cloud Design Patterns

This week I gave a talk on Cloud Design Patterns at the Ottawa .NET Community. I wanted to share the sides here and will most likely write on articles on the topic using real world examples. Samples in C# and node.js, in AWS and Azure.

For the talk I went through, what Cloud Design Patterns are and mainly focused on the patterns below without using any platform specifications. (i.e cloud agnostics)

  • The External Configuration Pattern
  • The Cache Aside Pattern
  • The Federated Identity Pattern
  • The Valet Key Pattern
  • The Gatekeeper Pattern
  • The Circuit Breaker Pattern

For each of them I also went through when you should use the pattern and when not to use it, I also provided Cloud Solutions Offering that one can use to implement the pattern.

Enjoy the slides 🙂

The Transcript:

  1. Agenda
    • What are Patterns?
    • The External Configuration Pattern
    • The Cache Aside Pattern
    • The Federated Identity Pattern
    • The Valet Key Pattern
    • The Gatekeeper Pattern
    • The Circuit Breaker Pattern
    • The Retry Pattern
    • The Strangler Pattern
  2. What are Patterns?
    • General reusable solution to a recurring problem
    • A template on how to solve a problem
    • Best practices
    • Patterns allow developers communicate with each other in well known and understand names for software interactions.
  3. External Configuration Pattern
    • Helps move configuration information out of the application deployment
    • This pattern can provide for easier management and control of configuration data
    • For sharing configuration data across applications and other application instances
  4. Typical Application
  5. Storing Configuration in file
  6. Multiple application
  7. Problems
    • Configuration becomes part of deployment
    • Multiple applications share the same configuration
    • Hard to have access control over the configuration
  8. External Configuration Pattern
  9. When to use the pattern
    • When you have shared configuration, multiple application
    • You want to manage configuration centrally by DevOps
    • Provide audit for each configuration
  10. When not to use
    • When you only have a single application there is no need to use this pattern it will make things more complex
  11. Cloud Solution Offerings
    • Azure Key Vault
    • Vault by Hashicorp
    • AWS KMS
    • Keywhiz
  12. Cache Aside Pattern
    • Load data on demand into a cache from datastore
    • Helps improve performance
    • Helps in maintain consistency between data held in the cache and data in the underlying data store.
  13. Typical Application
  14. Cache Aside Pattern
  15. When to use the pattern
    • Resource demand is unpredictable.
    • This pattern enables applications to load data on demand
    • It makes no assumptions about which data an application will require in advance
  16. When not to use
    • Don’t use it for data that changes very often
    • Things to consider
      • Sometimes data can be changed from outside process
      • Have an expiry for the data in cache
      • When update of data, invalidate the cache before updating the data in database
      • Pre populate the data if possible
    • Cloud Offerings
      • Redis (Azure and AWS)
      • Memcache
      • Hazelcast
      • Elastic Cache (AWS)
    • Federated Identity Pattern
      • Delegate authentication to an external identity provider.
      • Simplify development, minimize the requirement for user administration
      • Improve the user experience of the application
      • Centralized providing MFA for user authentication
    • Typical Application
    • Problem
      • Complex development and maintenance (Duplicated code)
      • MFA is not an easy thing
      • User administration is a pain with access control
      • Hard to keep system secure
      • No single sign on (SSO) everyone needs to login again to different systems
    • Federated Identity Pattern
    • When to use
      • When you have multiple applications and want to provide SSO for applications
      • Federated identity with multiple partners
      • Federated identity in SAAS application
    • When not to use it
      • You already have a single application and have custom code
      • that allows you to login
    • Things to consider
      • The identity Server needs to be highly available
      • Single point of failure, must have HA
      • RBAC, identity server usually does not have authorization information
      • Claims and scope within the security auth token
    • Cloud Offerings
      • Azure AD
      • Gemalto STA and SAS
      • Amazon IAM
      • GCP Cloud IAM
    • Valet Key Pattern
      • Use a token that provides clients with restricted direct access to a specific resource
      • Provide offload data transfer from the application
      • Minimize cost and maximize scalability and performance
    • Typical Application Client App Storage
    • Problem
    • Valet Key Pattern
    • Client App Generate Token Limited Time And Scope Storage
    • When to use it
      • The application has limited resources
      • To minimize operational cost
      • Many interaction with external resources (upload, download)
      • When the data is stored in a remote data store or a different datacenter
    • When not to use it
      • When you need to transform the data before upload or download
    • Cloud Offerings
      • Azure Blob Storage
      • Amazon S3
      • GCP Cloud Storage
    • Gatekeeper Pattern

      • Using a dedicated host instance that acts as a broker between clients and services
      • Protect applications and services
      • Validates and sanitizes requests, and passes requests and data between them
      • Provide an additional layer of security, and limit the attack surface of the system
    • Typical Application

    • Problem

    • Gatekeeper Pattern

    • When to use it

      • Sensitive information (Health care, Authentication)
      • Distributed System where perform request validation separately
    • When not to use

      • • Performance vs security
    • Things to consider

      • WAF should not hold any keys or sensitive information
      • Use a secure communication channel
      • Auto scale
      • Endpoint IP address (when scaling application does the WAF know the new applications)
    • Circuit Breaker Pattern

      • To handle faults that might take a variable amount of time to recover
      • When connecting to a remote service or resource
    • Typical Application

    • Problem

    • When to use it
      • To prevent an application from trying to invoke a remote service or access a shared resource if this operation is highly likely to fail
      • Better user experience
    • When not to use
      • Handling access to local private resources in an application, such as in-memory data structure
      • Creates an overhead
      • Not a substitute for handling exceptions in the business logic of your applications
    • Libraries
      • • Polly (http://www.thepollyproject.org/)
      • • Netflix (Hystrix) https://github.com/Netflix/Hystrix/wiki
    • Retry pattern
      • Enable an application to handle transient failures
      • When the applications tries to connect to a service or network resource
      • By transparently retrying a failed operation
    • Typical Application has Network Failure

    • Retry Pattern
      • • Retry after 2, 5 or 10 seconds
    • When to use it
      • Use retry for only transient failure that is more than likely to resolve themselves quicky
      • Match the retry policies with the application
      • Otherwise use the circuit break pattern
    • When not to use it
      • Don’t cause a chain reaction to all components
      • For internal exceptions caused by business logic
      • Log all retry attempts to the service
    • Libraries
      • Roll your own code
      • Polly (http://www.thepollyproject.org/)
      • Netflix (Hystrix) https://github.com/Netflix/Hystrix/wiki
    • Strangler Pattern

      • Incrementally migrate a legacy system
      • Gradually replacing specific pieces of functionality with new applications and services
      • Features from the legacy system are replaced by new system features eventually
      • Strangling the old system and allowing you to decommission it
    • Monolith Application
    • When to use
      • Gradually migrating a back-end application to a new architecture
    • When not to use
      • When requests to the back-end system cannot be intercepted
      • For smaller systems where the complexity of wholesale replacement is low
    • Considerations
      • Handle services and data stores that are potentially used by both new and legacy systems.
      • Make sure both can access these resources side-by-side
      • When migration is complete, the strangler façade will either go away or evolve into an adaptor for legacy clients
      • Make sure the façade doesn't become a single point of failure or a performance bottleneck.
oauth and openid_

Wanted to share my DevTeach Montreal 2017 talk where I talked about OAuth and OpenId Connect. The types of OAuth Grants, how to consume them, the flows in OAuth and what OpenId Connect comes into play, what does it solve.

Hope you like the presentation and if you are interested in more security topics, ping me and let me know what would you be interested in.

elastic search

Wanted to share my DevTeach talk slides on Elastic Search. Where I went into introducing the Elastic Stack. Consisting of Elastic Search, Logstash and Kibana. I also went into the constraints that we had and the design approaches that we took.

Hope you enjoy and expect more ElasticSearch blogs this year 🙂

Taswar Bhatti Talk on MS Bot Framework

In May 2017 I did a talk in Ottawa .NET User Group on Introduction to Microsoft Bot Framework, it was a interesting turnout and lots of conversation on what a bot can do for a business and how to use them.

Below you will find the slides for my talk on Microsoft Bot Framework. The sample demo code can be find on github https://github.com/Microsoft/BotBuilder-Samples, where we demo searching of Real Estate, image search, etc.

Ottawa IT Meetup Community: https://www.meetup.com/ottawaitcommunity/events/235920172/

If you are interested in more on bot framework and like to see more articles on it, please let me know 🙂

Taswar Bhatti Xamarin Dev Days

Last week I did a presentation on Xamarin Dev Days here in Ottawa, the presentation was on Introduction to Xamarin, unfortunately my demo did not work, the demo gods were not with me that day 🙁
The demo code uses Microsoft Cognitive Service Bing Search to search for images to display on a mobile phone. Mainly I was doing a demo on the Android platform. The code base is on Github
https://github.com/xamarin/dev-days-labs/tree/master/Demos/app-imagesearch-cogs

The slides for my presentation are located here.

I also did another demo for Carleton University Business Program on Xamarin and if you wish to see more Xamarin related article please let me know 🙂

Redis

Redis Transactions

Redis provides a way to do Transactions, but the transactions are somewhat different than what you know from a SQL Relational Database perspective. In SQL Relation Database world you can executed partially and then rolled back the entire transaction. While Redis uses the MULTI and EXEC commands where every command passed as part of a transaction is executed one after another until they have all completed and only after they have completed then other clients may execute their commands. You can think of it more like a queue that stores all your commands and when you call EXEC it loops through the queue and execute the task on the queue one after the other while being locked in the loop i,e no other thread could come in.

Below is an example of a Client queuing up the calls to Redis using MULTI in Redis client library to make a call to Redis Server to process the commands in an EXEC.

redis-transaction

redis-transaction



Note: You see Client2 also making a call to Redis but the command is only executed after the process of Client commands.

One important thing to note is when using Redis Transaction one cannot make decisions inside the block, but you can use WATCH to prevent a key to be changed in a transaction. E.g We can WATCH the key “A” in our sample and when we try to use it in MULTI and EXEC it will fail to change A. A will be discarded as shown below, when A was set as 4, even inside of the block we were not able to make the change the EXEC was in some way ROLLBACKED i.e DISCARD

In Redis StackExchange library uses Conditions to mimic Watch since Redis StackExchange uses Multiplex. Commands can come in different stages but conditions can be used to detect changes.

C# code using Redis Transactions

So this covers the basic usage of Redis Transactions, in the next blog post I will cover how to use Lua in Redis.

For the code please visit
https://github.com/taswar/RedisForNetDevelopers/tree/master/11.RedisTransaction

For previous Redis topics

  1. Intro to Redis for .NET Developers
  2. Redis for .NET Developer – Connecting with C#
  3. Redis for .NET Developer – String Datatype
  4. Redis for .NET Developer – String Datatype part 2
  5. Redis for .NET Developer – Hash Datatype
  6. Redis for .NET Developer – List Datatype
  7. Redis for .NET Developer – Redis Sets Datatype
  8. Redis for .NET Developer – Redis Sorted Sets Datatype
  9. Redis for .NET Developer – Redis Hyperloglog
  10. Redis for .NET Developer – Redis Pub Sub
  11. Redis for .NET Developers – Redis Pipeline Batching
Redis

Redis provides a way to use Pipeline Batching to send messages to Redis but first we must understand that Redis uses tcp request response protocol, some may know it as client server model. If you are coming from a web http world, you will have no issue understanding client server, where the browser acts like a client and the web server running (IIS, Nginx or Apache) as the server.

Below is an example of a Client calling the Redis library to make a call to Redis Server to store a value of 10.


In the above diagram as you can see there is a large amount of time where the client is just waiting for a response, the round trip time can have a significant impact if the latency between the network is long.

Redis Pipelining

Redis provides a mechanics called pipelining, which allows a client to send multiple messages to Redis Server without waiting for a reply on each message. Something like the diagram below. Note: Something to consider, Redis does stored these messages into queue such that it can reply to the client and as a result it is using memory on the server.
redis-pipeline

In .NET we are lucky that we have TPL and asycn/await built into our languages, so the design choice that StackExchange.Redis has done is to use the framework itself to handle such situations. One can simply use the Wait() keyword or ContinueWith() for completion of task. There is also another case, which is the CommandFlags.FireAndForget that you may have seen in my previous examples, the FireAndForget allows us to continue immediately in our code since we do not care about the response we get back from Redis Server.

C# code using Redis Pipeliine (TPL)

Redis Batching

StackExchange.Redis also provides a way to send batch request to Redis. What it allows us to do is to send a block of operations to the server together. The reason for this is that it will help reduce packet fragmentation when the connection to redis is slow. The downside is it will take a longer time to get the first operation complete to process, but overall it can improve the time to get all the operation processed. Below is an example of using Batching.

redis-batch

C# code using Redis batching

So this covers the basic usage of Redis Pipeline and Batching, the recommendation for StackExchange.Redis is to use the normal async/await of TPL to handle all the pipeline for you, since StackExchange.Redis does it best under the cover to handle multiplex connections. In the next blog post I will cover how to use Transactions in Redis.

For the code please visit
https://github.com/taswar/RedisForNetDevelopers/tree/master/10.RedisPipeline

For previous Redis topics

  1. Intro to Redis for .NET Developers
  2. Redis for .NET Developer – Connecting with C#
  3. Redis for .NET Developer – String Datatype
  4. Redis for .NET Developer – String Datatype part 2
  5. Redis for .NET Developer – Hash Datatype
  6. Redis for .NET Developer – List Datatype
  7. Redis for .NET Developer – Redis Sets Datatype
  8. Redis for .NET Developer – Redis Sorted Sets Datatype
  9. Redis for .NET Developer – Redis Hyperloglog
  10. Redis for .NET Developer – Redis Pub Sub
Redis

Redis Pub Sub, somehow may sound weird that it provides such a functionality since when you think of Redis the first thing that comes to mind is of a cache key value store. But indeed Redis does allow us to use the messaging paradigm by using channels to publish messages and for subscribers to listen for notification of the message. Redis Pub Sub allows multiple subscribers to listen to one or more channels, and to only receive messages that are of interest. It decouples the subscriber from the publisher since the subscriber has no knowledge of whom the publishers are and vice versa there could be multiple publisher not knowing whom the subscribers are.

Sample Diagram of Redis Pub Sub

RedisPubSub

Redis Pub Sub

There are definitely certain restrictions of using Redis Pub Sub as a Messaging System, it will not be like RabbitMQ, Kafka or Azure MessageBus etc. Those message bus are able to store the message for durability or even replay of an old message for consumption. Redis uses a listener model where there are no listeners (subscribers) it will not receive those messages. But if you wish to have a simple pub sub without the heavy tools then Redis does quite a good job at it.

Redis Pub Sub – Operations

  • PUBLISH channels message: Posts a message to the given channel O(N+M)
  • SUBSCRIBE [channel]: Subscribe to a given channel for message, O(N) where N is the number of channels to subscribe to
  • PSUBSCRIBE [channel]: Subscribes the client to the given patterns, O(N) where N is the number of patterns the client is already subscribed to.
  • PUBSUB CHANNELS pattern: Currently active channels, O(N)
  • PUBSUB NUMSUB channel: Number of subscribers to the channels provided, O(N)
  • PUBSUB NUMPAT: Number of subscriptions to all the patterns O(N)
  • PUNSUBSCRIBE: Unsubscribes the client from a pattern, O(N+M)
  • UNSUBSCRIBE: Unsubscribes the client from a channel, O(N) where N is the number of clients already subscribed to a channel.

C# code using Redis PubSub

So this covers the basic usage of Redis PubSub, in the next blog post I will cover how to use Pipelines in Redis.

For the code please visit
https://github.com/taswar/RedisForNetDevelopers/tree/master/9.RedisPubSub

For previous Redis topics

  1. Intro to Redis for .NET Developers
  2. Redis for .NET Developer – Connecting with C#
  3. Redis for .NET Developer – String Datatype
  4. Redis for .NET Developer – String Datatype part 2
  5. Redis for .NET Developer – Hash Datatype
  6. Redis for .NET Developer – List Datatype
  7. Redis for .NET Developer – Redis Sets Datatype
  8. Redis for .NET Developer – Redis Sorted Sets Datatype
  9. Redis for .NET Developer – Redis Hyperloglog
UA-4524639-2