Reducing memory usage in a multi-tenant application

61 views
Skip to first unread message

Jeff Gladnick

unread,
Dec 27, 2011, 5:54:49 PM12/27/11
to Railo
We have an (modelglue) application that runs multiple websites for
dentists.

Problem 1: session variables

The basic, public facing aspect of the site that our customers'
customer's (dental patients) would see is pretty straightforward, we
cache most of it, and doesn't require logging in or session
variables. Our customers (Dentists), however, need to be able to
login to their website to make changes. As you can imagine, dental
websites don't get a ton of traffic, maybe a few hundred visitors per
month on average.

We currently assign session variables to ALL visitors in
Application.cfm, and this clearly seems wasteful. One option we
considered was removing session variables entirely and going to
encrypted cookie storage. Perhaps there is an easier way to only
assign session variables to those who need them (customers and admin
users like me)? Or a better idea?

How much of an impact on memory usage is this even having though? Am
I worried about a 1% problem?

Problem 2: Application variables

Current architecture stores each dental website into it's own
application scope. So all the cfcs in the model are init'd and loaded
and stored for each site. This keeps things nice and encapsulated,
but I am wondering how well this will scale. Perhaps it would be more
efficent to use a single application scope (single application name
for all sites) and then store the unique siteId into session or
somewhere else and pass that along with each request for data.

Again, the traffic pattern is very low, while the number of websites
is very high.

Any ideas or suggestions for my two problems?

Billie Blaze

unread,
Dec 27, 2011, 6:23:27 PM12/27/11
to ra...@googlegroups.com
I run a similar multi tenant app. I actually opted to put session into memcached for several reasons. It doesn't require sticky sessions in a clustered environment, I assign a uuid token in cookie and onrequeststart populate session into request.session variable.

For sites, I load the data into request.community also onrequeststart. Lots of potential for caching here as well

Jeff Gladnick

unread,
Dec 27, 2011, 6:25:50 PM12/27/11
to Railo


On Dec 27, 4:23 pm, Billie Blaze <b...@diffuseaudio.com> wrote:
> I run a similar multi tenant app.    I actually opted to put session into memcached for several reasons.   It doesn't require sticky sessions in a clustered environment, I assign a uuid token in cookie and onrequeststart populate session into request.session variable.

Interesting, do you still feel this is the best way to go with other
options now available?

>
> For sites, I load the data into request.community also onrequeststart.    Lots of potential for caching here as well

By this do you mean you load ALL the cfcs on each request into the the
request scope?

Billie Blaze

unread,
Dec 27, 2011, 6:43:16 PM12/27/11
to ra...@googlegroups.com
I like how the session stuff works save the fact its in request. Waiting for railo memcached sessions tho.

I meant only settings in app scope. My app scoped singletons all receive communityid as an argument so one instance handles all sites

Sean Daniels

unread,
Dec 27, 2011, 6:55:49 PM12/27/11
to ra...@googlegroups.com

On Dec 27, 2011, at 6:43 PM, Billie Blaze wrote:

> I like how the session stuff works save the fact its in request. Waiting for railo memcached sessions tho.


Waiting to implement them or waiting for them to be available in Railo? Because they already are, as of 3.3. Add the memcached cache provider extension (I believe this is currently beta), and then create a named cache ex. "sessionStore":

this.sessionManagement = true;
this.sessionTimeout = createTimeSpan(0,0,45,0);
this.setClientCookies = true;
this.sessionType = "CFML";
this.sessionStorage = "sessionStore";
this.sessionCluster = true;

Bob's your uncle. Only major caveat is objects in session scope must be serializable. I also do not believe onSessionEnd() fires, due to the fact that in a cluster which server would it fire on.

- Sean

Billie Blaze

unread,
Dec 27, 2011, 7:18:59 PM12/27/11
to ra...@googlegroups.com
I guess I didn't realize they were there already. No time to implement right now but good to know!

Jeff Gladnick

unread,
Dec 28, 2011, 12:55:17 AM12/28/11
to Railo
Ok so I just installed the latest update. I do not see memcached in
there, i see ramcache an ehcache lite. do i need to download
memcached separately? Can i just use ehcache or not as good?

Then i just:
1) create a new cache, ie: mysessioncache
2) change my cfapplication tag to use sessionStorage="mysessioncache"

Anything else?

Gert Franz

unread,
Dec 28, 2011, 3:20:43 AM12/28/11
to ra...@googlegroups.com
Nope... that's all... Works with several servers in a cluster as well...

As for the application scope, I would generate one single Application Scope
(or use the server scope) for them so that the number of redundant instances
are limited

Greetings from Switzerland
Gert Franz
 
Railo Technologies      Professional Open Source
skype: gert.franz         ge...@getrailo.com
+41 76 5680 231           www.getrailo.com


-----Ursprüngliche Nachricht-----
Von: ra...@googlegroups.com [mailto:ra...@googlegroups.com] Im Auftrag von
Jeff Gladnick
Gesendet: Mittwoch, 28. Dezember 2011 06:55
An: Railo
Betreff: [railo] Re: Reducing memory usage in a multi-tenant application

Judah McAuley

unread,
Dec 28, 2011, 1:52:15 PM12/28/11
to ra...@googlegroups.com
I agree with Gert that you should only create one single Application
Scope not only for performance reasons but also for stability.
Generally speaking, you will have objects that are either transient (a
Customer object for each customer, a User object for each user, etc)
and you'll have service objects (a factory for creating and persisting
Customer objects, etc). The service objects should (in most cases)
follow the singleton pattern...there should only be one of them for
the application. When you are creating multiple versions of these
objects in different application scopes you not only increase memory
usage, you also run the chance (albeit small) that the objects will
differ in some way, meaning that the factory for creating Users for
customer A might be behaving differently than for creating Users for
customer B, which can be a nightmare to debug. If you load one copy
of the service into a shared scope (application or server), then you
know that everyone is using exactly the same code.

In the grand scheme of things it probably isn't a really big deal, but
something to consider from an architectural perspective.

Cheers,
Judah

Jeff Gladnick

unread,
Dec 29, 2011, 4:01:07 AM12/29/11
to Railo
Right now, the way my model-glue application is setup, in the init()
function of the controller we load up each CFC in the application and
pass in the SiteId which correlates to the current customer site
they're viewing.

We could switch this to:

1) Same strategy, except a single application name. This would
require changing the function calls on practically every request to
the database so that the unique SiteId is also passed along. We'd
have to make hundreds of changes so the siteId was now an argument in
practically every single function in every single cfc (200-300 I'd
guess)
2) we could keep the CFCs the same, and just change all the references
in the controller to createObject as they need it. So we're creating
the object on the fly as we need it.

Which will be better for our purposes.

Keep in mind that:
1) 99% of the traffic is outside the admin system, and viewing cached
pages
2) traffic to each site is only 200-300 visitors per month on average.

Judah McAuley

unread,
Dec 29, 2011, 5:53:24 PM12/29/11
to ra...@googlegroups.com
Hmm, I think I'd have to look at your code to be sure on the best way
to do this. How are you setting SiteId now? Is it a manual setting in
Application.cfc in the application scope? Couldn't you basically keep
most of the code the way it is and use a dependency injection
framework (Coldspring or Wirebox or DI/1) to fetch and inject the
SiteId in a manner similar to what you are doing now but dynamically
instead of statically?

Judah

Jeff Gladnick

unread,
Dec 31, 2011, 6:31:16 PM12/31/11
to Railo
SiteId is pulled from the database based on the domain name they are
visiting.

so domain1.com = siteID = 1
domain2.com = siteId = 2
etc
etc

I'm not completely following you on the coldspring injection issue -
how would that work exactly?
Reply all
Reply to author
Forward
0 new messages