Friday, November 9, 2012

ActiveMQ 5.3 vs. 5.7

I have been reviewing the latest features of ActiveMQ 5.7.  There are features in 5.7 that are not in the 5.3 version that I'm most familiar with.
Features like:
  • Message Priority
  • Web Sockets
  • Delay and Schedule Message Delivery
  • Cluster and Failover 
  • more
 I did some comparisons of ActiveMQ version 5.3 vs. 5.7 on the following local machine:
Intel Core i7 Q720 @ 1.6GHz with 8gb ram on 64 bit Windows 7 Ultimate.

Scenario 1: Producing and Consuming small 'hi' test messages of the amount of 10K on 5.7 ActiveMQ versions locally using ONE consumer with transacted messages (activemq-all-5.7.0.jar).
Scenario 2: Producing and Consuming small 'hi' test messages of the amount of 10K on 5.3 ActiveMQ versions locally using ONE consumer with transacted messages. (activemq-all-5.3.0.jar)
Scenario 3: Producing and Consuming small 'hi' test messages of the amount of 10K on 5.7 ActiveMQ versions locally using ONE consumer with NON transacted messages. (activemq-all-5.7.0.jar)Results:
Scenario 4: Producing and Consuming small 'hi' test messages of the amount of 10K on 5.3 ActiveMQ versions locally using ONE consumer with NON transacted messages. (activemq-all-5.3.0.jar)Results:

TransactedProducing 10,000 MessagesConsuming 10,000 MessagesActive MQ Version
Scenario 1 1.8 Seconds (avg)113 Seconds (avg)5.7
Scenario 2 2.8 Seconds (avg)110 Seconds (avg)5.3
Non-Transacted   
Scenario 3 2.8 Seconds (avg)12.3 Seconds (avg)5.7
Scenario 4 2.0 Seconds (avg)9.3 Seconds (avg)5.3


Conclusion: Speed isn't sufficient enough to warrant an upgrade in this case as they both are basically running at the same pace, however the most interesting part I saw here was the speed differences between Transacted messages (slower) and Non-Transacted Messages.  By a multiplier of about 10X, transacted message consumption through the ColdFusion Gateway slows down.  Its worth asking yourself "Can I handle a loss of messages?" at this speed difference.

The stability of the two of them should be similar but we will have a chance to put 5.7 to the test soon enough.  If you're seeing some strange issues in production like we are where ActiveMQ gets into a 'stalled' state where its neither producing, nor allowing consumption of messages in it's queues, you are not alone.  I've seen graphs that have shown ActiveMQ in this state and a restart is the only thing that brought it back to life (5.3).  After churning through millions of messages a day, I'd guess something might hit a breaking point, but hopefully 5.7 will give us a bit more stability over the long haul.  Better monitoring of the queue counts need to be in place for us to reliably use 5.3 now, however I like it for it's features, ease of use and integration with ColdFusion (thanks Sean Corfield for the Gateway and Documentation!).

Hung Threads using ActiveMQ and ColdFusion

I have been working with the ActiveMQ gateway for years now, and I just had to dig deeper as we encounter odd states with ActiveMQ over time in production.  After starting and stopping a cfGateway in the cfadmin 2x, I do a thread dump on the server and see that there are 4 threads that have been generated as a result.  The more times you start/stop, the more threads seem to stick around indefinitely (forever from what I see).  Is it possible there's some leak here or close not being properly called through cfadmin to Gateway?  I'm using cf9 local dev and activemq 5.7 but 5.3 also suffers from the same thing (threads pile up on start/stop).  At times I've also seen the Consumer Count not match the number of consumers that should be listed using ActiveMQ 5.3 and cf8 i.e. if there are 16 consumers, I've seen 32.  I shut the 16 off, and 16 still remain as 'registered' consumers (which may tell me that these background threads are alive, yet dead, as they typically never take any messages.

Thread Dump:



Anyone else have this problem or can confirm that this is a similar behavior to what you're seeing?

Tuesday, November 1, 2011

Deploying Cold Fusion to Google's App Engine and Big Table


At the ColdFusion User Group in Minneapolis Wednesday November 3, 2011 I will be presenting on the topic of "Deploying Cold Fusion to Google's App Engine and Big Table".


Learn the basics of app engine, the deployment methodologies required to develop a Cold Fusion application with Open Bluedragon on gae, and the nuances of working with 'big table', Googles distributed object data store.


Visit ColderFusion.com to RSVP!


Looking forward to seeing you there


Update:
Those interested in the files (pdf, some code minus the jar files) can download it here.  If you're looking for the magic bits for the working OpenBD version, see the pdf link for the enlist project link.

Monday, January 24, 2011

(Minneapolis) Application Architect / Developer Job - Jobs2Web, Inc.

Location: Minnetonka, MN, USA
Are you looking for an opportunity to maximize your career with one of Minnesota’s fastest growing internet startups? Jobs2Web is one of the few venture backed Minnesota startups which made the Inc 500 list in 2009 and 2010 for our fast growth, and now we’re looking to expand our team of passionate professionals in the human capital software marketplace.
Jobs2Web helps employers maximize their interactive recruiting strategy using our recruitment marketing platform. The Jobs2Web platform helps major employers to attract, capture, and communicate with quality talent all while measuring recruiting effectiveness using our patent-pending solutions which dramatically reduce dependency on expensive and less effective sourcing mediums.
Working within our company will put you close to our award winning technology that combines the best of recruiting and marketing solutions including Search Engine Marketing (SEO), SEM (Pay-Per-Click), Social Networks and Social Media Marketing, and Email Marketing.
The Architect / Developer enhances and maintains the Jobs2Web Recruitment Marketing Platform and associated applications.
Specifically, the Application Architect / Developer role will have impact through these responsibilities:
  • Create and maintain the application architecture for the Jobs2Web platform, provide application architecture guidance to the development group and other Jobs2Web departments, and engage in hands-on development as required.
  • Participate in a dynamic team environment, including frequent team feedback, collaboration, training and code review.
  • Professionally communicate with our internal clients, including Client Services and Sales.
  • Demonstrate ability to manage multiple priorities in a fast-paced environment.
Desired Qualifications and Competencies:
  • Bachelors Degree or equivalent work experience, with at least 7 years of application development experience required.
  • Experience with Java required. Desired areas of expertise include
    • Java EE 6
    • Apache Solr/Lucene
    • JMS
    • JPA/Hibernate
    • GlassFish 3
    • JSF
    • EJB 3.1
  • Experience in creation and maintenance of enterprise-grade application architecture, including SOA and messaging systems is required
  • Experience in SQL language and database technologies required.
  • Application of OO principles and use of best practices in web development required, with a strong background in application architecture.
  • ColdFusion MX experience desired, with experience using application frameworks and ColdFusion components.
  • Exposure to Adobe Flex 3 applications desired.
  • Competent in use of HTML, XML, CSS, and AJAX technologies.
  • Experience working in an Agile Scrum environment is desired.
  • Proficient in use of source control, defect management, and release management tools.
  • Consistently demonstrates strong team and individual performance.
  • Effective written and oral communication skills as well as strong interpersonal skills are essential.
  • Builds positive and effective relationships with individuals both internal and external to the organization.
  • Staffing/HR industry knowledge a plus.

Jobs2Web is an entrepreneurial employer that challenges and rewards all employees to succeed at every opportunity! Your talents are fully utilized as we are building this rapidly growing business. Our competitive market driven package includes a total rewards package of competitive pay, bonus, health insurance coverage, 401(k), career development, a very exciting marketplace and a really great work culture.

Nearest Major Market: Minneapolis
Job Segments: Architecture, ColdFusion, Computer, Creative, Database, Developer, Engineer, Engineering, Information Technology, Interactive Marketing, Java, Legal, Marketing, Patent, Product Development, Programmer, QA, Quality, Quality Assurance, Research, SEO, SQL, Technology, Web Design, XML

Wednesday, November 3, 2010

mxUnit Testing with Hudson CI Server @ CFUG Minneapolis

I'll be speaking Wednesday Nov 3, 2010 @ the ColdFusion User Group in St. Paul/Minneapolis on mxUnit Testing with Hudson Continuous Integration Server.

I'll be talking about basic Assertions, as well as going into some advanced mocking topics with the new 2.x release of the mxUnit Framework.
The second part of the evening we'll look at the Hudson Server to help execute our Unit Tests and provide jUnit reports that show execution trends as well as some special surprises.

See you there!

Wednesday, August 25, 2010

JDBC Connection String and Unicode

So we have a SQL Server 2005 JDBC Driver in production using the type 4 driver from Microsoft (Not the DataDirect Driver that ships with ColdFusion 8 Enterprise) and have it configured using the JDBC URL to specify the Host and some other parameters.  You do this by going into your ColdFusion Administrator and under Data & Services - Data Sources - Entering in a new connection specifying a Name and choosing 'Other' as the Driver:
Format: 


jdbc:sqlserver://xxx.xxx.xxx.xxxx;databaseName=MyDatabase;applicationName=Myclient;responseBuffering=adaptive;loginTimeout=5;lockTimeout=60000




Now the problem was that we had some other 'Connection String' information under the 'Advanced Settings' for this connection that we had configured to turn Unicode characters OFF for this dsn (thus using asii characters and varchar vs. unicode and nvarchar).  This was for performance as we had varchar fields on columns, and when we use cfquery param, and cf_sql_varchar, the dsn connection setting drives whether you are sending 'varchar(8000)' (non-unicode) or a' nvarchar(4000)' parameter to the database.


Database Indexes: If your index has to convert this nvarchar type to a varchar type of the column, then it will more than likely skip using the index all together and you loose the benefit of having an index on that column in your database (i.e. an email address field).


With Profiling ON we were able to watch the requests come through by manipulating the jdbc URL connection string as follows:
sendStringParametersAsUnicode=false will send the cfquery param data through as a varchar(8000) field
sendStringParametersAsUnicode =true (which is the default) will send it through as a nvarchar(4000) field




What we noticed is that those types of queries with:
<cfqueryparam value="#arguments.email#" cfsqltype="cf_sql_varchar">
would not use the index.


Another thing we noticed was that we had specified sendStringParametersAsUnicode=false in the 'Connection String' area under Advanced Settings.  This was NOT being taken into effect when sending parameterized queries to the db.  We HAD to move this string into the JDBC URL portion in order for it to work.  Bug in ColdFusion?? We're not sure, but we hope this helps someone out there.


This is how the dsn should look:









Friday, February 19, 2010

Executing Remote MXUnit Tests in CFBuilder/CFEclipse

I had some interesting problems the other week while working on some Model-Glue (gesture) RemoteService requests. I had posted to the Model-Glue Google Group about a few things I ran into when using the AbstractRemotingService.cfc, and getting url variables to work properly through the service, which they have addressed (Thank you!).

Obviously you need to read the wiki documentation first.

When you Create your RemoteService.cfc that exists at the root of your application, which will be your remoting front controller if you will, you need to tell it where your main model-glue template file is located. (download the zip file from www.model-glue.com and find the modelglueapplicationtemplate directory for the sample RemotingService.cfc)

Once you have a simple working version up / running by calling /RemotingService.cfc?method=executeEvent&event=my.event&format=json and pass in the data you need to, then you need a good way to test it right? If you use MXUnit then you have more than likely configured your IDE to execute these tests.

Figure 1.0


ISSUE:
The thing I ran into was that I was using model-glue's event security where I could specify certain events to be 'authentication' needed etc. My remoting service was calling an event that required prior authorization (i.e. session.isLoggedin() or some other means). Now, when I executed a test for this event in mxUnit through the Eclipse plugin for mxunit, it would Fail the test and kept telling me I was unauthorized to execute this event etc, and return the source code for my login screen instead (which would be correct if I was not logged in). However in my unit Test, I was making sure to Call my AuthorizationFacade and log me in in the 'setup' method and verified that 'isLoggedIn()' returned true.

RESOLUTION:
So in my unit test I am doing a cfhttp 'post' (or now that they supported url variables better in the service a 'get' ) request to the RemoteService.cfc passing in my data. HOWEVER, I needed to also pass in the following through my cfhttp call:


<cfhttpparam ....="formfield" name="CFIDE" value="#cookie.CFIDE#">
<cfhttpparam ....="formfield" name="CFTOKEN" value="#cookie.CFTOKEN#">
<cfhttpparam ....="formfield" name="JSESSIONID" value="#session.sessionid#">

Once this was in place, the calls worked as expected, and returned proper json data.

Friday, November 20, 2009

Mpls CFUG - Active MQ, Cluster Messaging, Topics and

I'll be presenting at the Minneapolis / St. Paul ColdFusion User Group again in December on Active MQ, Topics, Queues and Cluster Messaging for those interested. I'd love to pack the room before Christmas and show off all the exciting things Apache's Active MQ messaging can do with your ColdFusion applications, especially as it relates to cluster messaging. I'll be doing live demo's and code shows, as well as discussing how our company has used these techniques to help us address things like cache updating/invalidating, message driven events, maintenance queues, workflow etc.

Visit the ColderFusion.com website for more details but hope to see you out December 2, 1009, in the mean time, check out my new keyboard!

Updated: Download the code samples and presentation (+code) (requires activemq and blazeds deployed on tomcat as separate downloads) OR download the Entire thing with blazeDS, tomcat, amq and code in one large 200MB file

New Bluetooth Keyboard

I broke down and got one of the new Logitech deNovo Edge Keyboards from Amazon this week. I'm just breaking it in but so far it's very sweet. The sleek lazer cut plexiglass and brushed aluminum frame is very 'edgy', and at 11 mm thick, you'll feel like your on a laptop even when docked. It's even got a little touchpad so you can throw your mouse out if you want (but I wouldn't recommend that as the left/right click buttons are not large enough to make it full time usable). It will be perfect for the home theater remote keyboard when you have a PC hooked up to your system.


As you could tell from a prior post, I was wearing the 'C' letter out on my last keyboard, and having a quieter keyboard with cool functionality was something I was definitely interested in.
If you're out shopping this Black Friday, maybe you'll get lucky and see one on sale, but Amazon has it for a discount right now.
Happy coding - !

Wednesday, September 23, 2009

Model Glue - Variable CTRLINST is undefined

I'm playing around with Model Glue 3 (gesture) and event-types and message handling etc, and I ran across a nasty little productivity stopper.

I had defined an 'init' method which did nothing in my Model-Glue Controller for one of my controllers. I was getting a very obscure 'Variable CTRLINST is undefined.' message as a result.

<cffunction name="init" output="false" access="public" returntype="void" hint="">
<cfreturn />
</cffunction>


Removing the 'init' method from the controller solved the problem, and the error message went away - leaving me to run my application once again.

I had become so accustomed to putting an 'init' method regardless in all cfc's that If I had not had some prior controller code to look at - I would have been sitting on this error for a long time.

Hope this helps someone out there - Maybe a more appropriate message here would help (framework guys).

Monday, August 24, 2009

100+ Downloads for CFProperty Inspector ColdFusion Builder Extension

I made it past the 100+ downloads barrier for the ColdFusion Builder (pre-release) IDE with my CFProperty Inspector Extension. Thanks to all who've downloaded it and found it useful. Not bad numbers (10% download to view ratio) considering the IDE is not going to be released for another month or more.

Keep an eye open for more updates on the future as requests come in for the next version of the extension which allows you to write cfproperty tag meta data to existing components.

Thursday, July 23, 2009

Search Engine Death Match (Pt. 3) MPLS CFUG

I'll be speaking to the Minneapolis/St. Paul ColdFusion User Group (CFUG) on Aug. 6, 2009 about the Solr Integration in ColdFusion 9 (Centaur Beta). If you're in the area we'd love to see you, session starts at 6:30 - more information at ColderFusion.com.

Preview:
Search Engine Death Match: Solr (Luncene) vs. Verity

Search is King.
See how the 2 engines that ship with ColdFusion 9 stack up.
Learn more about the newly integrated Solr (based on Java Lucene) engine and how it stacks up against Verity.
Listen to real world stories about Solr's performance in a large scale Enterprise application.
Preview the features of Solr in Centaur Beta and determine if you're company could benefit from a better, more scalable, more reliable search solution.

Here is a preview of the session (pdf).

Hope you see you there!

Thursday, July 9, 2009

cf_PropertyInspector Bolt Extension Released!

Version 1 of the cf_PropertyInspector ColdFusion Builder/Bolt Extension for your IDE has been released on riaforge (pending).

Features Include:
For ease of Flex Integration, CFProperty tags are needed to have your methods available to ActionScript classes. Also complex webservices need this metadata defined for them. With existing components already created, having to go back in and add in all these cfproperty tags for all your 'accessors' is a pain. How about just right clicking -> 'Inspect CFC' and be done with it?? Best used if components exist under your webroot.

Features:
- Searching subdirectories
- Alpha sorting properties
- Wverwriting/keeping existing properties
- Lower case properties
- Exclusion of Methods (i.e getInstance, getBinaryNull etc)


Version 2 of this application will include the following:
- Backups of cfc's before modification
- Fine Tune properties before wizard completes
- Mapping support if your components are not located under your webroot.


Requirements:
ColdFusion 8+
ColdFusion Builder (Beta 2+) Build: 240677

Download NOW Available on riaforge.





















Final Result!

Sunday, May 17, 2009

Disable CFC Type Check Proof

I usually question a lot of the things I hear, and this one I just had to set the record straight. I can stand being corrected on things when I'm proven wrong so I'm hoping this will come across as simply a clarification and not any kind of 'I told you so'.

But in the CFADmin in CF8 'Settings' there is a 'Disable CFC type Check' option that allows you to turn ON the ability to ignore the argument 'types' of CFC's. For example, if I have a component of type 'com.adobe.reactor' and I pass that to a method expecting a 'com.adobe.transfer', if this option is ON, then there would be NO type checking and the argument would be successfully passed in (I'd assuming something else would fail after that however).

At cfObjective this year it was stated at a session that if this option was turned ON, a datatype like 'numeric' would be able to accept a 'string' value as there would be no type checking. This is simply not true

Proof:

Visit your cfadmin and turn ON the 'Disable CFC Type Check' option and restart your server.
Run the following script:


<cffunction name="test" output="false" access="public" returntype="any" hint="">
<cfargument name="num" type="com.adobe.transfer" required="true">
<cfreturn>
</cfreturn>

<cfset x="test('hi')">
<cfdump var="#x#">


You'll notice that this works just fine. Notice that I'm passing a string of 'hi' into the test method that is expecting a 'com.adobe.transfer' object. This works just fine with Disable CFC Type Checking ON as it simply treats the argument type as an 'ANY' type.

The CFAdmin states:
"When checked, UDF arguments of CFC type is not validated. The arguments are treated as type "ANY". Use this setting in a production environment only."


In this case 'com.adobe.transfer' is a UDF or user defined argument - I defined it in a directory of com/adobe/transfer.cfc.

However, when I change my test method to accept a 'numeric' value instead of this UDF value, you will see it will throw an exception:


<cffunction name="test" output="false" access="public" returntype="any" hint="">
<cfargument name="num" type="numeric" required="true">
<cfreturn>
</cfreturn>


"The NUM argument passed to the test function is not of type numeric."

This proves that ONLY UDF arguments are changed to 'ANY' types when 'Disable CFC Type Check' is turned ON in cfadmin (oh no I've gone cross eyed)

Imagine the security holes created by such an option if indeed the native datatypes were all treated as 'ANY'? SQL Injection could occur if you had typed the cfargument as a 'numeric' datatype. A malicious user could simply pass in a string value and bypass your initial line of attack - that being your cfargument data type. This would not be a good situation (more information on security and cfml see Jason Dean's blog at 12Robots.com).

The fact is, regardless of whether or not 'Disable CFC Type Check' is ON or OFF in your CFAdmin Setttings, native Datatypes still need to be adhered to, or an exception will be thrown.

Monday, May 4, 2009

A ColdFusion Developers Keyboard

How can you tell what kind of developer you are by your Keyboard??

Check out my keyboard - Notice the 'C' key is all but worn right off - This has been from about 2 years of programming on this external keyboard - My previous laptop was the same way!

Just for fun, shoot me a picture of your keyboard - whether ColdFusion Programmer, .Net Programmer or dba (would your 'S' key be worn down from all the SELECTS?).

Enjoy

Friday, May 1, 2009

Clustered Application Needs Re-Loading? Here's How

A question came up at last years cfObjective in Mike Brunt's presentation about reloading your application/model/ColdSpring config in mid-flight on a clustered application (your application has 2 or more CF instances running it).
We've spent some time investigating on our own and mostly just ignoring the issue and going through the tedious process of RDP-ing into the server and MMC-ing into each Servers Services Panel and restarting each instance and verifying they come back online - staggering them throughout a period of time until all cluster members were synced. This sucks.

Some of the proposed solutions included:
1.) Using ActiveMQ and a CF Gateway to Broadcast a 'reload' message and have the cluster members stagger their execution of it so they all don't reload at once (taking the app offline) via a thread sleep randomly based on the JVM instance etc.
2.) Appreloading via the url X times until you think you've HIT them all
3.) Use JRun's internal Web Server

I figured I'd give number 3 a try as I knew it would be the quickest and easiest to implement if it worked. Turns out it does.

What would happen is this: each CF instance has it's own dedicated IP and port that is running the application that can be accessed via a browser or over http (which can get automated but we'll get into that later).

The IP:Port is normally used if you wanted to get to a particular cluster members cfide to do some configurations etc i.e. 127.0.0.1:8303/cfide/administrator/index.cfm. However, if you browse to just 127.0.0.1:8303, you'll notice a nice directory structure that comes form your installation instance directory i.e. C:\JRun4\servers\{instancename}\cfusion.ear\cfusion.war if on Jrun.

So what we'll do is modify your jrun-web.xml file by adding the virtual root path to your application AND make sure if you use a separte jvm.config for your instance, that the default jvm.config is the same (more on that later).

Navigate to C:\JRun4\servers\{instancename}\cfusion.ear\cfusion.war\WEB-INF and open jrun-web.xml (if you don't have one you can try borrowing one from the default cfusion instance).

Add the following to the file and save:
<virtual-mapping>
<resource-path>/</resource-path>
<system-path>C:/webroot/approot</system-path>
</virtual-mapping>


This will make JRUN's internal web server root be your application's root directory.

Restart your CF instance.

If you are like most shops and use some kind of query parameter to execute your application reload procedure i.e. ?reloadsite=true or the like, then you can execute it via the ip:port?param and reload each cluster member by changing your ip:port.

Advanced Note: if you're like us and have a separate jvm configured for your CF instances (different than the stock jvm.config found in c:\JRun4\bin) then you'll need to make sure that
a.) either the jvm.config is the same as your CF Instance jvmconfig file
b.) make the jvm.config (default) the same as the CFInstance jvmconfig file, and then reconfigure that instance to use the 'jvm.config' file. This is key, as the internal JRun webserver will use the default jvm.config on startup, so if you have separated these jvm's out, reloading your app from ip:port will do nothing, as you have only just setup 2 separate jvm's running your application, and the one has NO knowledge of the other.

This is one method anyway - just make sure you have tested this thoroughly, especially if you have environment specific configurations that get loaded up depending on the url - as the IP address block may load up an unexpected set of configuration data from your application.

We'll be trying this out over the next few weeks and likely integrate it into the application in some kind of threaded reload routine if some configuration needs to be done mid-flight.

Enjoy!

Search Engine Death Match - Solr Wins (Pt. 2)

Abstraction:
They key to the integration of Solr and removal of Verity in our model was to abstract the search functionality of the application. The process started by creating a 'CollectionManager' which held the methods that were needed to interact with the Verity Server through cfsearch and consolodated all calls to 'cfsearch' through this service.

Coldspring:
Once that was done and working fine - we were ready to dive into Solr and figure out the nuances of the product and ultimately create a 'SolrManager' which would replace the collectionManager. Since we were using ColdSpring, this was a mere class path change in the coldspring.xml and that was it (I love ColdSpring).


Solr Data-Config:

I'll get a more formalized presentation together about swapping out Verity in favor of Solr, but in general, our data was in a SQL Database vs. xml etc, so we used the 'data-config.xml' to basically come up with our main query that populates the index (each solr instance has a single instance or collection vs. verity where there is 1 verity and many collections) as well as delta and delete queries for updating and removal of the collection data through JDBC. This data-config.xml file is simply an xml file in which you write sql to select your columns based on some criteria.
Note: it's best to have created, lastmodified, id, active columns in your database table you wish to index, as this will help Solr in determining what data has changed, and what has been deleted. Hint: keep these queries simple and use a 'view' if your sql is complex at all, as it will hide the complexity, making the file easier to read, and abstract the data in a way that you can change the view w/o having to change the data-config.xml file each time. It's also worth pointing out that the 'server time' should be exactly the same on both your database and solr machine (if not on the same box) as Solr stores internally the last time it indexed your data in a value referenced in your sql as '${dataimporter.last_index_time}'.
** If you have a 'smalldatetime' value for lastmodified where sql will 'round' the seconds of the field, then you may run into issues where Solr won't pick up on changes that have happened since it's last_index_time as compared to your database timestamps etc.

Solr Schema:
This is basically the data definition in a single schema.xml of your data. You define your 'fields' which are your columns of data from the data-config and the datatypes etc. This is very flexible including the ability to 'group' multiple fields together as a single field (copyfield) as well as defining what is the default field that's searched on if none is specified in the field (i.e. search for 'MN jobs' could search job titles, job desc, locations etc).

Solr Config:
There was a one line change here just to tell Solr to use the data-config for it's data:
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">data-config.xml</str>
</lst>
</requestHandler>


ColdFusion:
Wayne Graham has published an open source component out there at Ria Forge to assist in the low level cfhttp calls called SolManager. I've added this as a helper to my main 'SolrManager' component and i just call it to do the cfhttp calls to Solr and then I can do what I need with the data it returns.
Since the data is returned as xml, and the 'schema' of the data defines which columns / nodes are returned, I then have full control over the result. I convert the xml to a query using query new, xmlparse, and xmlsearch - and for our purpose as long as the query is in the same format as the one returned by a cfsearch through verity, the application won't know any difference.

Performance:
FAST! Solr does it's own caching and warming of searches and results and from my metrics, depending on the xml result set (which can EASILY be adjusted to limit the size of the xml as a '&rows=x' query string attribute in the search string) the performance is negligible if not better across the board for searches.

There's a lot more to discuss such as search syntax, replication, admin dashboard etc, but I'll leave a few stones un-turned for you to dive in and get your feet wet. They have good documentation on the site as well as an Extremely easy and efficient Jetty package that you can start up, execute a few commands on through a tutorial on their site, and get a feel for what to expect.

All in all, we were to the point where Verity was NO LONGER AN OPTION or a viable solution for our company and was causing nearly hourly baby sitting, so this solution was implemented JIT (Just in Time) and has saved us from feeling the effects of daily search stress.

Have fun and stay tuned for a full run down with code samples at MN CFUG soon.

Thursday, April 30, 2009

Search Engine Death Match - Solr Wins

It's now been over a month since we rolled out our Lucene / Solr powered search engine replacement for Verity in our application and let me sum it up this way: Zero maintenance.

Even though the OEM Verity licensing states 250K record max, we ran into a ceiling on a few of our machines when the number of Verity collections reached 82-88. We were doing constant rcadmin fixes to drop the thread counts for each collection down to 2 from 3 - but on each restart or at random points in time, the thread counts would return to 3. Instability and random collection switches were the main reasons we moved off the platform, and we'll never look back.

Solr took some configuration, but it was more than flexible enough for our needs, and with the delta updates and deletes, it made the maintenance near zero.

As far as parsing the resulting xml, that was done efficiently through coldfusion's xmlparse and xmlsearch to walk the nodes and return a 'like' verity recordset to the application. With ColdSpring we were able to swap out implementations easily and everything just worked. Great planning and execution resulted in a seamless rollout with Solr being run on RedHat on a dedicated machine.

I will most likely do a presentation at our next/upcoming CFUG (ColdFusion User Group) in Minneapolis/St.Paul in the near months about the transition and talk about integration points before ColdFusion 9 comes out and makes it all available with a single tag ;)

Long Live Open Source software

Tuesday, February 3, 2009

Query of Queries is Inconsistent (Adobe CFML Runtime)

I've never been a big fan of a half baked solution, but I will acknowledge that coldfusion's query of queries has come a long way from it's beginning and does do a good job on simple queries to get data from an existing query.

However, there are times when it simply sucks at doing what you think it should do, especially when it comes to multiple tables and joins, as well as converting datatypes to be alike.

I'll show a few examples of it's inconsistencies and attempt to point out some problems with it's implementation.

Example 1: Q-of-Q (Query of Queries) and Proper variable scoping inside a cffunction.

When inside a cffunction you always want to fully qualify your variables as best practice. If it's an arguments, prepend the value with 'arguments.' etc. The same goes for Q-of-Q. But I will show you where q-of-q forces you to break it's own best practice advice by not allowing you to do this simple task.

I will be referencing the following 2 queries:

<!--- Test Query 1 --->
<cfset q = QueryNew('id,name','Integer,varchar')>
<cfset queryaddrow(q,1) >
<cfset querysetCell(q,'id',1,1) >
<cfset querysetcell(q,'name','test',1) >

<!--- Test Query 2 --->
<cfset q2 = QueryNew('id,age','Integer,Integer')>
<cfset queryAddRow(q2,1) >
<cfset querySetCell(q2,'id',1,1) >
<cfset querySetCell(q2,'age',33,1) >


This is a simple query that creates a single row of data




What I'm going to do is merge the results of these 2 queries (as one may have come from a cfdirectory, and the other from a cfsearch, or cfquery etc).

My method takes 2 query arguments and joins them - but for now I'll just return the value of the 1st argument 'qry'.

<cffunction name="merge" output="false" access="public" returntype="query" hint="">
<cfargument name="qry" type="query" required="true"/>
<cfargument name="qry2" type="query" required="true"/>
<cfset var Private=StructNew()>

<cfquery name="private.q" dbtype="query">
select *
from arguments.qry

</cfquery>

<cfreturn private.q />
</cffunction>


Resulting in a simple query returned - nothing special.



Now when i wish to merge the 2 queries - I would assume that I could do something like this then if 'arguments.qry' worked to reference the qry argument.
<cffunction name="merge" output="false" access="public" returntype="query" hint="">
<cfargument name="qry" type="query" required="true"/>
<cfargument name="qry2" type="query" required="true"/>
<cfset var Private=StructNew()>

<cfquery name="private.q" dbtype="query">
select *
from arguments.qry, arguments.qry2
where arguments.qry.id = arguments.qry2.id

</cfquery>

<cfreturn private.q />
</cffunction>


WRONG!
Query Of Queries syntax error.
Encountered "arguments . qry .. Incorrect conditional expression, Expected one of [like|null|between|in|comparison] condition,

Ok fine - I won't use proper best practices and just reference the query w/o the arguments proper fully scoped value.

<cffunction name="merge" output="false" access="public" returntype="query" hint="">
<cfargument name="qry" type="query" required="true"/>
<cfargument name="qry2" type="query" required="true"/>
<cfset var Private=StructNew()>

<cfquery name="private.q" dbtype="query">
select *
from qry, qry2
where qry.id= qry2.id

</cfquery>

<cfreturn private.q />
</cffunction>


Results: works fine



Example 2: Using QofQ (query of queries) to reference a column from the table using dot notation

The ColdFusion LiveDocs states the following:
Example

If a structure named A contains a field named B, which contains a table named Products, you can refer to the table with dot notation, as follows:

SELECT tape_ID, length
FROM A.B.Products;



The 'structure' in my case is 'arguments', the field name is 'id', and the table name is 'qry'. With that in mind:

A=arguments
B=id
Products=qry

Therefore you could deduce that the following is true:


SELECT id, name
FROM arguments.id.qry


Results:

Query Of Queries syntax error.
Encountered ". Incorrect Select List, Incorrect select column, arguments.id cannot be followed by '.'


However, you would normally reference this data as 'arguments.qry.id'.

Results:

Query Of Queries syntax error.
Encountered ". Incorrect Select List, Incorrect select column, arguments.qry cannot be followed by '.'

The kicker is - Neither of these work.

Wednesday, January 28, 2009

Killer WDDX - Beware!

I came across a rather interesting 'feature' tonight while working on some logging and alerting processes. We have data in a structure that holds raw information about various processes that have occurred. In one of the structure keys there is an XMLObject (coldfusion parsed xml object). See the code snippet below and DO NOT run this on production equipment!!

<cfset stcStruct = StructNew()/>

<cfset xmlDoc = "<myxml><name>Kevin Penny</name></myxml>">
<cfset stcStruct.xml = xmlParse(xmlDoc)/>
<cfset stcStruct.id = 1>

<cfwddx action="cfml2wddx" input="#stcStruct#" output="tmp"/>


This takes a Structure, adds a key of 'xml' and places a parsed xml object in it's position. I'm then serializing the data into wddx via cfwddx as cfml2wddx.

I originally thought it was the date fields that I had in the structure, so I removed those, but the problem was the xml object's serialization.

Here's the error I get along with a partial dump:
java.lang.StackOverflowError

....
at java.beans.Introspector.instantiate(Introspector.java:1438)
at java.beans.Introspector.findExplicitBeanInfo(Introspector.java:410)
at java.beans.Introspector.(Introspector.java:359)
at java.beans.Introspector.getBeanInfo(Introspector.java:222)
at java.beans.Introspector.(Introspector.java:368)
at java.beans.Introspector.getBeanInfo(Introspector.java:222)
at java.beans.Introspector.(Introspector.java:368)
at java.beans.Introspector.getBeanInfo(Introspector.java:222)
at java.beans.Introspector.(Introspector.java:368)
at java.beans.Introspector.getBeanInfo(Introspector.java:222)
at java.beans.Introspector.(Introspector.java:368)
at java.beans.Introspector.getBeanInfo(Introspector.java:222)
at java.beans.Introspector.(Introspector.java:368)
at java.beans.Introspector.getBeanInfo(Introspector.java:222)
at java.beans.Introspector.getBeanInfo(Introspector.java:208)
at coldfusion.wddx.BeanSerializer.writeObject(BeanSerializer.java:48)
at coldfusion.wddx.WddxOutputStream.writeObject(WddxOutputStream.java:310)
at coldfusion.wddx.BeanSerializer.writeObject(BeanSerializer.java:120)
at coldfusion.wddx.WddxOutputStream.writeObject(WddxOutputStream.java:310)
at coldfusion.wddx.BeanSerializer.writeObject(BeanSerializer.java:120)
at coldfusion.wddx.WddxOutputStream.writeObject(WddxOutputStream.java:310)

I've submitted this as a bug to Adobe #75230.