Skip to content

Commit

Permalink
feat: throttle down mechanism (openemr#7587) (openemr#7588)
Browse files Browse the repository at this point in the history
  • Loading branch information
bradymiller authored Jul 24, 2024
1 parent 10bfca8 commit 637ac21
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 1 deletion.
8 changes: 8 additions & 0 deletions library/auth.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@
}
}

// below 2 function calls are only completed when environment setting 'THROTTLE_DOWN_WAIT_MILLISECONDS' is set
// used predominantly by demo farm to prevent abuse of demo farm
$throttleDownWaitMilliseconds = getenv('THROTTLE_DOWN_WAIT_MILLISECONDS', true) ?? 0;
if (empty($skipSessionExpirationCheck) && $throttleDownWaitMilliseconds > 0) {
SessionTracker::updateSessionThrottleDown();
SessionTracker::processSessionThrottleDown($throttleDownWaitMilliseconds);
}

require_once(dirname(__FILE__) . "/../src/Common/Session/SessionUtil.php");
function authCloseSession()
{
Expand Down
1 change: 1 addition & 0 deletions sql/database.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8632,6 +8632,7 @@ CREATE TABLE `session_tracker` (
`uuid` binary(16) NOT NULL DEFAULT '',
`created` timestamp NULL,
`last_updated` timestamp NULL,
`number_scripts` bigint DEFAULT 1,
PRIMARY KEY (`uuid`)
) ENGINE=InnoDB;

Expand Down
5 changes: 5 additions & 0 deletions sql/patch.sql
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,8 @@ ALTER TABLE `form_encounter` ADD `last_update` timestamp NOT NULL DEFAULT CURREN
#IfMissingColumn form_encounter ordering_provider_id
ALTER TABLE `form_encounter` ADD `ordering_provider_id` INT(11) DEFAULT '0' COMMENT 'ordering provider, if any, for this visit';
#EndIf

#IfMissingColumn session_tracker number_scripts
ALTER TABLE `session_tracker` ADD `number_scripts` bigint DEFAULT 1;
#EndIf

31 changes: 30 additions & 1 deletion src/Common/Session/SessionTracker.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static function isSessionExpired(): bool
error_log("OpenEMR Error: session_database_uuid session variable is missing");
return true;
}
$sessionTracker = sqlQueryNoLog("SELECT `last_updated`, NOW() as `current_time` FROM `session_tracker` WHERE `uuid` = ?", $_SESSION['session_database_uuid']);
$sessionTracker = sqlQueryNoLog("SELECT `last_updated`, NOW() as `current_time` FROM `session_tracker` WHERE `uuid` = ?", [$_SESSION['session_database_uuid']]);
if (empty($sessionTracker) || empty($sessionTracker['last_updated']) || empty($sessionTracker['current_time'])) {
error_log("OpenEMR Error: session entry in session_tracker table is missing or invalid");
return true;
Expand All @@ -64,4 +64,33 @@ public static function updateSessionExpiration(): void
{
sqlStatementNoLog("UPDATE `session_tracker` SET `last_updated` = NOW() WHERE `uuid` = ?", [$_SESSION['session_database_uuid']]);
}

// Function to update the throttle down function (ie. counting scripts)
// Only basically used for the online demos to prevent abuse of demo farm
public static function updateSessionThrottleDown(): void
{
sqlStatementNoLog("UPDATE `session_tracker` SET `number_scripts` = `number_scripts` + 1 WHERE `uuid` = ?", [$_SESSION['session_database_uuid']]);
}

// Function to throttle down requests when using the online demos to prevent abuse of the demo farm
public static function processSessionThrottleDown($throttleDownWaitMilliseconds): void
{
// calculate $timeThrottle['time_throttle'], which will be average time (in milliseconds) per script call
$timeThrottle = sqlQueryNoLog("SELECT `number_scripts`, `created`, NOW() as `current_timestamp` FROM `session_tracker` WHERE `uuid` = ?", [$_SESSION['session_database_uuid']]);
$timeThrottle['time_throttle'] = ((new \DateTime($timeThrottle['created']))->format('Uv') + ((int)$throttleDownWaitMilliseconds * $timeThrottle['number_scripts'])) - (new \DateTime($timeThrottle['current_timestamp']))->format('Uv');

// ensure scripts on average do not go faster than the THROTTLE_DOWN_WAIT_MIllISECONDS' environment setting
if (($timeThrottle['time_throttle'] ?? 0) > 0) {
$dieMilliseconds = getenv('THROTTLE_DOWN_DIE_MILLISECONDS', true) ?? 0;
if ($dieMilliseconds > 0 && ($timeThrottle['time_throttle'] ?? 0) > $dieMilliseconds) {
// throttle down and die since the 'THROTTLE_DOWN_DIE_MILLISECONDS' environment setting has been exceeded
error_log("DEBUG: die for script number " . $timeThrottle['number_scripts'] . " for " . $timeThrottle['time_throttle'] . " milliseconds");
usleep($timeThrottle['time_throttle'] * 1000);
die(xlt("These demos are not meant for headless server testing. Please do this on your own servers."));
}
// throttle down since the 'THROTTLE_DOWN_WAIT_MILLISECONDS' environment setting has been exceeded
error_log("DEBUG: throttling down for script number " . $timeThrottle['number_scripts'] . " for " . $timeThrottle['time_throttle'] . " milliseconds");
usleep($timeThrottle['time_throttle'] * 1000);
}
}
}

0 comments on commit 637ac21

Please sign in to comment.