appwrite/src/Appwrite/Database/DatabasePool.php

209 lines
5.4 KiB
PHP
Raw Normal View History

<?php
namespace Appwrite\Database;
2022-07-01 12:18:33 +00:00
use Appwrite\DSN\DSN;
use Appwrite\Extend\Exception;
2022-07-01 12:18:33 +00:00
use PDO;
use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
use Swoole\Database\PDOProxy;
2022-07-01 12:18:33 +00:00
use Utopia\App;
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
*/
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-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-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
{
$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-07-01 12:18:33 +00:00
$pool->put($db);
}
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-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-07-01 12:18:33 +00:00
$this->consoleDB = $consoleDB;
}
}