manager_name = $manager_name; } /** * Execute a Closure within a transaction. * * @param Closure $callback * @return mixed * * @throws \Exception */ public function transaction(Closure $callback) { $retry = 0; $done = false; $result = null; while (!$done and $retry < self::MaxRetries) { try { $em = Registry::getManager($this->manager_name); $con = $em->getConnection(); /** * Some database systems close the connection after a period of time, in MySQL this is system variable * `wait_timeout`. Given the daemon is meant to run indefinitely we need to make sure we have an open * connection before working any job. Otherwise we would see `MySQL has gone away` type errors. */ if ($con->ping() === false) { $con->close(); $con->connect(); } if (!$em->isOpen()) { Log::warning("DoctrineTransactionService::transaction: entity manager is closed!, trying to re open..."); $em = Registry::resetManager($this->manager_name); // new entity manager $con = $em->getConnection(); } $con->beginTransaction(); // suspend auto-commit $result = $callback($this); $em->flush(); $con->commit(); $done = true; } catch (RetryableException $ex) { Log::warning("retrying ..."); Registry::resetManager($this->manager_name); $con->rollBack(); Log::warning($ex); $retry++; if ($retry === self::MaxRetries) { throw $ex; } } catch (Exception $ex) { Log::warning("rolling back transaction"); $em->close(); $con->rollBack(); Log::error($ex); throw $ex; } } return $result; } }