New ColdFusion Users Group

The group isn't technically new, but the focus is. The Tulsa Macromedia Users Group is now the Tulsa ColdFusion Users Group. The code for the site is freely available as well.

The Site 

Following the idea I got from Ben Nadel's Skin Spider, I am releasing the code as I work on it. The feature set is pretty thin right now. I can manage users, meetings, presentations and downloads. I want to add a forum (Ray's Galleon forum seems to have a visual conflict with the CSS on my site which I need to fix) and CMS (I have one that I need to clean up a bit).

Feel free to download the code to see an example of what I consider to be a good approach to a basic site. I would love to get any feedback.

The Group 

More significantly, if you or someone you know is in the Tulsa area, let them know about the group. Our inaugural presentation is on extending the CF Administrator and will be by our own Jason Holden.

I would like to start scheduling presentations a few months in advance if possible. If you would like to present to our group (either in person or via Breeze/Connect), let us know .

Trying Fusion Reactor

One of my ongoing projects has 16,000 registered users. Certain conditions can cause a high percentage of those users to come to the site in one day.

At some points in the past, this has caused the site to drag. I have since made adjustments to the site, and we haven't seen the issue since.

Tracking down the cause of the performance problem proved difficult, however, as I had limited data to use in finding the problem.

The number of users on the site continues to grow, so the ability to track down performance problems will continue to become more important.

There are now two products on the market to help ColdFusion developers track performance metrics on their site, SeeFusion and Fusion Reactor. I chose Fusion Reactor because, well, I won it in Ray's coding contest (declared a draw).

I had our server administrator install Fusion Reactor (which he tells me was very easy to do). We then had to fix a server problem due to a currupted Verity Collection (this sems to happen to me from time-to-time, does anyone else have this problem?). Once we got that taken care of, Fusion Reactor was up and running.

So far, I have just been looking at the Request History. This provides quite a bit of information about each request. The information that concerns me on this report, however, is the number of milliseconds to process the page. I would like to see information about the queries running on the page, but I have to set up the JDBC Wrapper in order to do that. Fortunately, the instructions look easy. So perhaps I will find time to try that out tomorrow.

Another feature that I look forward to trying is the QZip compression. This could save us bandwidth and improve perceived performance as well.

So far, I am impressed with how easy it has been to get Fusion Reactor running. I look forward to trying out more features in the future.

Inserts and Updates

Peter Bell recently had a good post about handing inserts and updates . It made me realize that I should discuss the options available for DataMgr to handle these scenarios.

Peter points out that often it is nice to be able to save a record and have the code determine whether to add a new record or update and existing record - this is handled in DataMgr by the saveRecord() method.

He goes on to point out that sometimes you want to be able to explicitely state an insert or an update - DataMgr has insertRecord() and updateRecord() for that respectively.

The motivation here, however, is to go into more detail about your options when saving records with DataMgr.

The updateRecord() method always updates an existing record. It will throw an error if no record exists to update.

The insertRecord() method offers some decisions on how to handle the existence of a matching record via the onexists argument. The possible values for this argument are as follows:

  • insert (DataMgr will try to insert another record despite the existence of a matching record)
  • update (DataMgr will update the existing record)
  • error (DataMgr will throw an error if a matching record already exists)
  • skip (DataMgr will perform no action if a matching record already exists)

It may be interesting to note that saveRecord() internally just calls insertRecord() with onexists=update.

Without adding a ton of methods, this gives you a great deal of flexibility on how to handle existing records when inserting a record.

I will add, however, that I don't worry about these distinctions as much as Peter does. I make sure that my add forms don't pass in a valid primary key, and that ensure that they will always add a new record when saveRecord() is called. 

For more details on how to insert and update records, watch either the "Converting from SQL for Inserts and Updates" or "Converting from CFINSERT and CFUPDate" presentation from my presentations library

DataMgr is free and open-source. You can also take a look at DataMgr 2.

The Death of Concord

I just got a note from SourceForge that as of the end of this week the Concord project will be removed.

If you haven't heard of Concord, well, there is a good reason for that. It was a noble idea, but never got much farther than that. The idea was an open-source ColdFusion shopping cart.  It caused a fair amount of discussion on CF-Talk at its inception and spawned a project listing on SourceForge. The project had discussion on the direction of Concord for a week or so.

Then activity on the project faded pretty quickly. It had some discussion about how things should be, but nothing ever got done.

This certainly isn't the first open source project that I have seen fail in this way. So what happened? Why do some projects fail and others flourish?

I don't know, but I do have a theory. One criticism that I have often seen of open-source is that it is a sort of Communism and Communism tends not to be successful on a large scale. I think this criticism is actually inaccurate. It is true that contributors to open-source projects receive no direct financial compensation for their work. It isn't true that they receive no benefit.

When I am asked why I put so much effort into my open-source projects (DataMgr, CodeCop, sebtags), I generally either spin positive (to contribute to the community) or negative (my desire to be respected). Certainly both are true. More true then that, I just enjoy working on those projects.

It is also true that I do so because I expect the work to benefit me. If I can make some successful open-source projects, I can increase the degree to which I am recognized in the community. This helps make sure that more people know who I am and might want to send work my way if my work-load declines or might be willing to hire me if I decide to give up on self-employment.

This is where the "Communist" criticism fails. To some degree, I do expect to benefit from my work (even if not financially).

The "Communist" criticism does apply, however, to open-source projects started by a large group. In that environment, as in Communism, everyone involved stands to benefit equally regardless of their individual contributions.

Certainly, I concentrated my "open-source time" on my own projects - hoping others would give me direction for my contributions to Concord. I suspect others did the same.

For my part, I suspect that will be the last group-run open-source project I will sign up for. None have worked so far. If you want an open-source ColdFusion shopping cart, probably better to build it yourself before you ask for help. 

DataMgr and Logical Deletes

I have to start with a confession. In at least one area, DataMgr may have made my life worse.

In general, a record can be deleted in one of two ways. The record can be deleted from the table (a hard delete) or it can simply be marked as deleted (a logical delete). With a hard delete, you don't have to make sure that the record doesn't show up in any queries, but you also can't easily get it back. With a logical delete, you can get the record back, but you have to make sure not to retrieve it in any query.

Jennifer Livit once made an impassioned argument against hard deletes. Her point about the dangers of a hard delete are valuable. In fact, I relied on logical deletes for some time. Until, that is, I created DataMgr.

DataMgr has a method called deleteRecord, which (and this may not surprise you) deletes a record. Now, since DataMgr is limited in its knowledge of how you use the database table (it knows basically what the database knows), it does a hard delete.

OK. No problem. Just because I use DataMgr and it has a deleteRecord method, doesn't mean I have to use it. I can still do a logical delete in my component and make sure to tell each call to getRecords() for that table not to retrieve deleted records. The thing is, that isn't what happened. Using deleteRecord was so easy.

So, with DataMgr 2.0, I am correcting a mistake. DataMgr 2 will now support logical deletes. In order to use logical deletes, however, you must use loadXML to tell DataMgr which column you want to use to indicate the deletion. It will set that field appropriately upon deletion and it will also keep from retrieving records with getRecords that are marked as having been deleted.

This is done by way of the new "Special" attribute for a field. DataMgr can use boolean fields or data fields to indicate a deletion. With a boolean field, DataMgr will treat a value of true as a deletion. With a date field, DataMgr will treat any non-NULL value as a deletion.

Here is the XML for a table with a boolean deletion field:

<tables>
    <table name="Examples">
        <field ColumnName="SampleID" CF_DataType="CF_SQL_INTEGER" PrimaryKey="true" Increment="true" />
        <field ColumnName="SampleName" CF_DataType="CF_SQL_VARCHAR" Length="50" />
        <field ColumnName="SampleDescription" CF_DataType="CF_SQL_LONGVARCHAR" />
        <field ColumnName="isDeleted" CF_DataType="CF_SQL_BIT" Special="DeletionMark" />
    </table>
</tables>

Here is the XML for a table with a date deletion field:

<tables>
    <table name="Examples">
        <field ColumnName="SampleID" CF_DataType="CF_SQL_INTEGER" PrimaryKey="true" Increment="true" />
        <field ColumnName="SampleName" CF_DataType="CF_SQL_VARCHAR" Length="50" />
        <field ColumnName="SampleDescription" CF_DataType="CF_SQL_LONGVARCHAR" />
        <field ColumnName="DateDeleted" CF_DataType="CF_SQL_BIT" Special="DeletionMark" />
    </table>
</tables>

In either case, the names of the fields do not matter, only the special attribute. If the field is not CF_SQL_BIT or CF_SQL_DATE, however, DataMgr will ignore the Special="DeletionMark".

Use loadXml to pass this XML into DataMgr. You can see the CFC docs for syntax or review the "Synchronizing Database Structure" presentation for more on using loadXml.

DataMgr 2 is still in beta, but I am using it on production site, so feel free to download it and try it out. It is free for any use.

BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.