Stateless Authentication in Web Applications

+ 2 comments

In this edition, Abhijeet Thakur (abhijeet.thakur@indexnine.com) talks about Stateless Authentication in Web Applications.


Traditionally, web applications were built as a monolithic app, designed to scale within a given server. In order to authenticate valid users, the classic approach was to use a “cookie” or a small object containing user information and store it in a “session” object on the server side. (more about session). In this approach, “state” information was stored and retrieved at the server side.

This approach has limitations when it comes to scaling the application in question. Contemporary applications use “stateless authentication”, which avoids storing any kind of state on the server side.  Stateless authentication has the advantage of being suitable for web applications that are horizontal in nature, thereby allowing the app to scale by adding more servers to process requests.
In stateless authentication, client sends user-specific identification data to the server as part of the login process, and if the data is correct, the server responds with a valid token to the client. After the authentication is done, the client uses this token to send further requests, without giving any user-specific identification. The token sent by client contains all the information to validate a user, therefore any server can validate any user, allowing the user to scale the application horizontally.
JSON Web Token (JWT) is an open standard ( RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret ( with the HMAC algorithm ) or a public/private key pair using RSA. ( More about JWT token )

JSON Web Token consist of 3 parts separated by dot ( . ), which are Header, Payload and Signature. JWT typically looks like the following.

jwt.png
JWT has several advantages, including its stateless nature,  JWT tokens can be set to expire, so the same token cannot be reused indefinitely.

Just throw away the token when you're done with it, it will expire on its own. So, web applications no longer need to be logged out after every session.  A network round-trip (e.g., finding a session in the database, deserializing it, then extracting the information you're interested in) is likely to take more time than calculating a HMAC SHA256 to validate a token and parse its contents.So, JWT also helps improve performance.

JWT tokens are very widely used for authentication of REST applications. Researching JWT authentication best practice implementations for web applications can be very confusing.  This is in part due to the variety of ways one can implement JWT authentication. In our experience, an interesting plugin to implement JWT authentication in dropwizard is "dropwizard-jwt-cookie-authentication".

Dropwizard-jwt-cookie-authentication plugin is very simple to use. It stores and reads the jwt token directly into the cookie of the browser. So there is no need to store and add the token to each API request. This saves front-end developers the hassle of doing it themselves.This plugin is a dropwizard bundle, and is a breeze to be integrated in the back-end code. It automatically serializes/deserializes session information into/from JWT cookies.

Dropwizard-jwt-cookie-authentication plugin has built in functionality to create JWT tokens. To add Dropwizard-jwt-cookie-authentication bundle in dropwizard, add the dependency in your application. Default value to expire session tokens is 30 minutes and default value to expire persistent tokens is 7 days.

References:

Summary : JWT authentication is a secure and efficient way to authenticate users for horizontally scalable applications. The dropwizard jwt cookie authentication plugin for dropwizard, makes integrating JWT extremely easy.

Note for mobile apps:
If you want to use dropwizard jwt cookie authentication plugin with mobile apps then some extra effort is required. Because mobile applications don’t support cookies out of the box, when the server sends a response to mobile app, read the Set-Cookie property from the response header and save the JWT token into shared preferences. Similarly, set the JWT token in the request header from shared preferences before sending subsequent requests to server.

Supercharge your Web Forms using Angular - Formly

+ No comment yet


In this edition, Abhijeet Thakur (abhijeet.thakur@indexnine.com) talks about Angular-formly.

Forms, as we all know, are a vital part of web development. All of us who are familiar with AngularJS will agree with the fact that how easy it is to develop robust scalable from-ends using AngularJS.

But it comes with its own problems, one of the biggest being how to deal with big forms. The HTML bloats up, becomes unreadable to human-eye, too many tags, validations in each of the numerous fields and many more! We faced this in one of our projects. This project has more than 20 forms with each of them containing at least 60 fields. We had to invest a lot of time and effort into finding a workable solution to this recurring problem.

We found an optimum solution in the form of 'Angular-Formly’- an open source module maintained by Kent C.Dodds. As the official documentation also says, Angular-Formly gives your forms consistency, maintainability, simplicity, flexibility and sanity!

Angular-Formly suggests maintaining less HTML in favour of JavaScript.

An example of this ideology is cited on the angular-formyl site as well. Using the module we can go from:
<form>
   <div class="form-group">
      <label for="exampleInputEmail1">Email address</label>
      <input type="email" class="form-control" id="exampleInputEmail1"                                 placeholder="Enter email" ng-model="vm.user.email">
   </div>
   <div class="form-group">
      <label for="exampleInputPassword1">Password</label>
      <input type="password" class="form-control" id="exampleInputPassword1"                           placeholder="Password" ng-model="vm.user.password">
   </div>
   <div class="form-group">
      <label for="exampleInputFile">File input</label>
      <input type="file" id="exampleInputFile" ng-model="vm.user.file">
      <p class="help-block">Example block-level help text here.</p>
   </div>
   <div class="checkbox">
      <label>
      <input type="checkbox" ng-model="vm.user.checked"> Check me out
      </label>
   </div>
   <button type="submit" class="btn btn-default" ng-click="vm.submit(vm.user)">                  Submit                                                                         </button>
</form>

To only this:
<formly-form model="vm.user" fields="vm.userFields">
    <button type="submit" class="btn btn-default" ng-click="vm.submit(vm.user)">                    Submit                                                                      </button>
</formly-form> 
And use the power of Javascript to do the rest:

function YourCtrl() {
      var vm = this;

      vm.user = {};

      // note, these field types will need to be
      // pre-defined. See the pre-built and custom templates
      // http://docs.angular-formly.com/v6.4.0/docs/custom-templates
      vm.userFields = [
        {
          key: 'email',
          type: 'input',
          templateOptions: {
        type: 'email',
        label: 'Email address',
        placeholder: 'Enter email'
          }
        },
        {
          key: 'password',
          type: 'input',
          templateOptions: {
        type: 'password',
        label: 'Password',
        placeholder: 'Password'
          }
        },
        {
          key: 'file',
          type: 'file',
          templateOptions: {
        label: 'File input',
        description: 'Example block-level help text here',
        url: 'https://example.com/upload'
          }
        },
        {
          key: 'checked',
          type: 'checkbox',
          templateOptions: {
        label: 'Check me out'
          }
        }
      ];
    }

Getting started with Angular-Formly

For elaboration purpose we will create a custom form with 3 fields - first name,last name and email using Angular-Formly.

To get started you will need to install angular-formly and some dependencies.You can also use bower if your are familiar with it.
1) Add the following files in index.html
<!-- angular and required files for angular formly -->
<script src="lib/angular-formly/api-check.min.js"></script>
<!-- For Validations in Angular formly using ngMessage required angular version is 1.4.7 -->
<script src="lib/angular/angular_v1_4_7.min.js"></script>
<script src="lib/angular-formly/ui-bootstrap-tpls.min.js"></script>
<script src="lib/fileInput/fileinput.min.js"></script>
<script src="lib/angular-formly/formly.min.js"></script> 
<script src="lib/angular-formly/angular-formly-templates-bootstrap.min.js"></script>       
<script src="lib/angular-message/angular-messages.min.js"></script>
<script src="lib/angular-formly/angular-animate.min.js"></script> 

2.  Now add formly and formlyBootstrap as a dependency in app.js. You can also add ngAnimate and ngMessages which shows error messages at the time of validation.
// app.js
(function() {

  'use strict';
  angular.module('formlyApp', ['formly', 'formlyBootstrap','ngAnimate','ngMessages']);

})();
3. Now set up angular formly form in HTML.
<body ng-app="formlyApp" ng-controller="MainController as vm">
   <div class="container col-md-4 col-md-offset-4">
      <form novalidate>
         <h1>Customer Details</h1>
         <formly-form model="vm.customer" fields="vm.customerFields"                                     form="vm.customerForm">
            <button type="submit" class="btn btn-primary" ng-disabled="vm.customerForm.$invalid"> Submit</button>
         </formly-form>
      </form>
   </div>
</body>
4. Set Up the Controller

Create an array called vm.customerFields with the objects describing the fields in the form.
Key: Form field objects need a unique key which generally describes what the purpose of the field.

Type: The form types such as input, textarea, select, radio etc needs to be registered with the formlyConfig service/provider. (Already done for the Bootstrap templates)

TemplateOptions: This is an object where we further describe the properties for the field like - the kind of input, label and placeholder, whether the field is required. To add more fields in the form just add more objects into this array. This is very easy to maintain and edit anytime.

This is how the code snippet looks:
// scripts/MainController.js
...

function MainController(province) {

    var vm = this;

    // The model object that we reference
    // on the  element in index.html
    vm.customer = {};
    
    // An array of our form fields with configuration
    // and options set. We make reference to this in
    // the 'fields' attribute on the  element
    vm.customerFields = [
        {
            key: 'first_name',
            type: 'input',
            templateOptions: {
                type: 'text',
                label: 'First Name',
                placeholder: 'Enter your first name',
                required: true
            }
        },
        {
            key: 'last_name',
            type: 'input',
            templateOptions: {
                type: 'text',
                label: 'Last Name',
                placeholder: 'Enter your last name',
                required: true
            }
        },
        {
            key: 'email',
            type: 'input',
            templateOptions: {
                type: 'email',
                label: 'Email address',
                placeholder: 'Enter email',
                required: true
            }
        },
    ];
    
}

...
The form is now ready with 3 fields. You can perform custom validations for each field, disable fields, create custom fields using templates etc. (One of the best example of custom fields is here).

You can also set a wrapper to use this field globally.

An example where we used the ngMessage directive to show error messages for invalid inputs in a form. Using this wrapping made the whole process of displaying error messages very simple.
app.config(function (formlyConfigProvider) {
   formlyConfigProvider.setWrapper({
      name: 'validation',
      types: ['input'],
      template:'<formly-transclude></formly-transclude><div ng-messages="fc.$error" ng-if="form.$submitted || options.formControl.$touched" class="error-messages"><div ng-message="{{ ::name }}" ng-repeat="(name, message) in ::options.validation.messages" class="message">{{ message(fc.$viewValue, fc.$modelValue, this)}}</div></div>'
   });
})

The Gist

Hopefully we were able to elaborate some of the advantages of using Angular-Formly. It has very good documentation with tons of examples. There are prebuilt templates for Bootstrap,LumX, and Vanilla HTML and WIP template libraries for angular material,Iconic and foundation. It is very easy to create custom templates and reuse them. Configuring validations is a cake walk.

if you are a newcomer to angular, you should check out the official angular-formly website. You can get in touch with the module’s maintainer, Kent.C.Dodds for any questions or help. He is super responsive to any queries.

References:

Microservices architecture

+ No comment yet
In this edition, Rahul (rahul dot amodkar at indexnine dot com) reviews Microservices architecture while looking at the things to consider when it comes to choosing it. He lists  out the drawbacks and the advantages of using Microservices architecture.


Microservices has been a buzzword in software engineering recently. Everyone wants to build their application around Microservices architecture. Before you jump onto the bandwagon, lets do a critical review of Microservices. We will look at the cases where it is appropriate along with  advantages and disadvantages. 

Before we proceed let us look at how to define a microservice. Wikipedia says:
In computing, microservices is a software architecture style, in which complex applications are composed of small, independent processes communicating with each other using language-agnostic APIs.  These services are small, highly decoupled and focus on doing a small task.
 A Microservice

  • Addresses a small problem domain.
  • Is built and deployed independently.
  • Runs in its own processes.
  • Segregates data and config for its own use.
  • Integrates via well known interfaces.


A collection of such microservices forms a usable application. 

When to use Microservices 

Microservices Architecture is an excellent choice when :
  • The system is too complex or too big and it becomes difficult to manage. 
  • The system is expected to handle high traffic & load and needs to be highly scalable. 

Drawbacks of microservices

Microservice architecture is very attractive, but even staunch supporters agree with its drawbacks.  
  • Adds complexity to development due to its distributed nature.
  • Adds overhead in terms of costs and operational complexity (build-test-deploy-run).
  • Time to market may be hit adversely due to the distributed nature of the application.
  • Monitoring the application in production deployment becomes a complex job, with multiple services.
  • Service interface versioning needs to be managed for the multiple services.
  • The operational challenges of Microservices architecture mean you need high quality DevOps and release automation skills as part of your development team.
  • The asynchronous communication and distributed nature of the microservices based system poses challenges for testing the system as a whole.
  • Operational expenditure could be significantly higher depending on the complexity of the system.

Benefits of microservices

The architecture also brings benefits that has made it so popular:
  • Systems are inherently scalable as the services built this way are loosely coupled. The scalability is improved further if the communication between the services is asynchronous using a Enterprise Service Bus (ESB). 
  • It improves the team autonomy as smaller team have complete control over the microservice. These smaller team can deliver at a higher speed of delivery as they are focused on the specific service. 
  • It highly increases the overall availability of the system when combined with Fault Tolerant Architecture.
  • It allows each service to be built using the most appropriate tool and language for the job. 
  • Multiple teams can deliver relatively independently of each other under this model.
  • The approach eliminates long-term commitment to a single technology stack.
  • The development efforts are more scalable as one can develop, deploy and scale each service independently.
  • The product is more agile as defined interfaces can be use to completely swap out components if required.

Recommendation

The recommended approach that has worked with many teams is to start with a monolithic design with a clear mandate to break it down later into services and as boundaries between services become clear. This phased approach from monolithic to microservices has a higher success rate as compared to starting out with microservices from scratch. Start with a microservices approach right from the start only if you have extreme clarity about the service boundaries. 

On the flip side, be aware and educate your team of your long term strategy so you do not get stuck with monolithic glop beyond v1.0.

Conclusion 

While there are many advantages of applying Microservices architecture either for a greenfield or a brownfield project, be prepared to pay “microservices tax” due to the various drawbacks mentioned earlier. 

Paying the tax could however lead to a cleaner, agile and scalable system.

References
Wikipedia: 

Microservices.io

Apache Kafka: What sets it Apart?

+ No comment yet
In this edition, Rahul (rahul dot amodkar at indexnine dot com) looks at the advantages of Apache Kafka, the distributed commit log messaging system, that make it a leading messaging solution when it comes to large scale streaming data processing applications. 

final-ADVANTAGE - Copy - Copy.png

Each day, large amounts of data and metrics are collected from real time activity streams, performance metrics, application logs, web activity tracking and much more. Modern scalable applications need a messaging bus that can collect this massive continuous stream of data without sacrificing good performance and scalability. Apache Kafka is built ground up to solve the problem of a distributed, scalable, reliable message bus.

Introduction

Apache Kafka is a distributed messaging system originally built at LinkedIn and now part of the Apache Software Foundation. In the words of the authors : “Apache Kafka is publish-subscribe messaging rethought as a distributed commit log”.

While there are many mainstream messaging systems like RabbitMQ and ActiveMQ available, there are certain things that make Apache Kafka stand out when it comes to large scale message processing applications.

Performance

A single Kafka broker (server) can handle tens of millions of reads and writes per second from thousands of clients all day long on modest hardware. It is the preferred choice when it comes to ordered durable message delivery. Kafka beat RabbitMQ in terms of performance on large set of benchmarks. According to a research paper published at Microsoft Research, Apache Kafka published 500,000 messages per second and consumed 22,000 messages per second on a 2-node cluster with 6-disk RAID 10.

consumer.jpg
Source:  research.microsoft.com

Scalable

In Kafka, messages belonging to a topic are distributed among partitions. A topic has multiple partitions based on predefined parameters, for e.g all messages related to a user would go to a particular partition. The ability of a Kafka topic to be divided into partitions allows the topic to scale beyond a size that will fit on a single server. The concept of dividing a topic into multiple partition allows Kafka to provide both ordering guarantees and load balancing over a pool of consumer processes.

Another aspect that helps Kafka to scale better is the concept of consumer groups (a collection of message subscribers/consumers). The partitions in the topic are assigned to the consumers in the consumer group so that each partition is consumed by exactly one consumer in the group. This mechanism aids in parallelism of consuming the messages within a topic. The number of partitions dictates the maximum parallelism of the message consumers.
                   

Distributed By Default

Kafka is designed very differently than other messaging systems in the sense that it is fully distributed from the ground up. Kafka is run as a cluster comprised of one or more servers each of which is called a broker. A Kafka topic is divided into partitions using the built-in partitioning. It has a modern cluster-centric design that offers strong durability and fault tolerance guarantees. The Kafka cluster comprised of multiple servers/brokers can be spread over multiple data centers or availability zones or even regions. This way the application would be up and running even in a disaster scenario of losing a datacenter.

Replication

Kafka partitions are replicated across a configurable number of servers thus allowing automatic failover to these replicas when a server in the cluster fails. Apache Kafka treats replication as the default behaviour. This ability of the partitions to be replicated makes the Kafka messages resilient. If any server with certain partitions goes down, it can easily be replaced by other servers within the cluster with the help of the partition replicas.

Durable 

Messages are persisted on disk for a specific amount of time or for a specific total size of messages in a partition. The parameters are configurable on a per topic basis. One can choose to persist the messages forever. The time to be persisted is configurable per topic.

Guaranteed Ordered Messages

Kafka guarantees a stronger ordering of message delivery than a traditional messaging system. Traditional messaging systems hand out messages in order, but these messages are delivered asynchronously to the consumers. This could result in the message getting delivered out of order to different consumers. Kafka guarantees ordered delivery of messages within a partition. If a system requires total order over messages then this can be achieved by having a topic with no partitions. But this comes at the cost of sacrificing parallelism of message consumers.

Industry Adoption

Apache Kafka has become a popular messaging system in a short period of time with a number of organisations like LinkedIn, Tumblr, PayPal, Cisco, Box, Airbnb, Netflix, Square, Spotify, Pinterest, Uber, Goldman Sachs, Yahoo and Twitter among others using it in production systems.

Summary

Apache Kafka is an extremely fast and scalable message bus that supports publish-subscribe semantics. Due to its distributed and scalable nature, it is on its way to becoming a heavyweight in the scalable cloud applications arena. Although originally envisioned for log processing, IoT (Internet of things) applications will find Kafka a very important part of architecture due to the performance and distribution guarantees that it provides.



Want to learn more about how to build scalable applications? Please take a look at our Building Scalable Applications blog series Part I, Part II and Part III.

Building Scalable Applications: Handling Transient Failures in Cloud Applications

+ No comment yet
In this edition, Rahul (rahul dot amodkar at indexnine dot com) looks at an approach to handle the transient failures encountered in cloud platform deployments. We will look at a mechanism that can be employed to take care of transient failures without losing data or impacting user experience.


SMART-WAYS.jpg

While there are numerous advantages of deploying on the cloud, there are no guarantees that the cloud platform services will respond successfully every time. In a cloud environment periodic transient failures should be expected. The approach should be to minimize the impact of such failures on the application execution by anticipating and proactively handling failures.

The Problem With Cloud Services

The cloud is made up of hardware components that work together to present a service to the user. Since hardware is subject to failure, cloud services cannot guarantee 100% uptime. Small failures could cascade and affect multiple services, all of some of which could be down for brief periods of time.

When the consumer’s use of the cloud service exceeds a maximum allowed throughput, the system could throttle the consumer’s access to the particular service. Services deploy throttling as a self-defense response to limit the usage, sometimes delaying responses, other times rejecting all or some of an application’s requests. The onus is on the application to retry any requests rejected by the service.

What Needs To Be Done

Cloud applications need to retry operations when failures occur. It does not make sense to retry the operation immediately because most failures should be expected to last for a few minutes at least. We will consider a scenario where the database service is unavailable for a short time.

2.png
The figure above shows the flow of the operation which encounters a transient failure and the possible approach to recover from it. The message sent by the web instance is added to the primary message queue. This message is picked up for processing by the worker instances.

When the worker tries to write data to the database tier, it encounters a failure. In this case, the worker adds the data to be written to a “Deferred Processing Queue”.

A scheduled job runs every 5 minutes, listens to the “Deferred Processing Queue” and consumes the messages from this queue for processing. The scheduled job reinserts the message into the primary message queue where it is read by the worker instances and processed successfully if the database service is available. If not, the same process is followed again. This allows the cloud application to process the messages at a deferred time, and makes it resilient to failures.

Summary

Transient failures are a common phenomenon in cloud applications. A cloud-based application is expected to withstand such transient failures and prevent loss of data even under extreme circumstances.

Building Scalable Applications Part 3 : Cloud features that help you scale

+ 1 comment
In this edition, Rahul (rahul dot amodkar at indexnine dot com) looks at a typical cloud architecture that can support many types of applications. We will examine each tier and explore concepts such as load balancing and auto-scaling.

In previous posts, we covered the benefits of moving to the cloud for scale and the design principles of building scalable applications. In this post, we will look at a typical reference architecture for a cloud based scalable application and cloud features that help your application scale.

Architecture

The below listed figure illustrates a reference architecture commonly used by scalable applications. The architecture has the following usual suspects that allow it to handle high traffic and load patterns experienced by typical scalable applications:

  • Load Balancer(s) for the web tier.
  • Auto-scaling for web and worker instances.
  • Message queue
  • Cache
  • SQL and/or NoSQL database(s).


indexnine FINAL.png


Load Balancer

Load balancers are used to distribute load on application services across multiple systems. This allows the application to scale horizontally, and allows the application to add more capacity transparently in response to demand. By fronting application services using a simple proxy, a load-balancer provides clients a single internet location to communicate with, while fanning out  backend processing across multiple service instances. A load balancer can be used for any service within the application as needed. Most commonly a load balancer is used to front web instances.

Load balancers typically use a round-robin algorithm to route traffic to the service layer. In advanced configurations, they can also be configured to control the volume of traffic routed to different instances.

Sticky Sessions

For legacy applications, load balancers also support session stickiness, which means the load balancer routes all requests for a given session to a single instance. This can be used in case the concerned tier is not stateless. Since implementations of load balancers vary, consult the respective documentation to understand how sticky sessions are handled.

Most cloud vendors have load-balancing available as-a-service. On Amazon Web Services, you can configure a load-balancer to front a set of EC2 instances that checks for health of the instances periodically and routes traffic to them only if they are healthy.

Web Tier

A stateless web tier makes it easy to support horizontal scaling. As discussed in part 2, REST web services are designed to be stateless and therefore conducive to scale horizontally. Some modern frameworks that allow rapid creation of web apps are Dropwizard, Spring Boot (java), node.js and express (javascript).

Auto-scaling

Since the web tier is consumer facing, it is normally set up to auto-scale. Most cloud vendors support autoscaling by monitoring certain characteristics of the instances to be scaled. On Amazon Web Services, it is possible to track CPU usage or network bandwidth usage of instances and respond to extreme events by scaling out (expanding) or scaling in (contracting). To avoid cost escalation, you can also specify a higher limit for the number of instances auto-scaling will spawn.

Cache

Caching drives down access times for most frequently used documents. Since documents are stored in-memory the response times for web apps can improve manifold by using a cache. For e.g. User information might hardly change during the course of a session, but related information such as user permissions and role information needs to be accessed frequently. Therefore, the user information and the related roles and permissions are prime targets to be cached. This will result is faster rendering of pages that need to assess permissions before displaying data.

Redis and Memcached are two of the most popular caches available. Amazon web services provides both the implementations in the Elasticache offering.

CDN

Content-Delivery-Networks or CDNs are used to deliver static artifacts such as pictures, icons and static HTML pages much faster than a regular “single-source” website. Cloud vendors provide access to CDNs that will replicate static resources closer to the customer, thereby reducing latency. This results in excellent load times especially if there are large images and/or videos being used on the site or application.

Amazon Web Services CloudFront automatically identifies and serves static content from the closest datacenter irrespective of the region the site is hosted in. This results in drastic reduction of latency and faster loading of web artifacts.

Message Queue

Asynchronous communication ensures that the services/tiers are independent of each other. This characteristic allows the system to scale much farther than if all components are closely coupled together. Not all calls in your system need to be asynchronous. You can use the following criteria to qualify a call as asynchronous communication.

  • A call to a third party or external API. 
  • Long running processes
  • Error prone/changed frequently methods.
  • Any operation that does not need an immediate action as a response. 

While you implement the message bus based asynchronous communication, you need to take care that the message bus can scale to the rise in traffic. RabbitMQ is a popular message-oriented middleware used by many applications. You can also consider ActiveMQ, Kafka and Amazon SQS as the other message queue options.

Worker Tier

Similar to the Web tier the worker tier too needs to support horizontal scaling. The worker instances are independent processes that listen to the message queue and process the message as they are received.

The message bus size is a good parameter to monitor for scaling such that the number of worker instances increases with the increase in the number of message in the queue. Amazon AWS Auto Scaling groups and Rackspace Auto Scale are some examples of auto scaling options available with the respective cloud platforms.

Database Tier

A typical Database Tier of a scalable application would make use of both NoSQL and SQL databases as each of these have their own advantages. NoSQL can be used in the following scenarios where:

  • A relational database will not scale to your application’s traffic at an acceptable cost.
  • The data is unstructured/"schemaless". The data does not need explicit definition of schema up front and can just include new fields without any formality.
  • Your application data is expected to become so massive that it needs to be massively distributed
  • The application needs fast key-value access.

You should avoid the use of NoSQL and use RDBMS databases in scenarios where:

  • Complex/dynamic queries are needed. Complex queries are best served from an RDBMS.
  • Transactions need guarantee of ACID (Atomicity, Consistency, Isolation, Durability) properties.

When it comes to NoSQL databases there are multiple options available like MongoDB, Cassandra, AWS DynamoDB and CouchDB. Google recently announced the availability of, Google Cloud Bigtable, a NoSQL database that drives nearly all of Google’s largest applications including Google Search, Gmail and Analytics.

Summary

We discussed the various components of a reference architecture that can be used to run different types of applications on the cloud. Each application will dictate the nature and scale of usage of these different components, but most applications will need all of these components to achieve scale. Most cloud vendors have managed service offerings that provide access to these components. 

Building Scalable Applications (Part 2): Design for the cloud

+ No comment yet


This is part 2 of our series of blog posts targeting building scalable applications on the cloud. In this post, Rahul (rahul dot amodkar at indexnine dot com) looks at 5 key design principles to follow while building a cloud-based application.

In the previous post, we covered the benefits of moving to the cloud for scale. In this post, we will look at some best practices to build applications for the cloud.

1. Use messaging to decouple components

One of the important principles of scalable application design is to implement asynchronous communication between components. To start with you can break down the application into multiple smaller services and deploy a message queue for communication between these services.  A simple example of this would be to have your application’s Web instances put messages into a queue which are then picked by worker instances.

Such a mechanism is very effective for processing user requests that are time consuming, resource intensive, or depend on remote services that may not always be available.
Decoupling using a message queue

Keeping the communication asynchronous allows the system to scale much farther than if all components were closely coupled together.  By increasing compute resources for the workers, larger workloads can be disposed of quickly and in parallel. From a user experience standpoint, there are no interruptions or delays in the activity flow improving the user's perception of the product.
Most importantly, this allows the web tier of the application to be lightweight and therefore support more users per instance of compute that runs in the cloud.

2. Be Stateless

In order to scale horizontally, it is important to design the application back-end such that it is stateless. Statelessness means that the server that services a request does not retain any memory or information of prior requests. This enables multiple servers to serve the same client. By being stateless (or offloading state information to the client), services can be duplicated and scaled horizontally, improving availability and catering to spikes in traffic by scaling automatically.

REST or Representational State Transfer is a good way to architect back-end services. REST lends itself nicely to a stateless protocol and adhering to the principles of REST provides rich integration possibilities as it is based on the well-known HTTP protocol. Good quality clients to use the protocol are available in all languages, making access to the API simple and open regardless of client language preference.

3. Cache aggressively

Caching improves performance by storing recently used data for immediate reuse. Application throughput and latency are typically bound by how quickly data and context can be retrieved, shared and updated. Add cache at every level from the browser, your network, application servers and databases. You can implement caching by leveraging Content Delivery Networks (CDN) especially for static data or by using caching solutions like Redis and Memcached. Cache aggressively in order to significantly increase the application’s performance and its ability to scale.

A fast, in-memory cache speeds up look-ups

Before adding elements to a cache, assess the following 2 things:

  • How often is this data accessed ?
  • How often does this data change ?

If data is accessed very often and changes rarely, then this type of data is a prime target to be cached.

4. Expect Transient failures

A large cloud application spans many compute containers (VMs), Messaging services (Queues), Databases and other services. Although many managed services on the cloud provide excellent availability, it is still possible that failures may happen.

Transient failures may manifest themselves as temporary service outages (e.g unable to write to db) or a slowdown in performance (throttling).

Failing operations due to transient failures is not a good option.  Instead, implement a strategy that retries the operation.  Do not retry immediately because outages in most cases may last for a few minutes or more.  Implement a strategy that delays the retry thus allowing the resource to recover. Implementing such smart retry policies will help handle busy signals or outright failures without compromising user experience.

5. Don't be chatty

Since a cloud application is spread across multiple component services, every interaction might involve connection overhead. This means going back and forth many times can be detrimental to performance and expensive in the long run.

Pay attention to how data is accessed and consumed. Wherever possible, process data in batches to minimize excessive communication. For user interfaces, try and cache objects that may need frequent access, or better still, query most data needed for a view upfront and cache on the browser side to improve performance and reduce round-trips.

When it comes to user interfaces, a pure object-based REST API may not be sufficient to provide all the data needed for screens (especially dashboards), upfront. Avoid the temptation to make multiple calls to the back-end. Instead, build an aggregation API whose data can be cached to improve performance.

Remember, on the cloud, innocuous looking functions may end up making a web-service call, adding overhead to your functionality.

Summary

Use these design principles while building your cloud application. These principles will help you build applications that can scale to meet demand by leveraging auto-scaling. By recognizing the pitfalls of the cloud environment and designing around it, you can build fault-tolerant apps that users are happy with.