Saturday, March 31, 2012

JCache on NoSQL MySQL Cluster 7.2 ( Memcached )

With the release of MySQL Cluster 7.2 and the support for the native Memcached API we can also use the HA cluster for NoSQL besides JPA, SQL.
In this blogpost I will try out this NoSQL feature with JCache ( alias JSR 107 or javax.cache , it will be part of Java EE 7 and it will also work in Java 6 ).  JCache defines a standard Java Caching API for use by developers and a standard SPI (“Service Provider Interface”) for use by implementers. Coherence of Oracle will also support JCache. For more information see Greg Luck's blog.

With JCache and MySQL Cluster alone we can't get this example running. We also need to have Memcached JCache provider.  Leen Toelen already did the hard work, he made one which uses spymemcached as memcache client. So for this we need to download his code at github, after this we also need to download the latest spymemcached jars.

The code of Leen will work with JCache version 0.4, so we need to download the 0.4 version jars at https://oss.sonatype.org/index.html#nexus-search;quick~javax-cache . You can find the source code at JSR107 github repositories. For the JCache provider you also need to download the CDI-API jar.

For more information on Memcached or MySQL you can read this great blog of clusterdb, he explains it really well, like
  • How to setup your MySQL and Memcached environment
  • What is memcached
  • How it works 
  • Let it work on your existing tables 
or you can read the MySQL Cluster 7.2 whitepaper which can be downloaded at MySQL.com

So we start by downloading MySQL Cluster 7.2 and configuring this cluster. I won't explain this here, there are a lot of great blogs or guides which can help you with this.

After we got the cluster running we need to create the memcached database.

Also we need to have at least 10 API or MYSQLD entries in the config.ini of the cluster. After this change you need to reload this config file with the ndb_mgmd daemon.


[NDBD DEFAULT]
NoOfReplicas=2
DataDir=/usr/cluster/data
DataMemory=80M
IndexMemory=18M

[MYSQLD DEFAULT]
[NDB_MGMD DEFAULT]
DataDir= /usr/cluster/data

[TCP DEFAULT]

# Management Server
[NDB_MGMD]
NodeId=1
HostName=172.16.0.20 # IP address of this server

# Storage Nodes
[NDBD]
NodeId=2
HostName=172.16.0.21 # IP address of storage-node-1
DataDir= /usr/cluster/data

[NDBD]
NodeId=3
HostName=172.16.0.22 # IP address of storage-node-2
DataDir= /usr/cluster/data

[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]


Then we need to create the ndbmemcache database

mysql -p < /usr/share/mysql/memcache-api/ndb_memcache_metadata.sql
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| ndbinfo            |
| ndbmemcache        |
| performance_schema |
| test               |
+--------------------+


We can start memcached on the cluster nodes (what you like  ) and it needs to connect to the NDB management service.


/usr/sbin/memcached -E /usr/lib64/ndb_engine.so -u mysql -e "connectstring=mgt.alfa.local:1186;role=db-only" -vv

you should see a output like  this.

17-Mar-2012 21:12:55 CET NDB Memcache 5.5.19-ndb-7.2.4 started [NDB 7.2.4; MySQL 5.5.19]
Contacting primary management server (mgt.alfa.local:1186) ... 
Connected to "mgt.alfa.local:1186" as node id 6.
Retrieved 3 key prefixes for server role "db-only".
The default behavior is that: 
    GET uses NDB only
    SET uses NDB only
    DELETE uses NDB only.
The 2 explicitly defined key prefixes are "b:" (demo_table_large) and "t:" (demo_table_tabs)
Connected to "172.16.0.20" as node id 7.
Server started with 4 threads.
Priming the pump ... 
Connected to "172.16.0.20" as node id 8.
Scheduler: using 2 connections to cluster 0
Scheduler: starting for 1 cluster; c0,f0,t1
done [0.677 sec].
Loaded engine: NDB Memcache 5.5.19-ndb-7.2.4
Supplying the following features: compare and swap, persistent storage, LRU
<49 server listening (auto-negotiate)
<50 server listening (auto-negotiate)
<51 send buffer was 126976, now 268435456
<52 send buffer was 126976, now 268435456
<51 server listening (udp)
<52 server listening (udp)
<51 server listening (udp)
<52 server listening (udp)
<51 server listening (udp)
<52 server listening (udp)
<51 server listening (udp)
<52 server listening (udp)


When we go to the cluster management console ( ndb_mgm)  and type show, we should see something like this

ndb_mgm> show
Cluster Configuration
---------------------
[ndbd(NDB)] 2 node(s)
id=2 @172.16.0.21  (mysql-5.5.19 ndb-7.2.4, Nodegroup: 0, Master)
id=3 @172.16.0.22  (mysql-5.5.19 ndb-7.2.4, Nodegroup: 0)

[ndb_mgmd(MGM)] 1 node(s)
id=1 @172.16.0.20  (mysql-5.5.19 ndb-7.2.4)

[mysqld(API)] 24 node(s)
id=4 @172.16.0.21  (mysql-5.5.19 ndb-7.2.4)
id=5 @172.16.0.22  (mysql-5.5.19 ndb-7.2.4)
id=6 @172.16.0.21  (mysql-5.5.19 ndb-7.2.4)
id=7 @172.16.0.21  (mysql-5.5.19 ndb-7.2.4)
id=8 @172.16.0.21  (mysql-5.5.19 ndb-7.2.4)
id=9 @172.16.0.22  (mysql-5.5.19 ndb-7.2.4)
id=10 @172.16.0.22  (mysql-5.5.19 ndb-7.2.4)
id=11 @172.16.0.22  (mysql-5.5.19 ndb-7.2.4)
id=12 (not connected, accepting connect from any host)
id=13 (not connected, accepting connect from any host)


We are ready to do some test in java and we start with spymemcached java library ( in the memcachedclient I connect to my two memcached servers and use the default port 11211).
Just create a MemcachedClient and do your set or get operations.




With this as result
2012-03-31 17:29:33.190 INFO net.spy.memcached.MemcachedConnection:  Added {QA sa=/172.16.0.21:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue
2012-03-31 17:29:33.205 INFO net.spy.memcached.MemcachedConnection:  Added {QA sa=/172.16.0.22:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue
2012-03-31 17:29:33.221 INFO net.spy.memcached.MemcachedConnection:  Connection state changed for sun.nio.ch.SelectionKeyImpl@10c832d2
2012-03-31 17:29:33.221 WARN net.spy.memcached.MemcachedConnection:  Could not redistribute to another node, retrying primary node for greetings.
2012-03-31 17:29:33.221 INFO net.spy.memcached.MemcachedConnection:  Connection state changed for sun.nio.ch.SelectionKeyImpl@47808199
There is no message
Process exited with exit code 0.

Run it again.

2012-03-31 17:30:47.662 INFO net.spy.memcached.MemcachedConnection:  Added {QA sa=/172.16.0.21:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue
2012-03-31 17:30:47.678 INFO net.spy.memcached.MemcachedConnection:  Added {QA sa=/172.16.0.22:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue
2012-03-31 17:30:47.694 INFO net.spy.memcached.MemcachedConnection:  Connection state changed for sun.nio.ch.SelectionKeyImpl@10c832d2
2012-03-31 17:30:47.694 INFO net.spy.memcached.MemcachedConnection:  Connection state changed for sun.nio.ch.SelectionKeyImpl@47808199
Hello World!
Process exited with exit code 0.

We can see that the greetings key with its value is stored into the database ( you can also configure that this key is stored in memory instead of the database )


When we change the  key to b:greetings then the value will be stored in the large demo table which optimized for bigger values ( max 3mb) .

From clusterdb blogpost.
By default, the normal limit of 14K per row still applies when using the Memcached API; however, the standard configuration treats any key-value pair with a key-pefix of “b:” differently and will allow the value to be up to 3 Mb (note the default limit imposed by the Memcached server is 1 Mb and so you’d also need to raise that). Internally the contents of this value will be split between 1 row in ndbmemcache.demo_table_large and one or more rows in ndbmemcache.external_values.



Now let's try the same with JCache.

First create a javax.cache.spi.CachingProvider file in the following folder META-INF\services
This file must contain the JCache provider class name, in this case  net.spy.memcached.jcache.SpyCachingProvider
Then we need to create a SpyCachingProvider, set the Java parameter and configure the CacheManager & Cache.


In combination with CDI you can also use annotations. Enable CDI and inject this bean in your class.


1 comment:

  1. Thanks for sharing your info. I really appreciate your efforts and I will be waiting for your further write ups thanks once again.

    ReplyDelete