Using AttributeCollection To Manage Locking In ColdFusion
The other day, when I was demonstrating how to use an ordered struct as a fixed-size cache in ColdFusion, I had to synchronize access to the cache in order to limit the number of cache keys. Whenever I make a new demo, it gives me a chance to reconsider and experiment with different approaches. And, in the aforementioned demo, I used ColdFusion's attributeCollection
functionality to define my CFLock
tags. This felt like a rather elegant and easily consumable approach; and I wanted to showcase it more directly.
ColdFusion has so many subtle developer ergonomic luxuries, it can be hard to known which mechanic makes the most sense in any given situation. That's part of what makes CFML such a joy to write—you're always mixing and matching different techniques in an ongoing effort to strike the right balance between elegance, readability, and maintainability.
For example, in my previous demo (regarding the fixed-size cache), I defined my CFLock
attributes in the ColdFusion component's constructor function, init()
. In today's demo, I want to mix it up a little and use private methods to define the read and write lock attributes. By using methods, I gain a little more wiggle room in how things are implemented.
In the following ColdFusion component, I have two public methods: one for reading data and one for writing data. I don't actually read or write anything in this code; but, I do define the CFLock
tags that would synchronize access to an underlying data store.
You'll note that each CFlock
tag is defined using attributeCollection
, which is, itself, defined by a private method call, either synchronizedRead()
or synchronizedWrite()
:
component
output = false
hint = "I manage synchronized access to some sort of internal data store."
{
/**
* I read some data from the underlying synchronized data collection.
*/
public any function getSomeData() {
lock attributeCollection = synchronizedRead() {
// ... READ some data within the READ-ONLY lock.
}
}
/**
* I change some data in the underlying synchronized data collection.
*/
public void function mutateSomeData() {
lock attributeCollection = synchronizedWrite() {
// ... MUTATE some data within the EXCLUSIVE lock.
}
}
// ---
// PRIVATE METHODS.
// ---
/**
* I build the attributes for a read-only lock with the given timeout.
*/
private struct function synchronizedRead( numeric timeoutInSeconds = 1 ) {
return {
name: "ManagerLockForGreatGood",
type: "readonly",
timeout: timeoutInSeconds
};
}
/**
* I build the attributes for an exclusive lock with the given timeout.
*/
private struct function synchronizedWrite( numeric timeoutInSeconds = 1 ) {
// The only difference between the READ and the WRITE lock is the [type]. As such,
// we're going to use the read lock to define the base attributes and then simply
// override the type. This way, the lock name is only defined in one place.
return synchronizedRead( timeoutInSeconds ).append({
type: "exclusive"
});
}
}
What I love about this code is that it minimizes the "noise" added by the locking mechanics. If we're going to synchronize access to data, we need to have locking. But, the locking itself is just a distraction from what's actually taking place. By moving the lock attributes into method calls, each CFLock
is reduced to a single line of code; which allows the more important logic of the data access to be more prominent.
Also, by using private methods to define the CFLock
attributes, I'm able to have one method piggy-back off the other. In this case, the only difference between the readonly
lock and the exclusive
lock is the type. As such, I'm able to define the exclusive
lock by gathering the readonly
lock and then overriding the type
attribute.
The attributeCollection
feature is such a boon to CFML developers. Whether it's used to define tag attributes whole-hog; or, it's merely the base collection upon which other attributes are cascaded; it's just a wonderful developer ergonomic of the language.
Want to use code from this post? Check out the license.
Reader Comments
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →