Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PRESIDECMS-2745 add various request level caching to rules engine #1393

Open
wants to merge 9 commits into
base: stable
Choose a base branch
from
Open
21 changes: 14 additions & 7 deletions system/services/rulesEngine/RulesEngineConditionService.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,17 @@ component displayName="RulesEngine Condition Service" {
var conditionRecord = $getPresideObject( "rules_engine_condition" ).selectData( id=arguments.conditionId );

if ( conditionRecord.recordCount ) {
// deliberately against a cached query to avoid doing this multiple times
// where possible
if ( !QueryColumnExists( conditionRecord, "deserializedExpressions" ) ) {
QueryAddColumn( conditionRecord, "deserializedExpressions", [ DeSerializeJson( conditionRecord.expressions ) ] )
}

return {
id = arguments.conditionId
, name = conditionRecord.condition_name
, context = conditionRecord.context
, expressions = DeSerializeJson( conditionRecord.expressions )
, expressions = conditionRecord.deserializedExpressions
};
}

Expand Down Expand Up @@ -97,14 +103,14 @@ component displayName="RulesEngine Condition Service" {
, required string context
, struct payload = {}
) {
var condition = getCondition( arguments.conditionId );
var condition = getCondition( arguments.conditionId );

if ( condition.isEmpty() ) {
if ( StructIsEmpty( condition ) ) {
return false;
}

var finalPayload = Duplicate( arguments.payload );
finalPayload.append( _getContextService().getContextPayload(
var finalPayload = StructCopy( arguments.payload );
StructAppend( finalPayload, _getContextService().getContextPayload(
context = arguments.context
, args = arguments.payload
), false );
Expand Down Expand Up @@ -261,9 +267,10 @@ component displayName="RulesEngine Condition Service" {
var currentEvaluation = true;
var currentJoin = "and";
var expressionResult = true;
var arryLen = ArrayLen( arguments.expressionArray )

for( var i=1; i<=arguments.expressionArray.len(); i++ ) {
var item = arguments.expressionArray[i];
for( var i=1; i<=arryLen; i++ ) {
var item = arguments.expressionArray[ i ];
var isOddRow = ( i mod 2 == 1 );

if ( isOddRow ) {
Expand Down
49 changes: 30 additions & 19 deletions system/services/rulesEngine/RulesEngineContextService.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -130,15 +130,15 @@ component displayName="RulesEngine Context Service" {
var objects = [];

if ( arguments.includeChildContexts ) {
if ( mainObject.len() ) {
objects.append( mainObject );
if ( Len( mainObject ) ) {
ArrayAppend( objects, mainObject );
}

var subContexts = contexts[ arguments.context ].subcontexts ?: [];
for( var subContext in subContexts ) {
var subobjects = getContextObject( subContext, true );
if ( subobjects.len() ) {
objects.append( subobjects, true );
if ( Len( subobjects ) ) {
ArrayAppend( objects, subobjects, true );
}
}

Expand Down Expand Up @@ -192,24 +192,35 @@ component displayName="RulesEngine Context Service" {
* @args.hint Optional set of args to send to the context getPayload() handler
*/
public struct function getContextPayload( required string context, struct args={} ) {
var coldboxController = $getColdbox();
var expanded = listValidExpressionContextsForParentContexts( [ arguments.context ] );
var payload = {};

for( var cx in expanded ) {
var handlerAction = "rules.contexts.#cx#.getPayload";

if ( coldboxController.handlerExists( handlerAction ) ) {
payload.append( $getColdbox().runEvent(
event = handlerAction
, eventArguments = arguments.args
, private = true
, prePostExempt = true
) );
var cb = $getColdbox();
var noCache = $getRequestContext().isEmailRenderingContext() || $getRequestContext().isBackgroundThread();
var cacheKey = noCache ? "" : arguments.context & Hash( SerializeJson( args ) );

if ( noCache || ( !StructKeyExists( request, "_rulesEngineContextPayloadCache" ) || !StructKeyExists( request._rulesEngineContextPayloadCache, cacheKey ) ) ) {
var expanded = listValidExpressionContextsForParentContexts( [ arguments.context ] );
var payload = {};

for( var cx in expanded ) {
var handlerAction = "rules.contexts.#cx#.getPayload";

if ( cb.handlerExists( handlerAction ) ) {
StructAppend( payload, cb.runEvent(
event = handlerAction
, eventArguments = arguments.args
, private = true
, prePostExempt = true
) );
}
}

if ( noCache ) {
return payload;
}

request._rulesEngineContextPayloadCache[ cacheKey ] = payload;
}

return payload;
return request._rulesEngineContextPayloadCache[ cacheKey ];
}

// GETTERS AND SETTERS
Expand Down
51 changes: 33 additions & 18 deletions system/services/rulesEngine/RulesEngineExpressionService.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ component displayName="RulesEngine Expression Service" {
, required struct configuredFields
) {
_lazyLoadDynamicExpressions( context=arguments.context )

try {
var expression = _getRawExpression( expressionId );
} catch( any e ) {
Expand All @@ -307,30 +308,36 @@ component displayName="RulesEngine Expression Service" {

var contexts = expression.contexts ?: [];

if ( !contexts.findNoCase( arguments.context ) && !contexts.findNoCase( "global" ) ) {
if ( !ArrayFindNoCase( contexts, arguments.context ) && !ArrayFindNoCase( contexts, "global" ) ) {
throw(
type = "preside.rule.expression.invalid.context"
, message = "The expression [#arguments.expressionId#] cannot be used in the [#arguments.context#] context."
);
}

var handlerAction = expression.expressionhandler ?: "rules.expressions." & arguments.expressionId & ".evaluateExpression";
var eventArgs = {
context = arguments.context
, payload = arguments.payload
};
var requestCacheKey = arguments.expressionId & arguments.context & SerializeJson( arguments.payload ) & SerializeJson( arguments.configuredFields );

eventArgs.append( expression.expressionHandlerArgs ?: {} );
eventArgs.append( preProcessConfiguredFields( arguments.expressionId, arguments.configuredFields ) );
if ( !StructKeyExists( request, "_rulesEngineEvaluateExpressionCache" ) || !StructKeyExists( request._rulesEngineEvaluateExpressionCache, requestCacheKey ) ) {
var handlerAction = expression.expressionhandler ?: "rules.expressions." & arguments.expressionId & ".evaluateExpression";
var eventArgs = {
context = arguments.context
, payload = arguments.payload
};

var result = $getColdbox().runEvent(
event = handlerAction
, private = true
, prePostExempt = true
, eventArguments = eventArgs
);
eventArgs.append( expression.expressionHandlerArgs ?: {} );
eventArgs.append( preProcessConfiguredFields( arguments.expressionId, arguments.configuredFields ) );

return result;
var result = $getColdbox().runEvent(
event = handlerAction
, private = true
, prePostExempt = true
, eventArguments = eventArgs
);

request._rulesEngineEvaluateExpressionCache[ requestCacheKey ] = result;
}

return request._rulesEngineEvaluateExpressionCache[ requestCacheKey ];
}

/**
Expand Down Expand Up @@ -618,18 +625,24 @@ component displayName="RulesEngine Expression Service" {
}

private void function _lazyLoadDynamicExpressions( string context="", string filterObject="" ) {
var contextCacheKey = "context=#arguments.context#,filterObject=#arguments.filterObject#";

variables._lazyLoadDone = variables._lazyLoadDone ?: {};

if ( StructKeyExists( variables._lazyLoadDone, contextCacheKey ) ) {
return;
}

var objects = [];
var contextService = _getContextService();

if ( Len( Trim( arguments.filterObject ) ) ) {
objects.append( arguments.filterObject );
ArrayAppend( objects, arguments.filterObject );
}
if ( Len( Trim( arguments.context ) ) ) {
var contextObjects = contextService.getContextObject( arguments.context, true );
if ( contextObjects.len() ) {
objects.append( contextObjects, true );
if ( ArrayLen( contextObjects ) ) {
ArrayAppend( objects, contextObjects, true );
}
}

Expand All @@ -645,6 +658,8 @@ component displayName="RulesEngine Expression Service" {
variables._lazyLoadDone[ objectName ] = true;
}
}

variables._lazyLoadDone[ contextCacheKey ] = true;
}

private array function _getAdminUserRoles( string adminUserId=$getAdminLoginService().getLoggedInUserId() ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,12 @@ component extends="resources.HelperObjects.PresideBddTestCase" {
) );

mockColdbox = createEmptyMock( "preside.system.coldboxModifications.Controller" );
mockRequestContext = createStub();
service.$( "$getColdbox", mockColdbox );
service.$( "$getRequestContext", mockRequestContext );

mockRequestContext.$( "isEmailRenderingContext", false );
mockRequestContext.$( "isBackgroundThread" , true );

return service;
}
Expand Down
Loading