Dark magic to allow serializing session to database on shutdown
This commit is contained in:
parent
5560b140f5
commit
1cd41cc159
@ -255,7 +255,7 @@ class API extends InternalDoc
|
|||||||
$this->API->destructing = true;
|
$this->API->destructing = true;
|
||||||
$this->API->unreference();
|
$this->API->unreference();
|
||||||
}
|
}
|
||||||
if (isset($this->wrapper)) {
|
if (isset($this->wrapper) && !Magic::$signaled) {
|
||||||
$this->logger->logger('Prompting final serialization...');
|
$this->logger->logger('Prompting final serialization...');
|
||||||
Tools::wait($this->wrapper->serialize());
|
Tools::wait($this->wrapper->serialize());
|
||||||
$this->logger->logger('Done final serialization!');
|
$this->logger->logger('Done final serialization!');
|
||||||
|
@ -204,6 +204,20 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
'msg_resend_ans_req',
|
'msg_resend_ans_req',
|
||||||
];
|
];
|
||||||
const DEFAULT_GETUPDATES_PARAMS = ['offset' => 0, 'limit' => null, 'timeout' => 100];
|
const DEFAULT_GETUPDATES_PARAMS = ['offset' => 0, 'limit' => null, 'timeout' => 100];
|
||||||
|
/**
|
||||||
|
* Array of references to all instances of MTProto.
|
||||||
|
*
|
||||||
|
* This seems like a recipe for memory leaks, but this is actually required to allow saving the session on shutdown.
|
||||||
|
* When using a network I/O-based database+the EvDriver of AMPHP, calling die(); causes premature garbage collection of the event loop.
|
||||||
|
* This garbage collection happens always, even if a reference to the event handler is already present elsewhere (probably ev dark magic).
|
||||||
|
*
|
||||||
|
* Finally, this causes the process to hang on shutdown, since the database driver cannot receive a reply from the server, because the event loop is down.
|
||||||
|
*
|
||||||
|
* To avoid this, we store each MTProto instance in here (unreferencing on shutdown in unreference()), and call serialize() on all instances before calling die; in Magic.
|
||||||
|
*
|
||||||
|
* @var self[]
|
||||||
|
*/
|
||||||
|
public static array $references = [];
|
||||||
/**
|
/**
|
||||||
* Instance of wrapper API.
|
* Instance of wrapper API.
|
||||||
*
|
*
|
||||||
@ -505,9 +519,23 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
yield $this->session->offsetSet('data', $data);
|
yield $this->session->offsetSet('data', $data);
|
||||||
var_dump("Saved!");
|
|
||||||
return $this->session;
|
return $this->session;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Serialize all instances.
|
||||||
|
*
|
||||||
|
* CALLED ONLY ON SHUTDOWN.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function serializeAll(): void
|
||||||
|
{
|
||||||
|
Logger::log('Prompting final serialization (SHUTDOWN)...');
|
||||||
|
foreach (self::$references as $instance) {
|
||||||
|
Tools::wait($instance->wrapper->serialize());
|
||||||
|
}
|
||||||
|
Logger::log('Done final serialization (SHUTDOWN)!');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor function.
|
* Constructor function.
|
||||||
@ -519,6 +547,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
*/
|
*/
|
||||||
public function __magic_construct(SettingsAbstract $settings, APIWrapper $wrapper)
|
public function __magic_construct(SettingsAbstract $settings, APIWrapper $wrapper)
|
||||||
{
|
{
|
||||||
|
self::$references[\spl_object_hash($this)] = $this;
|
||||||
$this->wrapper = $wrapper;
|
$this->wrapper = $wrapper;
|
||||||
$this->setInitPromise($this->__construct_async($settings));
|
$this->setInitPromise($this->__construct_async($settings));
|
||||||
}
|
}
|
||||||
@ -1023,6 +1052,8 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
*/
|
*/
|
||||||
public function wakeup(SettingsAbstract $settings, APIWrapper $wrapper): \Generator
|
public function wakeup(SettingsAbstract $settings, APIWrapper $wrapper): \Generator
|
||||||
{
|
{
|
||||||
|
// Set reference to itself
|
||||||
|
self::$references[\spl_object_hash($this)] = $this;
|
||||||
// Set API wrapper
|
// Set API wrapper
|
||||||
$this->wrapper = $wrapper;
|
$this->wrapper = $wrapper;
|
||||||
// BC stuff
|
// BC stuff
|
||||||
@ -1132,6 +1163,9 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
public function unreference(): void
|
public function unreference(): void
|
||||||
{
|
{
|
||||||
$this->logger->logger("Will unreference instance");
|
$this->logger->logger("Will unreference instance");
|
||||||
|
if (isset(self::$references[\spl_object_hash($this)])) {
|
||||||
|
unset(self::$references[\spl_object_hash($this)]);
|
||||||
|
}
|
||||||
$this->stopLoops();
|
$this->stopLoops();
|
||||||
if (isset($this->seqUpdater)) {
|
if (isset($this->seqUpdater)) {
|
||||||
$this->seqUpdater->signal(true);
|
$this->seqUpdater->signal(true);
|
||||||
@ -1153,7 +1187,6 @@ class MTProto extends AsyncConstruct implements TLCallback
|
|||||||
$datacenter->disconnect();
|
$datacenter->disconnect();
|
||||||
}
|
}
|
||||||
$this->logger->logger("Unreferenced instance");
|
$this->logger->logger("Unreferenced instance");
|
||||||
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Destructor.
|
* Destructor.
|
||||||
|
@ -59,7 +59,7 @@ class Magic
|
|||||||
*/
|
*/
|
||||||
public static $isFork = false;
|
public static $isFork = false;
|
||||||
/**
|
/**
|
||||||
* Whether this is an IPC worker
|
* Whether this is an IPC worker.
|
||||||
*/
|
*/
|
||||||
public static bool $isIpcWorker = false;
|
public static bool $isIpcWorker = false;
|
||||||
/**
|
/**
|
||||||
@ -234,7 +234,7 @@ class Magic
|
|||||||
// Setup error reporting
|
// Setup error reporting
|
||||||
\set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
|
\set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
|
||||||
\set_exception_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionHandler']);
|
\set_exception_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionHandler']);
|
||||||
self::$isIpcWorker = defined(\MADELINE_WORKER_TYPE::class) ? \MADELINE_WORKER_TYPE === 'madeline-ipc' : false;
|
self::$isIpcWorker = \defined(\MADELINE_WORKER_TYPE::class) ? \MADELINE_WORKER_TYPE === 'madeline-ipc' : false;
|
||||||
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
|
if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
|
||||||
try {
|
try {
|
||||||
\error_reporting(E_ALL);
|
\error_reporting(E_ALL);
|
||||||
@ -412,6 +412,7 @@ class Magic
|
|||||||
$driver->unreference($key);
|
$driver->unreference($key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MTProto::serializeAll();
|
||||||
Loop::stop();
|
Loop::stop();
|
||||||
die($code);
|
die($code);
|
||||||
}
|
}
|
||||||
|
@ -425,6 +425,18 @@ class Connection extends SettingsAbstract
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set proxies.
|
||||||
|
*
|
||||||
|
* @param array $proxies Proxies
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setProxy(array $proxies): self
|
||||||
|
{
|
||||||
|
$this->proxy = $proxies;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Clear proxies.
|
* Clear proxies.
|
||||||
*
|
*
|
||||||
|
@ -46,7 +46,7 @@ abstract class SettingsAbstract
|
|||||||
!isset($defaults[$name])
|
!isset($defaults[$name])
|
||||||
|| $other->{$name} !== $defaults[$name] // Isn't equal to the default value
|
|| $other->{$name} !== $defaults[$name] // Isn't equal to the default value
|
||||||
)
|
)
|
||||||
&& $other->{$name} !== $this->{"get$uc"}
|
&& $other->{$name} !== $this->{$name}
|
||||||
) {
|
) {
|
||||||
$this->{"set$uc"}($other->{$name});
|
$this->{"set$uc"}($other->{$name});
|
||||||
$this->changed = true;
|
$this->changed = true;
|
||||||
|
@ -52,6 +52,7 @@ class Shutdown
|
|||||||
foreach (self::$callbacks as $callback) {
|
foreach (self::$callbacks as $callback) {
|
||||||
$callback();
|
$callback();
|
||||||
}
|
}
|
||||||
|
Magic::shutdown(0);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Add a callback for script shutdown.
|
* Add a callback for script shutdown.
|
||||||
|
Loading…
Reference in New Issue
Block a user