Performance: returnas objects vs query

82 views
Skip to first unread message

John C. Bland II

unread,
Mar 29, 2011, 5:23:21 AM3/29/11
to ColdFusion on Wheels
I've been semi-open about my major problems w/ Wheels as of late. This post seems to show there is an architectural issue w/ using returnas="objects". This means we have a major flaw in our application which is causing some really bad performance. I'm amped to revamp the app though now so we'll see how well it performs.

Mind you this is purely from rough tests but it shows some serious performance issues w/ return as objects.

Setup:
1) I setup a Micro EC2 server [~600 mb of RAM] using the Ubuntu+Railo AMI
2) Removed Mango Blog code [kept WEB-INF]
3) wget [wheels download url]
4) unzipe [wheels download]
5) Create an empty controller
6) Change home route to new controller
7) Create a simple view

I was terribly shocked to find the server was performing super-well w/ 0 changes [less than 500ms]. It dispelled my concerns about using CFWheels in the future for high profile sites.

Now, the view was simple. The idea was pull from a table where the results were pretty horrible on a live site and compare. There are 1007 records in this table [which resides in an RDS db].

View:
<cfset data = model("mytable").findAll() />
<cfoutput query="data">
#title#<br />
</cfoutput>

I ran the Apache Benchmark Tool on it like so:

The results were ridiculously astonishing [in a great way]:
 50%    220
 66%    242
 75%    258
 80%    270
 90%    304
 95%    329
 98%    367
 99%    370
100%    424 (longest request)

Huh? This same query for just 25 results takes longer than that on the live site.

So I changed the view to returnas="array":
<cfset data = model("mytable").findAll(returnas="objects") />
<cfloop array="#data#" index="item">
        #item.title#</br />
</cfloop>
</cfoutput>

The results were ridiculously astonishing here as well [in a bad way]:
Average of 4-7s/request

I didn't even attempt to run ab on it. I knew it'd be terrible.

I did a third test just to see how returnas="struct" worked:
<cfoutput>
<cfset data = model("mytable").findAll(returnas="struct") />
<cfloop collection="#data#" item="item">
#data[item].title#</br />
</cfloop>
</cfoutput>

This was even better performance:
Average of 1.5-1.8s/request

All of this to show proof of returnas="objects" is evil. Steer miles away unless you are pulling a single item [whichi s an object]. I took a sec to peep the docs and below is what I found.

RTFM [F stands for freaking; lol]
We recommend sticking to this convention as much as possible because of the CFML engines' slow CreateObject()function. Be careful when setting returnAs to objects. You won't want to create a lot of objects in your array and slow down your application unless you absolutely need to.

---
John C. Bland II
Blog: http://www.johncblandii.com
Company: http://www.katapultmedia.com
Twitter: @johncblandii

Per Djurner

unread,
Mar 29, 2011, 6:24:35 AM3/29/11
to cfwh...@googlegroups.com
I agree, it's a dangerous path to go when you start returning objects for no good reason.

I have actually never used "returnAs" in my apps, I'm sticking to the defaults to 100%.
The only situation where you'd want to use returnAs="object(s)" is when you need to fetch multiple related objects because you plan on using them in a Nested Properties form.

Another common (but bad) reason people return multiple objects is because they want to run methods on them.
This would seem to make total sense at first glance of course but in a procedural/object-oriented hybrid framework like Wheels it's (in my opinion) a much better idea to use Calculated Properties, for example, to work around this limitation.

Sometimes I wish we'd never added the "returnAs" argument... this would've more clearly positioned / articulated Wheels as a hybrid framework versus one where you strive to deal with objects all the time.

/ Per

--
You received this message because you are subscribed to the Google Groups "ColdFusion on Wheels" group.
To post to this group, send email to cfwh...@googlegroups.com.
To unsubscribe from this group, send email to cfwheels+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/cfwheels?hl=en.

tpet...@gmail.com

unread,
Mar 29, 2011, 9:04:32 AM3/29/11
to ColdFusion on Wheels
john, thank you for taking the time to perform those tests. yes,
returning objects is going to be slow, but as you stated, this is a
problem with creating a large number of objects in CFML and not
necessarily a wheels problem.

per is on target (as always) with his statement that most people
probably return objects because they want access to a method that on
that object.

one thing i feel people forget about is using afterFind callbacks on
you models. afterFind callbacks let you specify logic in a method that
gets attached as columns when returning queries and properties when
returning objects. this can really help you to avoid having to return
objects in your application. obviously afterFind callbacks won't fit
every situation, but for the more common situations that people create
custom methods and calculated properties on an object, they can really
help out.

a perfect example is that most people will either create a method or
calculated property to concatenate a user's first and last name into a
fullname string. the issue with using a calculated property for
something like is that you have to write database specific logic that
isn't portable and creating a custom method forces you to have to
create an object in order for the method to be accessed. by creating
an afterFind callback you can avoid all of this and still have the
logic you need in both queries and object properties.

i use afterFind callbacks quite a bit in my models and i'm amazed by
them. i strongly feel that they're one of the little known, forgotten
features that make using wheels a pleasure.

On Mar 29, 6:24 am, Per Djurner <per.djur...@gmail.com> wrote:
> I agree, it's a dangerous path to go when you start returning objects for no
> good reason.
>
> I have actually never used "returnAs" in my apps, I'm sticking to the
> defaults to 100%.
> The only situation where you'd want to use returnAs="object(s)" is when you
> need to fetch multiple related objects because you plan on using them in
> a Nested Properties form.
>
> Another common (but bad) reason people return multiple objects is because
> they want to run methods on them.
> This would seem to make total sense at first glance of course but in a
> procedural/object-oriented hybrid framework like Wheels it's (in my opinion)
> a much better idea to use Calculated Properties, for example, to work around
> this limitation.
>
> Sometimes I wish we'd never added the "returnAs" argument... this would've
> more clearly positioned / articulated Wheels as a hybrid framework versus
> one where you strive to deal with objects all the time.
>
> / Per
>
> On Tue, Mar 29, 2011 at 11:23 AM, John C. Bland II
> <johncblan...@gmail.com>wrote:
> > ab -n 1000 -c 10http://my-ec2.com/?controller=api&action=index

Mohamad El-Husseini

unread,
Mar 29, 2011, 10:04:52 AM3/29/11
to ColdFusion on Wheels
Tony, I am coming into a similar situation like this now. I want to
show a game's rating. I have a table that keeps gameId, userId, and
rating.

a) Create a calculated property in the Game.cfc model that selects an
average of the ratings from the Ratings.cfc model.

b) Create a rating column that's denormalized and updated it at every
new rating.

For a single game, the calculated property is great. But what if I
want to show 50 games at a time?

On Mar 29, 10:04 am, "tpetru...@gmail.com" <tpetru...@gmail.com>
wrote:

Andy Bellenie

unread,
Mar 29, 2011, 10:06:47 AM3/29/11
to cfwh...@googlegroups.com
I'd stick with a calculated property. It's the fastest solution.

Chris Peters

unread,
Mar 29, 2011, 10:12:50 AM3/29/11
to ColdFusion on Wheels
The good news is that both afterFind() and calculated properties
produce the same result. But yes, I'd do the calculated property if I
didn't need for the logic to be cross-database.

On Mar 29, 10:06 am, Andy Bellenie <andybelle...@gmail.com> wrote:
> I'd stick with a calculated property. It's the fastest solution.
>

tpet...@gmail.com

unread,
Mar 29, 2011, 11:38:39 AM3/29/11
to ColdFusion on Wheels
for something like mohammads' problem, a calculated property would be
the best bet. remember that when using afterFind callbacks, only a
single row of the query is passed into the callback. when you have go
across multiple rows, that's where calculated properties come into
play.

another thing that you can do also is create a view within your
database to hold all the calculation logic and create a hasOne
association to it.

really this is a very good topic for a blog post since it would be
good to know all the different approaches to these sort of problem
using calculated properties, afterFind callbacks and database views.
when to use them and what the benefits and gotchas are of each
approach.

Brad Gunn

unread,
Mar 29, 2011, 1:45:21 PM3/29/11
to cfwh...@googlegroups.com, Andy Bellenie
Normalization is rather over rated. It's kind of like saying, if I can't do this exactly OOP, or exactly MVC...
You should do what makes sense, makes clients happy and doesn't cause performance issues just for the sake of 'by the book.'

Why calculate the average on the fly anyway? I would say cron job it, or run it during the create statement of the review.
When you start getting massive traffic and massive quantities of reviews, doing a database average would be painful.

Test

John C Bland II

unread,
Mar 29, 2011, 1:58:48 PM3/29/11
to cfwh...@googlegroups.com
And in that blog post...please provide some simple performance metrics. :)

John C Bland II

unread,
Mar 29, 2011, 2:17:49 PM3/29/11
to cfwh...@googlegroups.com
Normalization usually provides more detail and is needed beyond the "exactly OOP" reason. For ratings you can make sure the same person isn't providing multiple ratings, etc.

+1 for a cron/create statement to update a field. That would save some cycles for sure.

Brad Gunn

unread,
Mar 29, 2011, 2:22:56 PM3/29/11
to cfwh...@googlegroups.com, John C Bland II
John,

  The 'multiple ratings from 1 user' logic would be placed in the reviews table. It's up to that model to determine that use case scenario. You'd only place statistics in their own table or in the table they are closest to representing. If I had to calculate even 5% of the statistics I do on the fly, I'd clearly have to give up on many projects. I do real estate, calculating demographics amongst several other things is clearly a cron process... 

John C Bland II

unread,
Mar 29, 2011, 2:27:19 PM3/29/11
to cfwh...@googlegroups.com
I don't think I said anything to the contrary. :) Just saying a separate table has its uses beyond simply fitting the OOP model.

Brad Gunn

unread,
Mar 29, 2011, 2:53:04 PM3/29/11
to cfwh...@googlegroups.com
Well, right. I was just remarking that people will avoid doing something just because it doesn't fit a standard. If you want to get highly technical, the only thing OOP about wheels (coldfusion) is that it uses objects and inheritance. I write Java (wicket, JBoss) and it's a different planet. 

John C Bland II

unread,
Mar 29, 2011, 2:56:11 PM3/29/11
to cfwh...@googlegroups.com
Ruh roh...Java devs are hardcore OOP. Public properties is evil. Must...have...getXXXX. lol. :)

Brad Gunn

unread,
Mar 29, 2011, 3:18:09 PM3/29/11
to cfwh...@googlegroups.com, John C Bland II
Yes, that's how we develop. There are reasons for it, especially when you use wicket to create a stateful website. There is simply too much going on that it actually becomes much like an offline program that you treat as having multiple threads. It's overkill for simple projects.

John C Bland II

unread,
Mar 29, 2011, 3:20:54 PM3/29/11
to cfwh...@googlegroups.com
I'm a clean-POJO guy myself. When I first learned Java I tried to write like that for every language. Didn't quite work out that well. :)

Brad Gunn

unread,
Mar 29, 2011, 3:26:09 PM3/29/11
to cfwh...@googlegroups.com, John C Bland II
No it never does. Unless you of course move between systems languages. It works like that for Perl, Python, C variants, Java. Even PHP has some resemblance .. static methods and variables etc. Overloading, overriding.

If coldfusion exercised overloading, CFWheels would die because one of the nice things about it is directly passing parameters... e.x. : tag(class='', id='')... in Java you'd need a constructor for every single one of those types. OR you'd do.

TextField name = new TextField('htmlIdentifier');
name.attributeModifier('class', new Model<String>('myClass'),'');

=]. Makes CFX tags a breeze though. You choose the technology that best suits the needs of the project. 
Reply all
Reply to author
Forward
0 new messages