Friday, September 26, 2014

Tuning ActiveMQ for Performance

Recently I needed to optimize our ActiveMQ configuration for performance.

Let me explain our deployment, we had a Jetty based Java app with GWT for the UI. We use gwt-ActiveMq with long polling to send messages to the UI. We were using activemq for everything, for out SM design, or async event listener architecture. We have no plans to scale from more then one box and for us the performance is critical.

So things done were:
  • Non-persisten messaging. Its atleast 20x faster. This can be achieved by:
  1. Set the NON_PERSISTENT message delivery flag on your MessageProducer.
  2. Set the persistent=false flag in the  element of the Xml Configuration or on the property BrokerService.
  • Remove the network. We used vm:// as transport so that the entire queue is in memory. No need to go over the network as our broker is embedded in the same context as the producer. You would have to change the broker address in the web.xml of the server as you won't get messages on the UI.
  • Serialization is barrier to speed. We used vm as transport and configured objectMessageSerializationDefered to true. As we are using them on the same box, there is no point in serializing and deserializing the messages. This would allow us to use same hiberante objects across states. No need to clone and store them.
  • Set alwaysSessionAsync=true. This reduces the context switching. There is lots of context switching which occurs when message is wrote on the queue and read from it.
  • Set dispatchAsync=false. This again reduces context switching.
  • Set optimizeAcknowledge=true. All the messages are acknowledged. This allows a more optimized way of acknowledging  the messages. Need to explore more for exact implementation details.
  • Don't prefetch messages in StateMachine. In case of our StateMachines, we always had 1 message which goes from one state to another. So, here there is no point prefetching messages.
  • Prefetch Topic Listners. But with topics, its better to prefetch messages.
  • Switch off flow control. If flow control is On, then the slowest consumers dictate the speed of the message. In activeMQ, the messages go into a message store as it is dispatched, if flow control is on, then the dispatch is done only on acknowledgments from the consumers and flow rate is determined based on the acknowledgments received. 
Final Configuration:

<amq:broker id="broker" useJmx="false" persistent="false" useShutdownHook="true" schedulerSupport="true">
<amq:transportConnectors>
<amq:transportConnector uri="vm://localhost?async=false" />
</amq:transportConnectors>
</amq:broker>

<amq:connectionFactory id="jmsFactory" brokerURL="vm://localhost?async=false"
copyMessageOnSend="false" objectMessageSerializationDefered="true" alwaysSessionAsync="false"
dispatchAsync="false" optimizeAcknowledge="true" useAsyncSend="true" optimizedMessageDispatch="true"/>



Bibligraphy:

Entire details is available in the webinar:
http://download.progress.com/5331/open/adobe/prc/psc/perf_tuning_activemq/index.htm

Rest of the details are in the below links:
http://activemq.apache.org/how-should-i-use-the-vm-transport.html
http://activemq.apache.org/how-do-i-disable-persistence.html
https://code.google.com/p/gwt-activemq/http://activemq.apache.org/message-cursors.html 

Monday, September 22, 2014

How to configure google app engine when your site is deployed on Heroku and uses CNAME for naked domain?

When we configure Heroku App to our domain we configure it using CNAME records. This means two entry in our DNS records, one for naked domain or xyz.com and other for www.xyz.com.

Now, if you want to receive mails over email ids configured with same domain with google apps, gmail then you need to configure MX records for xyz.com. Please see the previous post on this topic .

But there is a problem here, as CNAME record has higher priority, your domain wont return MX rule and always return CNAME rule and MX record validation would fail on the Google Domains page.

So, how to solve it:


  • Have A records entry if possible, with heroku this is  not possible.
  • Remove CNAME record from DNS entry for xyz.com and configure google domain, naked redirect to www.xyz.com.
  • Remove CNAME record from DNS entry for xyz.com and use Alias from Domian Control Panel.
  • Remove CNAME record from DNS entry for xyz.com and use Domain Forwarding service for xyz.com to www.xyz.com on the Domain Control Panel. This would be like a 301 redirect.

Hope this helps.

Tuesday, September 16, 2014

JS Event Bubbling Vs Event Capturing.

In Javascript or HTML DOM to be precise, there are two methods of event propagation, those are:

  1. Event Capturing.
  2. Event Bubbling.
This defines the way event flow in case of multiple event listeners.
Suppose you have a img  element inside a div element then and both have an event listener defined, then whose event listener should be called first?

  1. In case of event bubbling, the inner most elements event listener will be called first and it would be propagated out. So first the event listener of img will be called followed by the event listener of div element.
  2. In case of event capturing, the outer most elements event listener is called first followed by inner elements. So, in above example,
    div's event listener would be called first followed by inner most element that is img's event listener.
The event propagation method can be selected by passing third optional boolean argument to addEventListener method of JS. The syntax is:
addEventListner(event,event_handler,usecapture)
The default value is false, hence, by default event bubbling is supported.

How to do this in JQuery?

You can't do this in Jquery. Jquery does not support this as not all browser support this method. And the goal of Jquery is cross-browser JS framework.
Browser older then IE8 and Opera 7.0 does not support event capturing method.

Wednesday, September 10, 2014

Twitter typehead.js basic example with token field.

A simple example for twitter typehead.js.

The server should return an array of type:

[ { name: "asas", value: "1234" }, { name: "asas", value: "121212" } ]


Include following CSS and js:

<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<link href="tokenfield-typeahead.css" rel="stylesheet">
<link href="bootstrap-tokenfield.css" rel="stylesheet">
     <script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
     <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
     <script src="http://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.10.4/typeahead.bundle.min.js" ></script>

     <script src="bootstrap-tokenfield.js" ></script>

Style ur typeahead:

      <style>
.tt-query,
.tt-hint {
    width: 396px;
    height: 30px;
    padding: 8px 12px;
    font-size: 24px;
    line-height: 30px;
    border: 2px solid #ccc;
    -webkit-border-radius: 8px;
    -moz-border-radius: 8px;
    border-radius: 8px;
    outline: none;
}

.tt-query {
    -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}

.tt-hint {
    color: #999
}

.tt-dropdown-menu {
    width: 422px;
    margin-top: 12px;
    padding: 8px 0;
    background-color: #fff;
    border: 1px solid #ccc;
    border: 1px solid rgba(0, 0, 0, 0.2);
    -webkit-border-radius: 8px;
    -moz-border-radius: 8px;
    border-radius: 8px;
    -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
    -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
    box-shadow: 0 5px 10px rgba(0,0,0,.2);
}

.tt-suggestion {
    padding: 3px 20px;
    font-size: 18px;
    line-height: 24px;
}

.tt-suggestion.tt-is-under-cursor {
    color: #fff;
    background-color: #0097cf;

}

JS for intializing typehead,  tokenizer and bloodhound:

 <script>
      $(function() {
      
  var engine = new Bloodhound({
  datumTokenizer: function(d) {
  return Bloodhound.tokenizers.whitespace(d.value); 
  },
  queryTokenizer: function(d) {
  return Bloodhound.tokenizers.whitespace(d.name); 
  },
  prefetch: '/contact/fetch?host=lifesize.com&user=bsaraf&password=testing!!123&term=S',
  remote: '/contact/fetch?host=lifesize.com&user=bsaraf&password=testing!!123&term=%QUERY'
});
 
engine.initialize();
 
   $('#exampleInputEmail1').tokenfield({
  typeahead: [null, {  source: engine.ttAdapter() }]
});
   $('#exampleInputEmail1').tokenfield();
   $(document).ajaxSend(function(event, jqXHR, settings) {
       console.log("Loading");
   $('.tt-hint').css('background-repeat', 'no-repeat');
   $('.tt-hint').css('background-position', '98%');
   $('.tt-hint').css('background-image', 'url("http://primefaces-rocks.appspot.com/design/ajaxloading.gif")');
   });

   $(document).ajaxComplete(function(event, jqXHR, settings) {
      console.log("Loaded.");
   $('.tt-hint').css('background', '');
   });
});
 </script>

And a simple html:

<input type="text" class="form-control" id="exampleInputEmail1" placeholder="Enter email">


And what you get is:




The Bloodhound docuementation is available at: https://github.com/twitter/typeahead.js/blob/master/doc/bloodhound.md#remote


I had to do dynamic change the url to fetch results in bloodhound based on some paramters.
This I achieved by:


var engine = new Bloodhound({
           datumTokenizer: function(d) {
                   return Bloodhound.tokenizers.whitespace(d.value)
           },
           queryTokenizer: function(d) {
                   return Bloodhound.tokenizers.whitespace(d.name)
           },
           prefetch: '/contact/fetch?host=lifesize.com&user=bsaraf&password=testing!!123&term=S',
           remote:{
                        'url' : '/contact/fetch?term=%QUERY',
                        'replace' : function(url,query) {
                                var q = '/contact/fetch?host='+$('#exampleInputHostName').val()+'&user='
                                                + encodeURIComponent($('#exampleInputUser').val())
                                                + '&password='
                                                + encodeURIComponent($('#exampleInputPassword1').val())
                                                + '&term='
                                                + query;
                                return q;
                        }
          }

        });


If you see the above code remote has a new replace method which fetched the values from input  fields and appends them to url. Note, here method takes query as argument to give the exact term typed in by user.


Friday, September 5, 2014

Cafes around Indranagar and Koramangala for Work or Chill

I found a nice discussion on PMIT with the requirements of Cafe suggestion in around Koramangala or Indranagar with following:

1. You spot less/same crowd lazing around the whole day. 
2. You spot people hanging out with their books.
3. Bean bags around.
4. Lite music, bright light, decent Wi-Fi.
5. Nobody bothers to wake you up (even if you doze off  ) 


So I decided to put my own list:


COSTA COFFEE, Koramangala

Address: 80 feet, Road Koramangala.



No bean bags. Has WIFI but unreliable. Less Crowded during Weekdays, very crowded during weekends. You can spots likes of Bansals of Flipkart fame at times here. Lite music bright light. One of my Fav to Work

COFFEE ON CANVAS, Koramangala

Address: 1st Cross, 80 Feet Main Rd, Koramangala 4 Block



Bean Bags. Has WIFI but unreliable, Seems to get noisy on weekends and evenings. Light music, bright light. Good food. One of my Fav to Chill

Atta Gallata, Koramangala

Address: 134, 1st A Main Road KHB Colony, 5th Block Koramangala



Has Wifi, quiet not too noisy, light music, less crowd. have events on weekends. Not that bright lights. One of My Fav to Work in quiet environment.

Yoga House, Indranagar

Address: 89, 11th Cross, 60ft Road,, Indiranagar 2nd stage


Bright Lights, Pet friendly, Very Fast and Reliable Wifi, Less Crowded. Apt for Work.




Wednesday, September 3, 2014

Download playlist or songs from 8tracks.

Many times I come across songs on 8tracks which I am not able to find from other sources. Little bit of googling and searching around, I didn't find a reliable way of doing the same. So, I decided to write a script for me to do that.

For the benefit of larger audience, I have hosted it on one of my servers. You can access it from:

http://beedio.com/8tracks.html



Just go there an paste the 8tracks playlist url in the input field and press Fetch.

Its a simple Js script which utilises the 8tracks apis to simulate the play of the song and allow a download or play link where the permissions are available. The urls to download or play individual songs would get listed.

As it simulates play of song, so this script would take some time to go and fetch all the urls for download. Ideally, one should press fetch and then come back after few hours to the tab to see all the urls in the list.

I have tried to follow the norms of 8tracks as much as possible, if any infringement of  copyright or api usage policy is found then please do let me know and I would take it offline.

Tuesday, September 2, 2014

Z-Index computed as auto even though it is set with a value on Chrome

This is a little known fact about z-index and usually missed.  If you read the article on w3schools then also you might miss this. It says:

Note: z-index only works on positioned elements (position:absolute, position:relative, or position:fixed).

What this essentially mean that the element on which you are applying z-index, you need to have position attribute specified on it. Ideally use one of the above position values. If position attribute in the css style is not set z-index would be computed as auto. Google Chrome is quiet strict about this. Use a syntax like below.

img {
    position: relative;
    z-index: 100;
}

Thats the reason, why many times we don't see z-index working on out elements. 

Sources:
http://www.w3schools.com/cssref/pr_pos_z-index.asp
https://code.google.com/p/chromium/issues/detail?id=39426