Code Cabin TeamCODECABIN_ is a software development company that builds innovative high quality online business solutions, and provides world-class support that exceeds customer expectations.

“We have found that having a thorough understanding of JS is crucial to delivering high end, user friendly plugins and themes.”

In this project we learn about how Code Cabin leveraged the WP API and Node Websockets to improve performance with their WP Live Chat plugin.

Project Author Credit: Dylan Auty & Nick Duncan

 

An Overview of WP Live Chat Support Plugin

WP Live Chat Support is a WordPress Plugin which allows for agents to chat to clients on their site. This allows chat agents to offer support and generate sales.

It is important that WP Live Chat Support is able to maintain a consistent connection without impacting site resources in a way that would negatively affect the agent/client experience.

Unfortunately as the chat solution is entirely self hosted, keeping our performance impact to a minimum has become more of a challenge to maintain over time. This has become even more prevalent where low level hosting plans were being used.

We found ourselves in a bind and needed to develop a solution that would still allow for all data to be stored locally (self hosted), while reducing the overall impact on resources. Additionally we felt we needed to ensure all users on this service are able to exchange messages in a truly real-time environment.

 

Sending Messages Out of the Box

Let’s take a look at how we would send a message to the server using our legacy system.

When a user sends a message, a simple jQuery AJAX request is made to the WordPress ‘admin-ajax.php’ file. The request looks something like this:

var data = {
  action: 'wplc_user_send_msg',
  security: wplc_nonce,
  cid: wplc_cid,
  msg: wplc_chat,
  wplc_extra_data:wplc_extra_data
};

jQuery.post(wplc_ajaxurl, data, function(response) {
  //We don’t do anything with the data here
});

As we can see this is a very simply AJAX request, which would then be handled by our AJAX hook for storage and delivery.
Although this system worked as intended and was not very resource intensive, this was not the only request made to the AJAX file. Additionally the legacy system featured a few jQuery AJAX loops in order to handle message delivery, and chat status tracking.

In fact, we ran a few benchmark tests and found each loop had the following impact on resources:

  • Chat Dashboard Loop (31mb approx. usage held for over around 28 seconds)
  • Visitor Chat Loop (31mb approx. usage held for over around 28 seconds)
  • Agent Chat Loop (31mb approx. usage held for over around 28 seconds)

As we can see this means the resource usage would increase by roughly 31-93 megabytes depending on the actions being performed. It is important to remember that our legacy system was highly optimized and would release resources within a second or two, however on lower hosting plans this would have a noticeable impact.

After we performed these tests, we felt we should develop a hybrid system which could reduce the overall usage by roughly 60%.

 

Customizing Messages with Node, WebSockets & WP API

We spent quite some time finding what we believed to be the best solution for our problem. First we attempted to reduce the resource load by altering all our AJAX request to use the WordPress Rest API. Although this did have some impact on performance, we felt there was still room for improvement.

After weeks of research we finally found a solution that brought all the right things to the table, Node JS with WebSockets. Using these technologies we developed a high speed message exchange server which would be responsible for delivering messages between two peers.

This means we would be able to provide a truly real-time chat experience. However there was the issue of maintaining the ‘Self Hosted’ term associated with our chat solution, as our users value the fact that they retain all their data.

We therefore felt that this ‘Hybrid’ system would need to be used only for message exchange, without ever storing any data permanently. After some further research, we found that the best solution would be to store messages asynchronously. This would mean that our high speed Node JS server would handle all the heavy lifting, while still allowing the user to retain all the data.

Let’s have a look at how our new system handles a message:

wplc_server.sendMessage(wplc_ajaxurl, data, "POST", 120000, function() {
  wplc_server.asyncStorage(wplc_ajaxurl, data, 120000); //Request Complete - Run the Async Storage Call
});

Note: Our new `wplc_server` object has various checks in place to determine whether it should send data via the Socket or the Legacy system instead. This allows us to ensure the chat is fully functional in all environments.

As we can see from the code above, once a message has been sent we run an Async-Storage function. Let’s have a look at that now:

var prepared_data = {
  action : wplc_node_ajax_action,
  relay_action : wplc_send_data.action,
  chat_id : wplc_send_data.cid,
  security : wplc_send_data.security,
  messages : JSON.stringify(wplc_node_async_array),
  wplc_extra_data: document.wplc_extra_data
};

jQuery.ajax({
  url : wplc_send_url,
  data : prepared_data,
  type : "POST",
  timeout : wplc_send_timeout,
  success : function(response) {
    wplc_server_log("ASYNC STORAGE = SUCCESS");
  },
  error : function(error, exception){
    wplc_server_log("ASYNC STORAGE = FAIL");
  }
});

Here we make a simple ajax request to our WP Rest API (or AJAX fallback) for data storage. This means we can remove the message from the Node JS server as it will be retained on the user’s site.

Lastly, let’s have a look at our Rest API handler, which is used to store the message in the user’s database:

/*
* Register the async storage route
*/
function wplc_api_node_routes(){
  register_rest_route(
    'wp_live_chat_support/v1',
    '/async_storage', 
    array( 'methods' => 'POST', 'callback' => 'wplc_node_async_storage_rest')
  );
}

/*
* Handle the async storage
*/
function wplc_node_async_storage_rest(WP_REST_Request $request){
  $return_array = array();
  $return_array['request_status'] = false; //Default to be returned if something goes wrong
  
  /* Some Security Checks are in place here*/
  if(isset($request['chat_id']) && isset($request['messages'] && isset($request['relay_action'])){
    $chat_id = sanitize_text_field($request['chat_id']);
    $message_data = json_decode($request['messages']);
    $chat_session = wplc_return_chat_session_variable($chat_id);
    $action = $request['relay_action'];

      if($message_data !== NULL){
        if($action == "wplc_user_send_msg"){
          foreach ($message_data as $message) {
            $message = sanitize_text_field($message); //Clean up date
            wplc_record_chat_msg("1", $chat_id, $message); //Record the message
            wplc_update_active_timestamp($chat_id); //Keep chat session alive
          }
          $return_array['request_status'] = true;
          $return_array['request_information'] = __("Success", "wplivechat");
        } else if ($action == "wplc_admin_send_msg") {
          //Similar Process for the admin
        }
      } else {
        $return_array['request_information'] = __("Message data is corrupt", "wplivechat");
      }
  } else {
    $return_array['request_information'] = __("Please ensure all data is set", "wplivechat");
  }
 return $return_array;
}

Our Rest API callback will pass the data from the request to our ‘wplc_record_chat_msg’ function which is responsible for storing the message within the database.

Once the Async-Storage call is complete, and the message has been delivered to the other side, the message is removed from the Node JS server. This means that message data is only stored on our Node JS server temporarily.

 

Project Ideas

An asynchronous storage solution such as the one use in WP Live Chat Support could easily be reused in other applications.

Let’s take a look at a few:

  • Form Processing
  • Newsletter Subscription
  • Usage Tracking
  • General Data Collection

When to implement this type of solution primarily comes down to a simple question. Will adding a background process improve the user’s experience? If you answered yes, then it is probably a good idea.

It is a good idea to start making use of the Rest API routes instead of default AJAX handling, as it is scalable and more efficient. Over time, we expect this to become an industry standard.

 

Share Your Work

Have an example project you have built based on this?

Let Zac know and we will feature it here!