Skip to content

Commit

Permalink
Merge branch 'hotfix-10.7.37' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
DominicWatson committed Mar 9, 2017
2 parents 283764f + 8e5dcef commit 6bb3b3c
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 59 deletions.
2 changes: 1 addition & 1 deletion box.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name":"PresideCMS",
"version":"10.7.36",
"version":"10.7.37",
"author":"Pixl8 Interactive",
"createPackageDirectory":true,
"packageDirectory":"preside",
Expand Down
2 changes: 1 addition & 1 deletion box.json.no.deps
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name":"PresideCMS",
"version":"10.7.36",
"version":"10.7.37",
"author":"Pixl8 Interactive",
"createPackageDirectory":true,
"packageDirectory":"preside",
Expand Down
2 changes: 1 addition & 1 deletion support/build/build.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ build.number.remote.url=http://downloads.presidecms.com/presidecms/build.number

##############################
# MANUALLY UPDATE EACH VERSION
preside.version=10.7.36
preside.version=10.7.37
##############################
21 changes: 16 additions & 5 deletions support/docs/docs/02.devguides/29.taskmanager/page.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ component {
/**
* Rebuilds the search indexes from scratch, ensuring that they are all up to date with the latest data
*
* @priority 13
* @schedule 0 *\/15 * * * *
* @timeout 120
* @displayName Rebuild search indexes
* @displayGroup default
* @priority 13
* @schedule 0 *\/15 * * * *
* @timeout 120
* @displayName Rebuild search indexes
* @displayGroup search
* @exclusivityGroup search
*/
private boolean function rebuildSearchIndexes( event, rc, prc, logger ) {
return elasticSearchEngine.rebuildIndexes( logger=arguments.logger ?: NullValue() );
Expand Down Expand Up @@ -90,6 +91,16 @@ Tasks can be given a timeout value using the `@timeout` attribute. Values are in

You can optionally use display groups to break-up the view of tasks in to multiple grouped tabs. For example, you may have a group for maintenance tasks and another group for CRM data syncs. Simply use the `@displayGroup` attribute and tasks with the same "display group" will be grouped together in tabs.

### Exclusivity groups

You can optionally use exclusivity groups to ensure that related tasks do not run concurrently. For example, you may have several data syncing tasks that would be problematic if they all ran at the same time.

By default, the exclusivity group for a task is set to the *display group* of the task.

It you set the exclusivity group of a task to `none`, the task can be run at any point in time.

Use the `@exclusivityGroup` attribute to declare your exclusivity groups per task (or leave alone to use display group).

>>> If no groups are specified, a default group of "default" will be used.
### Invoking tasks programatically
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ component extends="testbox.system.BaseSpec" {
it( "should discover mix of Tasks.cfc and ScheduledTasks.cfc file in all the passed handler folders and return a structure of tasks that have been derived from the metadata on their handler actions", function(){
var wrapper = _getWrapper();
var tasks = {
task_1 = { name="Task 1", description="This is scheduled task 1", event="scheduledtasks.task_1", schedule="* 5 * * * *" , timeout=120, priority=0, isScheduled=true , displayGroup="default" }
, task_2 = { name="Task 2", description="This is scheduled task 2", event="tasks.task_2" , schedule="disabled" , timeout=600, priority=0, isScheduled=false, displayGroup="default" }
, task_3 = { name="Task 3", description="This is scheduled task 3", event="tasks.task_3" , schedule="* 5 * * * *" , timeout=600, priority=0, isScheduled=true , displayGroup="default" }
, task_4 = { name="Task 4", description="This is scheduled task 4", event="scheduledtasks.task_4", schedule="* 5 * * * *" , timeout=600, priority=0, isScheduled=true , displayGroup="Group x" }
, task_5 = { name="Task 5", description="This is scheduled task 5", event="scheduledtasks.task_5", schedule="* 14 3 * * *", timeout=600, priority=0, isScheduled=true , displayGroup="default" }
task_1 = { name="Task 1", description="This is scheduled task 1", event="scheduledtasks.task_1", schedule="* 5 * * * *" , timeout=120, priority=0, isScheduled=true , displayGroup="default", exclusivityGroup="default" }
, task_2 = { name="Task 2", description="This is scheduled task 2", event="tasks.task_2" , schedule="disabled" , timeout=600, priority=0, isScheduled=false, displayGroup="default", exclusivityGroup="none" }
, task_3 = { name="Task 3", description="This is scheduled task 3", event="tasks.task_3" , schedule="* 5 * * * *" , timeout=600, priority=0, isScheduled=true , displayGroup="default", exclusivityGroup="default" }
, task_4 = { name="Task 4", description="This is scheduled task 4", event="scheduledtasks.task_4", schedule="* 5 * * * *" , timeout=600, priority=0, isScheduled=true , displayGroup="Group x", exclusivityGroup="Group x" }
, task_5 = { name="Task 5", description="This is scheduled task 5", event="scheduledtasks.task_5", schedule="* 14 3 * * *", timeout=600, priority=0, isScheduled=true , displayGroup="default", exclusivityGroup="default" }
};

expect( wrapper.getConfiguredTasks() ).toBe( tasks );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ component extends="testbox.system.BaseSpec" {

expect( tasks ).toBe( expected );
} );

it( "should filter tasks by exclusivity group when passed", function(){
var tasks = {
dothis = { handler="sometask" , frequency="*/10 * * * *", exclusivityGroup="test1" }
, doSomethingElse = { handler="anothertask.something", frequency="43 2 * * *" , exclusivityGroup="test2" }
};
var tm = _getTaskManagerService( tasks );
var expected = [ "doSomethingElse" ];
var tasks = tm.listTasks( exclusivityGroup="test2" );

tasks.sort( "textnocase" );

expect( tasks ).toBe( expected );
} );
} );

describe( "getTask()", function(){
Expand Down Expand Up @@ -574,20 +588,27 @@ component extends="testbox.system.BaseSpec" {
} );

describe( "getRunnableTasks()", function(){
it( "should return an array of all the tasks that require running based on their database status", function(){
var tm = _getTaskManagerService();
var dummyRecordset = QueryNew( "task_key", "varchar", [["task_1"], ["task_2"],["task_3"] ]);
var tasks = [ "task_1", "task_2", "task_3" ];
it( "should return an array of all the tasks that require running based on their database status, taking mutual exclusivity into account", function(){
var tm = _getTaskManagerService({
task_1 = { exclusivityGroup="test" }
, task_2 = { exclusivityGroup="none" }
, task_3 = { exclusivityGroup="group" }
, task_4 = { exclusivityGroup="group" }
, task_5 = { exclusivityGroup="test" }
});
var dummyRecordset = QueryNew( "task_key", "varchar", [["task_1"], ["task_2"],["task_3"], ["task_4"] ]);
var tasks = [ "task_2", "task_3" ];
var rightNow = Now();

tm.$( "_getOperationDate", rightNow );
tm.$( "tasksAreRunning" ).$args( "test" ).$results( true );
tm.$( "tasksAreRunning" ).$args( "group" ).$results( false );

mockTaskDao.$( "selectData" ).$args(
selectFields = [ "task_key" ]
, filter = "enabled = :enabled and is_running = :is_running and next_run < :next_run"
, filterParams = { enabled=true, is_running=false, next_run=rightNow }
, orderBy = "priority desc"
, maxRows = 1
).$results( dummyRecordset );

expect( tm.getRunnableTasks() ).toBe( tasks );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ component {
/**
* This is scheduled task 2
*
* @schedule disabled
* @displayName Task 2
* @schedule disabled
* @displayName Task 2
* @exclusivityGroup none
*
*/
private boolean function task_2( event, rc, prc ) {
Expand Down
30 changes: 15 additions & 15 deletions system/handlers/core/AssetDownload.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,27 @@ component output=false {
var assetId = rc.assetId ?: "";
var versionId = rc.versionId ?: "";
var derivativeName = rc.derivativeId ?: "";
var isTrashed = IsTrue( rc.isTrashed ) ?: "";
var isTrashed = IsTrue( rc.isTrashed ?: "" );
var asset = "";
var assetSelectFields = [ "asset.title", ( Len( Trim( versionId ) ) ? "asset_version.asset_type" : "asset.asset_type" ) ];
var assetSelectFields = [ "asset.title", ( Len( Trim( versionId ) ) ? "asset_version.asset_type" : "asset.asset_type" ), "asset.is_trashed" ];

if ( Len( Trim( derivativeName ) ) ) {
try {
try {
if ( Len( Trim( derivativeName ) ) ) {
asset = assetManagerService.getAssetDerivative( assetId=assetId, versionId=versionId, derivativeName=derivativeName, selectFields=assetSelectFields );
} catch ( "AssetManager.assetNotFound" e ) {
asset = QueryNew('');
} catch ( "AssetManager.versionNotFound" e ) {
asset = QueryNew('');
} catch ( "storageProvider.objectNotFound" e ) {
asset = QueryNew('');
} elseif( Len( Trim( versionId ) ) ) {
asset = assetManagerService.getAssetVersion( assetId=assetId, versionId=versionId, selectFields=assetSelectFields );
} else {
asset = assetManagerService.getAsset( id=assetId, selectFields=assetSelectFields );
}
} elseif( Len( Trim( versionId ) ) ) {
asset = assetManagerService.getAssetVersion( assetId=assetId, versionId=versionId, selectFields=assetSelectFields );
} else {
asset = assetManagerService.getAsset( id=assetId, selectFields=assetSelectFields );
} catch ( "AssetManager.assetNotFound" e ) {
asset = QueryNew('');
} catch ( "AssetManager.versionNotFound" e ) {
asset = QueryNew('');
} catch ( "storageProvider.objectNotFound" e ) {
asset = QueryNew('');
}

if ( asset.recordCount ) {
if ( asset.recordCount && ( isTrashed == asset.is_trashed ) ) {
var assetBinary = "";
var type = assetManagerService.getAssetType( name=asset.asset_type, throwOnMissing=true );
var etag = assetManagerService.getAssetEtag( id=assetId, versionId=versionId, derivativeName=derivativeName, throwOnMissing=true, isTrashed=isTrashed );
Expand Down
17 changes: 9 additions & 8 deletions system/services/taskmanager/TaskManagerConfigurationWrapper.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,15 @@ component displayName="TaskManager Configuration Wrapper" {
var isScheduledTaskMethod = Len( Trim( f.schedule ?: "" ) );
if ( isScheduledTaskMethod ) {
tasks[ f.name ] = {
event = "#handler#.#f.name#"
, schedule = _parseCronTabSchedule( f.schedule )
, name = f.displayName ?: f.name
, description = f.hint ?: ""
, timeout = Val( f.timeout ?: 600 )
, priority = Val( f.priority ?: 0 )
, isScheduled = f.schedule != "disabled"
, displayGroup = f.displayGroup ?: "default"
event = "#handler#.#f.name#"
, schedule = _parseCronTabSchedule( f.schedule )
, name = f.displayName ?: f.name
, description = f.hint ?: ""
, timeout = Val( f.timeout ?: 600 )
, priority = Val( f.priority ?: 0 )
, isScheduled = f.schedule != "disabled"
, displayGroup = f.displayGroup ?: "default"
, exclusivityGroup = f.exclusivityGroup ?: ( f.displayGroup ?: "default" )
};
}
}
Expand Down
53 changes: 37 additions & 16 deletions system/services/taskmanager/TaskManagerService.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,23 @@ component displayName="Task Manager Service" {
}

// PUBLIC API METHODS
public array function listTasks() {
return _getConfiguredTasks().keyArray();
public array function listTasks( string exclusivityGroup="" ) {
if ( !Len( Trim( arguments.exclusivityGroup ) ) ) {
return _getConfiguredTasks().keyArray();
}

var allTasks = _getConfiguredTasks();
var filtered = [];

for( var task in allTasks ) {

if ( ( allTasks[ task ].exclusivityGroup ?: "" ) == arguments.exclusivityGroup ) {
filtered.add( task );
}
}

return filtered;

}

public struct function getTask( required string taskKey ) {
Expand Down Expand Up @@ -102,15 +117,14 @@ component displayName="Task Manager Service" {
return _getConfiguredTasks().keyExists( arguments.taskKey );
}

public boolean function tasksAreRunning() {
var areRunning = false;

for( var taskKey in listTasks() ){
var taskRunning = taskIsRunning( taskKey );
areRunning = areRunning || taskRunning;
public boolean function tasksAreRunning( string exclusivityGroup="" ) {
for( var taskKey in listTasks( exclusivityGroup=arguments.exclusivityGroup ) ){
if ( taskIsRunning( taskKey ) ) {
return true
}
}

return areRunning;
return false;
}

public boolean function taskIsRunning( required string taskKey ) {
Expand Down Expand Up @@ -175,19 +189,26 @@ component displayName="Task Manager Service" {
}

public array function getRunnableTasks() {
if ( tasksAreRunning() ) {
return [];
}

var runnableTasks = _getTaskDao().selectData(
var taskConfiguration = _getConfiguredTasks();
var runnableTasks = [];
var groupsToRun = {};
var nonRunningTasks = _getTaskDao().selectData(
selectFields = [ "task_key" ]
, filter = "enabled = :enabled and is_running = :is_running and next_run < :next_run"
, filterParams = { enabled = true, is_running = false, next_run = _getOperationDate() }
, orderBy = "priority desc"
, maxRows = 1
);

return runnableTasks.recordCount ? ValueArray( runnableTasks.task_key ) : [];
for( var task in nonRunningTasks ) {
var exclusivityGroup = taskConfiguration[ task.task_key ].exclusivityGroup ?: "";

if ( exclusivityGroup == "none" || ( !groupsToRun.keyExists( exclusivityGroup ) && !tasksAreRunning( exclusivityGroup ) ) ) {
runnableTasks.append( task.task_key );
groupsToRun[ exclusivityGroup ] = 1;
}
}

return runnableTasks;
}

/**
Expand Down

0 comments on commit 6bb3b3c

Please sign in to comment.