DEV Community

Elliot Park
Elliot Park

Posted on

Compatibility and Load Testing for Temporal Self-Hosted with CockroachDB Cloud

A proof-of-concept validating CockroachDB Cloud as a persistence backend for a self-hosted Temporal deployment.

What and Why

Temporal isn't on CockroachDB's official support matrix. It ships a postgres12 persistence plugin designed and tested against real PostgreSQL — not CockroachDB specifically. Before recommending CockroachDB as a backend for a self-hosted Temporal deployment, two questions need real answers rather than assumptions:

  1. Does it actually work? CockroachDB is PostgreSQL-wire-compatible, but wire compatibility isn't the same as full SQL-dialect compatibility. Temporal's schema and query patterns need to be validated end-to-end.

  2. What cluster size do I need to provision? Temporal persists every workflow state transition as a durable "checkpoint" (a history event). Sizing a cluster for a given checkpoint volume requires real, measured bytes-per-checkpoint data — not a vendor estimate.

This post documents the compatibility issues found and fixed, the load-testing methodology used to generate real data at scale, the results, and a reusable formula for extrapolating those results to any target checkpoint volume.

Architecture

Test environment:

  • MacBook Pro (Apple M3 Pro, 18 GB RAM)
  • Temporal Server v1.31.2 (self-hosted binary running locally on my laptop)
  • Temporal CLI v1.7.3
  • CockroachDB Cloud Advanced: 3 nodes, AWS us-east-1, 16 vCPU / 64 GiB RAM per node, v26.2.3

CockroachDB as persistence backend, via Temporal's postgres12 SQL plugin, split across two databases:

  • temporal — workflow execution state and event history (executions, history_node, history_tree, task-queue tables)
  • temporal_visibility — the queryable, denormalized index used by dashboards, CLI, and search (executions_visibility)

Simulated workload: This project simulates a Temporal workload for an e-commerce application that specializes in return-automation and post-purchase platform. A post-purchase return-automation saga — return eligibility check (an AI-classification activity), a package-protection branch, a signal-driven wait for physical package receipt with an SLA timer, an AI-driven disposition decision (refund / exchange / store credit), the disposition activity itself with saga-style compensation on failure, and customer notification. Built to be representative of a real branching saga, not a synthetic microbenchmark — average ~32 checkpoints per execution, ranging 12–45 depending on branch taken. Full reference implementation is in the appendix.

Compatibility Issues Found and Fixed

Temporal's schema migrations for temporal_visibility (versions v1.2 through v1.13) hit six distinct CockroachDB/PostgreSQL incompatibilities. All were fixed by patching the migration SQL files directly — no application-code changes, no fork of Temporal itself.

A separate, non-schema finding: default numHistoryShards: 4 caused severe workflow-task contention under concurrent load (context deadline exceeded, WorkflowTaskTimedOut cascades) — not a CockroachDB limitation, but an under-provisioned Temporal-side setting for the target throughput. Raising to numHistoryShards: 256 (512 is recommendation for production) increases write throughput and increases number of concurrent persistence calls.

Patching the schema

# Get schema files matching the Temporal binary version
curl -L https://github.com/temporalio/temporal/archive/refs/tags/v1.31.2.tar.gz | \
  tar -xz --strip-components=1 "temporal-1.31.2/schema"

cd schema/postgresql/v12/visibility/versioned

# v1.2 — remove CREATE EXTENSION block, strip jsonb_path_ops, fix convert_ts()
sed -i.bak \
  -e '/^DO LANGUAGE/,/^\$\$;/d' \
  -e 's/ jsonb_path_ops//g' \
  v1.2/advanced_visibility.sql
sed -i '' "s/RETURN s::timestamptz at time zone 'UTC';/RETURN parse_timestamp(s);/" \
  v1.2/advanced_visibility.sql

# v1.3, v1.7, v1.13 (search-attribute files) — strip jsonb_path_ops
sed -i.bak 's/ jsonb_path_ops//' v1.3/add_build_ids_search_attribute.sql
sed -i.bak 's/ jsonb_path_ops//' v1.7/add_pause_info_search_attribute.sql
sed -i.bak 's/ jsonb_path_ops//' v1.13/add_used_deployment_versions_search_attribute.sql

# v1.13 combined migration — remove dynamic cleanup block, strip CONCURRENTLY and jsonb_path_ops
sed -i.bak '/^DO LANGUAGE/,/^END \$\$;/d' v1.13/combined_v1.10_v1.13.sql
sed -i '' 's/CREATE INDEX CONCURRENTLY/CREATE INDEX/g' v1.13/combined_v1.10_v1.13.sql
sed -i '' 's/ jsonb_path_ops//g' v1.13/combined_v1.10_v1.13.sql

# Verify every fix landed
grep -rc "DO LANGUAGE" v1.2 v1.13                    # expect 0 in every real .sql file
grep -rc jsonb_path_ops v1.2 v1.3 v1.7 v1.9 v1.13     # expect 0
grep -rc CONCURRENTLY v1.13                           # expect 0
grep "RETURN parse_timestamp" v1.2/advanced_visibility.sql   # expect 1
grep "LANGUAGE plpgsql" v1.2/advanced_visibility.sql          # expect IMMUTABLE
Enter fullscreen mode Exit fullscreen mode

Loading the Schema

export SQL_PASSWORD='<your-password>'

./temporal-sql-tool \
  --plugin postgres12 \
  --ep <your-cluster-host>.cockroachlabs.cloud \
  -p 26257 \
  -u <your-sql-user> \
  --tls --tls-ca-file "<path-to-ca-cert>" \
  --db temporal create
./temporal-sql-tool --plugin postgres12 --ep <your-cluster-host>.cockroachlabs.cloud -p 26257 \
  -u <your-sql-user> --tls --tls-ca-file "<path-to-ca-cert>" \
  --db temporal setup-schema -v 0.0
./temporal-sql-tool --plugin postgres12 --ep <your-cluster-host>.cockroachlabs.cloud -p 26257 \
  -u <your-sql-user> --tls --tls-ca-file "<path-to-ca-cert>" \
  --db temporal update-schema --schema-name postgresql/v12/temporal

./temporal-sql-tool --plugin postgres12 --ep <your-cluster-host>.cockroachlabs.cloud -p 26257 \
  -u <your-sql-user> --tls --tls-ca-file "<path-to-ca-cert>" \
  --db temporal_visibility create
./temporal-sql-tool --plugin postgres12 --ep <your-cluster-host>.cockroachlabs.cloud -p 26257 \
  -u <your-sql-user> --tls --tls-ca-file "<path-to-ca-cert>" \
  --db temporal_visibility setup-schema -v 0.0
./temporal-sql-tool --plugin postgres12 --ep <your-cluster-host>.cockroachlabs.cloud -p 26257 \
  -u <your-sql-user> --tls --tls-ca-file "<path-to-ca-cert>" \
  --db temporal_visibility update-schema -d ./schema/postgresql/v12/visibility/versioned
Enter fullscreen mode Exit fullscreen mode

Starting Temporal Server

cd ~
~/temporal-server --config-file ~/temporal-server.yaml start

# In a new terminal
temporal --address localhost:7233 operator namespace create default
temporal --address localhost:7233 operator cluster health
temporal --address localhost:7233 -n temporal-system workflow list
Enter fullscreen mode Exit fullscreen mode

Starting Temporal UI Server

which go || brew install go cd ~ git clone https://github.com/temporalio/ui-server.git cd ui-server go build -o ui-server ./cmd/server mkdir -p ~/ui-config cp ~/temporal-ui-server.yaml ~/ui-config/development.yaml ~/ui-server/ui-server --root ~ --config ui-config start
# open http://localhost:8233
Enter fullscreen mode Exit fullscreen mode

Load Testing Methodology

Rather than relying on a single small sample, checkpoints were generated in escalating batches (100 → 1,000 → 5,000 → 20,000+) with verification at each stage:

  1. Smoke test (10–20 executions) to validate correctness end-to-end
  2. Staged batches, each followed by:
  • temporal workflow count --query "ExecutionStatus='Running'" to confirm full drain

  • Direct SQL against temporal/temporal_visibility for real execution/checkpoint counts (not estimates)

  • CockroachDB Cloud Console storage metrics per table

  1. Final run: 34,000+ total executions, exceeding the 1,000,000-checkpoint target

A custom batch.py script drove load: bounded-concurrency workflow starts with immediate signaling, fire-and-forget (no blocking on completion at scale), self-reporting throughput. A tuned worker.py (moderate max_concurrent_activities/max_concurrent_workflow_tasks, sized to the test laptop's cores/RAM.

Verification queries in CockroachDB

-- Full drain confirmation, cross-checked in SQL
USE temporal_visibility;
SELECT status, count(*) FROM executions_visibility
WHERE namespace_id = '<your-namespace-id>'
GROUP BY status;

-- Real checkpoint totals -- the actual sizing data, not an estimate
USE temporal;
SELECT
  count(*) AS total_executions,
  sum(next_event_id - 1) AS total_checkpoints,
  avg(next_event_id - 1) AS avg_checkpoints_per_execution
FROM executions e JOIN namespaces n ON n.id = e.namespace_id
WHERE n.name = 'default';

-- Logical (uncompressed, single-copy) bytes per checkpoint
SELECT
  (SELECT sum(pg_column_size(data)) FROM history_node) AS total_logical_bytes,
  (SELECT sum(next_event_id - 1) FROM executions e JOIN namespaces n ON n.id = e.namespace_id
     WHERE n.name = 'default') AS total_checkpoints,
  (SELECT sum(pg_column_size(data)) FROM history_node)::FLOAT
    / NULLIF((SELECT sum(next_event_id - 1) FROM executions e JOIN namespaces n ON n.id = e.namespace_id
       WHERE n.name = 'default')::FLOAT, 0)
    AS avg_logical_bytes_per_checkpoint;

-- Read/write mix, from actual observed statement statistics
SELECT
  metadata->>'db' AS database_name,
  left(metadata->>'querySummary', 6) AS verb,
  sum((statistics->'statistics'->>'cnt')::INT) AS total_executions
FROM crdb_internal.statement_statistics
WHERE metadata->>'db' IN ('temporal', 'temporal_visibility')
  AND metadata->>'stmtType' = 'TypeDML'
GROUP BY 1, 2
ORDER BY total_executions DESC;
Enter fullscreen mode Exit fullscreen mode

Results

Final measured totals (34,000+ executions):

The ~2.2x gap between logical (140 bytes) and physical (~308 bytes) per-checkpoint size reflects CockroachDB's replication factor and index overhead — a legitimate planning multiplier, not compression failure.

A notable finding: temporal_visibility (Advanced Visibility's SQL-based schema) consumed more physical storage than temporal itself in our tests, despite holding far less raw data.

Read/write mix (from crdb_internal.statement_statistics, temporal DB): 56% SELECT, 23% INSERT, 15% UPDATE, 6% DELETE. Reads are the majority — Temporal's optimistic-concurrency design requires a consistency-check read before most writes.

Extrapolating to Your Own Workload

Use the measured per-unit rates above with your own target volume:

This work validates that CockroachDB Cloud Advanced functions correctly as a persistence

  • Each checkpoint is a transaction in CockroachDB and the heuristic is 1 vCPU : 150 TPS
  • executions/month = checkpoints_per_month ÷ avg_checkpoints_per_execution
  • temporal DB storage = checkpoints_per_month × 308 bytes
  • temporal_visibility size = executions_per_month × 13,300 bytes
  • steady-state storage (N-day retention) = (temporal + temporal_visibility) × (retention_days ÷ 30)
  • recommended total = steady-state storage × 1.30-1 (headroom: fragmentation, non-uniform load, growth margin)
  • storage per node = recommended total ÷ node count

Disclaimer

This work validates that CockroachDB Cloud Advanced functions correctly as a persistence backend for self-hosted Temporal, given the schema patches documented above, and provides real measured data for capacity planning. Temporal is not on CockroachDB's official support matrix. This is compatibility-validated proof-of-concept work, not an officially supported or vendor-certified deployment pattern. Production adoption should include an engineering review of the schema patches, ongoing monitoring for behavior differences from Temporal's tested PostgreSQL backend, and the load-test validation flagged above before committing to a final cluster size.

Appendix: Reference Implementation

return_workflow.py

"""
Return-Automation Saga Workflow
================================
Simulates a post-purchase return/exchange/store-credit platform as a
durable Temporal saga. Designed to generate a realistic, varied checkpoint
(event history) shape for CockroachDB storage sizing.

Saga shape:
    ReturnInitiated
      -> EligibilityCheck (rules + AI classification activity)
      -> PackageProtectionCheck (branch)
      -> wait for carrier "package received" signal (with SLA timer)
      -> DispositionDecision (AI agent activity: refund/exchange/store-credit)
      -> disposition activity (with compensation on failure)
      -> CustomerNotify
    Concurrently: SupportEscalation signal can arrive at any point and
    short-circuits to a human-handled resolution path.

Each Activity call, Timer, and Signal is one or more durable checkpoints
(history events) persisted to CockroachDB via the `temporal` database.
"""

from __future__ import annotations

import asyncio
from dataclasses import dataclass
from datetime import timedelta
from enum import Enum

from temporalio import workflow, activity
from temporalio.common import RetryPolicy


# ---------------------------------------------------------------------------
# Data contracts
# ---------------------------------------------------------------------------

class Disposition(str, Enum):
    REFUND = "refund"
    EXCHANGE = "exchange"
    STORE_CREDIT = "store_credit"


@dataclass
class ReturnRequest:
    order_id: str
    customer_id: str
    item_sku: str
    return_reason: str
    order_value_cents: int
    has_package_protection: bool = False


@dataclass
class ReturnResult:
    order_id: str
    disposition: str
    escalated: bool
    compensated: bool
    checkpoints_estimate: int = 0


# ---------------------------------------------------------------------------
# Activities (all external side effects live here -- never in workflow code)
# ---------------------------------------------------------------------------

@activity.defn
async def classify_return_reason(reason: str) -> dict:
    """Simulates an AI-agent classification call (e.g. LLM) of free-text
    return reason into a structured eligibility signal. In production this
    activity would call out to an LLM provider; here it's simulated."""
    await asyncio.sleep(0.05)
    is_eligible = "counterfeit" not in reason.lower() and "used" not in reason.lower()
    return {"eligible": is_eligible, "confidence": 0.92, "category": "standard"}


@activity.defn
async def check_package_protection(order_id: str, protected: bool) -> dict:
    await asyncio.sleep(0.02)
    return {"claim_valid": protected, "coverage_cents": 5000 if protected else 0}


@activity.defn
async def recommend_disposition(req: ReturnRequest, eligibility: dict) -> str:
    """AI agent step: recommends refund / exchange / store credit based on
    order value, inventory signals, and customer history (simulated)."""
    await asyncio.sleep(0.05)
    if req.order_value_cents > 10000:
        return Disposition.EXCHANGE.value
    return Disposition.STORE_CREDIT.value


@activity.defn
async def process_refund(order_id: str, amount_cents: int) -> str:
    await asyncio.sleep(0.03)
    return f"refund:{order_id}:{amount_cents}"


@activity.defn
async def process_exchange(order_id: str, sku: str) -> str:
    await asyncio.sleep(0.03)
    return f"exchange:{order_id}:{sku}"


@activity.defn
async def process_store_credit(order_id: str, amount_cents: int) -> str:
    await asyncio.sleep(0.03)
    return f"credit:{order_id}:{amount_cents}"


@activity.defn
async def compensate_disposition(order_id: str, disposition: str) -> None:
    """Saga compensation: reverse whatever the disposition activity did."""
    await asyncio.sleep(0.02)


@activity.defn
async def notify_customer(order_id: str, disposition: str) -> None:
    await asyncio.sleep(0.02)


# ---------------------------------------------------------------------------
# Workflow (deterministic orchestration only)
# ---------------------------------------------------------------------------

DEFAULT_RETRY = RetryPolicy(maximum_attempts=3, backoff_coefficient=2.0)


@workflow.defn
class ReturnWorkflow:
    def __init__(self) -> None:
        self._package_received = False
        self._escalated = False

    @workflow.signal
    async def package_received(self) -> None:
        self._package_received = True

    @workflow.signal
    async def escalate_to_support(self) -> None:
        self._escalated = True

    @workflow.run
    async def run(self, req: ReturnRequest) -> ReturnResult:
        # 1. Eligibility check (AI classification activity)
        eligibility = await workflow.execute_activity(
            classify_return_reason,
            req.return_reason,
            start_to_close_timeout=timedelta(seconds=60),
            retry_policy=DEFAULT_RETRY,
        )

        if not eligibility["eligible"]:
            await workflow.execute_activity(
                notify_customer,
                args=[req.order_id, "rejected"],
                start_to_close_timeout=timedelta(seconds=60),
            )
            return ReturnResult(req.order_id, "rejected", False, False)

        # 2. Package protection branch
        if req.has_package_protection:
            await workflow.execute_activity(
                check_package_protection,
                args=[req.order_id, req.has_package_protection],
                start_to_close_timeout=timedelta(seconds=60),
                retry_policy=DEFAULT_RETRY,
            )

        # 3. Wait for physical package OR support escalation, with an SLA timer.
        #    This produces TimerStarted/Fired and SignalReceived checkpoints.
        try:
            await workflow.wait_condition(
                lambda: self._package_received or self._escalated,
                timeout=timedelta(days=14),
            )
        except asyncio.TimeoutError:
            # SLA breach: auto-expire the case
            return ReturnResult(req.order_id, "expired", self._escalated, False)

        if self._escalated:
            await workflow.execute_activity(
                notify_customer,
                args=[req.order_id, "escalated"],
                start_to_close_timeout=timedelta(seconds=60),
            )
            return ReturnResult(req.order_id, "escalated", True, False)

        # 4. Disposition decision (AI agent step)
        disposition = await workflow.execute_activity(
            recommend_disposition,
            args=[req, eligibility],
            start_to_close_timeout=timedelta(seconds=60),
            retry_policy=DEFAULT_RETRY,
        )

        # 5. Execute disposition, with saga-style compensation on failure
        compensated = False
        try:
            if disposition == Disposition.REFUND.value:
                await workflow.execute_activity(
                    process_refund,
                    args=[req.order_id, req.order_value_cents],
                    start_to_close_timeout=timedelta(seconds=60),
                    retry_policy=DEFAULT_RETRY,
                )
            elif disposition == Disposition.EXCHANGE.value:
                await workflow.execute_activity(
                    process_exchange,
                    args=[req.order_id, req.item_sku],
                    start_to_close_timeout=timedelta(seconds=60),
                    retry_policy=DEFAULT_RETRY,
                )
            else:
                await workflow.execute_activity(
                    process_store_credit,
                    args=[req.order_id, req.order_value_cents],
                    start_to_close_timeout=timedelta(seconds=60),
                    retry_policy=DEFAULT_RETRY,
                )
        except Exception:
            await workflow.execute_activity(
                compensate_disposition,
                args=[req.order_id, disposition],
                start_to_close_timeout=timedelta(seconds=60),
            )
            compensated = True

        # 6. Notify
        await workflow.execute_activity(
            notify_customer,
            args=[req.order_id, disposition],
            start_to_close_timeout=timedelta(seconds=60),
        )

        return ReturnResult(req.order_id, disposition, False, compensated)
Enter fullscreen mode Exit fullscreen mode

worker.py

"""
Worker for the return-automation saga.
Polls the `returns` task queue on the local Temporal Server
(backed by CockroachDB) and executes workflow tasks + activities.

Usage:
    python worker.py
"""

import asyncio
import logging

from temporalio.client import Client
from temporalio.worker import Worker

from return_workflow import (
    ReturnWorkflow,
    classify_return_reason,
    check_package_protection,
    recommend_disposition,
    process_refund,
    process_exchange,
    process_store_credit,
    compensate_disposition,
    notify_customer,
)

TEMPORAL_ADDRESS = "localhost:7233"
NAMESPACE = "default"
TASK_QUEUE = "returns"

logging.basicConfig(level=logging.INFO)
log = logging.getLogger("worker")


async def main() -> None:
    client = await Client.connect(TEMPORAL_ADDRESS, namespace=NAMESPACE)
    log.info(f"Connected to {TEMPORAL_ADDRESS}, namespace={NAMESPACE}")

    worker = Worker(
        client,
        task_queue=TASK_QUEUE,
        workflows=[ReturnWorkflow],
        activities=[
            classify_return_reason,
            check_package_protection,
            recommend_disposition,
            process_refund,
            process_exchange,
            process_store_credit,
            compensate_disposition,
            notify_customer,
        ],
        # Tuned down from SDK defaults to avoid CPU contention on a single
        # test machine -- size these to your own available cores/RAM.
        max_concurrent_activities=30,
        max_concurrent_workflow_tasks=30,
        max_concurrent_workflow_task_polls=8,
        max_concurrent_activity_task_polls=8,
    )

    log.info(f"Worker polling task queue '{TASK_QUEUE}'... (Ctrl+C to stop)")
    await worker.run()


if __name__ == "__main__":
    asyncio.run(main())
Enter fullscreen mode Exit fullscreen mode

batch.py

"""
Batch-starts return-case workflows at scale, toward a target checkpoint
count. Unlike waiting on each workflow's .result(), this does NOT block --
at thousands of workflows that's slow and memory-heavy. It starts +
signals each workflow with bounded concurrency, then lets the worker
process the backlog independently. Check progress separately via SQL
or `temporal workflow list`.

Usage:
    python batch_start.py --count 1000 --concurrency 25
"""

import argparse
import asyncio
import random
import time
import uuid

from temporalio.client import Client

from return_workflow import ReturnRequest, ReturnWorkflow

TEMPORAL_ADDRESS = "localhost:7233"
NAMESPACE = "default"
TASK_QUEUE = "returns"

REASONS = [
    "wrong size", "changed my mind", "arrived damaged",
    "not as described", "found cheaper elsewhere", "defective item",
]


def random_request() -> ReturnRequest:
    return ReturnRequest(
        order_id=f"order-{uuid.uuid4().hex[:8]}",
        customer_id=f"cust-{uuid.uuid4().hex[:6]}",
        item_sku=f"sku-{random.randint(1000, 9999)}",
        return_reason=random.choice(REASONS),
        order_value_cents=random.randint(1500, 25000),
        has_package_protection=random.random() < 0.3,
    )


async def start_and_signal_one(client: Client, sem: asyncio.Semaphore, counters: dict):
    async with sem:
        req = random_request()
        try:
            handle = await client.start_workflow(
                ReturnWorkflow.run,
                req,
                id=f"return-{req.order_id}",
                task_queue=TASK_QUEUE,
            )
            await handle.signal(ReturnWorkflow.package_received)
            counters["started"] += 1
        except Exception as e:
            counters["errors"] += 1
            if counters["errors"] <= 10:
                print(f"  ERROR starting/signaling {req.order_id}: {e}")


async def main(count: int, concurrency: int) -> None:
    client = await Client.connect(TEMPORAL_ADDRESS, namespace=NAMESPACE)
    print(f"Connected. Starting {count} workflows, concurrency={concurrency}...")

    sem = asyncio.Semaphore(concurrency)
    counters = {"started": 0, "errors": 0}
    start_time = time.monotonic()

    tasks = [
        asyncio.create_task(start_and_signal_one(client, sem, counters))
        for _ in range(count)
    ]

    last_report = 0
    while not all(t.done() for t in tasks):
        await asyncio.sleep(2)
        done = counters["started"] + counters["errors"]
        if done - last_report >= max(count // 20, 100):
            elapsed = time.monotonic() - start_time
            rate = done / elapsed if elapsed > 0 else 0
            print(f"  progress: {done}/{count} ({rate:.1f}/sec)")
            last_report = done

    await asyncio.gather(*tasks)

    elapsed = time.monotonic() - start_time
    print(f"\nDone. started={counters['started']} errors={counters['errors']} "
          f"in {elapsed:.1f}s ({counters['started']/elapsed:.1f}/sec)")
    print("Note: workflows may still be processing on the worker -- this only "
          "confirms they were successfully started+signaled, not completed.")
    print("Check completion progress with:")
    print(f'  temporal --address {TEMPORAL_ADDRESS} -n {NAMESPACE} workflow count '
          f'--query "WorkflowType=\'ReturnWorkflow\' AND ExecutionStatus=\'Running\'"')


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--count", type=int, required=True)
    parser.add_argument("--concurrency", type=int, default=25)
    args = parser.parse_args()
    asyncio.run(main(args.count, args.concurrency))
Enter fullscreen mode Exit fullscreen mode

temporal-server.yaml

log:
  stdout: true
  level: info

persistence:
  defaultStore: cockroachdb-default
  visibilityStore: cockroachdb-visibility
  numHistoryShards: 256
  datastores:
    cockroachdb-default:
      sql:
        pluginName: "postgres12"
        databaseName: "temporal"
        connectAddr: "<your-cluster-host>.cockroachlabs.cloud:26257"
        connectProtocol: "tcp"
        user: "<your-sql-user>"
        password: "<your-password>"
        maxConns: 200
        maxIdleConns: 200
        maxConnLifetime: "1h"
        tls:
          enabled: true
          caFile: "<path-to-ca-cert>"

    cockroachdb-visibility:
      sql:
        pluginName: "postgres12"
        databaseName: "temporal_visibility"
        connectAddr: "<your-cluster-host>.cockroachlabs.cloud:26257"
        connectProtocol: "tcp"
        user: "<your-sql-user>"
        password: "<your-password>"
        maxConns: 100
        maxIdleConns: 100
        maxConnLifetime: "1h"
        tls:
          enabled: true
          caFile: "<path-to-ca-cert>"

global:
  membership:
    maxJoinDuration: 30s
    broadcastAddress: "127.0.0.1"
  pprof:
    port: 7936

services:
  frontend:
    rpc:
      grpcPort: 7233
      membershipPort: 6933
      bindOnIP: '0.0.0.0'
      httpPort: 7243

  matching:
    rpc:
      grpcPort: 7235
      membershipPort: 6935
      bindOnLocalHost: true

  history:
    rpc:
      grpcPort: 7234
      membershipPort: 6934
      bindOnLocalHost: true

  worker:
    rpc:
      membershipPort: 6939

clusterMetadata:
  enableGlobalNamespace: false
  failoverVersionIncrement: 10
  masterClusterName: "active"
  currentClusterName: "active"
  clusterInformation:
    active:
      enabled: true
      initialFailoverVersion: 1
      rpcName: "frontend"
      rpcAddress: "localhost:7233"
      httpAddress: "localhost:7243"

dcRedirectionPolicy:
  policy: "noop"
Enter fullscreen mode Exit fullscreen mode

temporal-ui-server.yaml

temporalGrpcAddress: 127.0.0.1:7233
host: 0.0.0.0
port: 8233
enableUi: true
defaultNamespace: default
Enter fullscreen mode Exit fullscreen mode

Top comments (0)