Living in a Cluster

The Broadchoice Collaboration Platform (BCP) is deployed on a cluster of servers and this has a number of interesting implications for the design of the application as well as the actual deployment process and file system structure used.

I'll be posting several entries on clustering considerations but I wanted to start with something that surfaced with the recent launch of this blog. BlogCFC is part of our standard SVN repository (I'll also be blogging about our source code control processes) and so it is also deployed to the same cluster as the BCP. Each server runs the same code from its own local file system. BlogCFC allows authors to upload images and files that are used as part of the blog entries. If you simply deploy BlogCFC onto each server in the cluster and then create a blog entry and upload an image, that image will be stored directly on the file system of the server on which your request is processed (in fact, the server on which your entire session is processed, since we use "sticky session"). The other servers in the cluster won't know about the image. For user requests that come in to that first server, the image will be served correctly. For user requests that come in to the other servers, the image will be missing.

The BCP has the same issue - it allows authors to upload CSS, images and documents - but we have to ensure that all these uploaded assets are available to all servers automatically and immediately. Our approach was to design the application in such a way that shared assets are stored in specific directory trees that contain nothing but shared assets. We have a NAS - Network Attached Storage - which is mounted to every server as /var/www/html. That contains a documents directory and a custom assets directory - into which all uploaded content is placed. Symbolic links are used to "map" those shared directories to the appropriate place in the deployment directory tree:

/var/www/production/lib -> /var/www/html/lib
/var/www/production/wwwportal/custom -> /var/www/html/custom
We deploy our source tree to /var/www/production (direct from SVN) with lib and custom ignored by SVN. Each server then shares the same uploaded content without needing to use different file paths to how we would deploy to a non-clustered server.

Tonight, I applied the same fix to BlogCFC, adding blog_images and blog_enclosures directories on the NAS and adding symbolic links back into the BlogCFC deployment tree (as wwwblog/images and wwwblog/enclosures respectively). We have not yet dealt with making blog.init.cfm cluster-safe or the XML file generated by the pod manager. For both of those, we actually keep the files under SVN and handle changes as part of a managed process (i.e., by tickets in Trac).

This is just one of many things that need to be considered when designing applications for clustered environments. I'll be blogging about other clustering issues over the next few weeks.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Thomas Messier's Gravatar Have you considered storing the files in the database instead? I've never gone that route myself but it does seem to have some advantages. Any pros/cons to that approach that you're aware of?
# Posted By Thomas Messier | 8/8/08 10:37 AM
Sean Corfield's Gravatar We allow users to upload very large files - DMG and ISO files - and I wouldn't want to be placing multi-gigabyte files in the database. To be honest, I don't like storing any files in the DB - it 'feels' wrong to me. I'll see what our DBA says about this...
# Posted By Sean Corfield | 8/8/08 12:27 PM
Adrock's Gravatar The database can slow the serving of files down a bit, but not as much as you'd think. Most databases (like Oracle) don't actually store large files in the same structure as the rest of the data. Rather they just store a reference to the file that points to the file system.

That being the case, file size doesn't have too much effect on storage and retrieval. What you have to worry about is the speed of transmission between the DB and web server. However, your applications could do some local caching to help speed things up. In affect, treating files the same way CF treats CLIENT vars. If BlogCFC needs an image it doesn't have on a particular node, then it pulls it down from the DB and caches it locally for the next time. The nice benefit of keeping everything in one place (the DB) is that you only need a single backup and replication strategy.
# Posted By Adrock | 8/8/08 12:52 PM
Lincoln Milner's Gravatar We do things a little differently on our cluster. When we have code changes for a web site, we push them to our "A" server, which has a sync program that syncs it with the "B" server in the cluster. Both servers are live (it's not a primary/backup failover type cluster) so that means that when production code gets sent out there is a brief (less than a few minutes) chance that changes haven't propogated.

For user uploads (of which there are very few that need mirrored) we always have the process FTP to both. So if a user is adding a PDF to the site's library, the upload process will, regardless of the server you're session is connected to, FTP the file to the same location in both places. We have error handling that will whack the file from the "A" server if the upload to "B" fails for any reason, so there is less chance of orphaned files living out there.

This works well for our two machine cluster (though we're going to be expanding in the near future I think), but it probably won't scale terribly well with larger clusters. But it's been fabulous for us thus far (absolutely zero issues in almost a year of production use).
# Posted By Lincoln Milner | 8/8/08 1:11 PM
Robert Xiong's Gravatar This is Robert Xiong. I work as DBA and also doing software integration at Broadchoice. As Adrock has pointed out, the main disadvantage of having files out side of the database is we have to back up the database as well as the external files, which is pretty easy to deal with.

Keeping files out side of the database makes the database small and very efficient - small indexes, load faster as we only need to load meta data. It also allows us to manage our file grouping, sharing, access permission etc much easier. We also use Google Search Appliance (GSA) to crow the content for search purpose. By having files grouped by account etc in file system, it make GSA configuration easier (what to crow, what not to crow, etc). With all file content in database tables, it would be very hard to achieve all these.
# Posted By Robert Xiong | 8/8/08 2:31 PM
Rupert Fiasco's Gravatar What about storing all data elsewhere, e.g. Amazon S3? Then you dont have to worry about backups. And there is less load on your own servers.

Granted, there have been some issues with S3 in the past (it was down for about half a day last month), but overall its great. We currently have upwards of 50GB of user photos stored on S3 and would not want to have to deal with managing and backing that up ourselves.
# Posted By Rupert Fiasco | 8/8/08 2:40 PM
Sean Corfield's Gravatar @Rupert, we do use Amazon S3 for a number of things (and Amazon EC2) and we'll be blogging about that in due course. Most corporate customers are still a little wary about having sensitive documents stored out there "in the cloud" tho' so that makes it an unacceptable option for our primary use case.
# Posted By Sean Corfield | 8/8/08 2:55 PM
Jason Fisher's Gravatar @Sean,

If I'm reading the note correctly, it sounds like you use SESSION in your apps rather than CLIENT. Do you run into any issues with sessions across the cluster and/or with AOL users? Years ago, once we were up to 3 or 4 servers in our production CF cluster, we went to all CLIENT vars for browser persistence, simply because a 'down' on one server would smoothly handle transition of a user from one server to the next with no loss of data or work. If I open a form on A, spend 10 minutes filling it out during which A locks up, and then submit the form, the hardware load balancer will automatically drop my post request to B (or C or D ...). At that point the app tries to validate, and, since the CLIENT structs are in the database and validated against the browser tokens, the form still posts fine on B and the user never knows that the server they were just on went off-line. No need to log back in and figure out where you were. I bring up AOL users simply because we had a number of public, non-tech sites we hosted and AOL used to (maybe still does) reserve the right to change a user's IP address during a session, which would totally kill things like SESSION-based shopping carts, etc. Again, use of CLIENT vars completely solved that one as well.

Thanks
# Posted By Jason Fisher | 8/8/08 3:43 PM
Sean Corfield's Gravatar @Jason, answering that will be a whole separate blog post. Suffice to say, I don't use client variables and never have. I use a hardware load balancer with sticky session and I do not use session replication. FWIW, macromedia.com and adobe.com operate the same way. More on sessions and clusters later.
# Posted By Sean Corfield | 8/8/08 4:24 PM
Jason Fisher's Gravatar Aha, got it. You create the session contract between the browser and the load balancer, rather than between the browser and the server. That would provide much the same result, except that you're allowing the LB to handle the user interaction, which is what it's best at anyway. Looking forward to the detailed post when it comes!

Thanks
# Posted By Jason Fisher | 8/8/08 4:28 PM
Doug Sims's Gravatar @Robert -
Small World - I think I may have worked with you in the past on projects at Marketron. Now you are working with all my coldfusion heros:)
Its good to see several ex-Marketron folks finally come to appreciate the technology that upper management wanted to migrate away from.....
# Posted By Doug Sims | 8/10/08 2:57 AM
Mike Brunt's Gravatar @Sean, this is a great piece where you are laying out the needs to think of clustering at the inception of application development. Our contention is that clustering should be considered from day one as it is almost inevitable that it will happen and as you and others point out here, there can be challenges which can have considerable impact down the road. One point that I would like to emphasize which does not relate to what you are doing at Broadchoice. It is not a good idea, in my opinion, to attempt a clustering approach which is not scalable as eventually this could cause problems. Time almost always costs more than hardware.
# Posted By Mike Brunt | 8/11/08 10:37 AM
Robert Xiong's Gravatar Hi Doug. It's nice to hear from you again. We certainly have a very talented CF team here.
# Posted By Robert Xiong | 8/11/08 3:15 PM
Mo Barger's Gravatar FWIW you can symlink other parts of your application stack to make for an easier management experience. For example, I symlink back to a share my apache binaies and JVMs; only the conf files are specific to the service.
This is using Sun Cluster global storage, attaching luns to a group of computers, which is a similar approach to NAS.
the one thing I find lacking in your post is your use of the word cluster. Above I gave one example of a cluster solution, and of course there is the ColdFusion approach to clustering. "Cluster" is to scalable computer what Kleenex is to the consumer - no longer a brand name but a general term for an object. But when it comes down to it, "cluster" needs a little more definition sometimes. :)
# Posted By Mo Barger | 8/20/08 3:04 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1. Contact Blog Owner