マイグレーションノート (0.5.xから0.6.xへの移行)

パッケージ

全てのパッケージ

Module / Platform JVM Scala Native Scala.js Scaladoc
ldbc-sql Scaladoc
ldbc-core Scaladoc
ldbc-connector Scaladoc
jdbc-connector Scaladoc
ldbc-dsl Scaladoc
ldbc-statement Scaladoc
ldbc-query-builder Scaladoc
ldbc-schema Scaladoc
ldbc-codegen Scaladoc
ldbc-plugin Scaladoc
ldbc-zio-interop Scaladoc
ldbc-authentication-plugin Scaladoc
ldbc-aws-authentication-plugin Scaladoc

🎯 主要な変更点

1. OpenTelemetry テレメトリの大幅拡充

0.6.0 では OpenTelemetry Semantic Conventions v1.39.0 に準拠したテレメトリサポートが大幅に強化されました。

TelemetryConfig の追加

テレメトリの動作を細かく制御できる TelemetryConfig が追加されました。

import ldbc.connector.telemetry.TelemetryConfig

// デフォルト設定(仕様準拠)
val config = TelemetryConfig.default

// クエリサマリー生成を無効化
val noSummaryConfig = TelemetryConfig.withoutQueryTextExtraction

// 個別に設定を変更
val customConfig = TelemetryConfig.default
  .withoutQueryTextExtraction   // db.query.summary の自動生成を無効化
  .withoutSanitization          // クエリのサニタイズを無効化(機密データ露出に注意)
  .withoutInClauseCollapsing    // IN句の圧縮を無効化
オプション デフォルト 説明
extractMetadataFromQueryText true クエリテキストから db.query.summary を生成してスパン名に使用する
sanitizeNonParameterizedQueries true 非パラメータ化クエリのリテラルを ? に置換する(仕様で必須)
collapseInClauses true IN (?, ?, ?)IN (?) に圧縮してカーディナリティを抑制する

DatabaseMetrics と Meter の追加

接続プールのメトリクス(待機時間・使用時間・タイムアウト件数)を OpenTelemetry の Meter 経由で記録できるようになりました。

import org.typelevel.otel4s.metrics.Meter
import ldbc.connector.*

// Meter を注入してプールメトリクスを有効化
MySQLDataSource.pooling[IO](
  config  = mysqlConfig,
  meter   = Some(summon[Meter[IO]])
).use { pool =>
  // プールの待機時間・使用時間・タイムアウトが自動記録される
  pool.getConnection.use { conn => ... }
}

MySQLDataSource への新メソッド

MySQLDataSourcesetMetersetTelemetryConfig が追加されました。

import org.typelevel.otel4s.metrics.Meter
import ldbc.connector.*
import ldbc.connector.telemetry.TelemetryConfig

val datasource = MySQLDataSource
  .build[IO](host = "localhost", port = 3306, user = "ldbc")
  .setPassword("password")
  .setDatabase("mydb")
  .setMeter(summon[Meter[IO]])
  .setTelemetryConfig(TelemetryConfig.default.withoutQueryTextExtraction)

pooling メソッドのシグネチャ変更

MySQLDataSource.poolingMySQLDataSource.poolingWithBeforeAftermetertelemetryConfig パラメータが追加されました。既存のコードは引数なしで呼び出せるため後方互換性はありますが、メトリクスを使用する場合は更新が必要です。

移行前 (0.5.x):

MySQLDataSource.pooling[IO](
  config         = mysqlConfig,
  metricsTracker = Some(tracker),
  tracer         = Some(tracer)
).use { pool => ... }

移行後 (0.6.x):

MySQLDataSource.pooling[IO](
  config          = mysqlConfig,
  metricsTracker  = Some(tracker),
  meter           = Some(summon[Meter[IO]]),
  tracer          = Some(tracer),
  telemetryConfig = TelemetryConfig.default
).use { pool => ... }

TelemetryAttribute のキー名変更(破壊的変更)

TelemetryAttribute の定数名が Semantic Conventions v1.39.0 に合わせて変更されました。TelemetryAttribute を直接参照している場合は更新が必要です。

旧 (0.5.x) 新 (0.6.x)
DB_SYSTEM DB_SYSTEM_NAME
DB_OPERATION DB_OPERATION_NAME
DB_QUERY DB_QUERY_TEXT
STATUS_CODE DB_RESPONSE_STATUS_CODE
VERSION DB_MYSQL_VERSION
THREAD_ID DB_MYSQL_THREAD_ID
AUTH_PLUGIN DB_MYSQL_AUTH_PLUGIN

2. MySQL 9.x サポートの強化

0.6.0 から MySQL 9.x が正式にサポートされます。接続先の MySQL バージョンに応じて内部の挙動が自動的に切り替わります。

MySQL 9.3.0 以降の getCatalogs 変更

MySQL 9.3.0 以降では getCatalogs() の内部実装が mysql システムテーブルから INFORMATION_SCHEMA.SCHEMATA に切り替わります。ユーザーコードへの影響はありませんが、MySQL 9.3+ に接続している場合は INFORMATION_SCHEMA への SELECT 権限が必要になります。

getTablePrivileges の変更

DatabaseMetaData.getTablePrivileges() の内部実装が mysql.tables_priv から INFORMATION_SCHEMA.TABLE_PRIVILEGES に変更されました。

3. スキーマ: VECTOR 型の追加

ldbc-schema モジュールに MySQL の VECTOR 型を表す DataType が追加されました。

import ldbc.schema.*

class EmbeddingTable extends Table[Embedding]("embeddings"):
  def id:        Column[Long]         = column[Long]("id")
  def embedding: Column[Array[Float]] = column[Array[Float]]("embedding", VECTOR(1536))

  override def * = (id *: embedding).to[Embedding]

4. エラートレーシングの改善

Protocol 層でエラー発生時に span.setStatus(StatusCode.Error, message) が自動設定されるようになりました。分散トレーシングツール(Jaeger、Zipkin 等)でエラースパンが正しく表示されます。ユーザーコードへの変更は不要です。

5. サンプル Grafana ダッシュボード

コネクションプールの可観測性を素早く確認できる Grafana ダッシュボードを提供しています。Prometheus データソースが必要です。

ダッシュボードに含まれるパネル:

Grafana ダッシュボード

以下の JSON を Grafana にインポートすることで、上記のダッシュボードを作成できます。

ダッシュボード JSON を表示(クリックで展開)
{
  "__inputs": [
    {
      "name": "DS_PROMETHEUS",
      "label": "Prometheus",
      "description": "",
      "type": "datasource",
      "pluginId": "prometheus",
      "pluginName": "Prometheus"
    }
  ],
  "__elements": {},
  "__requires": [
    {
      "type": "grafana",
      "id": "grafana",
      "name": "Grafana",
      "version": "10.0.0"
    },
    {
      "type": "datasource",
      "id": "prometheus",
      "name": "Prometheus",
      "version": "1.0.0"
    },
    {
      "type": "panel",
      "id": "stat",
      "name": "Stat",
      "version": ""
    },
    {
      "type": "panel",
      "id": "gauge",
      "name": "Gauge",
      "version": ""
    },
    {
      "type": "panel",
      "id": "timeseries",
      "name": "Time series",
      "version": ""
    },
    {
      "type": "panel",
      "id": "heatmap",
      "name": "Heatmap",
      "version": ""
    },
    {
      "type": "panel",
      "id": "alertlist",
      "name": "Alert list",
      "version": ""
    }
  ],
  "annotations": {
    "list": [
      {
        "builtIn": 1,
        "datasource": {
          "type": "grafana",
          "uid": "-- Grafana --"
        },
        "enable": true,
        "hide": true,
        "iconColor": "rgba(0, 211, 255, 1)",
        "name": "Annotations & Alerts",
        "type": "dashboard"
      }
    ]
  },
  "description": "MySQL Connection Pool observability dashboard using OpenTelemetry Semantic Conventions (db.client.* metrics). Requires OTel Java Agent or manual OTel instrumentation with Prometheus exporter.",
  "editable": true,
  "fiscalYearStartMonth": 0,
  "graphTooltip": 1,
  "id": null,
  "links": [
    {
      "title": "OTel DB Metrics Spec",
      "url": "https://opentelemetry.io/docs/specs/semconv/db/database-metrics/",
      "type": "link",
      "icon": "external link",
      "targetBlank": true
    }
  ],
  "liveNow": false,
  "panels": [
    {
      "id": 102,
      "type": "row",
      "title": "DB Operation Duration (OTel Stable)",
      "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 },
      "collapsed": false,
      "panels": []
    },
    {
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "type": "gauge",
      "id": 50,
      "title": "Operation Duration — p50",
      "gridPos": { "h": 7, "w": 8, "x": 0, "y": 1 },
      "targets": [
        {
          "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
          "expr": "histogram_quantile(0.50, sum by (le) (increase(db_client_operation_duration_seconds_bucket{db_system_name=\"mysql\", job=~\"${job}\"}[$__range])))",
          "refId": "A", "editorMode": "code", "range": false, "instant": true
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "s", "decimals": 2, "min": 0, "max": 0.1,
          "color": { "mode": "thresholds" },
          "thresholds": { "mode": "absolute", "steps": [
            { "color": "green", "value": null },
            { "color": "yellow", "value": 0.01 },
            { "color": "orange", "value": 0.05 },
            { "color": "red", "value": 0.1 }
          ]}
        }, "overrides": []
      },
      "options": { "reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false }, "showThresholdLabels": false, "showThresholdMarkers": true, "orientation": "auto" }
    },
    {
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "type": "gauge",
      "id": 51,
      "title": "Operation Duration — p95",
      "gridPos": { "h": 7, "w": 8, "x": 8, "y": 1 },
      "targets": [
        {
          "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
          "expr": "histogram_quantile(0.95, sum by (le) (increase(db_client_operation_duration_seconds_bucket{db_system_name=\"mysql\", job=~\"${job}\"}[$__range])))",
          "refId": "A", "editorMode": "code", "range": false, "instant": true
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "s", "decimals": 2, "min": 0, "max": 0.1,
          "color": { "mode": "thresholds" },
          "thresholds": { "mode": "absolute", "steps": [
            { "color": "green", "value": null },
            { "color": "yellow", "value": 0.01 },
            { "color": "orange", "value": 0.05 },
            { "color": "red", "value": 0.1 }
          ]}
        }, "overrides": []
      },
      "options": { "reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false }, "showThresholdLabels": false, "showThresholdMarkers": true, "orientation": "auto" }
    },
    {
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "type": "gauge",
      "id": 52,
      "title": "Operation Duration — p99",
      "gridPos": { "h": 7, "w": 8, "x": 16, "y": 1 },
      "targets": [
        {
          "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
          "expr": "histogram_quantile(0.99, sum by (le) (increase(db_client_operation_duration_seconds_bucket{db_system_name=\"mysql\", job=~\"${job}\"}[$__range])))",
          "refId": "A", "editorMode": "code", "range": false, "instant": true
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "s", "decimals": 2, "min": 0, "max": 0.1,
          "color": { "mode": "thresholds" },
          "thresholds": { "mode": "absolute", "steps": [
            { "color": "green", "value": null },
            { "color": "yellow", "value": 0.01 },
            { "color": "orange", "value": 0.05 },
            { "color": "red", "value": 0.1 }
          ]}
        }, "overrides": []
      },
      "options": { "reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false }, "showThresholdLabels": false, "showThresholdMarkers": true, "orientation": "auto" }
    },
    {
      "id": 100,
      "type": "row",
      "title": "Connection Pool Status",
      "gridPos": { "h": 1, "w": 24, "x": 0, "y": 8 },
      "collapsed": false,
      "panels": []
    },
    {
      "id": 1, "title": "Active (used)", "type": "stat",
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "gridPos": { "h": 5, "w": 5, "x": 0, "y": 9 },
      "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "db_client_connection_count{db_client_connection_state=\"used\", db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}", "legendFormat": "{{db_client_connection_pool_name}}", "refId": "A", "editorMode": "code", "range": true }],
      "description": "db.client.connection.count{db.client.connection.state=used}",
      "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "graphMode": "area", "colorMode": "value" },
      "fieldConfig": { "defaults": { "color": { "mode": "fixed", "fixedColor": "light-blue" }, "unit": "short" }, "overrides": [] }
    },
    {
      "id": 2, "title": "Idle", "type": "stat",
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "gridPos": { "h": 5, "w": 5, "x": 5, "y": 9 },
      "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "db_client_connection_count{db_client_connection_state=\"idle\", db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}", "legendFormat": "{{db_client_connection_pool_name}}", "refId": "A", "editorMode": "code", "range": true }],
      "description": "db.client.connection.count{db.client.connection.state=idle}",
      "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "graphMode": "area", "colorMode": "value" },
      "fieldConfig": { "defaults": { "color": { "mode": "fixed", "fixedColor": "green" }, "unit": "short" }, "overrides": [] }
    },
    {
      "id": 3, "title": "Pending Requests", "type": "stat",
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "gridPos": { "h": 5, "w": 5, "x": 10, "y": 9 },
      "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "db_client_connection_pending_requests{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}", "legendFormat": "{{db_client_connection_pool_name}}", "refId": "A", "editorMode": "code", "range": true }],
      "description": "db.client.connection.pending_requests\n0以外が持続する場合はプール枯渇の前兆",
      "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "graphMode": "area", "colorMode": "value" },
      "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "thresholds": { "mode": "absolute", "steps": [{ "color": "green", "value": null }, { "color": "yellow", "value": 1 }, { "color": "red", "value": 3 }] }, "unit": "short" }, "overrides": [] }
    },
    {
      "id": 4, "title": "Wait Time p99", "type": "stat",
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "gridPos": { "h": 5, "w": 5, "x": 15, "y": 9 },
      "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "histogram_quantile(0.99, sum by (le) (rate(db_client_connection_wait_time_seconds_bucket{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}[$__rate_interval])))", "legendFormat": "p99", "refId": "A", "editorMode": "code", "range": true, "interval": "1m" }],
      "description": "db.client.connection.wait_time (p99)",
      "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "graphMode": "area", "colorMode": "value" },
      "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "thresholds": { "mode": "absolute", "steps": [{ "color": "purple", "value": null }, { "color": "yellow", "value": 0.03 }, { "color": "red", "value": 0.1 }] }, "unit": "s", "decimals": 3 }, "overrides": [] }
    },
    {
      "id": 5, "title": "Pool Usage", "type": "gauge",
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "gridPos": { "h": 5, "w": 4, "x": 20, "y": 9 },
      "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "db_client_connection_count{db_client_connection_state=\"used\", db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"} / ignoring(db_client_connection_state) db_client_connection_max{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}", "legendFormat": "{{db_client_connection_pool_name}}", "refId": "A", "editorMode": "code", "range": false, "instant": true }],
      "description": "active / max",
      "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "showThresholdLabels": false, "showThresholdMarkers": true },
      "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "thresholds": { "mode": "absolute", "steps": [{ "color": "green", "value": null }, { "color": "yellow", "value": 0.7 }, { "color": "red", "value": 0.9 }] }, "unit": "percentunit", "min": 0, "max": 1 }, "overrides": [] }
    },
    {
      "id": 6, "title": "Connection Pool Breakdown", "type": "timeseries",
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "gridPos": { "h": 9, "w": 12, "x": 0, "y": 14 },
      "targets": [
        { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "db_client_connection_count{db_client_connection_state=\"used\", db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}", "legendFormat": "Used (active)", "refId": "A", "editorMode": "code", "range": true },
        { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "db_client_connection_count{db_client_connection_state=\"idle\", db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}", "legendFormat": "Idle", "refId": "B", "editorMode": "code", "range": true },
        { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "db_client_connection_pending_requests{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}", "legendFormat": "Pending", "refId": "C", "editorMode": "code", "range": true },
        { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "db_client_connection_max{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}", "legendFormat": "Max (limit)", "refId": "D", "editorMode": "code", "range": true }
      ],
      "description": "db.client.connection.count by state + max",
      "options": { "tooltip": { "mode": "multi", "sort": "desc" }, "legend": { "displayMode": "list", "placement": "bottom", "calcs": ["lastNotNull"] } },
      "fieldConfig": {
        "defaults": { "custom": { "drawStyle": "line", "lineInterpolation": "smooth", "fillOpacity": 30, "stacking": { "mode": "normal", "group": "A" }, "lineWidth": 1, "showPoints": "never", "gradientMode": "opacity", "axisLabel": "connections", "spanNulls": true }, "unit": "short", "color": { "mode": "palette-classic" } },
        "overrides": [
          { "matcher": { "id": "byName", "options": "Used (active)" }, "properties": [{ "id": "color", "value": { "mode": "fixed", "fixedColor": "light-blue" } }] },
          { "matcher": { "id": "byName", "options": "Idle" }, "properties": [{ "id": "color", "value": { "mode": "fixed", "fixedColor": "green" } }] },
          { "matcher": { "id": "byName", "options": "Pending" }, "properties": [{ "id": "color", "value": { "mode": "fixed", "fixedColor": "orange" } }] },
          { "matcher": { "id": "byName", "options": "Max (limit)" }, "properties": [{ "id": "color", "value": { "mode": "fixed", "fixedColor": "red" } }, { "id": "custom.fillOpacity", "value": 0 }, { "id": "custom.stacking", "value": { "mode": "none" } }, { "id": "custom.lineStyle", "value": { "fill": "dash", "dash": [10, 10] } }, { "id": "custom.lineWidth", "value": 2 }] }
        ]
      }
    },
    {
      "id": 7, "title": "Latency Distribution", "type": "bargauge",
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "gridPos": { "h": 9, "w": 12, "x": 12, "y": 14 },
      "targets": [
        { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "increase(db_client_connection_wait_time_seconds_sum{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}[$__range]) / increase(db_client_connection_wait_time_seconds_count{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}[$__range])", "legendFormat": "Wait — {{db_client_connection_pool_name}}", "refId": "A", "editorMode": "code", "range": false, "instant": true },
        { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "increase(db_client_connection_create_time_seconds_sum{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}[$__range]) / increase(db_client_connection_create_time_seconds_count{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}[$__range])", "legendFormat": "Create — {{db_client_connection_pool_name}}", "refId": "B", "editorMode": "code", "range": false, "instant": true },
        { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "increase(db_client_connection_use_time_seconds_sum{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}[$__range]) / increase(db_client_connection_use_time_seconds_count{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}[$__range])", "legendFormat": "Use — {{db_client_connection_pool_name}}", "refId": "C", "editorMode": "code", "range": false, "instant": true }
      ],
      "description": "選択期間内の平均レイテンシ\ndb.client.connection.{wait_time, create_time, use_time}",
      "options": { "reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false }, "orientation": "horizontal", "displayMode": "gradient", "showUnfilled": true, "minVizWidth": 8, "minVizHeight": 16, "valueMode": "color" },
      "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "unit": "s", "decimals": 4, "thresholds": { "mode": "absolute", "steps": [{ "color": "green", "value": null }, { "color": "yellow", "value": 0.05 }, { "color": "red", "value": 0.5 }] } }, "overrides": [] }
    },
    {
      "id": 8, "title": "Connection Timeouts", "type": "timeseries",
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "gridPos": { "h": 8, "w": 12, "x": 0, "y": 23 },
      "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "increase(db_client_connection_timeouts_total{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}[15m])", "legendFormat": "Timeouts — {{db_client_connection_pool_name}}", "refId": "A", "editorMode": "code", "range": true, "instant": false, "interval": "15m" }],
      "description": "db.client.connection.timeouts\n15分間のタイムアウト発生数",
      "options": { "tooltip": { "mode": "multi", "sort": "desc" }, "legend": { "displayMode": "list", "placement": "bottom", "calcs": ["sum", "lastNotNull"] } },
      "fieldConfig": { "defaults": { "custom": { "drawStyle": "bars", "lineWidth": 1, "fillOpacity": 60, "showPoints": "never", "stacking": { "mode": "none" }, "gradientMode": "scheme" }, "unit": "short", "color": { "mode": "fixed", "fixedColor": "red" }, "noValue": "0" }, "overrides": [] }
    },
    {
      "id": 9, "title": "Connection Wait Time Heatmap", "type": "heatmap",
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "gridPos": { "h": 8, "w": 12, "x": 12, "y": 23 },
      "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "sum by (le) (increase(db_client_connection_wait_time_seconds_bucket{db_client_connection_pool_name=~\"${pool_name}\", job=~\"${job}\"}[15m]))", "legendFormat": "{{le}}", "refId": "A", "editorMode": "code", "range": true, "instant": false, "format": "heatmap", "interval": "15m" }],
      "description": "db.client.connection.wait_time bucket distribution",
      "options": { "calculate": true, "color": { "mode": "scheme", "scheme": "Oranges", "fill": "orange", "reverse": false, "exponent": 0.5, "steps": 32 }, "yAxis": { "unit": "s", "reverse": false, "max": 0.1, "decimals": 0 }, "cellGap": 1, "tooltip": { "show": true, "yHistogram": true }, "legend": { "show": true }, "calculation": { "xBuckets": { "mode": "size" }, "yBuckets": { "mode": "size" } } },
      "fieldConfig": { "defaults": { "custom": { "scaleDistribution": { "type": "log", "log": 2 } } }, "overrides": [] }
    },
    {
      "id": 10, "title": "Alert History", "type": "alertlist",
      "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" },
      "gridPos": { "h": 8, "w": 24, "x": 0, "y": 32 },
      "targets": [],
      "description": "",
      "options": { "viewMode": "list", "groupMode": "default", "groupBy": [], "maxItems": 10, "sortOrder": 3, "dashboardAlerts": true, "alertName": "", "alertInstanceLabelFilter": "", "stateFilter": { "firing": true, "pending": true, "noData": true, "normal": true, "error": true }, "folder": "", "tags": [] }
    }
  ],
  "refresh": "10s",
  "schemaVersion": 38,
  "style": "dark",
  "tags": ["mysql", "opentelemetry", "connection-pool", "otel", "db-client"],
  "templating": {
    "list": [
      { "current": { "selected": false, "text": "Prometheus", "value": "Prometheus" }, "hide": 0, "includeAll": false, "multi": false, "name": "DS_PROMETHEUS", "options": [], "query": "prometheus", "refresh": 1, "regex": "", "skipUrlSync": false, "type": "datasource" },
      { "allValue": "", "current": { "selected": true, "text": ["All"], "value": ["$__all"] }, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "definition": "label_values(db_client_connection_count, db_client_connection_pool_name)", "hide": 0, "includeAll": true, "label": "Pool Name", "multi": true, "name": "pool_name", "options": [], "query": { "query": "label_values(db_client_connection_count, db_client_connection_pool_name)", "refId": "StandardVariableQuery" }, "refresh": 2, "regex": "", "skipUrlSync": false, "sort": 1, "type": "query" },
      { "allValue": "", "current": { "selected": true, "text": ["All"], "value": ["$__all"] }, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "definition": "label_values(db_client_connection_count, job)", "hide": 0, "includeAll": true, "label": "Job", "multi": true, "name": "job", "options": [], "query": { "query": "label_values(db_client_connection_count, job)", "refId": "StandardVariableQuery" }, "refresh": 2, "regex": "", "skipUrlSync": false, "sort": 1, "type": "query" }
    ]
  },
  "time": { "from": "now-30m", "to": "now" },
  "timepicker": { "refresh_intervals": ["5s", "10s", "30s", "1m", "5m"] },
  "timezone": "",
  "title": "MySQL Connection Pool — Observability (OpenTelemetry)",
  "uid": "mysql-pool-otel",
  "version": 1,
  "weekStart": ""
}

移行ガイド

TelemetryAttribute の定数名を更新する

TelemetryAttribute の定数を直接使用している場合は、新しいキー名に更新してください。

移行前 (0.5.x):

import ldbc.connector.telemetry.TelemetryAttribute

val attr = Attribute(TelemetryAttribute.DB_SYSTEM, "mysql")
val op   = Attribute(TelemetryAttribute.DB_OPERATION, "SELECT")

移行後 (0.6.x):

import ldbc.connector.telemetry.TelemetryAttribute

val attr = Attribute(TelemetryAttribute.DB_SYSTEM_NAME, "mysql")
val op   = Attribute(TelemetryAttribute.DB_OPERATION_NAME, "SELECT")

OTel メトリクスを有効化する

接続プールのメトリクスを収集したい場合は、Meter インスタンスを渡してください。

import cats.effect.IO
import org.typelevel.otel4s.metrics.Meter
import ldbc.connector.*
import ldbc.connector.telemetry.TelemetryConfig

// otel4s のセットアップ(例)
OtelJava.global[IO].use { otel =>
  otel.meterProvider.get("ldbc").flatMap { meter =>
    MySQLDataSource.pooling[IO](
      config          = mysqlConfig,
      meter           = Some(meter),
      telemetryConfig = TelemetryConfig.default
    ).use { pool =>
      pool.getConnection.use { conn =>
        // db.client.connection.wait_duration などのメトリクスが記録される
        sql"SELECT 1".query[Int].to[List].readOnly(Connector.fromDataSource(pool))
      }
    }
  }
}

テレメトリ設定をカスタマイズする

クエリテキストをスパン名に含めたくない場合や、サニタイズを制御したい場合は TelemetryConfig を設定してください。

import ldbc.connector.telemetry.TelemetryConfig

// スパン名を固定名にする(クエリテキストを解析しない)
val config = TelemetryConfig.withoutQueryTextExtraction

// 全て無効化(開発・デバッグ用途のみ推奨)
val debugConfig = TelemetryConfig.default
  .withoutQueryTextExtraction
  .withoutSanitization
  .withoutInClauseCollapsing

まとめ

0.6.x への移行により、以下のメリットが得られます:

  1. OpenTelemetry 仕様準拠の強化: Semantic Conventions v1.39.0 に準拠した属性キー名と動作
  2. プールメトリクスの可視化: 接続待機時間・使用時間・タイムアウト件数を OTel メトリクスとして収集可能
  3. MySQL 9.x の正式サポート: MySQL 9.3+ の API 変更に自動追従
  4. 新しい VECTOR 型: MySQL の VECTOR 型をスキーマ定義で直接扱える
  5. エラートレーシングの改善: スパンのエラー状態が分散トレーシングツールで正確に表示される

ほとんどの変更は後方互換性があります。TelemetryAttribute の定数名を直接使用している場合のみ更新が必要です。