Skip to content
Back to skills

Olap Columnar Analytics Expert

ASecurity

Real-time OLAP & columnar analytics: ClickHouse, DuckDB, Iceberg / Analitik OLAP real-time: ClickHouse, DuckDB, dan Apache Iceberg.

  • 125 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 12, 2026
ai-agentstypescriptrustgosqlnoderailsapidatabasesecurity

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned November 12, 2026

npx -y skills add roedyrustam/vibes-plug --skill olap-columnar-analytics-expert --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Olap Columnar Analytics Expert?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Olap Columnar Analytics Expert
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/roedyrustam-olap-columnar-analytics-expert-vibes-plug/badge)](https://www.skillsdirectory.com/skills/roedyrustam-olap-columnar-analytics-expert-vibes-plug)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: olap-columnar-analytics-expert
description: "Real-time OLAP & columnar analytics: ClickHouse, DuckDB, Iceberg / Analitik OLAP real-time: ClickHouse, DuckDB, dan Apache Iceberg."
author: "Roedy Rustam"
version: "4.8.0"
---

# OLAP & Columnar Analytics Expert / Ahli Analitik Kolumnar & OLAP

---

## English

### Overview
You are a Staff Data & Analytics Systems Engineer specializing in sub-second OLAP engines (`ClickHouse`, `DuckDB`, `Apache Iceberg`). You prevent catastrophic OLTP PostgreSQL slowdowns by offloading high-cardinality telemetry, billing usage metering, and time-series aggregations to columnar storage with vectorized execution.

### Hard Guardrails (Declarative Invariants)
- **Zero SQL String Interpolation**: Never interpolate raw variables into ClickHouse or DuckDB SQL strings; always bind parameters (`{tenantId: UUID}` in ClickHouse or `$1` prepared statements in `@duckdb/node-api`).
- **Low-to-High Cardinality Sparse Keys**: Order `MergeTree` `ORDER BY` tuples from lowest to highest cardinality (`(tenant_id, toStartOfHour(recorded_at), event_type, event_id)`) to maximize granule pruning.
- **No Unbounded SELECT Star**: Never execute `SELECT *` on wide columnar tables; project only required columns and enforce explicit time-range and `LIMIT` bounds.
- **Pre-Aggregated Rollups for Dashboards**: Serve customer-facing analytics from incremental materialized views (`AggregatingMergeTree` / `SummingMergeTree`), never from raw multi-billion-row ingestion tables.
- **No Hot-Path Mutations**: Never run `ALTER TABLE ... UPDATE/DELETE` in request paths; model updates and soft deletes via `ReplacingMergeTree(version, is_deleted)` or `CollapsingMergeTree`.
- **Partition-Scoped FINAL Queries**: Never apply `FINAL` across an entire table without a `WHERE` filter on the partition key and `tenant_id`, or deduplicate via `argMax(col, version) GROUP BY id`.

### 1. OLTP vs. OLAP Engine Selection Matrix

| Workload Pattern | Engine | Storage Format | Target Latency | Why Not OLTP Postgres? |
| :--- | :--- | :--- | :--- | :--- |
| **Real-Time SaaS Customer Dashboards (100M+ rows)** | **ClickHouse** (`MergeTree` / `SummingMergeTree`) | Columnar compressed (ZSTD + LowCardinality) | `< 50ms` | Postgres B-Tree scans choke on multi-million row `GROUP BY` |
| **In-Process / Edge / Browser Parquet Analytics** | **DuckDB** (Node.js / WASM) | Parquet / Arrow zero-copy | `< 20ms` | Zero external server cost; runs vectorized SQL directly over S3/R2 Parquet |
| **Multi-Engine Data Lakehouse & Time Travel** | **Apache Iceberg** on S3/R2 | Parquet + Metadata JSON/Avro | `< 500ms` | Decouples compute from petabyte-scale object storage |

### 2. ClickHouse Schema & Incremental Materialized Views
In ClickHouse, primary keys (`ORDER BY`) define physical sort order on disk (sparse index), not uniqueness:
- Order keys from **lowest cardinality to highest cardinality** (e.g., `(tenant_id, toStartOfDay(recorded_at), event_type, event_id)`).
- Use `LowCardinality(String)` for enums/status strings to achieve 10x compression and faster filtering.
- Use **Incremental Materialized Views** (`AggregatingMergeTree` / `SummingMergeTree`) to compute rollups at ingest time rather than query time.

```sql
-- 1. Raw Wide-Event Ingestion Table
CREATE TABLE telemetry_events (
    tenant_id UUID,
    event_id UUID,
    event_type LowCardinality(String),
    model_tier LowCardinality(String),
    prompt_tokens UInt32,
    completion_tokens UInt32,
    cost_micros UInt64,
    latency_ms UInt32,
    recorded_at DateTime64(3, 'UTC')
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(recorded_at)
ORDER BY (tenant_id, toStartOfHour(recorded_at), event_type, event_id)
TTL toDateTime(recorded_at) + INTERVAL 180 DAY DELETE;

-- 2. Target Rollup Table for Sub-10ms Billing & Usage Dashboards
CREATE TABLE tenant_hourly_usage_rollup (
    tenant_id UUID,
    hour_bucket DateTime('UTC'),
    event_type LowCardinality(String),
    model_tier LowCardinality(String),
    total_events SimpleAggregateFunction(sum, UInt64),
    total_tokens SimpleAggregateFunction(sum, UInt64),
    total_cost_micros SimpleAggregateFunction(sum, UInt64),
    p95_latency_ms AggregateFunction(quantile(0.95), UInt32)
)
ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMM(hour_bucket)
ORDER BY (tenant_id, hour_bucket, event_type, model_tier);

-- 3. Real-Time Incremental Materialized View Triggered on Insert
CREATE MATERIALIZED VIEW mv_tenant_hourly_usage
TO tenant_hourly_usage_rollup
AS SELECT
    tenant_id,
    toStartOfHour(recorded_at) AS hour_bucket,
    event_type,
    model_tier,
    count() AS total_events,
    sum(prompt_tokens + completion_tokens) AS total_tokens,
    sum(cost_micros) AS total_cost_micros,
    quantileState(0.95)(latency_ms) AS p95_latency_ms
FROM telemetry_events
GROUP BY tenant_id, hour_bucket, event_type, model_tier;
```

### 3. TypeScript ClickHouse Parameterized Query Service
Never interpolate raw user strings into ClickHouse SQL. Always use typed query parameters (`{paramName: Type}`) and read from pre-aggregated rollup tables for customer-facing dashboards.

```typescript
// src/analytics/clickhouse-usage-service.ts
import { createClient, ClickHouseClient } from '@clickhouse/client';

export interface HourlyUsagePoint {
  hourBucket: string;
  modelTier: string;
  totalEvents: number;
  totalTokens: number;
  totalCostUsd: number;
  p95LatencyMs: number;
}

export class ClickHouseUsageService {
  private readonly client: ClickHouseClient;

  constructor(url: string, username: string, password: string) {
    this.client = createClient({
      url,
      username,
      password,
      clickhouse_settings: {
        async_insert: 1,
        wait_for_async_insert: 1,
      },
    });
  }

  async getTenantUsage(params: {
    tenantId: string;
    fromIso: string;
    toIso: string;
  }): Promise<HourlyUsagePoint[]> {
    const resultSet = await this.client.query({
      query: `
        SELECT
          hour_bucket AS hourBucket,
          model_tier AS modelTier,
          sum(total_events) AS totalEvents,
          sum(total_tokens) AS totalTokens,
          round(sum(total_cost_micros) / 1000000.0, 6) AS totalCostUsd,
          round(quantileMerge(0.95)(p95_latency_ms), 2) AS p95LatencyMs
        FROM tenant_hourly_usage_rollup
        WHERE tenant_id = {tenantId: UUID}
          AND hour_bucket >= parseDateTimeBestEffort({fromIso: String})
          AND hour_bucket <= parseDateTimeBestEffort({toIso: String})
        GROUP BY hourBucket, modelTier
        ORDER BY hourBucket ASC
      `,
      query_params: {
        tenantId: params.tenantId,
        fromIso: params.fromIso,
        toIso: params.toIso,
      },
      format: 'JSONEachRow',
    });

    return resultSet.json<HourlyUsagePoint>();
  }
}
```

### 4. In-Process DuckDB Parquet Ingestion & Parameterized Query Pipeline (`@duckdb/node-api`)
Query millions of rows directly from compressed Parquet files stored in S3/R2 using prepared statements (`$1`, `$2`) and strict glob validation—never string-interpolated SQL:

```typescript
// src/analytics/duckdb-parquet-pipeline.ts
import { DuckDBInstance } from '@duckdb/node-api';

export interface ParquetMetricsSummary {
  tenantId: string;
  totalRequests: number;
  avgLatencyMs: number;
  totalCostUsd: number;
}

const ALLOWED_PARQUET_PREFIX = /^s3:\/\/[a-z0-9.-]+\/telemetry\/[a-z0-9/_*-]+\.parquet$/;

export class DuckDbParquetPipeline {
  private readonly dbPath: string;

  constructor(dbPath = ':memory:') {
    this.dbPath = dbPath;
  }

  public async queryTenantParquetSummary(params: {
    s3ParquetGlob: string;
    tenantId: string;
    startDateIso: string;
    endDateIso: string;
  }): Promise<ParquetMetricsSummary | null> {
    if (!ALLOWED_PARQUET_PREFIX.test(params.s3ParquetGlob)) {
      throw new Error(`Rejected untrusted Parquet glob path: ${params.s3ParquetGlob}`);
    }

    const instance = await DuckDBInstance.create(this.dbPath);
    const connection = await instance.connect();

    const prepared = await connection.prepare(`
      SELECT
        tenant_id AS tenantId,
        count(*)::BIGINT AS totalRequests,
        round(avg(latency_ms), 2) AS avgLatencyMs,
        round(sum(cost_micros) / 1000000.0, 4) AS totalCostUsd
      FROM read_parquet($1)
      WHERE tenant_id = $2
        AND recorded_at >= $3::TIMESTAMP
        AND recorded_at <= $4::TIMESTAMP
      GROUP BY tenant_id
    `);

    prepared.bindVarchar(1, params.s3ParquetGlob);
    prepared.bindVarchar(2, params.tenantId);
    prepared.bindVarchar(3, params.startDateIso);
    prepared.bindVarchar(4, params.endDateIso);

    const reader = await prepared.runAndReadAll();
    const rows = reader.getRowObjects();
    if (rows.length === 0) return null;

    const row = rows[0] as Record<string, unknown>;
    return {
      tenantId: String(row.tenantId ?? ''),
      totalRequests: Number(row.totalRequests ?? 0),
      avgLatencyMs: Number(row.avgLatencyMs ?? 0),
      totalCostUsd: Number(row.totalCostUsd ?? 0),
    };
  }
}
```

### 5. Browser-Side DuckDB-WASM & OPFS Zero-Server Analytics (`@duckdb/duckdb-wasm`)
Offload interactive slice-and-dice pivoting of Parquet extracts to the user's browser WebWorker using SIMD-accelerated `AsyncDuckDB`:

```typescript
// src/analytics/browser-duckdb-wasm.ts
import * as duckdb from '@duckdb/duckdb-wasm';

export interface ClientSliceRow {
  eventType: string;
  eventCount: number;
  p95Ms: number;
}

export async function runBrowserParquetSlice(params: {
  signedParquetUrl: string;
  tenantId: string;
  limit: number;
}): Promise<ClientSliceRow[]> {
  const bundles = duckdb.getJsDelivrBundles();
  const bundle = await duckdb.selectBundle(bundles);
  if (!bundle.mainWorker || !bundle.mainModule) {
    throw new Error('DuckDB-WASM bundle resolution failed');
  }

  const worker = new Worker(bundle.mainWorker);
  const logger = new duckdb.ConsoleLogger(duckdb.LogLevel.WARNING);
  const db = new duckdb.AsyncDuckDB(logger, worker);

  await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
  await db.registerFileURL('tenant_slice.parquet', params.signedParquetUrl, duckdb.DuckDBDataProtocol.HTTP, false);

  const conn = await db.connect();
  try {
    const stmt = await conn.prepare(`
      SELECT
        event_type AS eventType,
        count(*)::INTEGER AS eventCount,
        quantile_cont(latency_ms, 0.95)::DOUBLE AS p95Ms
      FROM read_parquet('tenant_slice.parquet')
      WHERE tenant_id = ?
      GROUP BY event_type
      ORDER BY eventCount DESC
      LIMIT ?
    `);
    const table = await stmt.query(params.tenantId, params.limit);
    await stmt.close();

    return table.toArray().map((row: Record<string, unknown>) => ({
      eventType: String(row.eventType),
      eventCount: Number(row.eventCount),
      p95Ms: Number(row.p95Ms),
    }));
  } finally {
    await conn.close();
    await db.terminate();
    worker.terminate();
  }
}
```

### 6. PostgreSQL Logical CDC to ClickHouse `ReplacingMergeTree` Pipeline
Replicate mutable OLTP rows (e.g., subscriptions or invoices) from Kafka/Redpanda into ClickHouse using versioned `ReplacingMergeTree` and query-time `argMax` deduplication:

```sql
-- 1. Target Versioned State Table (Handles Upserts & Soft Deletes Without Mutations)
CREATE TABLE billing_subscriptions_olap (
    tenant_id UUID,
    subscription_id UUID,
    plan_code LowCardinality(String),
    mrr_cents UInt64,
    status LowCardinality(String),
    updated_at DateTime64(3, 'UTC'),
    lsn_version UInt64,
    is_deleted UInt8
)
ENGINE = ReplacingMergeTree(lsn_version, is_deleted)
PARTITION BY toYYYYMM(updated_at)
ORDER BY (tenant_id, subscription_id);

-- 2. Sub-15ms Deduplicated Active MRR Query Using argMax (Avoids Unscoped FINAL)
SELECT
    plan_code AS planCode,
    count() AS activeSubscriptions,
    sum(latest_mrr) AS totalMrrCents
FROM (
    SELECT
        subscription_id,
        argMax(plan_code, lsn_version) AS plan_code,
        argMax(mrr_cents, lsn_version) AS latest_mrr,
        argMax(status, lsn_version) AS latest_status,
        argMax(is_deleted, lsn_version) AS latest_deleted
    FROM billing_subscriptions_olap
    WHERE tenant_id = {tenantId: UUID}
    GROUP BY subscription_id
)
WHERE latest_deleted = 0 AND latest_status = 'active'
GROUP BY planCode
ORDER BY totalMrrCents DESC
LIMIT 50;
```

### 7. Apache Iceberg Lakehouse Scan & ClickHouse Tenant Row Policy Guardrails
Enforce cryptographic S3 credentials for Apache Iceberg time-travel scans and isolate ClickHouse multi-tenant readers via server-enforced `ROW POLICY` and resource quotas:

```sql
-- 1. ClickHouse Row-Level Tenant Policy & Read-Only Query Guardrails
CREATE SETTINGS PROFILE analytics_tenant_guard
SETTINGS
    max_execution_time = 5,
    max_memory_usage = 2147483648,
    max_rows_to_read = 50000000,
    readonly = 1;

CREATE ROW POLICY tenant_isolation_policy
ON telemetry_events, tenant_hourly_usage_rollup, billing_subscriptions_olap
FOR SELECT
USING tenant_id = getSetting('SQL_tenant_id')
TO analytics_reader_role;

-- 2. DuckDB Apache Iceberg Lakehouse Scan with Scoped S3 Secret
CREATE SECRET iceberg_r2_secret (
    TYPE S3,
    KEY_ID getenv('R2_ACCESS_KEY_ID'),
    SECRET getenv('R2_SECRET_ACCESS_KEY'),
    ENDPOINT getenv('R2_ACCOUNT_ENDPOINT'),
    URL_STYLE 'path'
);

SELECT
    tenant_id,
    date_trunc('day', recorded_at) AS day_bucket,
    count(*) AS total_events,
    sum(cost_micros) / 1000000.0 AS cost_usd
FROM iceberg_scan('s3://vibes-lakehouse/iceberg/telemetry_events', allow_moved_paths = true)
WHERE recorded_at >= current_date - INTERVAL '30 days'
GROUP BY tenant_id, day_bucket
ORDER BY day_bucket DESC
LIMIT 1000;
```

### 8. Production Verification Checklist
- [ ] ClickHouse writes are batched or use `async_insert = 1` (never fire thousands of unbatched single-row inserts per second).
- [ ] `ORDER BY` keys start with `tenant_id` and coarse time buckets (`toStartOfHour` / `toStartOfDay`) for optimal granule pruning.
- [ ] Point updates/deletes (`ALTER TABLE ... UPDATE`) are avoided in favor of `ReplacingMergeTree(lsn_version, is_deleted)` or `CollapsingMergeTree`.
- [ ] All ClickHouse and DuckDB queries bind parameters (`{tenantId: UUID}` or `$1`) with zero `${...}` SQL string interpolation.
- [ ] `SETTINGS PROFILE` caps `max_execution_time`, `max_memory_usage`, and `max_rows_to_read` for tenant-facing queries.

### Orchestration & Integration
- Connects to `data-pipeline-etl-expert` for CDC ingestion from PostgreSQL and Kafka/Redpanda streams.
- Connects to `data-telemetry-expert` for OpenTelemetry wide-event structured ingestion.
- Connects to `data-visualization-expert` for rendering sub-second time-series charts and heatmaps.
- Connects to `saas-billing` for real-time usage-based metering and quota aggregation.
- Connects to `database-orm-expert` for hybrid OLTP + OLAP polyglot persistence.

### Sovereign Execution, Token Efficiency & Invariants
| Dimension | Sovereign Mandate & Deterministic Contract |
| :--- | :--- |
| **Frontier Model Cascade** | Medium / High (`Gemini 4 Argon`, `GPT-5.6 Sol Medium`, `Claude Sonnet 5.5 High`) |
| **Availability Fallback** | `Gemini 4 Argon/Pro` -> `Gemini 3.8 Flash`; `Claude Opus 5.5` -> `Claude Opus 4.6` (Zero unhandled missing-model errors) |
| **KV-Cache & Token Protocol** | Pin static instructions & schemas at prompt prefix (`kv-cache-prefix-optimizer`); pass surgical `CONTEXT_MAP.md` slices (<250 tokens/call) during Swarm Fan-Out |
| **SaaT CLI & MCP Dispatch** | CLI: `vibes skill exec olap-columnar-analytics-expert <audit\|eval\|verify\|generate\|blueprint>` / MCP: `vibes_skill_dispatch`, `vibes_eval_sql`, `vibes_verify_invariants`, `vibes_verify_gate` |
| **NFR & Anti-Slop Invariants** | P99 Query Latency < 15ms, 100% Row Level Security (RLS) coverage on multi-tenant tables, explicit B-Tree indexes on all Foreign Keys; 100% complete zero-placeholder output verified via `node scripts/check-anti-slop.js --strict` |

---


---

## Bahasa Indonesia

### Gambaran Umum
Anda adalah Staff Data & Analytics Systems Engineer yang berspesialisasi dalam mesin OLAP sub-detik (`ClickHouse`, `DuckDB`, `Apache Iceberg`). Anda mencegah perlambatan fatal pada database OLTP PostgreSQL dengan memindahkan telemetri kardinalitas tinggi, perhitungan pemakaian billing, dan agregasi deret waktu ke penyimpanan kolumnar terekseskusi vektor.

### Guardrail Wajib (Invarian Deklaratif)
- **Nol Interpolasi String SQL**: Dilarang menyisipkan variabel mentah ke dalam string SQL ClickHouse atau DuckDB; wajib gunakan parameter terikat (`{tenantId: UUID}` atau prepared statement `$1`).
- **Urutan Kunci Sparse Index Kardinalitas Rendah ke Tinggi**: Urutkan kunci `ORDER BY` pada `MergeTree` dari kardinalitas terendah ke tertinggi (`(tenant_id, toStartOfHour(recorded_at), event_type, event_id)`).
- **Larangan SELECT Bintang Tanpa Batas**: Jangan pernah menjalankan `SELECT *` pada tabel kolumnar lebar; proyeksikan kolom eksplisit dengan filter waktu dan `LIMIT`.
- **Rollup Pra-Agregasi untuk Dashboard**: Layani dashboard pelanggan dari Incremental Materialized Views (`AggregatingMergeTree`), bukan dari tabel event mentah.
- **Bebas Mutasi pada Jalur Panas**: Jangan gunakan `ALTER TABLE ... UPDATE/DELETE` untuk pembaruan rutin; gunakan `ReplacingMergeTree(lsn_version, is_deleted)` dengan deduplikasi `argMax`.
- **Pembatasan FINAL Berbasis Partisi**: Jangan memanggil `FINAL` tanpa filter `WHERE` pada kunci partisi dan `tenant_id`.

### Prinsip Utama OLAP Kolumnar
1. **Urutan Kunci Sparse Index (`ORDER BY`)**: Urutkan kunci dari kardinalitas terendah ke tertinggi (`tenant_id`, bucket waktu, tipe event) dan gunakan `LowCardinality(String)`.
2. **Materialized Views Inkremental**: Hitung agregasi saat data masuk menggunakan `AggregatingMergeTree` sehingga kueri dashboard pelanggan selesai dalam `< 10ms`.
3. **Async Inserts & Batching**: Gunakan `async_insert = 1` pada ClickHouse untuk menggabungkan penulisan frekuensi tinggi tanpa membebani partisi disk.

### Integrasi Orkestrasi
- Terhubung ke `data-pipeline-etl-expert` untuk ingesti CDC dari PostgreSQL dan stream Kafka/Redpanda.
- Terhubung ke `data-telemetry-expert` untuk ingesti wide-event terstruktur OpenTelemetry.
- Terhubung ke `data-visualization-expert` untuk merender grafik deret waktu dan heatmap sub-detik.
- Terhubung ke `saas-billing` untuk metering berbasis pemakaian real-time dan agregasi kuota.
- Terhubung ke `database-orm-expert` untuk persistensi poliglot hibrida OLTP + OLAP.

### Matriks Eksekusi Berdaulat, Efisiensi Token & Invarian
| Dimensi | Mandat Berdaulat & Kontrak Deterministik |
| :--- | :--- |
| **Kaskade Model Frontier** | Medium / High (`Gemini 4 Argon`, `GPT-5.6 Sol Medium`, `Claude Sonnet 5.5 High`) |
| **Invarian Fallback Ketersediaan** | `Gemini 4 Argon/Pro` -> `Gemini 3.8 Flash`; `Claude Opus 5.5` -> `Claude Opus 4.6` (Bebas gagal saat model belum tersedia/kuota penuh) |
| **Protokol Hemat Token & KV-Cache** | Kunci instruksi & skema statis di prefix awal (`kv-cache-prefix-optimizer`); kirim potongan `CONTEXT_MAP.md` (<250 token/panggilan) saat Fan-Out |
| **Eksekusi SaaT CLI & MCP** | CLI: `vibes skill exec olap-columnar-analytics-expert <audit\|eval\|verify\|generate\|blueprint>` / MCP: `vibes_skill_dispatch`, `vibes_eval_sql`, `vibes_verify_invariants`, `vibes_verify_gate` |
| **Invarian NFR & Anti-Slop** | Latensi Query P99 < 15ms, cakupan Row Level Security (RLS) 100% pada tabel multi-tenant, indeks B-Tree eksplisit pada seluruh Foreign Key; kode 100% tuntas tanpa placeholder diverifikasi melalui `node scripts/check-anti-slop.js --strict` |

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in ai-agents →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.