Store main session in database

This commit is contained in:
Daniil Gentili 2020-09-24 23:25:54 +02:00
parent 4123941df3
commit 50ee77c229
14 changed files with 163 additions and 7 deletions

View File

@ -20,6 +20,7 @@
namespace danog\MadelineProto;
use Amp\Ipc\Sync\ChannelledSocket;
use Amp\Loop;
use danog\MadelineProto\Ipc\Client;
use danog\MadelineProto\Ipc\Server;
use danog\MadelineProto\Settings\Logger as SettingsLogger;
@ -159,6 +160,7 @@ class API extends InternalDoc
$this->APIFactory();
$this->logger->logger(Lang::$current_lang['madelineproto_ready'], Logger::NOTICE);
}
/**
* Reconnect to full instance.
*
@ -276,7 +278,7 @@ class API extends InternalDoc
}
}
}
$this->methods = self::getInternalMethodList($this->API);
$this->methods = self::getInternalMethodList($this->API, MTProto::class);
}
}

View File

@ -188,7 +188,10 @@ final class APIWrapper
yield from $this->API->initAsynchronously();
}
yield from $this->session->serialize($this, $this->session->getSessionPath());
yield from $this->session->serialize(
yield from $this->API->serializeSession($this),
$this->session->getSessionPath()
);
if ($this->API) {
yield from $this->session->storeLightState($this->API);

View File

@ -200,8 +200,8 @@ abstract class AbstractAPIFactory extends AsyncConstruct
/**
* Get fully resolved method list for object, including snake_case and camelCase variants.
*
* @param API|MTProto $value Value
* @param string $class Custom class name
* @param API|MTProto|Client $value Value
* @param string $class Custom class name
*
* @return array
*/

View File

@ -27,7 +27,10 @@ class DbPropertiesFactory
*/
public static function get(DatabaseAbstract $dbSettings, string $namePrefix, string $propertyType, string $name, $value = null): Promise
{
$class = __NAMESPACE__;
$propertyType = \strtolower($propertyType);
$class = $propertyType === 'arraynullcache' && !$dbSettings instanceof Memory
? __NAMESPACE__.'\\NullCache'
: __NAMESPACE__ ;
switch (true) {
case $dbSettings instanceof Memory:
@ -49,6 +52,7 @@ class DbPropertiesFactory
/** @var DbType $class */
switch (\strtolower($propertyType)) {
case 'arraynullcache':
case 'array':
$class .= 'Array';
break;

View File

@ -0,0 +1,10 @@
<?php
namespace danog\MadelineProto\Db\NullCache;
use danog\MadelineProto\Settings\Database\Mysql;
class MysqlArray extends Mysql
{
use NullCacheTrait;
}

View File

@ -0,0 +1,36 @@
<?php
namespace danog\MadelineProto\Db\NullCache;
trait NullCacheTrait
{
protected function getCache(string $key, $default = null)
{
}
/**
* Save item in cache.
*
* @param string $key
* @param $value
*/
protected function setCache(string $key, $value): void
{
}
/**
* Remove key from cache.
*
* @param string $key
*/
protected function unsetCache(string $key): void
{
}
protected function startCacheCleanupLoop(): void
{
}
protected function stopCacheCleanupLoop(): void
{
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace danog\MadelineProto\Db\NullCache;
use danog\MadelineProto\Settings\Database\Postgres;
class PostgresArray extends Postgres
{
use NullCacheTrait;
}

View File

@ -0,0 +1,10 @@
<?php
namespace danog\MadelineProto\Db\NullCache;
use danog\MadelineProto\Db\RedisArray as DbRedisArray;
class RedisArray extends DbRedisArray
{
use NullCacheTrait;
}

View File

@ -5404,6 +5404,15 @@ class InternalDoc extends APIFactory
{
return \danog\MadelineProto\Tools::isArrayOrAlike($var);
}
/**
* Whether we're an IPC client instance.
*
* @return boolean
*/
public function isIpc(): bool
{
return $this->API->isIpc();
}
/**
* Check whether provided bot API ID is a channel.
*

View File

@ -159,6 +159,24 @@ class Client
$this->run = false;
return $this->server->send(Server::SHUTDOWN);
}
/**
* Restart IPC server instance.
*
* @internal
*/
public function restartIpcServer(): Promise
{
return $this->server->send(Server::SHUTDOWN);
}
/**
* Whether we're an IPC client instance.
*
* @return boolean
*/
public function isIpc(): bool
{
return true;
}
/**
* Call function.
*

View File

@ -177,6 +177,7 @@ class Server extends SignalLoop
$result = $this->API->{$payload[0]}(...$payload[1]);
$result = $result instanceof \Generator ? yield from $result : yield $result;
} catch (\Throwable $e) {
$this->API->logger("Got error while calling IPC method: $e", Logger::ERROR);
$result = new ExitFailure($e);
}
try {

View File

@ -24,11 +24,13 @@ use Amp\File\StatCache;
use Amp\Http\Client\HttpClient;
use Amp\Loop;
use Amp\Promise;
use Amp\Success;
use Closure;
use danog\MadelineProto\Async\AsyncConstruct;
use danog\MadelineProto\Db\DbArray;
use danog\MadelineProto\Db\DbPropertiesFactory;
use danog\MadelineProto\Db\DbPropertiesTrait;
use danog\MadelineProto\Db\MemoryArray;
use danog\MadelineProto\Ipc\Server;
use danog\MadelineProto\Loop\Generic\PeriodicLoopInternal;
use danog\MadelineProto\Loop\Update\FeedLoop;
@ -468,6 +470,13 @@ class MTProto extends AsyncConstruct implements TLCallback
]
];
/**
* Nullcache array for storing main session file to DB.
*
* @var DbArray|Promise[]
*/
public $session;
/**
* List of properties stored in database (memory or external).
* @see DbPropertiesFactory
@ -478,8 +487,23 @@ class MTProto extends AsyncConstruct implements TLCallback
'full_chats' => 'array',
'channel_participants' => 'array',
'usernames' => 'array',
'session' => 'arrayNullCache'
];
/**
* Serialize session, returning object to serialize to db.
*
* @return \Generator
*/
public function serializeSession(object $data): \Generator
{
if ($this->session instanceof MemoryArray) {
return $data;
}
yield $this->session['data'] = \serialize($data);
return $this->session;
}
/**
* Constructor function.
*
@ -1130,6 +1154,24 @@ class MTProto extends AsyncConstruct implements TLCallback
$this->unreference();
$this->logger("Successfully destroyed MadelineProto");
}
/**
* Restart IPC server instance.
*
* @internal
*/
public function restartIpcServer(): Promise
{
return new Success(); // Can only be called from client
}
/**
* Whether we're an IPC client instance.
*
* @return boolean
*/
public function isIpc(): bool
{
return false;
}
/**
* Parse, update and store settings.
*
@ -1166,8 +1208,7 @@ class MTProto extends AsyncConstruct implements TLCallback
$this->setupLogger();
if ($reinit) {
$this->__construct();
yield from $this->initAsynchronously();
yield from $this->__construct_async($this->settings);
}
}
/**

View File

@ -22,6 +22,8 @@ namespace danog\MadelineProto;
use Amp\Deferred;
use Amp\Loop;
use Amp\Promise;
use danog\MadelineProto\Db\DbArray;
use danog\MadelineProto\Db\DriverArray;
use danog\MadelineProto\Ipc\Server;
use danog\MadelineProto\MTProtoSession\Session;
@ -191,6 +193,10 @@ abstract class Serialization
if ($isNew) {
$unserialized = yield from $session->unserialize();
if ($unserialized instanceof DriverArray) {
yield from $unserialized->initConnection($unserialized->dbSettings);
$unserialized = \unserialize(yield $unserialized['data']);
}
} else {
$unserialized = yield from self::legacyUnserialize($session->getLegacySessionPath());
}

View File

@ -177,6 +177,12 @@ class Settings extends SettingsAbstract
$this->serialization->merge($settings);
} elseif ($settings instanceof TLSchema) {
$this->schema->merge($settings);
} elseif ($settings instanceof DatabaseAbstract) {
if (!$this->db instanceof $settings) {
$this->db = $settings;
} else {
$this->db->merge($settings);
}
}
return;
}