From 35ab40cbb78a838e9c840cad1b3c9c39d49f912b Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 16 Dec 2013 16:50:39 +0100 Subject: [PATCH 1/3] [FIX] Inifite loop on archive_cleanup Fixes an infinite loop in cleaning up the archive table: * Shares come in faster than defined in cleanup sleep timeout Will use an upper limit ID which would match the delete query at the initial state. Skips archiving if no ID is found (e.g. no rows match the archive purge conditions). This will fix #997 once merged. --- public/include/classes/share.class.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index 5af52897..26b8f161 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -175,30 +175,40 @@ class Share Extends Base { if (!isset($this->config['purge']['shares'])) $this->config['purge']['shares'] = 25000; if (!isset($this->config['purge']['sleep'])) $this->config['purge']['sleep'] = 1; + // TODO: This could need some cleanup work somtime but works if ($this->config['payout_system'] == 'pplns') { // Fetch our last block so we can go back configured rounds $aLastBlock = $this->block->getLast(); // Fetch the block we need to find the share_id $aBlock = $this->block->getBlock($aLastBlock['height'] - $this->config['archive']['maxrounds']); + + // We need to find a hard limit id so we don't run into an infinite loop, skip process if we can't find a limit + $stmt = $this->mysqli->prepare("SELECT MAX(id) AS id FROM $this->tableArchive WHERE block_id < ? AND time < DATE_SUB(now(), INTERVAL ? MINUTE)"); + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $aBlock['id'], $this->config['archive']['maxage']) && $stmt->execute() && $result = $stmt->get_result()) + if ( ! $max_id = $result->fetch_object()->id ) return true; // Now that we know our block, remove those shares $affected = 1; while ($affected > 0) { // Sleep first to allow any IO to cleanup sleep($this->config['purge']['sleep']); - $stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE block_id < ? AND time < DATE_SUB(now(), INTERVAL ? MINUTE) LIMIT " . $this->config['purge']['shares']); - if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $aBlock['id'], $this->config['archive']['maxage']) && $stmt->execute()) { + $stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE block_id < ? AND time < DATE_SUB(now(), INTERVAL ? MINUTE) AND id <= ? LIMIT " . $this->config['purge']['shares']); + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $aBlock['id'], $this->config['archive']['maxage'], $max_id) && $stmt->execute()) { $affected = $stmt->affected_rows; } else { return $this->sqlError(); } } } else { + // We need to find a hard limit id so we don't run into an infinite loop, skip process if we can't find a limit + $stmt = $this->mysqli->prepare("SELECT MAX(id) AS id FROM $this->tableArchive WHERE time < DATE_SUB(now(), INTERVAL ? MINUTE)"); + if ($this->checkStmt($stmt) && $stmt->bind_param('i', $this->config['archive']['maxage']) && $stmt->execute() && $result = $stmt->get_result()) + if ( ! $max_id = $result->fetch_object()->id ) return true; $affected = 1; while ($affected > 0) { // Sleep first to allow any IO to cleanup sleep($this->config['purge']['sleep']); - $stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE time < DATE_SUB(now(), INTERVAL ? MINUTE) LIMIT " . $this->config['purge']['shares']); - if ($this->checkStmt($stmt) && $stmt->bind_param('i', $this->config['archive']['maxage']) && $stmt->execute()) { + $stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE time < DATE_SUB(now(), INTERVAL ? MINUTE) AND id <= ? LIMIT " . $this->config['purge']['shares']); + if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $this->config['archive']['maxage'], $max_id) && $stmt->execute()) { $affected = $stmt->affected_rows; } else { return $this->sqlError(); From 5c8a4ecb735a072fb552de13127233b9d3586225 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 16 Dec 2013 16:57:27 +0100 Subject: [PATCH 2/3] [FIX] Added more verbose output on failing crons for log --- cronjobs/archive_cleanup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cronjobs/archive_cleanup.php b/cronjobs/archive_cleanup.php index 83c347c4..c65d8006 100755 --- a/cronjobs/archive_cleanup.php +++ b/cronjobs/archive_cleanup.php @@ -27,7 +27,7 @@ require_once('shared.inc.php'); // If we don't keep archives, delete some now to release disk space if (!$share->purgeArchive()) { - $log->logError("Failed to delete archived shares, not critical but should be checked!"); + $log->logError("Failed to delete archived shares, not critical but should be checked: " . $share->getCronError()); $monitoring->endCronjob($cron_name, 'E0008', 1, true); } From cef176c754d9c36c1b2cb6fb993ec432f3700126 Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Tue, 17 Dec 2013 09:48:07 +0100 Subject: [PATCH 3/3] [FIX] Wrong parameter count --- public/include/classes/share.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/include/classes/share.class.php b/public/include/classes/share.class.php index 26b8f161..0b6beb89 100644 --- a/public/include/classes/share.class.php +++ b/public/include/classes/share.class.php @@ -192,7 +192,7 @@ class Share Extends Base { // Sleep first to allow any IO to cleanup sleep($this->config['purge']['sleep']); $stmt = $this->mysqli->prepare("DELETE FROM $this->tableArchive WHERE block_id < ? AND time < DATE_SUB(now(), INTERVAL ? MINUTE) AND id <= ? LIMIT " . $this->config['purge']['shares']); - if ($this->checkStmt($stmt) && $stmt->bind_param('ii', $aBlock['id'], $this->config['archive']['maxage'], $max_id) && $stmt->execute()) { + if ($this->checkStmt($stmt) && $stmt->bind_param('iii', $aBlock['id'], $this->config['archive']['maxage'], $max_id) && $stmt->execute()) { $affected = $stmt->affected_rows; } else { return $this->sqlError();