From 35ab40cbb78a838e9c840cad1b3c9c39d49f912b Mon Sep 17 00:00:00 2001 From: Sebastian Grewe Date: Mon, 16 Dec 2013 16:50:39 +0100 Subject: [PATCH] [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();