...
- Use Case:
- Consider the situation where a number of orders that have been added to a job chain. These orders should then be serialized to guarantee that each order has completed the job chain before the next order starts.
- Consider the situation that a number of job chains makes use of the same resource, e.g. by access to objects in a database. These orders should be serialized to guarantee that only one order of one job chain can access the resource at the same time.
- Solution Outline:
- The solution is twofold:
- Resource Locks implement a job chain that accepts shadow orders for access to resources. The job chain guarantees that only one order at a time is granted a resource lock. The job chain will suspend orders in originating job chains as long as a resource lock is blocked and will continue such orders if the resource lock is available. Any number of orders of the same or of different job chains can use the Resource Locks job chain for serialization.
- Resource Locks Lock Consumers implements a sample job chain that makes use of a resource that may be accessed exclusively by one order at a time.
- The solution is twofold:
- References
...
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
var jobChainPath = null; var jobChainNodeState = null; var jobChainNodeNextState = null; function spooler_initprocess_before() { spooler_log.info( ".. performing spooler_init()" ); jobChainPath var resourceJobChainParamDefault = spooler_task.order.job_chain.path"/resource_locks/resource_locks"; jobChainNodeState = spooler_task.order.job_chain_node.state; jobChainNodeNextState = spooler_task.order.job_chain_node.next_state; return true; } function spooler_process() { var rc = true; // suspend all orders, sorting is done on spooler_exit() spooler_log.info( ".. suspending current order" ); var resourceLockJobChainParam = "resource_job_chain"; var resourceLockNameParam = "resource_lock"; var resourceLockWakeUpParam = "resource_lock_wake_up"; var order = spooler_task.order.suspended = true; spooler_task.order.state = jobChainNodeState; var return rc; } functionparams = spooler.create_exitvariable_set() {; spooler_log.info( ".. peforming spooler_exit()" params.merge( spooler_task.params ); var rc = true params.merge( order.params ); var orderList = Array(); // selectname allof ordersthe ofrequested theresource: currentexplicitely jobset node by parameter varor responseassuming = spooler.execute_xml( "<show_job_chain job_chain='" + jobChainPath + "' what='job_chain_orders'/>" ); var orderDOM = new Packages.sos.xml.SOSXMLXPath( new java.lang.StringBuffer( response )the current job chain path var resourceLock = params.value( resourceLockNameParam ); var orderNodes =if orderDOM.selectNodeList( "/spooler/answer/job_chain/job_chain_node[@state = '" + jobChainNodeState + "']/order_queue/order" ); // traverse order list and add orders to sort array for( orderIndex=0; orderIndex<orderNodes.getLength(); orderIndex++ ) { var orderNode = orderNodes.item(orderIndex); var orderID = orderDOM.selectSingleNodeValue( orderNode, "@id" ); if (orderID == null) { continue; } ( !resourceLock ) { resourceLock = order.job_chain.path; } // name of the job chain that handles resource locks var resourceJobChainName = params.value( resourceLockJobChainParam ); if ( !resourceJobChainName ) { resourceJobChainName = resourceJobChainParamDefault; } // order wake up parameter indicates that order has acquired resource lock if ( params.value( resourceLockWakeUpParam ) == "yes" ) { spooler_log.info( ".... order foundacquired resource lock: " + orderIDresourceLock ); orderList.push( orderID ) return true; } //} alphabeticalelse stringif sort ( orderListparams.sort(function(a, b){return (a > b) - (a < b) }); // numeric sort // orderList.sort(function(a, b){return b - a) }); for(i=0; i<orderList.length; i++) { spooler_log.info( ".... moving order: " + orderList[i] value( resourceLockWakeUpParam ) == "no" ) { spooler_log.info( ".. order suspended and waiting for resource lock: " + resourceLock ); order.state = order.job_chain_node.state; order.suspended = true; return false; } var resourceJobChain = spooler.job_chain( resourceJobChainName ); var resourceOrder = spooler.create_order(); var responseresourceParams = spooler.execute_xmlcreate_variable_set(); resourceParams.set_var( "<modifyrequestor_order job_chain='" + jobChainPath + "' order='" + orderList[i] + "' state='" + jobChainNodeNextState + "' suspended='no'/>" , order.job_chain.path ); resourceParams.set_var( "requestor_order", order.id ); var orderDOMresourceOrder.params = new Packages.sos.xml.SOSXMLXPath( new java.lang.StringBuffer( response ) )resourceParams; resourceOrder.title = resourceLock; resourceOrder.at = "now+5"; varif errorCode = orderDOM.selectSingleNodeValue( "//ERROR/@code" ); var errorText = orderDOM.selectSingleNodeValue( "//ERROR/@text" )( (order.job_chain.path + "/" + order.id).length <= 100 ) { resourceOrder.id = order.job_chain.path + "/" + order.id; if} ( errorCode || errorText ) { resourceJobChain.add_order( resourceOrder ); spooler_log.errorinfo( "........ modify order state response: errorCode= order has requested resource lock: " + errorCode + ", errorText=" + errorText ); rc = false; } } return rcresourceLock ); params.set_var( resourceLockWakeUpParam, "no" ); order.state = order.job_chain_node.state; // always suspend current order, it will be waked up by the requested resource lock order.suspended = true; return false; } |
Usage
- Add two orders to the
job_chain1
job chain.- Use an order ID in descending alphabetical order, e.g. "cba" for the order ID of the first order and "abc" for the order ID of the second.
- Both orders will be suspended at the first node of the job chain.
- After an idle timeout of 10s both orders will be moved to the next job node in the job chain.
- This time the orders will be processed in ascending alphabetical order.
...