Showing posts with label Scalability. Show all posts
Showing posts with label Scalability. Show all posts

Tuesday, April 19, 2011

Scalability Stories

'It is not the strongest of species that survives, nor the most intelligent. It is the one that is most adaptable to change' -- This statement is so emphatic in every slide !

http://www.slideshare.net/tackers/why-we-chose-mongodb-for-guardiancouk

In a nutshell, the N-tier System swiftly evolved from traditional View-Service-DB architecture to View-Service-Memcached-DB.

Memcached is a very good candidate for distributed indexing. Extract main id from Web Request and then find related associated index and making fast lookup from mysql storage.

Hibernate can be effectively tuned to handle large data set specially by leveraging terracotta big data.

But even if ORM hides complexities, DB has strong influence on domain model.
Still one needs to implement Complex Caching and Optimizations.
Memcached still need lots of writes to DB and Ehcache needs to write-behind the DB.

Facebook heavily relies upon customized php-thrift-memcached-mysql-cassandra power-packed combo !

Guardian Architecture highlighted a very simplistic idea ... carrying the JSON from backend to frontend and vice-versa. Json/Rest is the smooth replacement for heavyweight soap/Axis2 in middle-layer. Json such a nice forwardly-extensible structure naturally fitting into to javascript and python. Here comes MongoDB - the Document-oriented Database storing parsed Json documents. The best part is schema can be changed in runtime without any down-time.

JSON can define different classes as tagged documents, multiple docs part of same collection.

So where has the DB gone ? Vanished !

And there is Apache SOLR, Hosted in EC2 [Document Oriented Search Engine, (NO DB .. huh) ..] .. a super fast Read API.

Its a huge paradigm shift.

http://www.slideshare.net/tackers/why-we-chose-mongodb-for-guardiancouk

So far the simplest explanation of CAP theory .. http://www.slideshare.net/jboner/scalability-availability-stability-patterns

This is the best Applicability-Chart of CAP theory - slide 9 of http://www.slideshare.net/pacoid/hidden-gems-found-with-hadoop

Upcoming Event : Register at http://www.10gen.com/conferences/mongosf2011

Best Practices of Enterprise System Architecture


An Enterprise System should be Highly Scalable, Always Available, Easily Manageable, Fast Performer, Auto-Healer and Capable of Super Fast Searching and Intelligent Analysis through Machine Learning.


Scalability means the system does not downgrade even when load increases multiple times.
For example, when number of users of an ecommerce system increases heavily further leading to sudden increase in transactions per user, the system should still keep performing with same SLA and handle the traffic smoothly ensuring that business is not down.

One of the design choice for large-scale system is to scale out horizontally. Create one DB per Entity (User/ Item/ Product) and store only Key-Value in that table. Replicate the DB in various machines and implement a scheme for data-sharding.
For example, in the Modulo(Entity_Pky, 100) approch, User Bob with id 1000 will be located in machine #10 . So if number of users increases suddenly, new User DB will be created in new machine and the new user info will be inserted automatically.

There are other data-sharding policies like Range-based partitions, Lookup based approach, Read-Most-Write-Least Model.

SimpleDB can be effectively used to manage lookup-orieneted entity info.
This is an out-of-the-box offering from Amazon AWS as a hosted and managed solution administered by Amazon itself. SimpleDB does the heavy-lifting of multi-value data fetching for a key, batch operations, consistent reads.

Its better to delegate the tradional 'data management operations' like 'Relations, Transactions, Locks, Sequences, Constraints' to Application Layer as SimpleDB is meant for handling simple things !!   Such a typical example is eBay DAL layer.

Actually the System Architect should choose the Entities which are meant for mere lookup (like User, Item, Manufacturer, Order) and mark them as best candidates for SimpleDB items. Those entities will be then guarnteed to be highly available. Off course this pattern is not suitable for financial transactions in banking domain and rule-based complex events where every fetch query triggers   sub-query-based rules or procedures.

Discussing further details on implementing SimpleDB is out-of-scope here.

If we want to handle complex business transactions while updating Entity Info and manage intricate relatiuonships then we should take resort to Cassandra or mongoDB.

But we should remember that one needs to take care of Scalability in Application layer while adopting NoSQL Dbs.

 If the data are hierarchical in nature - we can leverage a Graph Database (MonogoDB) which is as opposed to conventional sql-db is a de-normalized graph processor.

Availability : Load-balancing and Clustering are standard practices for making applications available. Normally DNS Resolver routes to a pool of servers for a requested application and Load-balancer picks up one server.

Machines should be load-balanced in such a way the moving the user from one machine to another machine can be achieved easily without shutting down the system.

Fast Searching :

NoSql is a must for fast lookup of trillions of  Business Entity data and persisting time-critical entities by locking data storage row for a  negligible amount of time (contrary to traditional RDBMS), yet be able to write-through / broadcast write events to sub0system grids (Search Grid / Analytics Machine / Events Collector etc.)
Cassandra does a very neat job for that matter.

"The sparse two-dimensional “super-column family” architecture allows for rich data model representations (and better performance) beyond just a simple key-value look up.....
Some of the most attractive features of Cassandra are its uniquely flexible consistency and replication models. Applications can determine at call level what consistency level to use for reads and writes (single, quorum or all replicas). This, combined with customizable replication factor, and special support to determine which cluster nodes to designate as replicas, makes it particularly well suited for cross-datacenter and cross-regional deployments. In effect, a single global Cassandra cluster can simultaneously service applications and asynchronously replicate data across multiple geographic locations...."

Its worth paying for the learning curve and operational compexities in exchange of the 'scalability, availability and performance advantages of the NoSQL persistence model'....

In traditional DB, what happens if one of the node containing one of the table in a join condition is down !
Simple - now the whole application that depends on that join condition is unavailable !
Well there is no join condition in NoSql !! Cassandra is best for cross-regional deployments and scaling with no single points of failure.

Next question – does the NoSql gurantees data-consistency the same way Relational DB vouches for read-after-write consistency at the cost of Blocking the read untill Transaction is finished !

Well NoSql follows CAP theorem not ACID principle !
So if you think 'immediate consistency' need to be ensured for super-critical tasks like Bidding / Buying, better await till data committed before reading the latest data !  In certain cases of 'Eventual Consistency' like searching data where we expect fast response we may not await for latest data to availble but rather instantly display the pre-computed search result !!

Also there is no concurrency bottlenecks ! Replicas are mostly Multi-master Highly Available !
NoSql ensures Asynchronous Lock-based Reconciliation as opposed to Synchronous Lock-based Reconciliation bby SQL.

This means more work but saving time !! Using Message -Multicast do write-behind to replicated database / grid without taouching master db. Then synchronize with master DB after a specific time period. This saves lot of time spent in Index Arrangement and Sequential Updation.

Say you want to search – 'Blue Sony mp3 player' ! NoSql will hold the lock for a row only for couple of milliseconds as opposed to locking the entire table by RDBMS ! NoSql will just lookup the id of next entity (manufatures) against the id of the main entity (product) and move the mesage to the next table !! At the same time, it looks up the product requested by the next user !!  No Joins , Just Intelligent Routing !!  Handling millions of requests concurrently has never looked so esy before !!!

Which is for What ?

Memcahed : Static Key-Value Pair
Neo4j  : Network Graph Store
Hbase  : Row-Orieneted Sparse Column Store
MongoDB : (Key, Document) Storage.


Fast Data Analysis

Apache Hbase – best suited for Data Analytics, Data Mining, Enterprise Search and other Data Query Services.

If the main business is to mine PeraBytes of data and perform parallel range-queries and then combine the results through batch Map-Reduce (say Enterprise Search for Video Content), then one  of the defacto choice is Apache Hbase configured with haddop Jobs, using HDFS as a shared storage platform. Hbase comes with availability trade-off i.e. the persistent domain entities may not immediately available in the Search Result. The reason for this is huge Hadoop Map-Reduce jobs are performed parallely in offline mode so that main source of data is not hogged by Hadoop Jobs and is highly Scalable.

It should be noted that Hadoop is not meant for searching in real-time. Its actually an offline batch-processing system well-suited for BI analytics, data aggregation, normalization in parallel.
Hadoop provides a framework which will automatically take care of interProcess co-ordination, distributed counters, failure detection, automatic restart of failed process, co-ordinated, sorting and much more.

There is different stages like mapper, combiner, partitions, reducer etc, instead of we writing from the scratch, the framework takes care of it.

Erlang / Scala also provide out-of-the box parallel processing.

Its important to know that there are different tools based on hadoop that serve different purposes :

Hive : SQL query for MR
Pig : Scripting for MR
HBase : Low-latency, Big Table like database on hadoop
Oozie : Workflo Mgmt on Hadoop
Sqoop - RDBMS import / export
Zookeeper : Fast, distributed, coordination system
Hue : advanced web env. for Hadoop and custom applications

Here come the ultimate offering from Spring - http://www.springsource.org/spring-data

Apache has a solution for combining lucene with hadoop for blazing-fast document search.

Fast Cache :

Implementing a cold cache with minimum memory footprint (MySQL native memory, Memcached, Terracota Ehcahce) is absolutely important.

Application Server should not remeber the state of the Entity rather should just persist in DB. The metadata should be stored in Cold Cache.  The persistent pojo objects should never be cached in memory.

If queries are mostly read-only, very less updates - the mySQL native memory scores high ! MySQL InnoDB storage engine’s dictionary cache provides blazing fast data acceess. On demand, data blocks are read from disk and stored in memory using LRU chains or other advanced mechanisms(like MySQL’s Midpoint Insertion Strategy) .

In order to lookup the value for a key, Memcached clients issue multiple parallel requests to memcached servers where keys are sharded across those servers.
For a frequently changing data set , Memcahed is surely better than DB Cache

Probably the fastes cache on planet is - is Ehcache using Terracotta BigMemory http://blog.terracottatech.com/2010/09/bigmemory_explained_a_bit.html

Here directly the native byte-buffer of the OS is used bypassing main-memory.

A write-optimized data store. Something that aggregates the writes in RAM, and writes out generational updates. Take a look at Google's BigTable paper for a good description of this strategy.

A very good reference : http://research.yahoo.com/files/sigmod278-silberstein.pdf



Everything Asynchronous

All communications in every layer should be asynchronous to reduce the latency.

There are 2 types of latency.
1. user latency - how fast user gets back the control on web site
2. execution latency - how fast the execution takes place in backend

UI behavior should be completely Ajax and send the main events to queue and schedule for batch updates in offline mode. There is absoulutely no room for a wait_state i.e. all response should be immediate and non-blocking.

Common Flow :

-- Submit a job/task to a thread and return to the user immediately.
-- The thread should perform the operation in background (it may communicate to LRU cache optimized for concurrent access / Graph Structure / Iry-IIry master-slave replicatioed env / Map-Reduce based sturctures)
-- Once done with the opertaion it should return the result thru a CallbackHandler and client will get notification.

There are multiple patterns for Asynchronous Communications.

(1) Store all jobs in Event Queue. Select a queue based on a contract/ algo depending on type of task. Then use multiple event brokers/ listeners to handle the jobs from queue (thrid party ESB like Tibco / apache fuse/ apache camel / ...) can be used.
(2) Message Multicast - say user enters a new item in system. do not update the iry db immediately. rather send messages to pollers / searchers - that there is a 'New' event. Return to user immedialtely. Now the updater thread will update Iry db. Then searcher will behind the scene query Iry db / data source to find what has been just added and it will add it to its search grid !
(3) Batch Processing (schedule offline procesing). Identify which type of job requests can be scheduled for offline processing and do not need immediate attention !

Prallel Threading

Use Executor Service to effectively manage pool of threads - 
 Remember  a simple set of worker threads 'without the manager' - can simply lead to 
 (i) Resource Thrashing (each thread is expensive - execution call stack / context switching)
 (ii) Request Overload ..  if all requests are provided threads for execution
 (iii) Thread Leakage : sive of threadpool diminishes due to uncaught errors but requests are not served !
So there should be a proper manager to allocate threads either FixedThreadPool / ScheduledThreadPool / SingleThreadPool / QueuedThreadPool with proper RejectionPolicy.

This manager should also place the completed result in a non-blocking queue from where result can be taken off asynchrounously.

Thread should not wait to acquire locks - leverage advanced processor optimizations to use LATCH concept to lock/unlock threads at the same time. Locks should be acquired / released in any order.

Fork/Join and CountDownLatch are powerful concepts for running threads parallely.

You can download and try out the open-source Thrift which is a C++ Fwk for multi-threading and asynchronous processing of job


Auto-Healing and Self-Recovery :

Systems should be falut-tolernt and should know how to degrade gracefully in the scenario of unprecented load.

The JMX agents and other Robot Apps should continously monitor the system and gather intelligence to take the best desicion.

A high-degree of automation is requireed for easily recovering from failures and managing the system smoothly.

Normally automation is driven though Centrallized Logging System and Self-organization Artificial Intelligence. BI tools are used to analyze user experience and provide Best Match through continous inhouse experimentations.

Scalable systems are mangeable if new partitions can be added, DB instance can be horizantally scaled out and new application servers turned on without affecting users of the system.

Case Study :

 Twitter :

Twitter Example :
user SAM tweets -
> store tweet > iterate social graph
> split chunks into parallel jobs > prepend packet into its memcahced blob / (some other cache) - if not present in cache - talk to db / hadoop

user RAM who follows SAM - sees sam's tweet -
> read mysql blob from memcache (or other cache) > deserialze integers > sort > slice > probablistic truncation (fast but may not be all consistent).

Facebook Example :

Alex friend of bob - logs in 
> Web-tier Calls a C++ based Service (thrift)
> Thrift has the user-id of Bob and calls Aggregator to find all friends ids of Alex
> Aggregator in parallel calls the multi-feed leaves....  (each leaf node is one user for which their is one DB .. one DB .. has a key-value pair (uid, user-data) ... no traditional sql (this is like noSql graph db)
> feed result says  [Bob, Sam, Paul] - these  are all alexe's friends .... returns those ids  (multi-feed) - by calling all DB servers in parallel .. finds the Indexes ..
> Aggregator says ..now got  ids of 40 most interesting stories ....   It .. ranks them ... based on certain criteria .....
> For each Id, .. get the metadata (timestamp, user name, comment..) from memcached (in ur cache it could be any other cache) - parallel query on multi-core Fedorra
> Now web tier renders the data.





Hibernate known facts unknown traps

Why to use ORM ?
(i)   HQL offers joins and aggregate functions. HQL expressed using Domain Object properties rather than DB Columns and completely decoupled from DB Schema.  SQL power is leveraged at Domain Object level.
(ii)  Unlike iBatis, Hibernate abstracts underlying DB and Data Model.
(iii) Hibernate performs Change detection – via Snapshot Comparisons .
      Hibernate creates runtime proxies for persistent objects thru dynamic byte code generation using Javaassist. We can change it to CGLib.
(iv) Unlike jdo – Hibernate does not need to modify persistent objects to observe their state.
(v)  Unlike EJB, hibernate can be run as a stand alone tool outside jree container.           
 
SessionFactory and Session
The purpose of the Hibernate SessionFactory (called EntityManager in JEE) is to create Sessions, initialize JDBC connections and pool them (using a pluggable provider like C3P0).

A SessionFactory is immutable cache of compiled mappings (plus associations / inheritence / aggregations) for a single database.
It is built from a Configuration holding mapping information, cache information and a lot of other information usually provided by means of a hibernate.cfg.cml file or through a Spring bean configuration.

A Session is a unit of work at its lowest level - representing a transaction in database
lingo.
Session is not thread safe and is maintained as a threadlocal value.
When a Session is created and operations are done on Hibernate entities, e.g. setting an attribute of an entity, Hibernate does not and update the underlying table immediately. Instead Hibernate keeps track of the state of an entity, whether it is dirty or not, and flushes (commits) updates at the end at the end of a unit of work. This is what Hibernate calls the first level cache.

The 1st level cache
Definition: The first level cache is where Hibernate keeps track of the possible dirty states of the ongoing Session's loaded and touched entities. The ongoing Session represents a unit of work and is always used and can not be turned of. The purpose of the first level cache is to hinder to many SQL queries or updates beeing made to the database, and instead batch them together at the end of the Session. When you think about the 1st level cache think Session.              
 
How does Hibernate implement Lazy-Initialization for single-ended collection?
>> Hibernate3 generates proxies (at startup) for all target entities i.e. Persistent classes using runtime-bytecode enhancement (via CGLIB). Then enable them for many-2-one and one-2-many associations.

>> Hibernate uses a subclass of the original class and the proxied class must implement a default constructor with package visibility ...  so all persistent classes should have a default constructor

How to improve performance using natural-id ?
  
  
      
              .. should not be mutable ...
           .......
 
session.createCriteria(User.class)
  .add(Restrictions.naturalId()
     .set(“name”, “Bob”)
).setCacheable(true)
.uniqueResult();
***
Since we have mentioned that the fields used are natural keys, hibernate query cache is smart enough to understand that we can bypass the uptodate check and depend on the assembling logic for handling it properly.
 
How can I retrieve info about a collection without initializing it ?
Fetch the size of a collection :
((Integer) s.createFilter(collection, “select count(*)”).list().get(0)).intValue()
            >> retrieve a subset of a collection
                 : s.createFilter( lazyCollection, “”).setFirstResult(0).setMaxResults(10).list();             
 
How to benefit from Query-Caching ?
>>    Check query cache for the query
>> if results are found, check if they are latest (that is no entry in update timestamps table or one which predates the cache)
>> if they are not up-to-date then assemble the object (assembling involves creating the object from its primary key or group of columns from their values or other strategies)  
The query cache works something like this:  http://www.javalobby.org/java/forums/t48846.html
| ["from Person as p where p.parent.id=? and p.firstName=?", [ 1 , "Joey"] ] -> [  2 ] ] |
 
The combination of the query and the values provided as parameters to that query is used as a key, and the value is the list of identifiers for that query

How Query cache work with 2nd Level Cache ?
Query-Cache : The intention is to cache the results against the query (the sql along with the parameters and their values)..... 
We set hibernate.cache.use_query_cache = true
Then Hibernate creates 2 memory regions :
  Region1 : one holding cache query result sets
  Region2 : other holding timestamps of the most recent updates to queryable tables ...
>> it caches only id values and result of value type ...  in order to fetch the state of the actual        entities from 2nd level cache...


>> the safest invalidation logic is to mantain update timestamps for each table. When any value is looked up from the query cache, we would also check if any of the tables involved in this query have been updated since the results were cached, if they were, the safest thing to do is query the db again. This is from a very simplistic point what hibernate does. It maintains the timestamps in the update timestamp cache. The query results are cached with the query as its key in the query cache.
stores only the primary key for queries that return results of only one type.

>> A lot of heavily used data is cached at the second level. However, most of this data is looked up using its natural key, the second level cache however would store it using its primary key as the key, so we could go ahead and use the query cache to save this lookup.
             
 
 
few points regarding 2nd-Level-Caching
  *** The 2nd level cache is a process scoped cache that is associated with one SessionFactory. It will survive Sessions and can be reused in new Session by same SessionFactory (which usually is one per application).
 
**** The hibernate cache does not store instances of an entity - instead Hibernate uses something called dehydrated state. Hibernate 'dehydrates' query results and persistent objects into their primitive components and identifiers. Conceptually you can think of it as a Map which contains the id as key and an array as value. Or something like below for a cache region:
 
It stores this decomposed data in the L2 and query results cache, and on a cache hit, it rehydrates/recomposes them into the requested persistent object

{ id -> { atribute1, attribute2, attribute3 } }
{ 1 -> { "a name", 20, null } }
{ 2 -> { "another name", 30, 4 } }

If the entity holds a collection of other entities then the other entity also needs to be cached. In this case it could look something like:

{ id -> { atribute1, attribute2, attribute3, Set{item1..n} } }
{ 1 -> { "a name", 20, null , {1,2,5} } }
{ 2 -> { "another name", 30, 4 {4,8}} }

The actual implementation of the 2nd level cache is not done by Hibernate (there is a simple Hashtable cache available, not aimed for production though). Hibernate instead has a plugin concept for caching providers which is used by e.g. EHCache.
 
 
In well-designed Hibernate domain models, we should avoid direct many-2-many collections – and instead use – one-to-many associations with inverse=true ....  For these associations, the update is handled by the many-2-one end of the association ... 
 

How to get rid of out-of-memory with hibernate cache ?
>> Lessons Learned
If you use hibernate query caching, and actually want to use memory for caching useful results, and waste as little as possible with overhead, follow some simple advice:
                   Write your HQL queries to use identifiers in any substitutable parameters.WHERE clauses, IN lists, etc. Using full objects results in the objects being kept on the heap for the life of the cache entry.
                   Write your Criteria restrictions to use identifiers as well.
                   Use the 'smart' query cache implementation  to eliminate duplicate objects used in query keys. This helps a little if you use the ids in your HQL/Criteria, but if you still must use objects then it helps a lot.
   final Product product= ...;
** Don't do this
final String hql = "from Product  as product  where product.order = ?"
** Do this
final String hql = "from Product as product  where product.order.id = ?"
....   q.setParameter(0, order.getId());
 
 
final Query q = session.createQuery(hql);
q.setParameter(0, mate);
q.setCacheable(true);
 
Beware of fetch-mode=join .. pitfalls

Beware of duplicate values :
*** Join returning duplicate rows ..
Soln. Call method setResultTransformer() on your criteria object prior to execution with argument CriteriaSpecification.DISTINCT_ROOT_ENTITY.

*** If there are any mapped collections whose elements also contain mapped collections, the results of the query will not only be incorrect, the objects themselves will be incorrect. Each mapped collection may contain repeated elements.
 
 
 
Avoid N+1 problem :

Lazy Association should be enabled if we want to avoid infamous N+1 problem ! Otherwise we can turn on 'eager fetching' if the data associated with main query is smaller in size.

OpenSessionInViewFilter

We should effectively flush the session by overriding closeSession() of OpenSessionInViewFilter.
In case of Spring MVC we can use Interceptor over the Filter.
http://72miles.com/blog/spring-opensessioninviewinterceptor-vs-opensessioninviewfilter/

Other Tricks

If we specify 'unsaved-value' = null for primary key, then Hibernate will find out when to do insert and when to do update.

In case session is closed when lots of data getting loaded upfront, then we should implement
Open Session in View  (anti)pattern. Session will remain opened long enough to perform lazy associations.

Avoiding out-of-memory in cache

When making new objects persistent, you must flush() and then clear() the session regularly, to control the size of the first-level cache.
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
  
for ( int i=0; i<100000; i++ ) {
    Customer customer = new Customer(.....);
    session.save(customer);
    if ( i % 20 == 0 ) { //20, same as the JDBC batch size
        //flush a batch of inserts and release memory:
        session.flush();
        session.clear();
    }
}
  
tx.commit();
session.close();
Optimization for Large Data Sets

Query query = session.createQuery(....);
query.setFirstResult(RowsPerPage);
query.setMaxResult(PageSize);

Lightweight Data Pattern : Fetch only what you need
Scales for millions records.

Set Correct Transactional Boundary
Set @Transactional (readOnly = true) to avoid unnecessary dirty-checking by Hibernate Engine.

Use Hibernate Validator in Domain Model

Usage of Proper Caching Strategy

Either use or @Cache (usage=ConcurrencyStrategy.READ_WRITE)
Refer to a properly configured cache region from query.
query.setCacheRegion(“query.OrderCache”);


Multi-Tenancy Support in Hibernate 4
http://opensource.atlassian.com/projects/hibernate/browse/HHH-5697