Adding a Cross Instance, Cross Restarts and Cross Application Cache to Node Applications on Oracle Application Container Cloud image 12

Adding a Cross Instance, Cross Restarts and Cross Application Cache to Node Applications on Oracle Application Container Cloud

In a previous post I described how to do Continuous Integration & Delivery from Oracle Developer Cloud to Oracle Application Container Cloud on simple Node applications: Automating Build and Deployment of Node application in Oracle Developer Cloud to Application Container Cloud. In this post, I am going to extend that very simple application with the functionality to count requests. With every HTTP request to the application, a counter is incremented and the current counter value is returned in the response.

image

The initial implementation is a very naïve one: the Node application contains a global variable that is increased for each request that is handled. This is naïve because:

  • multiple instances are running concurrently and each is keeping its own count; because of load balancing, the subsequent requests are handled by various instances and the responses will show a somewhat irregular request counter pattern; the total number of requests is not known: each instance as a subtotal for that instance
  • when the application is restarted – or even a single instance is restarted or added – the request counter for each instance involved is reset

Additionally, the request count value is not available outside the Node application and it can only be retrieved by calling the application -which in turn increases the count.

A much better implementation would be one that uses a cache – that is shared by the application instances and that survives application (instance) restarts. This would also potentially make the request count value available to other microservices that can access the same cache – if we allow that to happen.

This post demonstrates how an Application Cache can be set up on Application Container Cloud Service and how it can be leveraged from a Node application. It shows that the request counter will be shared across instances and survives redeployments and restarts.

image

Note: there is still the small matter of race conditions that are not addressed in this simple example because read,update and write are not performed as atomic operation and no locking has been implemented.

The steps are:

  • Add (naïve) request counting capability to greeting microservice
  • Demonstrate shortcomings upon multiple requests (handled by multiple instances) and by instance restart
  • Implement Application Cache
  • Add Application Cache service binding to ACCS Deployment profile for greeting in Developer Cloud Service
  • Utilize Application Cache in greeting microservice
  • Redeploy greeting microservice and demonstrate that request counter is shared and preserved

Sources for this article are in GitHub: https://github.com/lucasjellema/greeting .

Add (naïve) request counting capability to greeting microservice

The very simple HTTP request handler is extended with a global variable requestCounter that is displayed and incremented for each request:

image

It’s not hard to demonstrate shortcomings upon multiple requests (handled by multiple instances) :

image

Here we see how subsequent requests are handled (apparently) by two different instances that each have their own, independently increasing count.

After application restart, the count is back to the beginning.

Implement Application Cache

To configure an Application Cache we need to work from the Oracle Application Container Cloud Service console.

image

image

Specify the details – the name and possibly the sizing:

image

image

Press Create and the cache will be created:

image

I got notified about its completion by email:

image

 

Add Application Cache service binding to ACCS Deployment profile for greeting in Developer Cloud Service

In order to be able to access the cache from within an application on ACCS, the application needs a service binding to the Cache service. This can be configured in the console (manually) as well as via the REST API, psm cli and the deployment descriptor in the Deployment configuration in Developer Cloud Service.

Manual configuration through the web ui looks like this:

image

or though a service binding:

image

image

and applying the changes:

image

image

I can then utilize the psm command line interface to inspect the JSON definition of the application instance on ACCS and so learn how to edit the deployment.json file with the service binding for the application cache. First setup psm:

image

And inspect the greeting application:

psm accs app -n greeting -o verbose -of json

image

to learn about the JSON definition for the service binding:

image

Now I know how to update the deployment descriptor in the Deployment configuration in Developer Cloud Service:

SNAGHTML13e985e

The next time this deployment is performed, the service binding to the application cache is configured.

Note: the credentials for accessing the application cache have to be provided and yes, horrible as it sounds and is, the password is in clear text!

It seems that the credentials are not required. The value of password is now BogusPassword – which is not the true value of my password – and still accessing the cache works fine. Presumably the fact that the application is running inside the right network domain qualifies it for accessing the cache.

The Service Binding makes the following environment variable available to the application – populated at runtime by the ACCS platform:

image

Utilize Application Cache in greeting microservice

The simplest way to make use of the service binding’s environment variable is demonstrated here (note that this does not yet actually use the cache):

image

and the effect on requests:

image

Now to actually interact with the cache – through REST calls as explained here: http://www.oracle.com/webfolder/technetwork/tutorials/obe/cloud/apaas/node/node-accs-caching-basic/node-accs-caching-basic.html – we will use a node module node-rest-client. This module is added to the application using

npm install node-rest-client –save

image

Note: this instruction will update package.json and download the module code. Only the changed package.json is committed to the git repository. When the application is next built in Developer Cloud Service, it will perform npm install prior to zipping the Node application into a single archive. That action of npm install ensures that the sources of node-rest-client are downloaded and will get added to the greeting.zip file that is deployed to ACCS.

Using this module, the app.js file is extended to read from and write to the application cache. See here the changed code (also in GitHub https://github.com/lucasjellema/greeting/blob/master/app.js):

var http = require('http');
var Client = require("node-rest-client").Client;

var version = '1.2.3';

// Read Environment Parameters
var port = Number(process.env.PORT || 8080);
var greeting = process.env.GREETING || 'Hello World!';

var requestCounter = 0;

var server = http.createServer(function (request, response) {
  getRequestCounter( function (value) {
     requestCounter = (value?value+1:requestCounter+1);
     // put new value in cache  - but do not wait for a response          
     console.log("write value to cache "+requestCounter);
     writeRequestCounter(requestCounter);         
     response.writeHead(200, {"Content-Type": "text/plain"});
     response.end( "Version "+version+" says an unequivocal: "+greeting 
                 + ". Request counter: "+ requestCounter +". \n"
                 );
  })
});

server.listen(port);

// functionality for cache interaction
// for interaction with cache
var CCSHOST = process.env.CACHING_INTERNAL_CACHE_URL; 
var baseCCSURL = 'http://' + CCSHOST + ':8080/ccs';
var cacheName = "greetingCache";
var client = new Client(); 


var keyString = "requestCount";

function getRequestCounter(callback)  {
    client.get(baseCCSURL.concat('/').concat(cacheName).concat('/').concat(keyString),
        function(data, rawResponse){
            var value;
            // If nothing there, return not found
            if(rawResponse.statusCode == 404){
              console.log("nothing found in the cache");
              value = null;
            }
            else{
              // Note: data is a Buffer object.
              console.log("value found in the cache "+data.toString());
              value = JSON.parse(data.toString()).requestCounter;
            }          
            callback(value);
        }
     );
};//getRequestCounter

function writeRequestCounter(requestCounter) {
var args = {
        data: { "requestCounter": requestCounter},
        headers: { "Content-Type" : "application/json" }
    };
      client.put(baseCCSURL.concat('/').concat(cacheName).concat('/').concat(keyString),
        args,
        function (data, rawResponse) {   
            // Proper response is 204, no content.
            if(rawResponse.statusCode == 204){
              console.log("Successfully put in cache "+JSON.stringify(data))
            }
            else{
              console.error("Error in PUT "+rawResponse);
              console.error('writeRequestCounter returned error '.concat(rawResponse.statusCode.toString()));
            } 
        }
      );
}// writeRequestCounter

Redeploy greeting microservice and demonstrate that request counter is shared and preserved

When we make multiple invocations to the greeting service, we see a consistently increasing series of count values:

image

Even when the application is restarted or redeployed, the request count is preserved and when the application becomes available again, we simply resume counting.

The logs from the two ACCS application instances provide insight in what takes place – how load balancing makes these instances handle requests intermittently – and how they read each others’ results from the cache:

image

 

Resources

Sources for this article are in GitHub: https://github.com/lucasjellema/greeting .

Blog article by Mike Lehmann, announcing the Cache feature on ACCS: https://blogs.oracle.com/developers/caching-with-oracle-application-container-cloud

Documentation on ACCS Caches: https://docs.oracle.com/en/cloud/paas/app-container-cloud/cache/getting-started-caches.html

Tutorials on cache enabling various technology based applications on ACCS: https://docs.oracle.com/en/cloud/paas/app-container-cloud/create-sample-caching-applications.html

Tutorial on Creating a Node.js Application Using the Caching REST API in Oracle Application Container Cloud Service http://www.oracle.com/webfolder/technetwork/tutorials/obe/cloud/apaas/node/node-accs-caching-basic/node-accs-caching-basic.html#section4

Public API Docs for Cache Service – https://apicatalog.oraclecloud.com/ui/views/apicollection/oracle-public/cache-app/v1

Using psm to retrieve deployment details of ACCS application: https://docs.oracle.com/en/cloud/paas/java-cloud/pscli/accs-app.html (to find out how Application Cache reference is defined)