2022-06-14 14:57:57 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace Appwrite\Database;
|
|
|
|
|
|
2022-07-01 12:18:33 +00:00
|
|
|
use Appwrite\DSN\DSN;
|
2022-06-14 14:57:57 +00:00
|
|
|
use Appwrite\Extend\Exception;
|
2022-07-01 12:18:33 +00:00
|
|
|
use PDO;
|
|
|
|
|
use Swoole\Database\PDOConfig;
|
2022-06-14 14:57:57 +00:00
|
|
|
use Swoole\Database\PDOPool;
|
|
|
|
|
use Swoole\Database\PDOProxy;
|
2022-07-01 12:18:33 +00:00
|
|
|
use Utopia\App;
|
2022-06-14 14:57:57 +00:00
|
|
|
|
|
|
|
|
class DatabasePool {
|
|
|
|
|
|
2022-06-23 08:50:00 +00:00
|
|
|
/**
|
|
|
|
|
* @var array
|
2022-07-01 12:18:33 +00:00
|
|
|
*
|
|
|
|
|
* Array to store mappings from database names to PDOPool instances.
|
2022-06-23 08:50:00 +00:00
|
|
|
*/
|
2022-06-14 14:57:57 +00:00
|
|
|
protected array $pools = [];
|
|
|
|
|
|
2022-07-01 12:18:33 +00:00
|
|
|
/**
|
|
|
|
|
* @var array
|
|
|
|
|
*
|
|
|
|
|
* Array to store mappings from database names to DSNs
|
|
|
|
|
*/
|
|
|
|
|
protected array $databases = [];
|
|
|
|
|
|
2022-06-23 08:50:00 +00:00
|
|
|
/**
|
|
|
|
|
* @var string
|
|
|
|
|
*/
|
|
|
|
|
protected string $consoleDB = '';
|
|
|
|
|
|
|
|
|
|
/**
|
2022-07-01 12:18:33 +00:00
|
|
|
* Constructor for Database pools
|
|
|
|
|
*
|
|
|
|
|
* @param array $consoleDB
|
|
|
|
|
* @param array $projectDB
|
2022-06-23 08:50:00 +00:00
|
|
|
*
|
|
|
|
|
*/
|
2022-07-01 12:18:33 +00:00
|
|
|
public function __construct(array $consoleDB, array $projectDB)
|
2022-06-23 08:50:00 +00:00
|
|
|
{
|
2022-07-01 12:18:33 +00:00
|
|
|
if(count($consoleDB) != 1) {
|
|
|
|
|
throw new Exception('Console DB should contain only one entry', 500);
|
2022-06-23 08:50:00 +00:00
|
|
|
}
|
|
|
|
|
|
2022-07-01 12:18:33 +00:00
|
|
|
if(empty($projectDB)) {
|
|
|
|
|
throw new Exception('Project DB is not defined', 500);
|
|
|
|
|
}
|
2022-06-23 08:50:00 +00:00
|
|
|
|
2022-07-01 12:18:33 +00:00
|
|
|
$this->consoleDB = array_key_first($consoleDB);
|
|
|
|
|
$this->databases = array_merge($consoleDB, $projectDB);
|
|
|
|
|
|
|
|
|
|
/** Create PDO pool instances for all the databases */
|
|
|
|
|
foreach ($this->databases as $name => $dsn) {
|
|
|
|
|
$dsn = new DSN($dsn);
|
|
|
|
|
$pool = new PDOPool(
|
|
|
|
|
(new PDOConfig())
|
|
|
|
|
->withHost($dsn->getHost())
|
|
|
|
|
->withPort($dsn->getPort())
|
|
|
|
|
->withDbName($dsn->getDatabase())
|
|
|
|
|
->withCharset('utf8mb4')
|
|
|
|
|
->withUsername($dsn->getUser())
|
|
|
|
|
->withPassword($dsn->getPassword())
|
|
|
|
|
->withOptions([
|
|
|
|
|
PDO::ATTR_ERRMODE => App::isDevelopment() ? PDO::ERRMODE_WARNING : PDO::ERRMODE_SILENT, // If in production mode, warnings are not displayed
|
|
|
|
|
]),
|
|
|
|
|
64
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$this->pools[$name] = $pool;
|
|
|
|
|
}
|
2022-06-23 08:50:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-07-01 12:18:33 +00:00
|
|
|
* Get a single PDO instance
|
2022-06-23 08:50:00 +00:00
|
|
|
*
|
2022-07-01 12:18:33 +00:00
|
|
|
* @param string $name
|
2022-06-23 08:50:00 +00:00
|
|
|
*
|
2022-07-01 12:18:33 +00:00
|
|
|
* @return ?PDO
|
2022-06-23 08:50:00 +00:00
|
|
|
*/
|
2022-07-01 12:18:33 +00:00
|
|
|
public function getDB(string $name): ?PDO
|
2022-06-23 08:50:00 +00:00
|
|
|
{
|
2022-07-01 13:43:38 +00:00
|
|
|
$dsn = $this->databases[$name] ?? throw new Exception("Database with name : $name not found.", 500);
|
2022-07-01 12:18:33 +00:00
|
|
|
|
|
|
|
|
$dsn = new DSN($dsn);
|
|
|
|
|
$dbHost = $dsn->getHost();
|
|
|
|
|
$dbPort = $dsn->getPort();
|
|
|
|
|
$dbUser = $dsn->getUser();
|
|
|
|
|
$dbPass = $dsn->getPassword();
|
|
|
|
|
$dbScheme = $dsn->getDatabase();
|
|
|
|
|
|
|
|
|
|
$pdo = new PDO("mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4", $dbUser, $dbPass, array(
|
|
|
|
|
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4',
|
|
|
|
|
PDO::ATTR_TIMEOUT => 3, // Seconds
|
|
|
|
|
PDO::ATTR_PERSISTENT => true,
|
|
|
|
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
|
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
return $pdo;
|
2022-06-23 08:50:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-07-01 12:18:33 +00:00
|
|
|
* Get a PDO instance from the list of available database pools . To be used in co-routines
|
2022-06-23 08:50:00 +00:00
|
|
|
*
|
|
|
|
|
* @param string $name
|
2022-07-01 12:18:33 +00:00
|
|
|
*
|
|
|
|
|
* @return ?PDOProxy
|
2022-06-23 08:50:00 +00:00
|
|
|
*/
|
2022-07-01 12:18:33 +00:00
|
|
|
public function getDBFromPool(string $name): ?PDOProxy
|
2022-06-14 14:57:57 +00:00
|
|
|
{
|
2022-07-01 12:18:33 +00:00
|
|
|
$pool = $this->pools[$name] ?? throw new Exception("Database pool with name : $name not found. Check the value of _APP_PROJECT_DB in .env", 500);
|
|
|
|
|
return $pool->get();
|
2022-06-14 14:57:57 +00:00
|
|
|
}
|
|
|
|
|
|
2022-06-23 08:50:00 +00:00
|
|
|
/**
|
2022-07-01 12:18:33 +00:00
|
|
|
* Return a PDO instance back to its database pool
|
|
|
|
|
*
|
|
|
|
|
* @param PDOProxy $db
|
2022-06-23 08:50:00 +00:00
|
|
|
* @param string $name
|
|
|
|
|
*
|
2022-07-01 12:18:33 +00:00
|
|
|
* @return void
|
2022-06-23 08:50:00 +00:00
|
|
|
*/
|
2022-07-01 12:18:33 +00:00
|
|
|
public function put(PDOProxy $db, string $name): void
|
2022-06-14 14:57:57 +00:00
|
|
|
{
|
|
|
|
|
$pool = $this->pools[$name] ?? null;
|
|
|
|
|
if ($pool === null) {
|
2022-07-01 12:18:33 +00:00
|
|
|
throw new Exception("Failed to put PDO into database pool. Database pool with name : $name not found", 500);
|
2022-06-14 14:57:57 +00:00
|
|
|
}
|
2022-07-01 12:18:33 +00:00
|
|
|
$pool->put($db);
|
2022-06-14 14:57:57 +00:00
|
|
|
}
|
|
|
|
|
|
2022-06-23 08:50:00 +00:00
|
|
|
/**
|
2022-07-01 12:18:33 +00:00
|
|
|
* Function to get a random PDO instance from the available database pools
|
2022-06-23 08:50:00 +00:00
|
|
|
*
|
|
|
|
|
* @return array [PDO, string]
|
|
|
|
|
*/
|
2022-07-01 12:18:33 +00:00
|
|
|
public function getAnyFromPool(): array
|
2022-06-23 08:50:00 +00:00
|
|
|
{
|
|
|
|
|
$key = array_rand($this->pools);
|
2022-07-01 12:18:33 +00:00
|
|
|
$pool = $this->getDBFromPool($key);
|
|
|
|
|
|
2022-06-23 08:50:00 +00:00
|
|
|
return [
|
|
|
|
|
'name' => $key,
|
2022-07-01 12:18:33 +00:00
|
|
|
'db' => $pool
|
2022-06-23 08:50:00 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2022-07-01 12:18:33 +00:00
|
|
|
* Convenience methods for console DB
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Function to get a single instace of the console DB
|
|
|
|
|
*
|
|
|
|
|
* @return ?PDO
|
|
|
|
|
*/
|
|
|
|
|
public function getConsoleDB(): ?PDO
|
|
|
|
|
{
|
|
|
|
|
if (empty($this->consoleDB)) {
|
|
|
|
|
throw new Exception('Console DB is not defined', 500);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return $this->getDB($this->consoleDB);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Function to get an instance of the console DB from the database pool
|
|
|
|
|
*
|
|
|
|
|
* @return ?PDOProxy
|
|
|
|
|
*/
|
|
|
|
|
public function getConsoleDBFromPool(): ?PDOProxy
|
|
|
|
|
{
|
|
|
|
|
if (empty($this->consoleDB)) {
|
|
|
|
|
throw new Exception("Console DB not set", 500);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->getDBFromPool($this->consoleDB);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the console DB back to the console database pool
|
2022-06-23 08:50:00 +00:00
|
|
|
*
|
|
|
|
|
* @param PDOProxy $db
|
|
|
|
|
*
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
2022-07-01 12:18:33 +00:00
|
|
|
public function putConsoleDB(PDOProxy $db): void
|
2022-06-14 14:57:57 +00:00
|
|
|
{
|
2022-07-01 12:18:33 +00:00
|
|
|
$this->put($db, $this->consoleDB);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Function to set the name of the console database
|
|
|
|
|
*
|
|
|
|
|
* @param string $consoleDB
|
|
|
|
|
*
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
public function setConsoleDB(string $consoleDB): void
|
|
|
|
|
{
|
|
|
|
|
if(!isset($this->pools[$consoleDB])) {
|
|
|
|
|
throw new Exception("Console DB with name : $consoleDB not found. Add it using ", 500);
|
2022-06-14 14:57:57 +00:00
|
|
|
}
|
2022-07-01 12:18:33 +00:00
|
|
|
|
|
|
|
|
$this->consoleDB = $consoleDB;
|
2022-06-14 14:57:57 +00:00
|
|
|
}
|
|
|
|
|
}
|