Search code examples
coldfusioncoldfusion-2018

Is it safe to store an CFC user content handler in the Application scope?


I have read a lot of posts about storing CFCs in the Application scope, and I understand that if a CFC stores data then it should not be in the Application scope. All CFCs that do non-util stuff would store data - when you pass in parameters like a username or email address - so I don't get when and when not to use the Application scope for a non-util cfc.

My question is that I have a posthandler.cfc component of about 500 lines of code which handles posts from a user (just like SO would handle each question being posted on this site). The posthandler.cfc component:

  • 'cleans' any images and text submitted by the user
  • places the images in the correct folder
  • writes all the text to a database
  • returns a URL where the post can be viewed

The returned URL is received by a simple Jquery ajax call which redirects the user to the URL.

This happens quite regularly on the site and at the moment a new CFC instance is being created for each post. Would it safe to put it in the Application scope instead and not cause race/locking conditions?


Solution

  • Just passing in parameters doesn't "save" anything. Conceptually, each thread has its own arguments and local scope, which are not visible to any other thread, and cease to exist when the function exits. So from that perspective, there's no conflict.

    Also, storing data doesn't mean saving it to a database table. It refers to components that maintain state by storing data in a shared scope/object/etc.. With "shared" meaning the resource is accessible to other threads, and can potentially be modified by multiple threads at the same time, leading to race conditions.

    For example, take this (contrived) component function that "saves" information in the variables scope. If you create a new instance of that component each time, the function is safe because each request gets it's own instance and separate copy of the variables scope to play with.

     public numeric function doStuff( numeric num1, numeric num2 ) {
        variables.firstNum = arguments.num1 * 12;
        variables.secondNum = arguments.num2 * 10;
    
        return variables.firstNum / variables.secondNum;
     }
    

    Now take that same component and put it in the application scope. It's no longer safe. As soon as you store it in the application scope, the instance - AND its variables - become application scoped as well. So when the function "saves" data to the variables scope it's essentially updating an application variable. Obviously those aren't thread safe because they're are accessible to all requests. So multiple threads could easily read/modify the same variables at the same time, resulting in a race condition.

    // "Essentially" becomes this .... 
    public numeric function doStuff( numeric num1, numeric num2 ) {
        application.firstNum = arguments.num1 * 12;
        application.secondNum = arguments.num2 * 10;
    
        return application.firstNum / application.secondNum;
    }
    

    Also, as James A Mohler pointed out, the same issue occurs when you omit the scope. Declaring a function variable without a scope does NOT make it local to the function. It makes it part of the default scope: variables - (creating the same thread safety problem described above). This behavior has led to many a threading bug, when developers forget to scope a single query variable or even a loop index. So be sure to explicitly scope EVERY function variable.

     // Implicitly creates "variables.firstNum" and "variables.secondNum"
     public numeric function doStuff( numeric num1, numeric num2 ) {
        firstNum = arguments.num1 * 12;
        secondNum = arguments.num2 * 10;
    
        return firstNum / secondNum;
     }
    

    Aside from adding locking, both examples could be made thread safe by explicitly using local scope instead. By storing data in the transient, local scope, it's not visible to other threads and ceases to exist once the function exits.

     public numeric function doStuff( numeric num1, numeric num2 ) {
        local.firstNum = arguments.num1 * 12;
        local.secondNum = arguments.num2 * 10;
    
        return local.firstNum / local.secondNum;
     }
    

    Obviously there are other cases to consider, such as complex objects or structures, which are passed by reference, and whether or not those objects are modified within the function. But hopefully that sheds some light on what's meant by "saving data" and how scoping can make the difference between a stateless component (safe for the application scope) and stateful components (which are not).

    TL;DR;

    In your case, it sounds like most of the information is not shared, and is request level (user info, uploaded images, etc..), so it's probably safe to store in the application scope.