change eligibility batch from ssn to policy number, minor fix to filename with extra...
[openemr.git] / library / ajax / execute_background_services.php
blob905646853cb24ab8bd109ffb234284b1fdf1945b
1 <?php
2 /**
3 * Manage background operations that should be executed at intervals.
5 * This script may be executed by a suitable Ajax request, by a cron job, or both.
7 * When called from cron, optinal args are [site] [service] [force]
8 * @param site to specify a specific site, 'default' used if omitted
9 * @param service to specify a specific service, 'all' used if omitted
10 * @param force '1' to ignore specified wait interval, '0' to honor wait interval
12 * The same parameters can be accessed via Ajax using the $_POST variables
13 * 'site', 'background_service', and 'background_force', respectively.
15 * For both calling methods, this script guarantees that each active
16 * background service function: (1) will not be called again before it has completed,
17 * and (2) will not be called any more frequently than at the specified interval
18 * (unless the force execution flag is used). A service function that is already running
19 * will not be called a second time even if the force execution flag is used.
21 * Notes for the default background behavior:
22 * 1. If the Ajax method is used, services will only be checked while
23 * Ajax requests are being received, which is currently only when users are
24 * logged in.
25 * 2. All services are checked and called sequentially in the order specified
26 * by the sort_order field in the background_services table. Service calls that are "slow"
27 * should be given a higher sort_order value.
28 * 3. The actual interval between two calls to a given background service may be
29 * as long as the time to complete that service plus the interval between
30 * n+1 calls to this script where n is the number of other services preceding it
31 * in the array, even if the specified minimum interval is shorter, so plan
32 * accordingly. Example: with a 5 min cron interval, the 4th service on the list
33 * may not be started again for up to 20 minutes after it has completed if
34 * services 1, 2, and 3 take more than 15, 10, and 5 minutes to complete,
35 * respectively.
37 * Copyright (C) 2013 EMR Direct <http://www.emrdirect.com/>
39 * LICENSE: This program is free software; you can redistribute it and/or
40 * modify it under the terms of the GNU General Public License
41 * as published by the Free Software Foundation; either version 2
42 * of the License, or (at your option) any later version.
43 * This program is distributed in the hope that it will be useful,
44 * but WITHOUT ANY WARRANTY; without even the implied warranty of
45 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
46 * GNU General Public License for more details.
47 * You should have received a copy of the GNU General Public License
48 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
50 * @package OpenEMR
51 * @author EMR Direct <http://www.emrdirect.com/>
52 * @link http://www.open-emr.org
57 //ajax param should be set by calling ajax scripts
58 $isAjaxCall = isset($_POST['ajax']);
60 //if false, we may assume this is a cron job and set up accordingly
61 if (!$isAjaxCall) {
62 $ignoreAuth = 1;
63 //process optional arguments when called from cron
64 $_GET['site'] = (isset($argv[1])) ? $argv[1] : 'default';
65 if (isset($argv[2]) && $argv[2]!='all') {
66 $_GET['background_service'] = $argv[2];
69 if (isset($argv[3]) && $argv[3]=='1') {
70 $_GET['background_force'] = 1;
74 //an additional require file can be specified for each service in the background_services table
75 require_once(dirname(__FILE__) . "/../../interface/globals.php");
77 //Remove time limit so script doesn't time out
78 set_time_limit(0);
80 //Release session lock to prevent freezing of other scripts
81 session_write_close();
83 //Safety in case one of the background functions tries to output data
84 ignore_user_abort(1);
86 /**
87 * Execute background services
88 * This function reads a list of available services from the background_services table
89 * For each service that is not already running and is due for execution, the associated
90 * background function is run.
92 * Note: Each service must do its own logging, as appropriate, and should disable itself
93 * to prevent continued service calls if an error condition occurs which requires
94 * administrator intervention. Any service function return values and output are ignored.
97 function execute_background_service_calls()
99 /**
100 * Note: The global $service_name below is set to the name of the service currently being
101 * processed before the actual service function call, and is unset after normal
102 * completion of the loop. If the script exits abnormally, the shutdown_function
103 * uses the value of $service_name to do any required clean up.
105 global $service_name;
107 $single_service = isset($_REQUEST['background_service']) ? $_REQUEST['background_service'] : '';
108 $force = (isset($_REQUEST['background_force']) && $_REQUEST['background_force']);
110 $sql = 'SELECT * FROM background_services WHERE ' . ($force ? '1' : 'execute_interval > 0');
111 if ($single_service!="") {
112 $services = sqlStatementNoLog($sql.' AND name=?', array($single_service));
113 } else {
114 $services = sqlStatementNoLog($sql.' ORDER BY sort_order');
117 while ($service = sqlFetchArray($services)) {
118 $service_name = $service['name'];
119 if (!$service['active'] || $service['running'] == 1) {
120 continue;
123 $interval=(int)$service['execute_interval'];
125 //leverage locking built-in to UPDATE to prevent race conditions
126 //will need to assess performance in high concurrency setting at some point
127 $sql='UPDATE background_services SET running = 1, next_run = NOW()+ INTERVAL ?'
128 . ' MINUTE WHERE running < 1 ' . ($force ? '' : 'AND NOW() > next_run ') . 'AND name = ?';
129 if (sqlStatementNoLog($sql, array($interval,$service_name))===false) {
130 continue;
133 $acquiredLock = generic_sql_affected_rows();
134 if ($acquiredLock<1) {
135 continue; //service is already running or not due yet
138 if ($service['require_once']) {
139 require_once($GLOBALS['fileroot'] . $service['require_once']);
142 if (!function_exists($service['function'])) {
143 continue;
146 //use try/catch in case service functions throw an unexpected Exception
147 try {
148 $service['function']();
149 } catch (Exception $e) {
150 //do nothing
153 $sql = 'UPDATE background_services SET running = 0 WHERE name = ?';
154 $res = sqlStatementNoLog($sql, array($service_name));
159 * Catch unexpected failures.
161 * if the global $service_name is still set, then a die() or exit() occurred during the execution
162 * of that service's function call, and we did not complete the foreach loop properly,
163 * so we need to reset the is_running flag for that service before quitting
166 function background_shutdown()
168 global $service_name;
169 if (isset($service_name)) {
170 $sql = 'UPDATE background_services SET running = 0 WHERE name = ?';
171 $res = sqlStatementNoLog($sql, array($service_name));
175 register_shutdown_function('background_shutdown');
176 execute_background_service_calls();
177 unset($service_name);