Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 0.11.0 — sdk dimensions

### Added

- Two event-only SDK dimensions in `Metric::EVENT_COLUMNS` and
`Metric::getEventSchema()`: `sdk` (originating SDK name, e.g.
`web`, `flutter`, `console`, `cli`) and `sdkVersion` (e.g.
`14.0.0`). Both are optional strings. In ClickHouse both map to
`LowCardinality(Nullable(String))` with `CODEC(ZSTD(3))`. Existing
tables auto-materialize the columns on `setup()` via the
`ADD COLUMN IF NOT EXISTS` path. Gauges are unchanged; these columns
are not added to the primary key or indexes.

## 0.10.0 — premium geo dimensions

### Added
Expand Down
3 changes: 3 additions & 0 deletions src/Usage/Adapter/ClickHouse.php
Original file line number Diff line number Diff line change
Expand Up @@ -1213,6 +1213,8 @@ private function getColumnType(string $id, string $type = 'event'): string
// are high-cardinality and intentionally fall through to Nullable(String))
'continentCode', 'subdivisions', 'connectionType',
'connectionUsageType', 'autonomousSystemNumber',
// sdk identity
'sdk', 'sdkVersion',
];

if (in_array($id, $lowCardinality, true)) {
Expand Down Expand Up @@ -1256,6 +1258,7 @@ private function getColumnCodec(string $id): string
'city', 'continentCode', 'subdivisions', 'isp',
'autonomousSystemNumber', 'autonomousSystemOrganization',
'connectionType', 'connectionUsageType', 'connectionOrganization',
'sdk', 'sdkVersion',
];

if (in_array($id, $zstdColumns, true)) {
Expand Down
6 changes: 6 additions & 0 deletions src/Usage/Metric.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class Metric extends ArrayObject
'osCode', 'osName', 'osVersion',
'clientType', 'clientCode', 'clientName', 'clientVersion',
'clientEngine', 'clientEngineVersion',
// sdk identity
'sdk', 'sdkVersion',
'deviceName', 'deviceBrand', 'deviceModel',
];

Expand Down Expand Up @@ -92,6 +94,7 @@ class Metric extends ArrayObject
* - osCode / osName / osVersion: parsed user-agent OS fields
* - clientType / clientCode / clientName / clientVersion: parsed client
* - clientEngine / clientEngineVersion: parsed client engine
* - sdk / sdkVersion: originating SDK name and version
* - deviceName / deviceBrand / deviceModel: parsed device fields
*
* Gauge-only dimension columns (see GAUGE_COLUMNS):
Expand Down Expand Up @@ -654,6 +657,9 @@ public static function getEventSchema(): array
$stringColumn('clientVersion', 255),
$stringColumn('clientEngine', 256),
$stringColumn('clientEngineVersion', 255),
// sdk identity
$stringColumn('sdk', 256),
$stringColumn('sdkVersion', 255),
$stringColumn('deviceName', 256),
$stringColumn('deviceBrand', 256),
$stringColumn('deviceModel', 255),
Expand Down
14 changes: 14 additions & 0 deletions tests/Usage/Adapter/ClickHouseColumnTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,18 @@ public function testHighCardinalityPremiumGeoColumns(): void
);
}
}

/**
* SDK dims must map to LowCardinality(Nullable(String)).
*/
public function testLowCardinalitySdkColumns(): void
{
foreach (['sdk', 'sdkVersion'] as $col) {
$this->assertSame(
'LowCardinality(Nullable(String))',
$this->columnType($col),
"{$col} should be LowCardinality(Nullable(String))"
);
}
}
}
5 changes: 5 additions & 0 deletions tests/Usage/Adapter/ClickHouseSchemaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public function testEventsTableCarriesCodecsAndLowCardinality(): void
$this->assertStringContainsString('`connectionType` LowCardinality(Nullable(String)) CODEC(ZSTD(3))', $ddl);
$this->assertStringContainsString('`city` Nullable(String) CODEC(ZSTD(3))', $ddl);
$this->assertStringContainsString('`isp` Nullable(String) CODEC(ZSTD(3))', $ddl);

// sdk: both dims are low-cardinality.
$this->assertStringContainsString('`sdk` LowCardinality(Nullable(String)) CODEC(ZSTD(3))', $ddl);
$this->assertStringContainsString('`sdkVersion` LowCardinality(Nullable(String)) CODEC(ZSTD(3))', $ddl);
}

public function testEventsTableSwapsBloomForSetOnLowCardinality(): void
Expand Down Expand Up @@ -264,6 +268,7 @@ private function expectedDimAssertions(array $columns, string $type): array
'hostname', 'ip',
'continentCode', 'subdivisions', 'connectionType',
'connectionUsageType', 'autonomousSystemNumber',
'sdk', 'sdkVersion',
];

$baseKey = ['id', 'metric', 'value', 'time', 'tenant'];
Expand Down
30 changes: 30 additions & 0 deletions tests/Usage/MetricTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ public function testEventColumnsConstant(): void
'osCode', 'osName', 'osVersion',
'clientType', 'clientCode', 'clientName', 'clientVersion',
'clientEngine', 'clientEngineVersion',
'sdk', 'sdkVersion',
'deviceName', 'deviceBrand', 'deviceModel',
];
$this->assertSame($expected, Metric::EVENT_COLUMNS);
Expand Down Expand Up @@ -681,6 +682,35 @@ public function testPremiumGeoColumnsArePresentAsEventStrings(): void
}
}

/**
* The sdk dimensions are event-only string columns that are present in
* EVENT_COLUMNS and the event schema (as non-required strings) but never
* leak into the gauge column set.
*/
public function testSdkColumnsArePresentAsEventStrings(): void
{
$sdk = ['sdk', 'sdkVersion'];

$schema = Metric::getEventSchema();
$eventIds = array_column($schema, '$id');
$gaugeIds = array_column(Metric::getGaugeSchema(), '$id');

foreach ($sdk as $col) {
$this->assertContains($col, Metric::EVENT_COLUMNS, "EVENT_COLUMNS missing {$col}");
$this->assertContains($col, $eventIds, "Event schema missing {$col}");
$this->assertNotContains($col, Metric::GAUGE_COLUMNS, "{$col} must be event-only");
$this->assertNotContains($col, $gaugeIds, "{$col} must not be in gauge schema");

$matches = array_values(array_filter(
$schema,
static fn (array $attr): bool => $attr['$id'] === $col,
));
$this->assertCount(1, $matches, "{$col} must appear exactly once");
$this->assertSame('string', $matches[0]['type'], "{$col} must be a string column");
$this->assertFalse($matches[0]['required'], "{$col} must be optional");
}
}

/**
* Test that the gauge schema contains the new team and resource id columns.
*/
Expand Down
Loading