Localmode, how to migrate from Classic to Modern Local Scope Mode
Migrating from Classic to Modern Local Scope Mode
Lucee offers two modes for handling unscoped variables in functions: Classic and Modern.
Switching to localmode=true (per function or application-wide) dramatically boosts performance, but requires testing.
Understanding Local Scope Modes
The local scope mode setting defines how unscoped variables are handled in functions:
- Classic Mode (CFML default): Unscoped variables go to
variablesscope, unless the key exists inargumentsorlocal. - Modern Mode: Unscoped variables always go to
localscope.
Why Migrate to Modern Mode?
- Thread Safety: Variables isolated to function instance - prevents race conditions with shared components
- Predictable Behavior: All unscoped variables behave the same way
- Performance: Local scope lookups are faster than cascading scope resolution
Classic Mode Race Condition Example
component {
function createToken(id) {
token = createUUID(); // Stored in variables scope
storeToken(id, token);
return token;
}
}
If this component is in application scope and used concurrently, token becomes shared - multiple threads can overwrite each other's values.
Configuring Local Scope Mode
Server Level (Lucee Administrator)
Under "Settings > Scope", set "Local scope mode" to "Modern" or "Classic".
Server Configuration (.CFConfig.json)
{
"localScopeMode": "modern"
}
Application Level (Application.cfc)
this.localMode = "modern"; // or "classic"
Function Level
function test() localMode="modern" {
// Function body
}
Migration Strategy: Using Cascading Write Logging
Switching directly to Modern mode may break existing applications. A safer approach:
- Enable cascading write logging
- Fix all unscoped variable occurrences
- Switch to Modern mode
Step 1: Enable Cascading Write Logging
Set environment variables or system properties (Lucee 6.2.1.82+):
LUCEE_CASCADING_WRITE_TO_VARIABLES_LOG/-Dlucee.cascading.write.to.variables.log- log name for detectionsLUCEE_CASCADING_WRITE_TO_VARIABLES_LOGLEVEL/-Dlucee.cascading.write.to.variables.loglevel- log level (DEBUG, INFO, WARN, ERROR)
Example:
# Environment variables
LUCEE_CASCADING_WRITE_TO_VARIABLES_LOG=application
LUCEE_CASCADING_WRITE_TO_VARIABLES_LOGLEVEL=INFO
# System properties
-Dlucee.cascading.write.to.variables.log=application
-Dlucee.cascading.write.to.variables.loglevel=INFO
Step 2: Analyze Logs and Modify Code
Log entries look like:
Variable Scope Cascading Write Detected: The variable [token] is being implicitly written to the variables scope at [MyComponent.cfc:42]. This occurs when no explicit scope (such as local, arguments, or variables) is specified in the assignment.
For each occurrence, add explicit scope:
variables.token = createUUID();- if it should remain component-levellocal.token = createUUID();- if it should be function-local
Common Patterns Requiring Attention
Component Properties
Properties stored at the component level need explicit variables scope:
// Before
function init(datasourceName) {
datasourceName = arguments.datasourceName;
}
// After
function init(datasourceName) {
variables.datasourceName = arguments.datasourceName;
}
Local Counters and Temporary Variables
Variables local to a function call:
// Before
function processItems(items) {
result = [];
for(i=1; i <= arrayLen(items); i++) {
processed = processItem(items[i]);
arrayAppend(result, processed);
}
return result;
}
// After
function processItems(items) {
local.result = [];
for(local.i=1; local.i <= arrayLen(items); local.i++) {
local.processed = processItem(items[local.i]);
arrayAppend(local.result, local.processed);
}
return local.result;
}
Step 3: Test Thoroughly
- Test your application thoroughly
- Verify all functionality works correctly
- Check for unexpected behaviors or errors
- Ensure no more log entries appear
Step 4: Disable Logging and Switch to Modern Mode
Once all code is updated and tested:
- Remove the environment variables or system properties
- Switch to Modern mode at your preferred configuration level