I'll be at webdu on Thurs/Fri

I'm excited to say that I'll be attending webdu here in Sydney on Thursday and Friday this week. Looking forward to an impressive lineup of speakers.

I'm new to Sydney, so please don't be a stranger and introduce yourself, here's a picture of what I look like.

Also, I just found out about code-wars as well, not sure what to make of it, but sounds like a blast, I'll see you there.

Moving to Sydney

At 10:30pm tonight I will be getting on a plane to relocate to Sydney, Australia. The bags are packed, and I'm looking forward to the next exciting chapter in my life. As of Monday, my company was acquired by Thomson Reuters, and I will be working as an independent consultant for them.

I'm looking forward to getting involve with all of you in the CF community when I arrive. Don't be a stranger, please introduce yourselves as I hardly know anyone there. I'm really hoping that I can win a pass to @webdu in their twitter competition, it would be a great opportunity to make some connections. If you're on twitter I wouldn't mind a shameless plug for a ticket :) (@mikeschierberl).

Additionally, if you are in Sydney, I'd love recommendations on things to do/see from a locals perspective. I've been there a few times, but I will be coming with a clean slate and an open mind.

Deploying cached CSS and JS with Ant

If you are using YSlow or an equivalent tool you're probably aware of some of the best practices to follow to optimize client-side load times. In this post I'm going to outline a quick and easy approach that we use at Planitax to deploy static content using Ant.

The best practices that I hope to address with this build strategy are...
1) Add an Expires or a Cache-Control Header
2) Minify JavaScript and CSS

Both of these are pretty straightforward, but it's easy to run into problems when deploying updates to content that might have an expire date in the future. You might run into this problem when you make changes to js, css, or images in your application. If a browser has cached content locally, the user might see stale images, or worse yet experience errors due to out of date javascript or css files.

When we developed a deployment strategy we wanted an approach that was transparent to the end user, easy to build, and had minimal impact on developers checking in code. We needed to meet the following criteria.

  • Content should be able to have a Expire date at any point in the future
  • Content can be deployed to QA/Production at any time using a one step build
  • Webserver should automatically apply expires headers to static assets without additional deployment steps.
  • Developers should always be able to check into a single repository location. Branching/Deploying should not require a copy of a file to be checked in.
  • Developers should not be required to run a build target in order to see changes to static content.
  • js/css should be minified on production deployments, but not in the source code repository.


From these requirements we came up with a strategy that looked something like this.

  • Static assets that can be cached will always live in the /assets folder off the application root. Content will be organized into subfolders for /assets/js, /assets/css, and /assets/images.
  • Developers will work with expires headers turned off for these folders so changes will appear immediately.
  • Developers will check files in directly to this location. The repository location will always be /assets.
  • When deploying to QA/Production, a copy of the static assets will be created with a new version identifier. This copy will also be minified as part of the deploy step.
  • The application will be able to determine the correct copy of static content to use regardless of the deployment environment.
  • QA/Production web servers will automatically add an expires header to any content located in the /assets folder.

The first step was to build an ant target that would take care of creating a new folder, copying the assets and minifying content appropriately. We use JSMin to minify our JS. In order to execute this example you will need to download the JSMin Ant Task from Google Code. Note, when setting up your taskdef, the classpath attribute should point to the relative location that you have saved the .jar file. For this example it will be in the /lib subfolder off of root.

<taskdef name="jsmin" classname="net.matthaynes.jsmin.JSMin_Task" classpath="./lib/jsmin.0.2.3.jar" />

   <target name="jsmin">
<!-- tstamp creates variables for date and time of current execution -->
      <tstamp />
      <copy todir="./assets/js${DSTAMP}${TSTAMP}" includeEmptyDirs="true" preservelastmodified="true" overwrite="false" failonerror="true" verbose="true">
         <fileset dir="./assets/js">
            <include name="**/*.*" />
         </fileset>
      </copy>
      <jsmin destdir="./assets/js${DSTAMP}${TSTAMP}" force="true">
         <fileset dir="./assets/js" includes="*.js" />
      </jsmin>
   </target>

This code creates a folder with a name that looks something like this... ./assets/js200901141546 , it's named this way so we can do alpha sorts easily as you will see in the next step. We don't check this folder in to source control.

Note: You can adapt this code for css or images by modifying the copy command, if you only need to copy/minify JS you might be able to get by without the copy command.

The next step is to make your application aware of the correct folder to be used. This is easy to do with a cfdirectory based on the naming convention we used above. First we will create a variable that identifies the most recent/current folder created by the name that contains the timestamp. The nice thing about this is that it will identify the correct folder for prod servers as well as developers who only have a ./assets/js folders. It does not require developers to run the build target in order to see changes.

<cfdirectory
         action="list"
         directory="#expandPath('./assets/')#"
         name="jsDirectoryList"
         filter="js*"
         sort="name desc" >

         
<cfset application.jsAssetsSubFolder = jsDirectoryList.name>

The only tricky part is to make sure that all your links to assets include the new variable that you have created. Your links will look something like this...

<cfoutput>
   <script type="text/javascript" src="./assets/#application.jsAssetsSubFolder#/script.js"></script>
</cfoutput>

I'm sure there are countless ways to approach this, and your deployment strategy may differ significantly based on your environment, and or application. I'm interested to see how others have approached this and other deployment tasks.

Thread safety and the var scope - live example

Recently, I've talked to a few developers who knew about the var scope, but didn't quite grasp the concept of making a variable local to a function. Additionally, I've come across a few posts that indicated that setting a var statement was strictly to conserve memory (along with other reasons that missed the point).

To help anyone struggling with understanding the impact of the var scope I've put together a quick example to demonstrate what can happen when you improperly scope a variable.

The example here focuses on CFCs that are loaded into a shared scope (session, application, etc), and are accessed simultaneously by multiple users.Running the code will make 2 simultaneous requests to the same function in separate iframes. The example contains a loop that outputs the index of a loop from 1 to 100. If the loop executes correctly the numbers appear in green, if the numbers are out of sequence, they will be red. To make the results more dramatic I've included a 1-40ms pause in each loop iteration.

run example with var statement
run example without var statement

Here's the cfc that is loaded in server scope (simplified for reading). Note the only difference in the functions is that the "count" variable has a var statement in one function and doesn't in the other function.

<cfcomponent name="thread">
   
   <cffunction name="nonVarFunction" >
      <cfset var lastCount = 0>
      <cfset var idx = 0>
      
      <cfset count = 0>
      
      executing "nonVarFunction()"<br/>
      
      <cfloop from="1" to="100" index="idx">
         <cfset lastCount = count>
         <cfset count = count + 1>
         <cfthread action="sleep" duration="#randRange(1,40)#" />
         <cfoutput>
            <cfif count-1 EQ lastCount>
             <!--- display green--->
             #count#
            <cfelse>
             <!--- display red--->
             #count#
            </cfif>
         </cfoutput>
         <cfflush>
      </cfloop>
   </cffunction>
   
   <cffunction name="VarFunction" >
      <cfset var lastCount = 0>
      <cfset var idx = 0>
      
      <cfset var count = 0>
      
      executing "VarFunction()"<br/>
      
      <cfloop from="1" to="100" index="idx">
         <cfset lastCount = count>
         <cfset count = count + 1>
         <cfthread action="sleep" duration="#randRange(1,40)#" />
         <cfoutput>
            <cfif count-1 EQ lastCount>
             <!--- display green--->
             #count#
            <cfelse>
             <!--- display red--->
             #count#
            </cfif>
         </cfoutput>
         <cfflush>
      </cfloop>
   </cffunction>
</cfcomponent>

What happened?

If the example executed correctly, you should have seen all green numbers in both windows for the var scope example, and a mix of green and red in the example that executed the function without the var statement. The presence of the "var" statement makes the variable local to the function. This means that when the function runs, it creates a new instance of the variable that is unique each time the function is executed. Without the var statement in place, the variable gets "shared" between everyone who is accessing this component. When a component lives in a shared scope (session, application) the variable will be shared for everyone accessing that scope. Think of the following scenario.

User 1: set count = 1
User 2: set count = 1
User 1: set count = count + 1
User 2: set count = count + 1
User 1: count = 2
User 2: count = 3

In this case the developer most likely intended to have both users see count = 2, however the variable was shared and as a result a user's result was impacted by another user's request. Note: this may be the intended behavior of the code, and that's why there is no definite rule on why you should "always" or "never" var scope a variable.

Unfortunately, recognizing issues that appear as a result of improper scope can be difficult. Many times they result in server errors that are intermittent and only appear under load. This can sometimes make it practically impossible to reproduce the error in a test environment. Also, many tags create variables that are easy to miss (cfquery, cfloop, etc).

Luckily, there's a tool to help (shameless plug here). Take a look at varscoper. Hopefully this will become an integral tool in your development process.

More Entries

BlogCFC was created by Raymond Camden. This blog is running version 5.9.002. Contact Blog Owner