Skip to content

Commit 3f53e48

Browse files
committed
feat: add HTTP keep-alive support and connection statistics to ClickHouse adapter
1 parent 00dae44 commit 3f53e48

2 files changed

Lines changed: 110 additions & 0 deletions

File tree

src/Usage/Adapter/ClickHouse.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ class ClickHouse extends SQL
7272
/** @var bool Whether to enable gzip compression for HTTP requests/responses */
7373
private bool $enableCompression = false;
7474

75+
/** @var bool Whether to enable HTTP keep-alive for connection pooling */
76+
private bool $enableKeepAlive = true;
77+
78+
/** @var int Number of requests made using this adapter instance */
79+
private int $requestCount = 0;
80+
7581
/**
7682
* @param string $host ClickHouse host
7783
* @param string $username ClickHouse username (default: 'default')
@@ -155,6 +161,34 @@ public function setCompression(bool $enable): self
155161
return $this;
156162
}
157163

164+
/**
165+
* Enable or disable HTTP keep-alive for connection pooling.
166+
* When enabled, HTTP connections are reused across multiple requests, reducing latency.
167+
*
168+
* @param bool $enable Whether to enable keep-alive (default: true)
169+
* @return self
170+
*/
171+
public function setKeepAlive(bool $enable): self
172+
{
173+
$this->enableKeepAlive = $enable;
174+
return $this;
175+
}
176+
177+
/**
178+
* Get connection statistics for monitoring.
179+
*
180+
* @return array{request_count: int, keep_alive_enabled: bool, compression_enabled: bool, query_logging_enabled: bool}
181+
*/
182+
public function getConnectionStats(): array
183+
{
184+
return [
185+
'request_count' => $this->requestCount,
186+
'keep_alive_enabled' => $this->enableKeepAlive,
187+
'compression_enabled' => $this->enableCompression,
188+
'query_logging_enabled' => $this->enableQueryLogging,
189+
];
190+
}
191+
158192
/**
159193
* Get the query execution log.
160194
*
@@ -506,11 +540,21 @@ private function query(string $sql, array $params = []): string
506540
// Update the database header for each query (in case setDatabase was called)
507541
$this->client->addHeader('X-ClickHouse-Database', $this->database);
508542

543+
// Enable keep-alive for connection pooling
544+
if ($this->enableKeepAlive) {
545+
$this->client->addHeader('Connection', 'keep-alive');
546+
} else {
547+
$this->client->addHeader('Connection', 'close');
548+
}
549+
509550
// Enable compression if configured
510551
if ($this->enableCompression) {
511552
$this->client->addHeader('Accept-Encoding', 'gzip');
512553
}
513554

555+
// Track request count for statistics
556+
$this->requestCount++;
557+
514558
// Build multipart form data body with query and parameters
515559
// The Fetch client will automatically encode arrays as multipart/form-data
516560
$body = ['query' => $sql];
@@ -575,11 +619,21 @@ private function insert(string $table, array $data): void
575619
$this->client->addHeader('X-ClickHouse-Database', $this->database);
576620
$this->client->addHeader('Content-Type', 'application/x-ndjson');
577621

622+
// Enable keep-alive for connection pooling
623+
if ($this->enableKeepAlive) {
624+
$this->client->addHeader('Connection', 'keep-alive');
625+
} else {
626+
$this->client->addHeader('Connection', 'close');
627+
}
628+
578629
// Enable compression if configured
579630
if ($this->enableCompression) {
580631
$this->client->addHeader('Accept-Encoding', 'gzip');
581632
}
582633

634+
// Track request count for statistics
635+
$this->requestCount++;
636+
583637
// Join JSON strings with newlines
584638
$body = implode("\n", $data);
585639

tests/Usage/Adapter/ClickHouseTest.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,4 +594,60 @@ public function testCompression(): void
594594
$sum = $usage->sumByPeriod('compression.test.batch', '1h');
595595
$this->assertIsInt($sum);
596596
}
597+
598+
/**
599+
* Test connection pooling functionality
600+
*/
601+
public function testConnectionPooling(): void
602+
{
603+
// Create a new adapter instance
604+
$host = getenv('CLICKHOUSE_HOST') ?: 'clickhouse';
605+
$username = getenv('CLICKHOUSE_USER') ?: 'default';
606+
$password = getenv('CLICKHOUSE_PASSWORD') ?: 'clickhouse';
607+
$port = (int) (getenv('CLICKHOUSE_PORT') ?: 8123);
608+
$secure = (bool) (getenv('CLICKHOUSE_SECURE') ?: false);
609+
610+
$adapter = new ClickHouseAdapter($host, $username, $password, $port, $secure);
611+
$adapter->setNamespace('utopia_usage_pooling_test');
612+
$adapter->setTenant(1);
613+
614+
if ($database = getenv('CLICKHOUSE_DATABASE')) {
615+
$adapter->setDatabase($database);
616+
}
617+
618+
$usage = new Usage($adapter);
619+
$usage->setup();
620+
621+
// Test enabling keep-alive
622+
$result = $adapter->setKeepAlive(true);
623+
$this->assertInstanceOf(ClickHouseAdapter::class, $result);
624+
625+
// Test disabling keep-alive
626+
$result = $adapter->setKeepAlive(false);
627+
$this->assertInstanceOf(ClickHouseAdapter::class, $result);
628+
629+
// Re-enable for testing
630+
$adapter->setKeepAlive(true);
631+
632+
// Get initial stats
633+
$stats = $adapter->getConnectionStats();
634+
$this->assertIsArray($stats);
635+
$this->assertArrayHasKey('request_count', $stats);
636+
$this->assertArrayHasKey('keep_alive_enabled', $stats);
637+
$this->assertArrayHasKey('compression_enabled', $stats);
638+
$this->assertArrayHasKey('query_logging_enabled', $stats);
639+
$this->assertTrue($stats['keep_alive_enabled']);
640+
641+
$initialCount = $stats['request_count'];
642+
643+
// Make some requests
644+
$usage->log('pooling.test', 100, '1h', ['test' => 'value']);
645+
$usage->find([]);
646+
$usage->count([]);
647+
648+
// Verify request count increased
649+
$newStats = $adapter->getConnectionStats();
650+
$this->assertGreaterThan($initialCount, $newStats['request_count']);
651+
$this->assertGreaterThanOrEqual(3, $newStats['request_count'] - $initialCount);
652+
}
597653
}

0 commit comments

Comments
 (0)