Working with multiple Clickhouse instances in a project

This commit is contained in:
Denis Glushkov
2023-03-24 12:47:46 +05:00
parent 821a2879ef
commit 7429faa99a
13 changed files with 221 additions and 41 deletions
+5
View File
@@ -1,3 +1,8 @@
## 1.17.0 [2023-03-24]
### Features
1. Working with multiple Clickhouse instances in a project
## 1.16.3 [2023-03-03]
### Bug fixes
+57
View File
@@ -301,3 +301,60 @@ MyTable::where('field_one', 123)
// Array data type
MyTable::insertAssoc([[1, 'str', new InsertArray(['a','b'])]]);
```
### Working with multiple Clickhouse instances in a project
**1.** Add second connection into your config/database.php:
```php
'clickhouse2' => [
'driver' => 'clickhouse',
'host' => 'clickhouse2',
'port' => '8123',
'database' => 'default',
'username' => 'default',
'password' => '',
'timeout_connect' => 2,
'timeout_query' => 2,
'https' => false,
'retries' => 0,
],
```
**2.** Add model
```php
<?php
namespace App\Models\Clickhouse;
use PhpClickHouseLaravel\BaseModel;
class MyTable2 extends BaseModel
{
protected $connection = 'clickhouse2';
protected $table = 'my_table2';
}
```
**3.** Add migration
```php
<?php
return new class extends \PhpClickHouseLaravel\Migration
{
protected $connection = 'clickhouse2';
public function up()
{
static::write('CREATE TABLE my_table2 ...');
}
public function down()
{
static::write('DROP TABLE my_table2');
}
};
```
+9
View File
@@ -6,6 +6,7 @@ services:
build: tests/docker
depends_on:
- clickhouse
- clickhouse2
volumes:
- ./:/src
@@ -18,3 +19,11 @@ services:
hard: 262144
ports:
- "8124:8123"
clickhouse2:
image: yandex/clickhouse-server
ulimits:
nproc: 65535
nofile:
soft: 262144
hard: 262144
+29 -33
View File
@@ -21,6 +21,7 @@ class BaseModel
use HasAttributes;
use HidesAttributes;
use HasEvents;
use WithClient;
/**
* The table associated with the model.
@@ -57,14 +58,13 @@ class BaseModel
* @var Dispatcher
*/
protected static $dispatcher;
/**
* The name of connection.
* The name of the database connection to use.
*
* @var string
*/
public $connection = 'clickhouse';
protected $connection = Connection::DEFAULT_NAME;
/**
* Get the table associated with the model.
@@ -95,14 +95,6 @@ class BaseModel
return $this->tableSources ?? $this->getTable();
}
/**
* @return Client
*/
public static function getClient(): Client
{
return DB::connection($this->connection)->getClient();
}
/**
* Create and return an un-saved model instance.
* @param array $attributes
@@ -174,7 +166,8 @@ class BaseModel
*/
public static function insert(array $rows): Statement
{
return static::getClient()->insert((new static)->getTableForInserts(), $rows);
$instance = new static();
return $instance->getClient()->insert($instance->getTableForInserts(), $rows);
}
/**
@@ -186,7 +179,8 @@ class BaseModel
*/
public static function insertBulk(array $rows, array $columns = []): Statement
{
return static::getClient()->insert((new static)->getTableForInserts(), $rows, $columns);
$instance = new static();
return $instance->getClient()->insert($instance->getTableForInserts(), $rows, $columns);
}
/**
@@ -198,9 +192,8 @@ class BaseModel
public static function prepareAndInsert(array $rows, array $columns = []): Statement
{
$rows = array_map('static::prepareFromRequest', $rows, $columns);
return static::getClient()
->insert((new static)->getTableForInserts(), $rows, $columns);
$instance = new static();
return $instance->getClient()->insert($instance->getTableForInserts(), $rows, $columns);
}
/**
@@ -218,7 +211,8 @@ class BaseModel
$row = array_replace(array_flip($keys), $row);
}
}
return static::getClient()->insertAssocBulk((new static)->getTableForInserts(), $rows);
$instance = new static();
return $instance->getClient()->insertAssocBulk($instance->getTableForInserts(), $rows);
}
/**
@@ -261,7 +255,8 @@ class BaseModel
*/
public static function select($select = ['*']): Builder
{
return (new Builder(static::getClient()))->select($select)->from((new static())->getTable());
$instance = new static();
return (new Builder($instance->getClient()))->select($select)->from($instance->getTable());
}
/**
@@ -324,7 +319,8 @@ class BaseModel
*/
public static function optimize(bool $final = false, ?string $partition = null): Statement
{
$sql = "OPTIMIZE TABLE " . (new static)->getTableSources();
$instance = new static();
$sql = "OPTIMIZE TABLE " . $instance->getTableSources();
if ($partition) {
$sql .= " PARTITION '$partition'";
}
@@ -332,27 +328,28 @@ class BaseModel
$sql .= " FINAL";
}
return static::getClient()->write($sql);
return $instance->getClient()->write($sql);
}
public static function truncate(): Statement
{
return static::getClient()->write('TRUNCATE TABLE ' . (new static())->getTableSources());
$instance = new static();
return $instance->getClient()->write('TRUNCATE TABLE ' . $instance->getTableSources());
}
/**
* @param TwoElementsLogicExpression|string|Closure $column
* @param string|null $operator
* @param int|float|string|null $operator or $value
* @param int|float|string|null $value
* @param string $concatOperator Operator::AND for example
* @return Builder
*/
public static function where($column, $operator = null, $value = null, string $concatOperator = Operator::AND)
public static function where($column, $operator = null, $value = null, string $concatOperator = Operator::AND): Builder
{
$static = new static;
$builder = (new Builder(static::getClient()))->select(['*'])
->from($static->getTable())
->setSourcesTable($static->getTableSources());
$instance = new static();
$builder = (new Builder($instance->getClient()))->select(['*'])
->from($instance->getTable())
->setSourcesTable($instance->getTableSources());
if (is_null($value)) {
// Fix func_num_args() in where clause in BaseBuilder
$builder->where($column, $operator);
@@ -369,11 +366,10 @@ class BaseModel
*/
public static function whereRaw(string $expression): Builder
{
$static = new static;
return (new Builder(static::getClient()))->select(['*'])
->from($static->getTable())
->setSourcesTable($static->getTableSources())
$instance = new static();
return (new Builder($instance->getClient()))->select(['*'])
->from($instance->getTable())
->setSourcesTable($instance->getTableSources())
->whereRaw($expression);
}
}
+10 -1
View File
@@ -14,15 +14,24 @@ use Tinderbox\ClickhouseBuilder\Query\Grammar;
class Builder extends BaseBuilder
{
use WithClient;
/** @var string */
protected $tableSources;
/** @var Client */
protected $client;
/**
* The name of the database connection to use.
*
* @var string|null
*/
protected $connection = Connection::DEFAULT_NAME;
public function __construct(Client $client = null)
{
$this->grammar = new Grammar();
$this->client = $client ?? DB::connection('clickhouse')->getClient();
$this->client = $client ?? $this->getClient();
}
/**
+3
View File
@@ -9,6 +9,9 @@ use Illuminate\Database\Connection as BaseConnection;
class Connection extends BaseConnection
{
public const DEFAULT_NAME = 'clickhouse';
/** @var Client */
protected $client;
+10 -7
View File
@@ -4,15 +4,18 @@ declare(strict_types=1);
namespace PhpClickHouseLaravel;
use ClickHouseDB\Client;
use ClickHouseDB\Statement;
use ClickHouseDB\Transport\CurlerRequest;
use Illuminate\Database\Migrations\Migration as BaseMigration;
use Illuminate\Support\Facades\DB;
use Symfony\Component\Console\Output\ConsoleOutput;
class Migration extends BaseMigration
{
use WithClient;
protected $connection = Connection::DEFAULT_NAME;
/**
* @param string $sql
* @param array $bindings
@@ -20,16 +23,16 @@ class Migration extends BaseMigration
*/
protected static function write(string $sql, array $bindings = []): Statement
{
$instance = new static();
foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $trace) {
if ($trace['function'] == 'pretend') {
$name = static::class;
(new ConsoleOutput())->writeln("<comment>Clickhouse</comment> <info>$name:</info> $sql");
(new ConsoleOutput())->writeln(
"<comment>Clickhouse</comment> <info>$name on connection $instance->connection:</info> $sql"
);
return new Statement(new CurlerRequest());
}
}
/** @var Client $client */
$client = DB::connection('clickhouse')->getClient();
return $client->write($sql, $bindings);
return $instance->getClient()->write($sql, $bindings);
}
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace PhpClickHouseLaravel;
use ClickHouseDB\Client;
use Illuminate\Support\Facades\DB;
trait WithClient
{
public function getClient(): Client
{
return DB::connection($this->connection)->getClient();
}
}
+1
View File
@@ -13,6 +13,7 @@ cp -r /src/tests/* /app/tests
cp /src/tests/config/database.php /app/config/database.php
cp /src/tests/config/app.php /app/config/app.php
cp /src/tests/migrations/exampleTable.php /app/database/migrations/2022_01_01_000000_example.php
cp /src/tests/migrations/example2Table.php /app/database/migrations/2022_01_01_000001_example.php
cat /src/tests/config/.env >> /app/.env
# Running tests
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Tests\Models;
use PhpClickHouseLaravel\BaseModel;
class Example2 extends BaseModel
{
protected $connection = 'clickhouse2';
protected $table = 'examples2';
}
+22
View File
@@ -0,0 +1,22 @@
<?php
use Illuminate\Support\Facades\DB;
use Tests\Models\Example2;
use Tests\TestCase;
class MultiInstancesTest extends TestCase
{
public function testSecondConnection()
{
Example2::truncate();
Example2::insertAssoc([['f_int' => 1, 'f_int2' => 2, 'f_string' => 'a']]);
usleep(3e4); // some lag in clickhouse server
$rows = Example2::where('f_int', 1)->getRows();
$this->assertCount(1, $rows);
$this->assertEquals(2, $rows[0]['f_int2']);
/** @var \ClickHouseDB\Client $db */
$db = DB::connection('clickhouse2')->getClient();
$rows = $db->select("SELECT * FROM examples2 LIMIT 1")->rows();
$this->assertEquals(1, $rows[0]['f_int']);
}
}
+13
View File
@@ -50,6 +50,19 @@ return [
'max_partitions_per_insert_block' => 300,
],
],
'clickhouse2' => [
'driver' => 'clickhouse',
'host' => 'clickhouse2',
'port' => '8123',
'database' => 'default',
'username' => 'default',
'password' => '',
'timeout_connect' => 2,
'timeout_query' => 2,
'https' => false,
'retries' => 0,
],
],
/*
+37
View File
@@ -0,0 +1,37 @@
<?php
return new class extends \PhpClickHouseLaravel\Migration {
protected $connection = 'clickhouse2';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
static::write(
"
CREATE TABLE examples2 (
created_at DateTime64 DEFAULT now64(),
f_int Int64,
f_int2 Int64,
f_string String
)
ENGINE = MergeTree()
ORDER BY (f_int)
"
);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
static::write('DROP TABLE examples2');
}
};