PHPackages                             rasuvaeff/yii3-outbox-clickhouse - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. rasuvaeff/yii3-outbox-clickhouse

ActiveLibrary[Database &amp; ORM](/categories/database)

rasuvaeff/yii3-outbox-clickhouse
================================

Batched ClickHouse exporter for the Yii3 outbox

v1.1.1(1mo ago)00↓50%BSD-3-ClausePHPPHP 8.3 - 8.5CI passing

Since Jun 13Pushed 1w agoCompare

[ Source](https://github.com/rasuvaeff/yii3-outbox-clickhouse)[ Packagist](https://packagist.org/packages/rasuvaeff/yii3-outbox-clickhouse)[ Docs](https://github.com/rasuvaeff/yii3-outbox-clickhouse)[ RSS](/packages/rasuvaeff-yii3-outbox-clickhouse/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependencies (31)Versions (5)Used By (0)

rasuvaeff/yii3-outbox-clickhouse
================================

[](#rasuvaeffyii3-outbox-clickhouse)

[![Stable Version](https://camo.githubusercontent.com/4319ab4b39cce7a81bd34a20ab9c0fee08d1e3a6fb4a22fea1dddadef9705778/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f796969332d6f7574626f782d636c69636b686f7573652f762f737461626c65)](https://packagist.org/packages/rasuvaeff/yii3-outbox-clickhouse)[![Total Downloads](https://camo.githubusercontent.com/c216ea05dc2e0d72ee0b8ec2d9e6ee7e90d94a8fcb65b5e2fa4a79405f8f742b/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f796969332d6f7574626f782d636c69636b686f7573652f646f776e6c6f616473)](https://packagist.org/packages/rasuvaeff/yii3-outbox-clickhouse)[![Build](https://github.com/rasuvaeff/yii3-outbox-clickhouse/actions/workflows/build.yml/badge.svg)](https://github.com/rasuvaeff/yii3-outbox-clickhouse/actions)[![Static analysis](https://github.com/rasuvaeff/yii3-outbox-clickhouse/actions/workflows/static-analysis.yml/badge.svg)](https://github.com/rasuvaeff/yii3-outbox-clickhouse/actions)[![Psalm Level](https://camo.githubusercontent.com/f14952938d3aab44c1cc09318ace69e8895d22c38ec5360574989b9dd41b9016/68747470733a2f2f73686570686572642e6465762f6769746875622f7261737576616566662f796969332d6f7574626f782d636c69636b686f7573652f6c6576656c2e737667)](https://shepherd.dev/github/rasuvaeff/yii3-outbox-clickhouse)[![License](https://camo.githubusercontent.com/0bf3b987fc8ecf2e389f6dcca4ef07092f53efc72b471c345d1746350d53a6cc/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f796969332d6f7574626f782d636c69636b686f7573652f6c6963656e7365)](https://packagist.org/packages/rasuvaeff/yii3-outbox-clickhouse)[Русская версия](README.ru.md)

Batched ClickHouse exporter for [`rasuvaeff/yii3-outbox`](https://github.com/rasuvaeff/yii3-outbox). A worker drains the outbox and writes large batched inserts to ClickHouse, so the request path stays fast and durable and ClickHouse outages are absorbed by the outbox retry machinery. **Domain-agnostic** — reuse it for A/B analytics, audit logs, product events, anything append-only.

> Using an AI coding assistant? [llms.txt](llms.txt) has a compact API reference you can use.

Why not write to ClickHouse from the request?
---------------------------------------------

[](#why-not-write-to-clickhouse-from-the-request)

A per-request flush produces one small insert per request — ClickHouse hates many small inserts, and a ClickHouse outage breaks the request. This package instead batches **across** requests from a durable outbox and retries on failure. For a request-scoped direct sink, see `rasuvaeff/yii3-ab-testing-clickhouse`.

Requirements
------------

[](#requirements)

- PHP 8.3+
- `rasuvaeff/yii3-outbox` ^1.0, `rasuvaeff/clickhouse-toolkit` ^1.1
- `symfony/console` ^6.4 || ^7.0 (for the worker command)
- A PSR-18 HTTP client + PSR-17 factories (e.g. `guzzlehttp/guzzle`)

Installation
------------

[](#installation)

```
composer require rasuvaeff/yii3-outbox-clickhouse
```

Usage
-----

[](#usage)

### Worker

[](#worker)

```
use Rasuvaeff\ClickHouseToolkit\ClickHouseClientFactory;
use Rasuvaeff\ClickHouseToolkit\ClickHouseConfig;
use Rasuvaeff\Yii3OutboxClickHouse\ClickHouseOutboxExporter;
use Rasuvaeff\Yii3OutboxClickHouse\DefaultClickHouseWriterFactory;
use Rasuvaeff\Yii3OutboxClickHouse\MapClickHouseMessageRouter;
use Rasuvaeff\Yii3Outbox\RetryPolicy;

$router = new MapClickHouseMessageRouter(routes: [
    'ab.exposure' => [
        'table' => 'ab_exposures',
        'columns' => ['event_id', 'experiment', 'variant', 'subject_id'],
    ],
]);

$exporter = new ClickHouseOutboxExporter(
    storage: $storage,            // a yii3-outbox StorageInterface (e.g. yii3-outbox-db)
    router: $router,
    retryPolicy: new RetryPolicy(maxAttempts: 5, delaySeconds: 30),
    clock: $clock,
    writerFactory: new DefaultClickHouseWriterFactory(
        clientFactory: new ClickHouseClientFactory(new ClickHouseConfig(host: 'clickhouse')),
        batchSize: 1000,
    ),
);

$result = $exporter->export();   // one batch
```

### Worker

[](#worker-1)

Run the loop with the bundled console command (registered for `yiisoft/yii-console`, also works in plain Symfony Console):

```
./yii outbox:clickhouse:export                 # run forever
./yii outbox:clickhouse:export --once          # single batch (e.g. from cron)
./yii outbox:clickhouse:export --max-iterations=100
```

Or drive the framework-agnostic `ClickHouseOutboxExportRunner` yourself:

```
use Rasuvaeff\Yii3OutboxClickHouse\ClickHouseOutboxExportRunner;

$runner = new ClickHouseOutboxExportRunner($exporter, idleSleepSeconds: 5, busySleepSeconds: 1);
$runner->run(
    static fn (int $iteration): bool => true,                 // stop condition
    static fn (int $seconds): mixed => sleep($seconds),       // sleeper
);
```

### Routing

[](#routing)

`MapClickHouseMessageRouter` maps `type => [table, columns]`. Each row is built from the decoded JSON payload in column order; a configured `event_id` column (default name `event_id`) is filled from the message id instead of the payload.

### Idempotency (at-least-once)

[](#idempotency-at-least-once)

Outbox delivery is at-least-once: a retry after a partial failure can insert a row twice. Make the target table a `ReplacingMergeTree` ordered by the event id, so duplicates collapse on merge:

```
CREATE TABLE ab_exposures (
    event_id   String,
    experiment String,
    variant    String,
    subject_id String,
    ts         DateTime DEFAULT now()
) ENGINE = ReplacingMergeTree ORDER BY event_id;
```

### Failure semantics

[](#failure-semantics)

FailureDecisionEffectUnknown type / bad payload / missing field (`ClickHouseRouteException`)terminal`markFailed`ClickHouse down / transport error (`ClickHouseWriteException`)retryable`save`, stays `Pending`, retried per `RetryPolicy``export()` never throws on a ClickHouse outage. `ClickHouseExportResult` reports `published` / `retryScheduled` / `terminalFailed` / `skipped` and per-group detail. If a caller wants a catchable domain exception, `exportOrFail()` wraps a failed batch in `Exception\ClickHouseExportException` and carries the result object.

### Yii3 DI

[](#yii3-di)

`config/di.php` binds the exporter, router, decoder, failure decider and writer factory. It does **not** bind `StorageInterface` — that is owned by the storage backend (`yii3-outbox-db`) or the application. Configure routes in params:

```
// config/params.php
'rasuvaeff/yii3-outbox-clickhouse' => [
    'batchSize' => 1000,
    'fetchLimit' => 1000,
    'eventIdColumn' => 'event_id',
    'routes' => ['ab.exposure' => ['table' => 'ab_exposures', 'columns' => ['event_id', 'experiment']]],
    'retry' => ['maxAttempts' => 5, 'delaySeconds' => 30],
],
```

Security
--------

[](#security)

- Table/column identifiers and values go through `clickhouse-toolkit`(parameterized inserts, identifier validation).
- Payloads may contain PII; retention is the table/schema designer's responsibility.
- ClickHouse credentials live in `ClickHouseConfig`, never in payloads.

Examples
--------

[](#examples)

See [`examples/`](examples/).

Development
-----------

[](#development)

```
make build
```

Core `yii3-outbox` is consumed via a path repository while unpublished — see [AGENTS.md](AGENTS.md) for the monorepo-root Docker invocation.

License
-------

[](#license)

BSD-3-Clause. See [LICENSE.md](LICENSE.md).

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance96

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~7 days

Total

3

Last Release

31d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b0812d5572a7041dfe36e222d295b2e6dc55833a605350fcde58a51a5965ed30?d=identicon)[rasuvaeff](/maintainers/rasuvaeff)

---

Top Contributors

[![rasuvaeff](https://avatars.githubusercontent.com/u/1352718?v=4)](https://github.com/rasuvaeff "rasuvaeff (13 commits)")

---

Tags

analyticsbatch-processingclickhouseevent-exportoutbox-patternphpyii3exporterbatchclickhouseanalyticsyii3outbox

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm, Rector

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/rasuvaeff-yii3-outbox-clickhouse/health.svg)

```
[![Health](https://phpackages.com/badges/rasuvaeff-yii3-outbox-clickhouse/health.svg)](https://phpackages.com/packages/rasuvaeff-yii3-outbox-clickhouse)
```

###  Alternatives

[matomo/matomo

Matomo is the leading Free/Libre open analytics platform

21.7k38.9k](/packages/matomo-matomo)[symfony/symfony

The Symfony PHP framework

31.4k87.2M2.2k](/packages/symfony-symfony)[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

85036.3k](/packages/flow-php-flow)[ecotone/ecotone

Enterprise architecture layer for Laravel and Symfony — CQRS, Event Sourcing, Durable Workflows (Sagas, Orchestrators), Projections, and Outbox messaging via PHP attributes.

564576.7k54](/packages/ecotone-ecotone)[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k16](/packages/tempest-framework)[kimai/kimai

Kimai - Time Tracking

4.8k9.0k1](/packages/kimai-kimai)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
