Cluster Scope

July 6, 2008 · By Michael Offner · 19 Comments

The following description should help you to use the new cluster scope, introduced in Railo 3.0 (Beta). The description is based on MacOSX, but it should work for Windows and Linux as well.

Let's start. First download Railo Express 3.0 Beta from our Website at
www.railo-technologies.com/en/index.cfm?treeID=361

Copy the content of the downloaded zip to a folder of your choice, rename it to railo8888 and make a second copy named railo9999, so that you end up with to folders containing the railo express content.


Change to folder "railo9999".


Open the file server.xml and change the port definition on line 16 from 8888


to 9999.


Start both Railo application servers, (double) click start[.bat] in both folders (railo8888 and railo9999).


Two command prompts should open.


Now change to your browser and tip. http://localhost:8888/railo-context/admin/server.cfm


Enter a password for your server administrator.


Change to page Remote/Security Key.


Copy the Security Key.


Now open a second browser window and open the url. http://localhost:9999/railo-context/admin/server.cfm


Change to page Remote/Clients.


click on


Fill the form as follows (but use your server password and your security key), then save it.


Change to page Remote/Security Key.


Copy the Security Key.


Change back to the other browser (http://localhost:8888/railo-context/admin/server.cfm) and create the same client in this server administrator, but this time with the other security key and the server "http://localhost:9999".


You will now have a bidirectional connection between the two Railo application servers.

Go now to the webroot folder of one of the two Railo server, for example the railo8888 folder, and open the index.cfm file.


Remove the content and add the following code.
your are on:
<cfdump var="#cgi.SERVER_PORT#">
<cfset cluster["port"&cgi.SERVER_PORT]="Hello from "&cgi.SERVER_PORT>
<cfdump var="#cluster#">


Do the same for the other Railo server. If you changed the index.cfm file in railo8888 the change now the one in railo9999.


your are on:
<cfdump var="#cgi.SERVER_PORT#">
<cfset cluster["port"&cgi.SERVER_PORT]="Hello from "&cgi.SERVER_PORT>
<cfdump var="#cluster#">


Go back to the first browser and call http://localhost:8888/. You should see that you have requested railo 8888 and that this instance of the Railo server has written the key "port8888" to the cluster scope.


Open or go back to the second browser and request http://localhost:9999/. This time you are on the railo 9999 instance and you should see both values in the cluster scope, from port8888 and port9999.


voilĂ  you have it!

now stop one railo with stop[.bat], for example railo 9999


and call the other railo in browser
If you stopped railo9999, request http://localhost:8888/. You should see that the cluster scope still shows the two entries.


Because railo9999 is not running, railo8888 can not synchronize the cluster scope. Go to the Railo administrator of the railo8888 instance http://localhost:8888/railo-context/admin/server.cfm and go to Services/Tasks.


In the list you will see one open task, select it and open the detail view.


In the detail view you will see that Railo tried to synchronize the requests. The synchronization failed because the second Railo instance is not available.


Restart railo9999 again and call it in a browser. You will see that railo9999 already took the scope data from the other server instance.


Go back to Services/Tasks list in the Railo administrator. You will see that the open taks is gone.


When you invoke the cluster scope the first time after restarting the server instance, Railo asks the connected instance for the cluster data. This means that you don't need to wait for the synchronisation.

Tags:

19 responses so far ↓

  • 1 Mike Brunt // Jul 7, 2008 at 2:38 AM

    Michael, this looks very interesting and as soon as I get the time I will be setting this up in my Lab. I assume the clustering is within Railo itself in the same way that ColdFusion-J2EE handles things? What I mean is that there is still a need to cluster the external web server. I am a bit lacking in knowledge of Railo but I assume it must come with an Internal web server? If so, has anyone ever used this in Production or load -tested it to see how it might behave in such an environment?
  • 2 Raymond Camden // Jul 7, 2008 at 1:29 PM

    It would be cool if you didn't need to go to the admin to determine if the updates are failing. I should be able to - via CFML - to do something like:

    &lt;cfif clusterScopeFailed()&gt;
    Sorry, we need to kill the site. Our servers are fubared!
    &lt;cfabort&gt;
    &lt;/cfif&gt;

    You get the idea. If the admin knows that it wasn't able to update, then you should have programmatic access as well.
  • 3 Michael Streit // Jul 7, 2008 at 2:12 PM

    the short answer is, yes you can already!
    for exmple:
    &lt;cfadmin action=&quot;getRemoteClientTasks&quot;
    type=&quot;server&quot;   password=&quot;myserverpassword&quot; returnVariable=&quot;tasks&quot;&gt;
    &lt;cfadmin action=&quot;executeRemoteClientTask&quot; type=&quot;#request.adminType#&quot; password=&quot;#session[&quot;password&quot;&amp;request.adminType]#&quot; id=&quot;#data.ids[idx]#&quot;&gt;
    &lt;cfadmin action=&quot;removeRemoteClientTask&quot;
    type=&quot;#request.adminType#&quot;
    password=&quot;#session[&quot;password&quot;&amp;request.adminType]#&quot;
    id=&quot;#data.ids[idx]#&quot;&gt;
    Everything you can do in the Railo administrator (RA) you can to programticly as well. the RA is a (very) simple cfml app based on the tag &lt;cfadmin&gt; (and search and schedule tags), sorry there is no doc for the &lt;cfadmin&gt; tag at the moment, but it will definitly follow.
  • 4 Oscar Arevalo // Jul 7, 2008 at 2:37 PM

    Looks really cool... does the cluster scope accept any type of variable? (simple, array, queries, CFCs, etc) or is it like the traditional client scope in which only simple variables can be stored and you need to serialize/deserialize any complex variable?
  • 5 Streit Michael // Jul 7, 2008 at 2:52 PM

    at the moment only simple types are supported, the problem is not the sync, it is that it is very delicate to check if there are any changes in complex objects.
    for example:
    &lt;cfset cluster.mystruct[4].mycfc.getInnerCFC().setName(&quot;susi&quot;)&gt;

    but railo support serialisation for most of the types inside cfml, including array,struct,cfcs and java object (that are Serializable).
    for example:
    &lt;cfset ser=serialize(myCFC)&gt;
    &lt;cfdump var=&quot;#evaluate(ser)#&quot;&gt;
  • 6 Raymond Camden // Jul 7, 2008 at 3:36 PM

    Interesting. Of course, this is only a solution for a site where you have full control over the admin. (Which is typically the situation where you would have a cluster anyway.)
  • 7 Pete Freitag // Jul 7, 2008 at 6:04 PM

    @Ray, perhaps a better way would be to have an application/server level setting &quot;throwExcecptionOnClusterFailure&quot;, then you could catch the exception type and handle it as you desire, or just decide to ignore them.
  • 8 Raymond Camden // Jul 7, 2008 at 6:06 PM

    Hmm, that could work too. Or perhaps a method of App.cfc (onClusterFailure). I mean really you could handle it onError, but this feels like something that may merit it's own handler.
  • 9 Pete Freitag // Jul 7, 2008 at 6:09 PM

    Though, on second thought, after taking a closer look at the blog entry, I think the way it works is best for performance... you don't want to block and wait for the other cluster members to receive the value when you set a key, you want it to do that in another thread.

    If you had it throw the exception, you would have to wait for the operation to complete / or fail, that would be very slow.
  • 10 Raymond Camden // Jul 7, 2008 at 6:11 PM

    It could always through an error asynch, running onError (or some new method). No need for your code to wait.
  • 11 Pete Freitag // Jul 7, 2008 at 6:15 PM

    Yes, I like the onClusterFailure event method approach because that could run outside the scope of the request for maximum performance, and you could still possibly have a setting to make cluster scope blocking or non-blocking.
  • 12 Streit Michael // Jul 7, 2008 at 6:51 PM

    there is one thing that speak against this listener, the sync is executed async from the task spooler (same spooler used for mail and admin sync).

    the spooler tries muliple time to execute the task when fails (see above the detail view), if you for example restart one client all other client fails with sync, but after the client is available again, they send all data fails before, nothing lost no problem, no error. but when you have this listener you get a lot errors for nothing, you get the error but not the success message.

    why you wanna handle this error, when the error already is handled by railo.
    by the way, all tasks of a client are peristent and still exists after a restart.
  • 13 Raymond Camden // Jul 7, 2008 at 6:58 PM

    Are you saying your mechanism is perfect? ;) Are you saying it will try 1 million times to connect? I assume after a certain point it gives up. That is when you should notify the application.
  • 14 Streit Michael // Jul 7, 2008 at 7:43 PM

    your right, that's a possibilty invoking this listener only one time AFTER the last try, but the name of the listender should be more common, because the spooler is used for multiple type of task (cfthread-type-task,cfmail,cluster,admin-sync and in future pherhaps even for more) the name should be &quot;onTaskFail&quot; for example.
    the cluster scope and the other task types are not on application level, mean you can have multiple App.cfc with different name scopes. the listener should be in the Server.cfc, then &quot;onServerStart&quot; you can use the cluster scope as well.

    one thing i forget, even the last try fails the data are not lost, because they are still in pool can be executed manually or when the remote client is available again he pull the data and this info you should provided as well.
    pherhaps &quot;onTaskFail&quot; will be called after last try, but one minute later the server is running again and pull the data and everything is fine, should then a other method be invoked like &quot;onSuccessAfterFail&quot;
  • 15 Andrew Grosset // Sep 17, 2009 at 7:29 PM

    Could this be used to synchronise application variables across two servers not necessarily in a cluster eg forums.mywebsite.com on one server and mywebsite.com on an other server? The application struct I'm thinking of is dynamic/constantly changing and I need to find a way to ensure each server has the same application struct.
  • 16 Gert Franz // Sep 18, 2009 at 11:11 AM

    Well yes and no. We are working on something called &quot;applicationstorage&quot; that might solve exactly this problem. You CAN use the cluster scope in order to synchronize your application scope, but if the application scope is quite big it get's very slow. And in addition the cluster scope only contains simple values so you need to serialize the scope every time you want to synchronize it.

    Gert
  • 17 Andrew Grosset // Sep 23, 2009 at 2:14 AM

    &quot;applicationstorage&quot; - that's interesting. In the mean time I'm using memcache to synchronise some application variables across two different railo installs on my devlopment pc (railo express) so far it seems to be working!

    Andrew.
  • 18 Gert Franz // Sep 23, 2009 at 7:14 AM

    Andrew,

    may I ask how you do this?

    Gert
  • 19 Gert Franz // Sep 24, 2009 at 1:01 AM

    @all: this comment is only since I accidentially deleted Andrew's comment:

    Two servers: server A and server B.

    When user logs in to server A his session is also stored in memcache.
    On server A I use an &quot;onClick&quot; javascript url to call via ajax a function to check if user's session is current (logged in).
    If it is I create a string: getTickcount and users session.id and encrypt it and call it &quot;token&quot;. I return this to the calling page where a javascript function builds a form and places the token in the form and submits it to server B. In the onRequestStart of application.cfc (server B) if &quot;form.token&quot; is detected it is passed to a function that decrypts the token, checks the time difference (compares getTickcount is less than 500ms) and if the user does not already have a session on server B checks memcache for this users session if it is available then the session struct is copied from memcache to server B's session struct and the user is logged in on server B.

    One drawback to this: when you store something in memcache you give it an expiry time say 20 mins and this doesn't change unless you set the value to memcache again, compare this to a session value of 20mins: if you hit the server every 19mins the session will never expire so I just give memcache a longer expiry time.

    The same principal can be used to duplicate application variables across from server A to server B. As long as you update memcache whenever you change the application (or session) struct then you can keep the user synchronised across the two servers.

    I have not exhaustively tested this yet as I'm still developing/testing the idea, but so far its ok.

    Andrew.

    PS
    memcache for coldfusion has been developed by Jon Hirschi http://www.flexablecoder.com

    It can be downloaded here: http://cfmemcached.riaforge.org

Leave a Comment

Leave this field empty: