Skip to content

Commit

Permalink
Implemented rcube::sleep() method for disconnecting all external conn…
Browse files Browse the repository at this point in the history
…ection in long-running/sleeping scripts
  • Loading branch information
alecpl committed Sep 21, 2016
1 parent a227589 commit c1c0a0d
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 31 deletions.
82 changes: 63 additions & 19 deletions program/lib/Roundcube/rcube.php
Original file line number Diff line number Diff line change
Expand Up @@ -209,25 +209,7 @@ public function get_memcache()
}

$this->memcache = new Memcache;
$this->mc_available = 0;

// add all configured hosts to pool
$pconnect = $this->config->get('memcache_pconnect', true);
$timeout = $this->config->get('memcache_timeout', 1);
$retry_interval = $this->config->get('memcache_retry_interval', 15);

foreach ($this->config->get('memcache_hosts', array()) as $host) {
if (substr($host, 0, 7) != 'unix://') {
list($host, $port) = explode(':', $host);
if (!$port) $port = 11211;
}
else {
$port = 0;
}

$this->mc_available += intval($this->memcache->addServer(
$host, $port, $pconnect, 1, $timeout, $retry_interval, false, array($this, 'memcache_failure')));
}
$this->memcache_init();

// test connection and failover (will result in $this->mc_available == 0 on complete failure)
$this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist
Expand All @@ -240,6 +222,34 @@ public function get_memcache()
return $this->memcache;
}

/**
* Get global handle for memcache access
*
* @return object Memcache
*/
protected function memcache_init()
{
$this->mc_available = 0;

// add all configured hosts to pool
$pconnect = $this->config->get('memcache_pconnect', true);
$timeout = $this->config->get('memcache_timeout', 1);
$retry_interval = $this->config->get('memcache_retry_interval', 15);

foreach ($this->config->get('memcache_hosts', array()) as $host) {
if (substr($host, 0, 7) != 'unix://') {
list($host, $port) = explode(':', $host);
if (!$port) $port = 11211;
}
else {
$port = 0;
}

$this->mc_available += intval($this->memcache->addServer(
$host, $port, $pconnect, 1, $timeout, $retry_interval, false, array($this, 'memcache_failure')));
}
}

/**
* Callback for memcache failure
*/
Expand Down Expand Up @@ -1021,6 +1031,40 @@ public function add_shutdown_function($function)
$this->shutdown_functions[] = $function;
}

/**
* When you're going to sleep the script execution for a longer time
* it is good to close all external connections (sql, memcache, SMTP, IMAP).
*
* No action is required on wake up, all connections will be
* re-established automatically.
*/
public function sleep()
{
foreach ($this->caches as $cache) {
if (is_object($cache)) {
$cache->close();
}
}

if ($this->storage) {
$this->storage->close();
}

if ($this->db) {
$this->db->closeConnection();
}

if ($this->memcache) {
$this->memcache->close();
// after close() need to re-init memcache
$this->memcache_init();
}

if ($this->smtp) {
$this->smtp->disconnect();
}
}

/**
* Quote a given string.
* Shortcut function for rcube_utils::rep_specialchars_output()
Expand Down
18 changes: 12 additions & 6 deletions program/lib/Roundcube/rcube_cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,11 @@ function close()
}

// reset internal cache index, thanks to this we can force index reload
$this->index = null;
$this->index = null;
$this->index_changed = false;
$this->cache = array();
$this->cache_sums = array();
$this->cache_changes = array();
}

/**
Expand Down Expand Up @@ -642,13 +646,15 @@ private function max_packet_size()
$this->max_packet -= 2000;
}
else if ($this->type == 'memcache') {
$stats = $this->db->getStats();
$remaining = $stats['limit_maxbytes'] - $stats['bytes'];
$this->max_packet = min($remaining / 5, $this->max_packet);
if ($stats = $this->db->getStats()) {
$remaining = $stats['limit_maxbytes'] - $stats['bytes'];
$this->max_packet = min($remaining / 5, $this->max_packet);
}
}
else if ($this->type == 'apc' && function_exists('apc_sma_info')) {
$stats = apc_sma_info();
$this->max_packet = min($stats['avail_mem'] / 5, $this->max_packet);
if ($stats = apc_sma_info()) {
$this->max_packet = min($stats['avail_mem'] / 5, $this->max_packet);
}
}
}

Expand Down
18 changes: 12 additions & 6 deletions program/lib/Roundcube/rcube_cache_shared.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,11 @@ function close()
}

// reset internal cache index, thanks to this we can force index reload
$this->index = null;
$this->index = null;
$this->index_changed = false;
$this->cache = array();
$this->cache_sums = array();
$this->cache_changes = array();
}

/**
Expand Down Expand Up @@ -629,13 +633,15 @@ private function max_packet_size()
$this->max_packet -= 2000;
}
else if ($this->type == 'memcache') {
$stats = $this->db->getStats();
$remaining = $stats['limit_maxbytes'] - $stats['bytes'];
$this->max_packet = min($remaining / 5, $this->max_packet);
if ($stats = $this->db->getStats()) {
$remaining = $stats['limit_maxbytes'] - $stats['bytes'];
$this->max_packet = min($remaining / 5, $this->max_packet);
}
}
else if ($this->type == 'apc' && function_exists('apc_sma_info')) {
$stats = apc_sma_info();
$this->max_packet = min($stats['avail_mem'] / 5, $this->max_packet);
if ($stats = apc_sma_info()) {
$this->max_packet = min($stats['avail_mem'] / 5, $this->max_packet);
}
}
}

Expand Down
14 changes: 14 additions & 0 deletions program/lib/Roundcube/rcube_db.php
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,20 @@ public function reset()
$this->last_result = null;
}

/**
* Terminate database connection.
*/
public function closeConnection()
{
$this->db_connected = false;
$this->db_index = 0;

// release statement and connection resources
$this->last_result = null;
$this->dbh = null;
$this->dbhs = array();
}

/**
* Formats input so it can be safely used in a query
*
Expand Down
14 changes: 14 additions & 0 deletions program/lib/Roundcube/rcube_db_oracle.php
Original file line number Diff line number Diff line change
Expand Up @@ -601,4 +601,18 @@ public function rollbackTransaction()

return $this->last_result = $this->dbh->rollBack();
}

/**
* Terminate database connection.
*/
public function closeConnection()
{
// release statement and close connection(s)
$this->last_result = null;
foreach ($this->dbhs as $dbh) {
oci_close($dbh);
}

parent::closeConnection();
}
}
2 changes: 2 additions & 0 deletions program/lib/Roundcube/rcube_imap.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,9 @@ public function connect($host, $user, $pass, $port=143, $use_ssl=null)
*/
public function close()
{
$this->connect_done = false;
$this->conn->closeConnection();

if ($this->mcache) {
$this->mcache->close();
}
Expand Down

2 comments on commit c1c0a0d

@CRtEurope
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was not a good idea. Now we all permanently get connection errors every few moments. And the email boxes remain empty. After logging out and logging in again everything seems to work as usual but a moment later you again get connection errors.
It would be better to withdraw this change as before everything worked perfectly without any connection error.

@alecpl
Copy link
Member Author

@alecpl alecpl commented on c1c0a0d Sep 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure? I don't see how it could be. Part of this is not used at all and part only on caches "closing" so, really it would be strange. Are you using memcache? What connection errors?

Please sign in to comment.