Working with multiple Clickhouse instances in a project
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Models;
|
||||
|
||||
use PhpClickHouseLaravel\BaseModel;
|
||||
|
||||
class Example2 extends BaseModel
|
||||
{
|
||||
protected $connection = 'clickhouse2';
|
||||
protected $table = 'examples2';
|
||||
}
|
||||
@@ -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']);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user