Just a reminder that I'm doing an Adobe eSeminar tomorrow at 2pm on Transfer Caching Mechanisms
One of the most powerful features of Transfer is its highly
configurable, in-built caching layer that allows for significant
performance gains for a given application when configured correctly.
During this eSeminar we'll discuss caching concepts such as
'caching algorithms' and 'memory sensitive caching', so that as a
Transfer Developer, you'll have a better understanding of the
intricacies of Transfer.
Click here to register
Click here for World Times
As per usual, this is something that came out of my work with
Transfer, but is something that applies to any ColdFusion application that exists.
So in the context of ColdFusion, what exactly are we referring to when we say
Thread? Generally the first thing we think of is <cfthread>,
which executes some code on its own given Thread. But, we should also
remember that the original page that was executing, is its own Thread
as well. If we run a scheduled task, that is also it's own Thread.
Wikipedia defines a
Thread very well:
"A
thread in computer science is short for a
thread of execution. Threads are a way for a program to split itself into two or more simultaneously (or pseudo-simultaneously) running tasks..."
So when looking at Threads, we can consider:
- Any Coldfusion page execution,
- Any remote CFC execution
- Any Scheduled Task execution
- Any CFThread execution
To be its own Thread, because, it is!
So what defines a
Selfish Thread?
A Selfish Thread is a thread that takes up almost all of the CPU's processing, without allowing any other Thread to be able to utilise the CPU at all.
Some code like this, would be a good example of a Thread being selfish -
<cfscript>
for(counter = 1; counter <= 10000; counter++)
{
writeOutPut(counter & "<br/>");
}
</cfscript>
It's a very tight loop, and there is no waiting, or pausing, or 'room'
for any other processing to do anything else while this loop processes.
Now it should be noted, that a Selfish Thread may not necessarily be a
bad thing. In many instances, we want this loop to completed, without
waiting for any other Thread to interrupt it. But in cases when Thread
execution can go on for a long time, this can be highly disruptive to
an application, as nothing else can be done during that time.
The common CF solution I often see for this, is the scheduled task that
runs at 3:00am, so that it doesn't bother any of the users. This can
work perfectly well for many applications, but what if your application
is 24 hours? Or is something that has to run every hour, what do you do
then?
Before we get into this too much, I want to make note of something - managing Threads is bit
black magic, and a bit
trial and error. Since Threads are managed differently per OS, and there are differences per JVM, some of these techniques will work, and some will not, so make sure you
test everything thoroughly so you know that it is affective for your OS and JVM configuration.
The other thing to note, is that any Thread that is running, is an actual instance of th Java object
java.lang.Thread. If at any point and time we want access to the actual Thread object that the given process is running we can run:
currentThread = createObject("java", "java.lang.Thread").currentThread();
Will return a reference to the currently executing Thread object, which will be very handy as we move along.
The first thing we should look at, is <cfthread>. CFThread has a 'priority' attribute that can be set to 'HIGH', 'NORMAL' or 'LOW', which
should control
the level of priority that a Thread has. For example, a HIGH priority
thread should have processing precedence over a LOW priority thread.
For example:
<cfthread action="run" name="foo1" priority="LOW">
<!--- do some processing --->
</cfthread>
In reality, I've not seen this actually do much (in
my tests), and it does not seem to actually effect a Thread's
Thread.getPriority(),
which we will talk about later. That being said, there may be some
other mechanism under the hood, and its not going to hurt anything if
you choose to use it.
From here, we can look at setting a Threads priority, which can be
applied to any CF based Thread (i.e. pages, scheduled tasks, cfthread
etc). A Threads priority goes from 1 to 10, where 1 is the lowest
priority, and 10 is the highest. 5 is usually considered 'Normal'.
In theory, a lower priority Thread should give way to a high priority
Thread whenever the higher priority Thread requires CPU processing
time. As stated earlier, depending onJVM and OS, this may, or may not happen.
To set the Thread's priority, all you need to do is grab the current thread, like we did above and call:
currentThread.setPriority(2); //set it to a lower priority.
//do some processing...
Since ColdFusion tends to pool Thread (i.e. stores them for reuse), we
should reset the Thread's priority after we are done with it, so that
it doesn't stay that when it gets used to execute another piece of
code. e.g.
priority = currentThread.getPriority();
currentThread.setPriority(2); //set it to a lower priority.
//do some expensive, long running processing...
currentThread.setPriority(priority); //reset it
This way, when the Thread get re-used, the Priority is not set to
something that is inappropriate for the processing it is doing.
There are also mechanisms in Java that allow you tell the JVM when a good time is for the current thread to
yield to other threads that need to do some processing.
This simply
hints to the JVM that 'hey! now would be a really good time for me to pause for a second, if you wanted to do something else'. The JVM can totally ignore this if it chooses, and depending on OS and JVM, it may well do.
To do this, we call the static method
yield(), on java.lang.Thread, like so:
createObject("java", "java.lang.Thread").yield();
So we can now take our very selfish loop above, and do something similar to:
<cfscript>
for(counter = 1; counter <= 10000; counter++)
{
writeOutPut(counter & "<br/>");
createObject("java", "java.lang.Thread").yield(); //here is a good place to pause
}
</cfscript>
This is actually a very poor use of yield(), simply because in a
display, we would never want the server to pause when displaying some
data, but it displays how it works reasonably well.
The interesting thing is, yield() automatically resolves what the
current thread it is processing on, and works that way, rather than the
setPriority() method we saw above, which required us to use a specific Thread.
Quite probably the least useful, but the most consistent way of managing selfish threads, is by putting the thread to
sleep, which will allow other Threads access to the CPU while that thread is asleep.
This is the least useful, as
no matter what, the Thread
will pause. Nothing else may be happening on the server, but the
Thread will pause anyway, which can mean wasted cycles for whatever it
is you are doing.
That being said, this will always work, no matter what JVM or OS you are on, so there is a trade off.
There are three ways we can make the current thread sleep in ColdFusion,
In cfscript:
<cfscript>
sleep(1000);
</cfscript>
via a Tag
<cfthread action="sleep" duration="1000" />
And via Java,
currentThread = createObject("java", "java.lang.Thread").currentThread();
currentThread.sleep(1000);
Either way, in the above example, the current thread will pause for 1000 milliseconds.
In a real world example, there is no reason we can't combine these
techniques. If we had some processing we wanted to happen
asynchronously, but we knew it was going to take a while to complete,
we could do something like the following:
<cfthread action="run" name="foo1" priority="LOW">
currentThread = createObject("java", "java.lang.Thread").currentThread();
priority = currentThread.getPriority();
currentThread.setPriority(3); //set it to a lower priority.
for(counter = 1; counter <= 10000; counter++)
{
doSomethingExpensive();
createObject("java", "java.lang.Thread").yield(); //could pause here
}
currentThread.setPriority(priority); //reset it
</cfthread>
Which gives us multiple ways in which to tell Java to make sure that other Threads are able to access the CPU.
Next time you are looking at a long running, expensive process, you now have multiple options about how you want to manage it.
After a few minor delays, Transfer 1.1 Final is ready for final release.
A few bugs and fixes occured during the release candidate period, so make sure you upgrade to the final release if you are running the release candidate.
For those of you not familiar with the new features of 1.1, some of the highlights of 1.1 are:
Huge Performance Enhancements!
During Testing (on my machine), a 500 Object load has been reduced from an average of 8 seconds down to an average of 6 seconds. This is a speed increase of around 25%!. I've even seen Object loads of 500 objects as small at 2 seconds.
Even for the performance alone, it is worth upgrading!
Transfer Object Proxies
This of this like Lazy Loading, but on steroids!
With a simple new setting 'proxied' on onetomany, manytoone and manytomany elements like so:
<onetomany lazy="true" proxied="true">
...
<onetomany>
Objects in the collection will be loaded as Proxies of the real object, and their underlying data will be loaded on an individual basis upon request.
A very handy feature when dealing with large collections.
New Discard Algorithm
Before, if you had a configuration when A -manytoone->B, and B was discarded from the cache, both A and B would have been discarded.
Now in the new algorithm, if B gets discarded, A simply unloads its many to one, and stays resident in the cache. This ensures there is less unnecessary too-and-fro between the database and Transfer's cache.
Cache Monitoring and Reporting
While there is a new CachMonitor component that allows you to get fine grained reporting on what the cache is doing, to get a re-built cache report, it is as simple as:
<cfimport prefix="report" taglib="/transfer/tags/reports">
<--- Basic Report --->
<report:cacheReport monitor="#application.transferFactory.getTransfer().getCacheMonitor()#">
<--- Detailed Report --->
<report:cacheReport monitor="#application.transferFactory.getTransfer().getCacheMonitor()#" mode="detail" chartsize="300">
TQL Custom Tags
Big thanks to Elliot Sprehn for contributing these TQL custom tags, that make it super easy to do TQL queries, in a <cfquery> style!
For example:
<cfimport prefix="t" taglib="/transfer/tags">
<--- Do list operations --->
<t:query name="result" transfer="#getTransfer()#">
select
u.firstName, u.lastName
from
user.User as u
where
u.email like <t:queryparam value="%example.com" type="string">
</t:query>
<--- Do read operations --->
<--- Get a single record as a TransferObject --->
<t:query name="user" action="read" class="user.User" transfer="#getTransfer()#">
from
user.User as u
where
u.email = <t:queryparam value="user@foo.com" type="string">
</t:query>
Lots of bug fixes
The usual slew of bug fixes, improvements, and other various adjustments.
If you want more information on what got included in this release, and why to upgrade, I would highly recommend the 'What's new in Transfer 1.0 (and 1.1) ' presentation recordings, and also make sure you check out the Release Notes .
Transfer 1.1 download .
I've put together a short Google Forms survey on Transfer to get some consolidated feedback from the community at large on what people are doing with Transfer, what features they want in the future, and what can be done to improve it overall.
It isn't limited to just people who are using Transfer. If you are not, I also want to hear your reasons why, as hopefully it is something that can be improved upon in the future.
It shouldn't take you much longer than around fifteen minutes to complete, and I would really appreciate the feedback.
Transfer Survey
All,
This Month's CFUG is lined up!
Location:
NGA.net, Level 2, 17 Raglan St, South Melbourne
Map:
http://link.toolbot.com/google.com/73016
When:
16th of October, Meeting starts at 7:00, so get there before hand (doors
open at 6:30).
Agenda:
Recorded Presentation by Dan Wilson
Refactoring in ColdFusion
If you would like to know how to migrate an existing procedurally programmed application into an object oriented one, grab a chair and sit for a while. We'll discuss some sensible guidelines designed to help you make incremental changes towards OO nirvana. We'll also look at lots of code samples, we all like code samples, right?
If you are going to attend, please RSVP to mark (dot) mandel (at) gmail.com.
Only those that RSVP are eligible for the door prizes, so make sure you apply!
See the CFUG Melbourne Calendar at:
http://www.cfcentral.com.au/Events/index.cfm
Or add to your Google Calendar - search for 'CFUG Melbourne'.
As per usual, we'll grab pizza during the evening, so we have
something to scoff down!
Look forward to seeing you all there.
I came to the realisation the other day, that my blog has really
become almost totally announcement based. A series of presentation
announcements, eSeminars, ColdFusion User Groups, Code Releases and
various other 'hey! this is what is going on' posts.
It used to be chock full of weird and interesting things you could do
with ColdFusion, and combining with the Java layer that sits underneath
it to do even weirder and even more interesting things with that.
Sadly, I've been super busy over the past <insert long time
frame>, and I've let things slip.
That being said, I plan on returning to the original theme of this
blog, and start publishing some of the new things I've found out about
ColdFusion, and what can be done with it. I've got some interesting
ideas on article on ColdFusion and Threading, and also some more on
onMissingMethod and method injection and manipulation, so be prepared!
So what has kept me so busy? Quite a lot actually. In no particular order...
Consulting full time.
This
has been one of the biggest changes, and adjustments for me to make
over the past 6 months or so. That being said, it is going very well,
and I am keeping myself very busy (Almost
too busy!). I love working from home, and love the flexibility working for myself gives me.
I also find it is a really rewarding experience. Working for yourself
challenges you in a variety of ways, as you only have yourself to rely
on, and you are solely responsible to make it or break it.
While
mostly I've been doing Transfer related work, I've also been doing some
mentoring and memory leak analysis, all of which have been really
interesting. Coming from a background in which I have tended to work
on single projects for months/years at a time, having a variety of
undertakings going at once is a nice change.
Speaking at webDU
I didn't even blog about this, which was very remiss of me! But I went to
webDU, which was great fun. I gave a presentation on TQL, and although I had some projector issues, it seemed to go quite well.
Writing Transfer code
I also got very busy writing
Transfer
1.2 in my spare (?) time. I'm really getting excited about Transfer
all over again, and I think I have some really interesting ideas for
the future. The next version is almost mapped out in my head... but I
will wait until after the Transfer Survey before setting it in stone.
Writing DevNet Frameworks Article
Writing
articles always take far longer than you expect them to, but when they
finally get published, it feels really great. From the feedback I've
received the
Introduction to ColdFusion Frameworks has been well received, and given some people new to frameworks some perspective over the landscape.
Something Super Cool Special
Been spending some time doing something really, really cool, but I can't really talk about it yet...
Don't you hate it when people say that? ;o)
Technical Editing for FAQU
I've
also started technical editing for the Fusion Authority Quarterly
Update, which one was one of those random opportunities that show up
during a conversation I was having with Judith Dinowitz. Editing other
people's articles in an interesting endeavour, but (I find) much easier
than writing them in the first place ;o)
Went on Holiday
I actually hadn't had a proper holiday
in several years, and it has made a huge difference! Taking a solid
break for a few weeks has meant I've come back with a new passion for
writing code, and a variety of other things in my life. I always
forget how much change a holiday can bring.
So that's my life at the moment in a nut shell. Figured I would let people know what was going on.
I'm feeling pretty excited, because my first article for Adobe DevNet has finally been published.
An introduction to ColdFusion frameworks
It covers the differences between Model-View-Controller, Dependency Injection, and Persistence frameworks, and also gives a general overview of some of the most popular offerings that are available in ColdFusion right now.
There was a bit of a rush towards the end, and some pieces of it are not as perfect as I would have liked, but I hope that it serves as a way for people to enter into the ColdFusion framework world, gives them enough information so that they understand the differences between framework types, and also allow them to make an informed decision on which one to use based on their personal preferences and needs.