diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index cd9db5dc2..3efa878c8 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -13,6 +13,9 @@ addons-cluster/apecloud-mysql/ @xuriwuyun @leon-inf @apecloud/kb-reviewers @apec
addons/clickhouse/ @leon-inf @apecloud/kb-reviewers @apecloud/kb-addon-reviewers
addons-cluster/clickhouse/ @leon-inf @apecloud/kb-reviewers @apecloud/kb-addon-reviewers
+addons/dolt/ @leon-inf @apecloud/kb-reviewers @apecloud/kb-addon-reviewers
+addons-cluster/dolt/ @leon-inf @apecloud/kb-reviewers @apecloud/kb-addon-reviewers
+
addons/elasticsearch/ @iziang @vipshop @leon-inf @apecloud/kb-reviewers @apecloud/kb-addon-reviewers
addons-cluster/elasticsearch/ @iziang @vipshop @leon-inf @apecloud/kb-reviewers @apecloud/kb-addon-reviewers
diff --git a/README.md b/README.md
index 139a9a672..6a59415e0 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@ KubeBlocks add-ons.
| ---- | ---- | ----------- | ----------- |
| apecloud-mysql | apecloud-mysql-8.0.30
wescale-0.2.7 | ApeCloud MySQL is a database that is compatible with MySQL syntax and achieves high availability through the utilization of the RAFT consensus protocol. | xuriwuyun |
| clickhouse | clickhouse-22.3.18
clickhouse-22.3.20
clickhouse-22.6.1
clickhouse-22.8.21
clickhouse-24.8.3
clickhouse-25.4.4
clickhouse-25.9.7 | ClickHouse is an open-source column-oriented OLAP database management system. Use it to boost your database performance while providing linear scalability and hardware efficiency. | ApeCloud |
+| dolt | dolt-1.84.0 | Dolt is a MySQL-compatible SQL database with Git-style versioning. | |
| elasticsearch | elasticsearch-6.8.23
elasticsearch-7.10.1
elasticsearch-7.10.2
elasticsearch-7.7.1
elasticsearch-7.8.1
elasticsearch-8.1.3
elasticsearch-8.15.5
elasticsearch-8.8.2
kibana-6.8.23
kibana-7.10.1
kibana-7.10.2
kibana-7.7.1
kibana-7.8.1
kibana-8.1.3
kibana-8.15.5
kibana-8.8.2
kibana-8.9.1 | Elasticsearch is a distributed, RESTful search engine optimized for speed and relevance on production-scale workloads. | iziang vipshop |
| etcd | etcd-3.5.15
etcd-3.5.6
etcd-3.6.1 | Etcd is a strongly consistent, distributed key-value store that provides a reliable way to store data that needs to be accessed by a distributed system or cluster of machines. | ApeCloud |
| falkordb | falkordb-4.12.5
falkordb-cluster-4.12.5
falkordb-sent-4.12.5 | FalkorDB is an in-memory graph database based on Redis. | ApeCloud dudizimber |
diff --git a/addons-cluster/dolt/Chart.yaml b/addons-cluster/dolt/Chart.yaml
new file mode 100644
index 000000000..43b0d444d
--- /dev/null
+++ b/addons-cluster/dolt/Chart.yaml
@@ -0,0 +1,33 @@
+apiVersion: v2
+name: dolt-cluster
+type: application
+version: 1.0.0
+description: A Dolt cluster Helm chart for KubeBlocks.
+
+dependencies:
+ - name: kblib
+ version: 0.1.2
+ repository: file://../kblib
+ alias: extra
+
+appVersion: "1.84.0"
+
+keywords:
+ - dolt
+ - database
+ - sql
+ - replication
+
+home: https://github.com/apecloud/kubeblocks-addons
+icon: https://kubeblocks.io/img/logo.png
+
+maintainers:
+ - name: ApeCloud
+ url: https://kubeblocks.io/
+
+sources:
+ - https://github.com/apecloud/kubeblocks/
+ - https://github.com/dolthub/dolt
+
+annotations:
+ category: Database
diff --git a/addons-cluster/dolt/templates/_helpers.tpl b/addons-cluster/dolt/templates/_helpers.tpl
new file mode 100644
index 000000000..05b6914db
--- /dev/null
+++ b/addons-cluster/dolt/templates/_helpers.tpl
@@ -0,0 +1,21 @@
+{{/*
+ComponentDefinition from mode (must match addons/dolt: dolt-replication | dolt-standalone).
+*/}}
+{{- define "dolt-cluster.componentDef" -}}
+{{- if eq .Values.mode "standalone" -}}
+dolt-standalone
+{{- else -}}
+dolt-replication
+{{- end -}}
+{{- end }}
+
+{{/*
+Replica count: standalone is always 1; otherwise use .Values.replicas.
+*/}}
+{{- define "dolt-cluster.replicas" -}}
+{{- if eq .Values.mode "standalone" -}}
+1
+{{- else -}}
+{{ .Values.replicas }}
+{{- end -}}
+{{- end }}
diff --git a/addons-cluster/dolt/templates/cluster.yaml b/addons-cluster/dolt/templates/cluster.yaml
new file mode 100644
index 000000000..725c1366f
--- /dev/null
+++ b/addons-cluster/dolt/templates/cluster.yaml
@@ -0,0 +1,17 @@
+apiVersion: apps.kubeblocks.io/v1
+kind: Cluster
+metadata:
+ name: {{ include "kblib.clusterName" . }}
+ namespace: {{ .Release.Namespace }}
+ labels: {{ include "kblib.clusterLabels" . | nindent 4 }}
+spec:
+ terminationPolicy: {{ .Values.extra.terminationPolicy }}
+ clusterDef: dolt
+ topology: {{ .Values.mode }}
+ componentSpecs:
+ - name: dolt
+ serviceVersion: {{ .Values.version | quote }}
+ replicas: {{ include "dolt-cluster.replicas" . }}
+ {{- include "kblib.componentResources" . | indent 6 }}
+ {{- include "kblib.componentStorages" . | indent 6 }}
+ {{- include "kblib.componentMonitor" . | indent 6 }}
diff --git a/addons-cluster/dolt/values.schema.json b/addons-cluster/dolt/values.schema.json
new file mode 100644
index 000000000..6e9d09ae0
--- /dev/null
+++ b/addons-cluster/dolt/values.schema.json
@@ -0,0 +1,102 @@
+{
+ "$schema": "http://json-schema.org/schema#",
+ "type": "object",
+ "title": "Dolt cluster",
+ "description": "Values for the dolt-cluster Helm chart (KubeBlocks).",
+ "properties": {
+ "mode": {
+ "title": "Mode",
+ "description": "Topology: replication (primary/standby) or standalone (single node).",
+ "type": "string",
+ "default": "replication",
+ "enum": ["replication", "standalone"]
+ },
+ "version": {
+ "title": "Service version",
+ "description": "Dolt serviceVersion; must match a release in ComponentVersion `dolt`.",
+ "type": "string",
+ "default": "1.84.0"
+ },
+ "replicas": {
+ "title": "Replicas",
+ "description": "Replica count when mode is replication (standalone always uses 1).",
+ "type": "integer",
+ "default": 2,
+ "minimum": 1,
+ "maximum": 32
+ },
+ "cpu": {
+ "title": "CPU",
+ "description": "CPU cores per pod.",
+ "type": ["number", "string"],
+ "default": 0.5,
+ "minimum": 0.5,
+ "maximum": 64,
+ "multipleOf": 0.5
+ },
+ "memory": {
+ "title": "Memory (Gi)",
+ "description": "Memory limit/request in Gi.",
+ "type": ["number", "string"],
+ "default": 0.5,
+ "minimum": 0.5,
+ "maximum": 1000
+ },
+ "storage": {
+ "title": "Storage (Gi)",
+ "description": "Persistent volume size in Gi.",
+ "type": ["number", "string"],
+ "default": 10,
+ "minimum": 1,
+ "maximum": 10000
+ },
+ "extra": {
+ "type": "object",
+ "description": "KubeBlocks shared options (kblib).",
+ "properties": {
+ "terminationPolicy": {
+ "title": "Termination policy",
+ "description": "Cluster deletion behavior.",
+ "type": "string",
+ "default": "Delete",
+ "enum": ["DoNotTerminate", "Delete", "WipeOut"]
+ }
+ },
+ "additionalProperties": true
+ },
+ "prometheus": {
+ "type": "object",
+ "description": "Prometheus Operator PodMonitor options.",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "default": false,
+ "description": "If true, render a PodMonitor when the PodMonitor CRD exists."
+ },
+ "metricsPath": {
+ "type": "string",
+ "default": "/metrics",
+ "description": "HTTP path for metrics scrape."
+ },
+ "interval": {
+ "type": "string",
+ "default": "",
+ "description": "Optional scrape interval (empty uses Prometheus default)."
+ },
+ "scrapeTimeout": {
+ "type": "string",
+ "default": "",
+ "description": "Optional per-scrape timeout (empty uses Prometheus default)."
+ },
+ "additionalLabels": {
+ "type": "object",
+ "default": {},
+ "description": "Extra labels on the PodMonitor resource.",
+ "additionalProperties": { "type": "string" }
+ }
+ },
+ "additionalProperties": true
+ }
+ },
+ "additionalProperties": true
+}
diff --git a/addons-cluster/dolt/values.yaml b/addons-cluster/dolt/values.yaml
new file mode 100644
index 000000000..d0df79008
--- /dev/null
+++ b/addons-cluster/dolt/values.yaml
@@ -0,0 +1,22 @@
+# Default values for dolt-cluster.
+
+## @param mode Dolt topology: `replication` (primary/standby) or `standalone` (single node).
+mode: replication
+
+## @param version serviceVersion in ComponentVersion.
+version: 1.84.0
+
+## @param replicas number of dolt nodes when `mode` is `replication` (ignored when `mode` is `standalone`, which is always one replica).
+replicas: 2
+
+## @param cpu CPU cores.
+cpu: 0.5
+
+## @param memory Memory in Gi.
+memory: 0.5
+
+## @param storage storage size in Gi.
+storage: 10
+
+extra:
+ terminationPolicy: Delete
\ No newline at end of file
diff --git a/addons/dolt/.helmignore b/addons/dolt/.helmignore
new file mode 100644
index 000000000..0e8a0eb36
--- /dev/null
+++ b/addons/dolt/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/addons/dolt/Chart.yaml b/addons/dolt/Chart.yaml
new file mode 100644
index 000000000..9fb0478b2
--- /dev/null
+++ b/addons/dolt/Chart.yaml
@@ -0,0 +1,24 @@
+apiVersion: v2
+name: dolt
+description: Dolt is a MySQL-compatible SQL database with Git-style versioning.
+
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 1.0.0
+
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "1.84.0"
diff --git a/addons/dolt/config/dolt-server.yaml.tpl b/addons/dolt/config/dolt-server.yaml.tpl
new file mode 100644
index 000000000..5f4a280ba
--- /dev/null
+++ b/addons/dolt/config/dolt-server.yaml.tpl
@@ -0,0 +1,25 @@
+log_level: info
+data_dir: ${DATA_DIR}
+
+listener:
+ host: 0.0.0.0
+ port: 3306
+
+behavior:
+ read_only: false
+
+metrics:
+ labels: {}
+ host: 0.0.0.0
+ port: 11228
+
+cluster:
+ bootstrap_role: ${BOOTSTRAP_ROLE}
+ bootstrap_epoch: 1
+
+ remotesapi:
+ port: ${REMOTES_API_PORT}
+
+ standby_remotes:
+ - name: standby
+ remote_url_template: http://${STANDBY_HOST}.${HEADLESS_SERVICE_NAME}:${REMOTES_API_PORT}/{database}
diff --git a/addons/dolt/config/dolt-standalone.yaml.tpl b/addons/dolt/config/dolt-standalone.yaml.tpl
new file mode 100644
index 000000000..0ad86267b
--- /dev/null
+++ b/addons/dolt/config/dolt-standalone.yaml.tpl
@@ -0,0 +1,17 @@
+log_level: info
+data_dir: ${DATA_DIR}
+cfg_dir: ${DATA_DIR}/.doltcfg
+privilege_file: ${DATA_DIR}/.doltcfg/privileges.db
+branch_control_file: ${DATA_DIR}/.doltcfg/branch_control.db
+
+listener:
+ host: 0.0.0.0
+ port: 3306
+
+behavior:
+ read_only: false
+
+metrics:
+ labels: {}
+ host: 0.0.0.0
+ port: 11228
\ No newline at end of file
diff --git a/addons/dolt/dashboards/dolt-server.json b/addons/dolt/dashboards/dolt-server.json
new file mode 100644
index 000000000..a0c968d20
--- /dev/null
+++ b/addons/dolt/dashboards/dolt-server.json
@@ -0,0 +1,2059 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "target": {
+ "limit": 100,
+ "matchAny": false,
+ "tags": [],
+ "type": "dashboard"
+ },
+ "type": "dashboard"
+ }
+ ]
+ },
+ "description": "Dolt SQL Server metrics from the Prometheus /metrics endpoint (dss_*, process_*, go_*, sys_*).",
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 1,
+ "id": null,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 1,
+ "panels": [],
+ "title": "Dolt SQL Server",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "description": "Clients connected to this Dolt SQL server instance.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "yellow",
+ "value": 50
+ },
+ {
+ "color": "red",
+ "value": 200
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 6,
+ "x": 0,
+ "y": 1
+ },
+ "id": 2,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto"
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "sum(dss_concurrent_connections{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"})",
+ "legendFormat": "connections",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Concurrent connections",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "description": "Queries currently executing.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "yellow",
+ "value": 10
+ },
+ {
+ "color": "red",
+ "value": 50
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 6,
+ "x": 6,
+ "y": 1
+ },
+ "id": 3,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto"
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "sum(dss_concurrent_queries{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"})",
+ "legendFormat": "queries",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Concurrent queries",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "description": "Encoded Dolt version as exposed by dss_dolt_version (implementation detail of the binary).",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "decimals": 0,
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "text",
+ "value": null
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 6,
+ "x": 12,
+ "y": 1
+ },
+ "id": 4,
+ "options": {
+ "colorMode": "none",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "value_and_name"
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "max(dss_dolt_version{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"})",
+ "legendFormat": "dss_dolt_version",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Dolt version (metric)",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "description": "Approximate process uptime (wall clock).",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 6,
+ "x": 18,
+ "y": 1
+ },
+ "id": 5,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto"
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "max(time() - process_start_time_seconds{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"})",
+ "legendFormat": "uptime",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Process uptime",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 6
+ },
+ "id": 6,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "max"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "dss_concurrent_connections{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "{{pod}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "dss_concurrent_queries{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "{{pod}} queries",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "Connections & concurrent queries",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "ops"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 6
+ },
+ "id": 7,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "max"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "rate(dss_connects{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}[$__rate_interval])",
+ "legendFormat": "{{pod}} connects/s",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "rate(dss_disconnects{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}[$__rate_interval])",
+ "legendFormat": "{{pod}} disconnects/s",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "Connect / disconnect rate",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 14
+ },
+ "id": 8,
+ "panels": [],
+ "title": "Query duration",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "description": "Estimated query latency from dss_query_duration histogram.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 24,
+ "x": 0,
+ "y": 15
+ },
+ "id": 9,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "max"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "histogram_quantile(0.50, sum(rate(dss_query_duration_bucket{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}[$__rate_interval])) by (le, pod))",
+ "legendFormat": "p50 {{pod}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "histogram_quantile(0.90, sum(rate(dss_query_duration_bucket{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}[$__rate_interval])) by (le, pod))",
+ "legendFormat": "p90 {{pod}}",
+ "range": true,
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "histogram_quantile(0.99, sum(rate(dss_query_duration_bucket{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}[$__rate_interval])) by (le, pod))",
+ "legendFormat": "p99 {{pod}}",
+ "range": true,
+ "refId": "C"
+ }
+ ],
+ "title": "Query duration (histogram_quantile)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "bars",
+ "fillOpacity": 80,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "normal"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "ops"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 24,
+ "x": 0,
+ "y": 23
+ },
+ "id": 10,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "sum(rate(dss_query_duration_bucket{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}[$__rate_interval])) by (le)",
+ "legendFormat": "le={{le}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Query duration histogram (rate by bucket)",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 30
+ },
+ "id": 11,
+ "panels": [],
+ "title": "Process",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 8,
+ "x": 0,
+ "y": 31
+ },
+ "id": 12,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "max"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "rate(process_cpu_seconds_total{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}[$__rate_interval])",
+ "legendFormat": "{{pod}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "CPU usage (cores)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 15,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "bytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 8,
+ "x": 8,
+ "y": 31
+ },
+ "id": 13,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "max"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "process_resident_memory_bytes{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "RSS {{pod}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "process_virtual_memory_bytes{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "VSZ {{pod}}",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "Process memory",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "Bps"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 8,
+ "x": 16,
+ "y": 31
+ },
+ "id": 14,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "max"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "rate(process_network_receive_bytes_total{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}[$__rate_interval])",
+ "legendFormat": "rx {{pod}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "rate(process_network_transmit_bytes_total{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}[$__rate_interval])",
+ "legendFormat": "tx {{pod}}",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "Process network throughput",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": [
+ {
+ "matcher": {
+ "id": "byRegexp",
+ "options": "/max/"
+ },
+ "properties": [
+ {
+ "id": "custom.lineStyle",
+ "value": {
+ "dash": [
+ 10,
+ 10
+ ],
+ "fill": "dash"
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 24,
+ "x": 0,
+ "y": 39
+ },
+ "id": 15,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "process_open_fds{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "open {{pod}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "process_max_fds{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "max {{pod}}",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "Open file descriptors",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 46
+ },
+ "id": 16,
+ "panels": [],
+ "title": "Go runtime",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 47
+ },
+ "id": 17,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "max"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "go_goroutines{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "goroutines {{pod}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "go_threads{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "threads {{pod}}",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "Goroutines & OS threads",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 15,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "bytes"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 47
+ },
+ "id": 18,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "max"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "go_memstats_heap_inuse_bytes{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "heap inuse {{pod}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "go_memstats_stack_inuse_bytes{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "stack {{pod}}",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "Go heap & stack",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "description": "Summary quantiles are exposed as quantile=1 or 1.0 depending on the client; some scrapers omit the top quantile. Average STW pause uses _sum/_count and is always present.",
+ "gridPos": {
+ "h": 7,
+ "w": 12,
+ "x": 0,
+ "y": 55
+ },
+ "id": 19,
+ "options": {
+ "legend": {
+ "calcs": [
+ "max"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "go_gc_duration_seconds{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\",quantile=\"1\"} or go_gc_duration_seconds{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\",quantile=\"1.0\"}",
+ "legendFormat": "GC max (summary q≈1) {{pod}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "rate(go_gc_duration_seconds_sum{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}[$__rate_interval]) / rate(go_gc_duration_seconds_count{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}[$__rate_interval])",
+ "legendFormat": "avg STW pause {{pod}}",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "GC pause (summary vs avg)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "none"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 12,
+ "x": 12,
+ "y": 55
+ },
+ "id": 20,
+ "options": {
+ "legend": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "go_gc_gogc_percent{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "GOGC % {{pod}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "go_sched_gomaxprocs_threads{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "GOMAXPROCS {{pod}}",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "GOGC & GOMAXPROCS",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 62
+ },
+ "id": 21,
+ "panels": [],
+ "title": "System (pod)",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "description": "Host-level utilization as reported by Dolt (0–100 scale).",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 20,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "line+area"
+ }
+ },
+ "mappings": [],
+ "max": 100,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "yellow",
+ "value": 70
+ },
+ {
+ "color": "red",
+ "value": 90
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 24,
+ "x": 0,
+ "y": 63
+ },
+ "id": 22,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean",
+ "max"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "sys_cpu_usage{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "CPU % {{pod}}",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "sys_mem_usage{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "Memory % {{pod}}",
+ "range": true,
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "sys_disk_usage{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}",
+ "legendFormat": "Disk % {{pod}}",
+ "range": true,
+ "refId": "C"
+ }
+ ],
+ "title": "System CPU / memory / disk usage",
+ "type": "timeseries"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 71
+ },
+ "id": 23,
+ "panels": [],
+ "title": "Prometheus scrape (/metrics)",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 10,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "lineInterpolation": "smooth",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ }
+ ]
+ },
+ "unit": "ops"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 16,
+ "x": 0,
+ "y": 72
+ },
+ "id": 24,
+ "options": {
+ "legend": {
+ "calcs": [
+ "mean"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "multi",
+ "sort": "desc"
+ }
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "sum(rate(promhttp_metric_handler_requests_total{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"}[$__rate_interval])) by (code, pod)",
+ "legendFormat": "{{pod}} {{code}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Scrape requests by HTTP code",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "yellow",
+ "value": 1
+ },
+ {
+ "color": "red",
+ "value": 5
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 7,
+ "w": 8,
+ "x": 16,
+ "y": 72
+ },
+ "id": 25,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "textMode": "auto"
+ },
+ "pluginVersion": "10.1.1",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "editorMode": "code",
+ "expr": "max(promhttp_metric_handler_requests_in_flight{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\",pod=~\"$instance\"})",
+ "legendFormat": "in flight",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Metric handler in-flight scrapes",
+ "type": "stat"
+ }
+ ],
+ "refresh": "30s",
+ "schemaVersion": 38,
+ "style": "dark",
+ "tags": [
+ "dolt",
+ "kubeblocks",
+ "database"
+ ],
+ "templating": {
+ "list": [
+ {
+ "current": {
+ "selected": false,
+ "text": "Prometheus",
+ "value": "Prometheus"
+ },
+ "hide": 0,
+ "includeAll": false,
+ "label": "Data Source",
+ "multi": false,
+ "name": "datasource",
+ "options": [],
+ "query": "prometheus",
+ "queryValue": "",
+ "refresh": 1,
+ "regex": "",
+ "skipUrlSync": false,
+ "type": "datasource"
+ },
+ {
+ "allValue": ".+",
+ "current": {
+ "selected": true,
+ "text": [
+ "All"
+ ],
+ "value": [
+ "$__all"
+ ]
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "definition": "label_values(dss_concurrent_connections, namespace)",
+ "hide": 0,
+ "includeAll": true,
+ "label": "Namespace",
+ "multi": true,
+ "name": "namespace",
+ "options": [],
+ "query": {
+ "query": "label_values(dss_concurrent_connections, namespace)",
+ "refId": "StandardVariableQuery"
+ },
+ "refresh": 2,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 1,
+ "type": "query"
+ },
+ {
+ "allValue": ".+",
+ "current": {
+ "selected": true,
+ "text": [
+ "All"
+ ],
+ "value": [
+ "$__all"
+ ]
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "definition": "label_values(dss_concurrent_connections{namespace=~\"$namespace\"}, app_kubernetes_io_instance)",
+ "hide": 0,
+ "includeAll": true,
+ "label": "Cluster",
+ "multi": true,
+ "name": "cluster",
+ "options": [],
+ "query": {
+ "query": "label_values(dss_concurrent_connections{namespace=~\"$namespace\"}, app_kubernetes_io_instance)",
+ "refId": "StandardVariableQuery"
+ },
+ "refresh": 2,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 5,
+ "type": "query"
+ },
+ {
+ "allValue": ".+",
+ "current": {
+ "selected": true,
+ "text": [
+ "All"
+ ],
+ "value": [
+ "$__all"
+ ]
+ },
+ "datasource": {
+ "type": "prometheus",
+ "uid": "${datasource}"
+ },
+ "definition": "label_values(dss_concurrent_connections{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\"}, pod)",
+ "hide": 0,
+ "includeAll": true,
+ "label": "Pod",
+ "multi": true,
+ "name": "instance",
+ "options": [],
+ "query": {
+ "query": "label_values(dss_concurrent_connections{namespace=~\"$namespace\",app_kubernetes_io_instance=~\"$cluster\"}, pod)",
+ "refId": "StandardVariableQuery"
+ },
+ "refresh": 2,
+ "regex": "",
+ "skipUrlSync": false,
+ "sort": 5,
+ "type": "query"
+ }
+ ]
+ },
+ "time": {
+ "from": "now-6h",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "browser",
+ "title": "Dolt SQL Server",
+ "uid": "dolt-sql-server",
+ "version": 1,
+ "weekStart": ""
+}
diff --git a/addons/dolt/scripts/docker-entrypoint.sh b/addons/dolt/scripts/docker-entrypoint.sh
new file mode 100644
index 000000000..9e4b76157
--- /dev/null
+++ b/addons/dolt/scripts/docker-entrypoint.sh
@@ -0,0 +1,441 @@
+#!/bin/bash
+set -eo pipefail
+
+# mysql_log prints a timestamped (ISO 8601), color-coded structured log message.
+# The message includes a log level and the message itself.
+# If is omitted, it reads from stdin, allowing multi-line input.
+#
+# Arguments:
+# $1 - : Log level (e.g., Warn, Error, Debug)
+# $2 - : Message to log; if omitted, the function reads from stdin
+#
+# Usage:
+# mysql_log [MESSAGE]
+# mysql_log Warn "Disk space low"
+# echo "Database connection lost" | mysql_log Error
+#
+# Output:
+# 2025-10-16T12:34:56+00:00 [Warn] [Entrypoint] Disk space low
+# 2025-10-16T12:35:01+00:00 [Error] [Entrypoint] Database connection lost
+_mysql_log() {
+ local level="$1"; shift
+
+ local dt
+ dt="$(date --rfc-3339=seconds)"
+
+ local color_reset="\033[0m"
+ local color=""
+ case "$level" in
+ Warn) color="\033[1;33m" ;; # yellow
+ Error) color="\033[1;31m" ;; # red
+ Debug) color="\033[1;34m" ;; # blue
+ esac
+
+ local msg="$*"
+ if [ "$#" -eq 0 ]; then
+ msg="$(cat)"
+ fi
+
+ printf '%b%s [%s] [Entrypoint] %s%b\n' "$color" "$dt" "$level" "$msg" "$color_reset"
+}
+
+
+# _dbg logs a message of type 'Debug' using mysql_log.
+_dbg() {
+ _mysql_log Debug "$@"
+}
+
+# mysql_note logs a message of type 'Note' using mysql_log.
+mysql_note() {
+ _mysql_log Note "$@"
+}
+
+# mysql_warn logs a message of type 'Warning' using mysql_log and writes to stderr.
+mysql_warn() {
+ _mysql_log Warn "$@" >&2
+}
+
+# mysql_error logs a message of type 'ERROR' using mysql_log, writes to stderr, prints a container removal hint, and
+# exits with status 1.
+mysql_error() {
+ _mysql_log Error "$@" >&2
+ mysql_note "Remove this container with 'docker rm -f ' before retrying"
+ exit 1
+}
+
+# exec_mysql executes a SQL query using Dolt, retrying until success or timeout. Ensures reliability during slow
+# container or resource startup. On timeout, it prints the provided error prefix followed by filtered Dolt output.
+# Errors are parsed to remove blank lines and extract only relevant error text. Use --show-result to display successful
+# query results.
+#
+# Usage:
+# exec_mysql [--show-result] "" ""
+# exec_mysql [--show-result] "" < /docker-entrypoint-initdb.d/init.sql
+# cat /docker-entrypoint-initdb.d/init.sql | exec_mysql [--show-result] ""
+#
+# Output:
+# Prints query output only if --show-result is specified.
+exec_mysql() {
+ local show_result=0
+ if [ "$1" = "--show-result" ]; then
+ show_result=1
+ shift
+ fi
+
+ local error_message="$1"
+ local query="${2:-}"
+ local timeout="${DOLT_SERVER_TIMEOUT:-300}"
+ local start_time now output status
+
+ start_time=$(date +%s)
+
+ while true; do
+ if [ -n "$query" ]; then
+ output=$(dolt sql -q "$query" 2>&1)
+ status=$?
+ else
+ set +e # tmp disabled to initdb.d/ file err
+ output=$(dolt sql < /dev/stdin 2>&1)
+ status=$?
+ set -e
+ fi
+
+ if [ "$status" -eq 0 ]; then
+ [ "$show_result" -eq 1 ] && echo "$output" | grep -v "^$" || true
+ return 0
+ fi
+
+ if echo "$output" | grep -qiE "Error [0-9]+ \([A-Z0-9]+\)"; then
+ mysql_error "$error_message$(echo "$output" | grep -iE "Error|error")"
+ fi
+
+ if [ "$timeout" -ne 0 ]; then
+ now=$(date +%s)
+ if [ $((now - start_time)) -ge "$timeout" ]; then
+ mysql_error "$error_message$(echo "$output" | grep -iE "Error|error" || true)"
+ fi
+ fi
+
+ sleep 1
+ done
+}
+
+CONTAINER_DATA_DIR="/var/lib/dolt"
+INIT_COMPLETED="$CONTAINER_DATA_DIR/.init_completed"
+DOLT_CONFIG_DIR="/etc/dolt/doltcfg.d"
+SERVER_CONFIG_DIR="/etc/dolt/servercfg.d"
+DOLT_ROOT_PATH="/.dolt"
+SERVER_PID=-1
+
+# check_for_dolt_binary verifies that the dolt binary is present and executable in the system PATH.
+# If not found or not executable, it logs an error and exits.
+check_for_dolt_binary() {
+ local dolt_bin
+ dolt_bin=$(which dolt)
+ if [ ! -x "$dolt_bin" ]; then
+ mysql_error "dolt binary executable not found"
+ fi
+}
+
+# get_env_var returns the value of an environment variable, preferring DOLT_* over MYSQL_*.
+# Arguments:
+# $1 - The base variable name (e.g., "USER" for MYSQL_USER or DOLT_USER)
+# Output:
+# Prints the value of the first set variable, or an empty string if neither is set.
+get_env_var() {
+ local var_name="$1"
+ local dolt_var="DOLT_${var_name}"
+ local mysql_var="MYSQL_${var_name}"
+
+ if [ -n "${!dolt_var}" ]; then
+ echo "${!dolt_var}"
+ elif [ -n "${!mysql_var}" ]; then
+ echo "${!mysql_var}"
+ else
+ echo ""
+ fi
+}
+
+# get_env_var_name returns the name of the environment variable that is set, preferring DOLT_* over MYSQL_*.
+# Arguments:
+# $1 - The base variable name (e.g., "USER" for MYSQL_USER or DOLT_USER)
+# Output:
+# Prints the name of the first set variable, or both names if neither is set.
+get_env_var_name() {
+ local var_name="$1"
+ local dolt_var="DOLT_${var_name}"
+ local mysql_var="MYSQL_${var_name}"
+
+ if [ -n "${!dolt_var}" ]; then
+ echo "DOLT_${var_name}"
+ elif [ -n "${!mysql_var}" ]; then
+ echo "MYSQL_${var_name}"
+ else
+ echo "MYSQL_${var_name}/DOLT_${var_name}"
+ fi
+}
+
+# get_config_file_path_if_exists checks for config files of a given type in a directory.
+# Arguments:
+# $1 - Directory to search in
+# $2 - File type/extension to search for (e.g., 'json', 'yaml')
+# Output:
+# Sets CONFIG_PROVIDED to the path of the config file if exactly one is found, or empty otherwise.
+# Logs a warning if multiple config files are found and uses the default config.
+get_config_file_path_if_exists() {
+ CONFIG_PROVIDED=
+ local CONFIG_DIR=$1
+ local FILE_TYPE=$2
+ if [ -d "$CONFIG_DIR" ]; then
+ mysql_note "Checking for config provided in $CONFIG_DIR"
+ local number_of_files_found
+ number_of_files_found=$(find "$CONFIG_DIR" -type f -name "*.$FILE_TYPE" | wc -l)
+ if [ "$number_of_files_found" -gt 1 ]; then
+ CONFIG_PROVIDED=
+ mysql_warn "Multiple config files found in $CONFIG_DIR, using default config"
+ elif [ "$number_of_files_found" -eq 1 ]; then
+ local files_found
+ files_found=$(ls "$CONFIG_DIR"/*."$FILE_TYPE")
+ mysql_note "$files_found file is found"
+ CONFIG_PROVIDED=$files_found
+ else
+ CONFIG_PROVIDED=
+ fi
+ fi
+}
+
+# docker_process_init_files Runs files found in /docker-entrypoint-initdb.d before the server is started.
+# Taken from https://github.com/docker-library/mysql/blob/master/8.0/docker-entrypoint.sh
+# Usage:
+# docker_process_init_files [file [file ...]]
+# e.g., docker_process_init_files /always-initdb.d/*
+# Processes initializer files based on file extensions.
+docker_process_init_files() {
+ local f
+ echo
+ for f; do
+ case "$f" in
+ *.sh)
+ if [ -x "$f" ]; then
+ mysql_note "$0: running $f"
+ if ! "$f"; then
+ mysql_error "Failed to execute $f: "
+ fi
+ else
+ mysql_note "$0: sourcing $f"
+ if ! . "$f"; then
+ mysql_error "Failed to execute $f: "
+ fi
+ fi
+ ;;
+ *.sql)
+ mysql_note "$0: running $f"
+ exec_mysql --show-result "Failed to execute $f: " < "$f"
+ ;;
+ *.sql.bz2)
+ mysql_note "$0: running $f"
+ bunzip2 -c "$f" | exec_mysql --show-result "Failed to execute $f: "
+ ;;
+ *.sql.gz)
+ mysql_note "$0: running $f"
+ gunzip -c "$f" | exec_mysql --show-result "Failed to execute $f: "
+ ;;
+ *.sql.xz)
+ mysql_note "$0: running $f"
+ xzcat "$f" | exec_mysql --show-result "Failed to execute $f: "
+ ;;
+ *.sql.zst)
+ mysql_note "$0: running $f"
+ zstd -dc "$f" | exec_mysql --show-result "Failed to execute $f: "
+ ;;
+ *)
+ mysql_warn "$0: ignoring $f"
+ ;;
+ esac
+ echo
+ done
+}
+
+# set_dolt_config_if_defined checks for a user-provided Dolt config file in $DOLT_CONFIG_DIR.
+# If a single JSON config file is found, it copies it to $HOME/$DOLT_ROOT_PATH/config_global.json,
+# overwriting the default config. Logs an error and exits if the copy fails.
+set_dolt_config_if_defined() {
+ get_config_file_path_if_exists "$DOLT_CONFIG_DIR" "json"
+ if [ ! -z "$CONFIG_PROVIDED" ]; then
+ if ! /bin/cp -rf "$CONFIG_PROVIDED" "$HOME/$DOLT_ROOT_PATH/config_global.json" 2>&1; then
+ mysql_error "Failed to copy config file from '$CONFIG_PROVIDED' to '$HOME/$DOLT_ROOT_PATH/config_global.json'. Check file permissions and paths."
+ fi
+ fi
+}
+
+# create_database_from_env creates a database if the DATABASE environment variable is set.
+# It retrieves the database name from environment variables (preferring DOLT_DATABASE over MYSQL_DATABASE)
+# and attempts to create the database using exec_mysql.
+create_database_from_env() {
+ local database
+ database=$(get_env_var "DATABASE")
+
+ if [ -n "$database" ]; then
+ mysql_note "Creating database '${database}'"
+ exec_mysql "Failed to create database '$database': " "CREATE DATABASE IF NOT EXISTS \`$database\`;"
+ fi
+}
+
+# create_user_from_env creates a new database user from environment variables.
+# It prefers DOLT_USER/PASSWORD over MYSQL_USER/PASSWORD, and optionally grants access to a database.
+# Requires both USER and PASSWORD to be set; if only the password is set, it logs a warning and does nothing.
+# It does not allow creating a 'root' user via these environment variables.
+create_user_from_env() {
+ local user
+ local password
+ local database
+
+ user=$(get_env_var "USER")
+ password=$(get_env_var "PASSWORD")
+ database=$(get_env_var "DATABASE")
+
+ if [ "$user" = 'root' ]; then
+ mysql_error "$(get_env_var_name "USER")="root", $(get_env_var_name "USER") and $(get_env_var_name "PASSWORD") are for configuring the regular user and cannot be used for the root user."
+ fi
+
+ if [ -n "$user" ] && [ -z "$password" ]; then
+ mysql_error "$(get_env_var_name "USER") specified, but missing $(get_env_var_name "PASSWORD"); user creation requires a password."
+ elif [ -z "$user" ] && [ -n "$password" ]; then
+ mysql_warn "$(get_env_var_name "PASSWORD") specified, but missing $(get_env_var_name "USER"); password will be ignored"
+ return
+ fi
+
+ if [ -n "$user" ]; then
+ local user_host
+ user_host=$(get_env_var "USER_HOST")
+ user_host="${user_host:-${DOLT_ROOT_HOST:-localhost}}"
+
+ mysql_note "Creating user '${user}@${user_host}'"
+ exec_mysql "Failed to create user '$user': " "CREATE USER IF NOT EXISTS '$user'@'$user_host' IDENTIFIED BY '$password';"
+ exec_mysql "Failed to grant server access to user '$user': " "GRANT USAGE ON *.* TO '$user'@'$user_host';"
+
+ if [ -n "$database" ]; then
+ exec_mysql "Failed to grant permissions to user '$user' on database '$database': " "GRANT ALL ON \`$database\`.* TO '$user'@'$user_host';"
+ fi
+ fi
+}
+
+# is_port_open checks if a TCP port is open on a given host.
+# Arguments:
+# $1 - Host (IP or hostname)
+# $2 - Port number
+# Returns:
+# 0 if the port is open, non-zero otherwise.
+is_port_open() {
+ local host="$1"
+ local port="$2"
+ timeout 1 bash -c "cat < /dev/null > /dev/tcp/$host/$port" &>/dev/null
+ return $?
+}
+
+# dolt_server_initializer starts the Dolt SQL server in the background and waits until it is ready to accept connections.
+# It manages the server process, restarts it if necessary, and checks for readiness by probing the configured port.
+# The function retries until the server is available or a timeout is reached, handling process management and logging.
+# Arguments:
+# $@ - Additional arguments to pass to `dolt sql-server`
+# Returns:
+# 0 if the server starts successfully and is ready to accept connections; exits with error otherwise.
+dolt_server_initializer() {
+ local timeout="${DOLT_SERVER_TIMEOUT:-300}"
+ local start_time
+ start_time=$(date +%s)
+
+ SERVER_PID=-1
+
+ trap 'mysql_note "Caught Ctrl+C, shutting down Dolt server..."; [ $SERVER_PID -ne -1 ] && kill "$SERVER_PID"; exit 1' INT TERM
+
+ while true; do
+ if [ "$SERVER_PID" -eq -1 ] || ! kill -0 "$SERVER_PID" 2>/dev/null; then
+ [ "$SERVER_PID" -ne -1 ] && wait "$SERVER_PID" 2>/dev/null || true
+ SERVER_PID=-1
+ dolt sql-server --host=0.0.0.0 --port=3306 "$@" 2>&1 &
+ SERVER_PID=$!
+
+ fi
+
+ if is_port_open "0.0.0.0" 3306; then
+ mysql_note "Dolt server started."
+ return 0
+ fi
+
+ local now elapsed
+ now=$(date +%s)
+ elapsed=$((now - start_time))
+ if [ "$elapsed" -ge "$timeout" ]; then
+ kill "$SERVER_PID" 2>/dev/null || true
+ wait "$SERVER_PID" 2>/dev/null || true
+ SERVER_PID=-1
+ mysql_error "Dolt server failed to start within $timeout seconds"
+ fi
+
+ sleep 1
+ done
+}
+
+# _main is the main entrypoint for the Dolt Docker container initialization.
+_main() {
+ check_for_dolt_binary
+
+ local dolt_version
+ dolt_version=$(dolt version | grep 'dolt version' | cut -f3 -d " ")
+ mysql_note "Entrypoint script for Dolt Server $dolt_version starting..."
+
+ declare -g CONFIG_PROVIDED
+
+ # dolt config will be set if user provided a single json file in /etc/dolt/doltcfg.d directory.
+ # It will overwrite config_global.json file in $HOME/.dolt
+ set_dolt_config_if_defined
+ CONFIG_PROVIDED=
+
+ # if there is a single yaml provided in /etc/dolt/servercfg.d directory,
+ # it will be used to start the server with --config flag.
+ get_config_file_path_if_exists "$SERVER_CONFIG_DIR" "yaml"
+ if [ -n "$CONFIG_PROVIDED" ]; then
+ set -- "$@" --config="$CONFIG_PROVIDED"
+ fi
+
+ mysql_note "Starting Dolt server"
+ # Attempt to configure the root user directly through the sql-server using built-in environment variable support
+ # The user creation queries with `dolt sql` can interfere with this process so we run them after the server is started
+ DOLT_ROOT_HOST="${DOLT_ROOT_HOST:-localhost}"
+
+ # `dolt sql` can hold locks that prevent the server from starting during system hangs without this func
+ dolt_server_initializer "$@"
+ # Ran in a subshell to avoid exiting the main script, and so, we can use fallback below
+ local has_correct_host
+ has_correct_host=$(exec_mysql --show-result "Could not check root host: " \
+ "SELECT User, Host FROM mysql.user WHERE User='root' AND Host='${DOLT_ROOT_HOST}' LIMIT 1;" | \
+ grep -c "$DOLT_ROOT_HOST" || true)
+
+ # args or system hangs may conflict with sql-server root env vars support
+ if [ "$has_correct_host" -eq 0 ]; then
+ mysql_warn "Environment variables failed to initialize 'root@${DOLT_ROOT_HOST}'; docker-entrypoint-initdb.d scripts queries may have conflicted. Overriding root user..."
+ exec_mysql "Could not create root user: " "CREATE USER IF NOT EXISTS 'root'@'${DOLT_ROOT_HOST}' IDENTIFIED WITH mysql_native_password BY '${DOLT_ROOT_PASSWORD}';" # override password
+ exec_mysql "Could not set root privileges: " "GRANT ALL PRIVILEGES ON *.* TO 'root'@'${DOLT_ROOT_HOST}' WITH GRANT OPTION;"
+ fi
+
+ create_database_from_env
+
+ create_user_from_env
+
+ exec_mysql --show-result "Could not list users: " "SELECT User, Host FROM mysql.user;"
+
+ if [[ ! -f $INIT_COMPLETED ]]; then
+ if ls /docker-entrypoint-initdb.d/* >/dev/null 2>&1; then
+ docker_process_init_files /docker-entrypoint-initdb.d/*
+ else
+ mysql_warn "No files found in /docker-entrypoint-initdb.d/ to process"
+ fi
+ touch "$INIT_COMPLETED"
+ fi
+
+ mysql_note "Dolt init process done. Ready for connections."
+ wait "$SERVER_PID"
+}
+
+_main "$@"
\ No newline at end of file
diff --git a/addons/dolt/scripts/setup-mysql-replication.sh b/addons/dolt/scripts/setup-mysql-replication.sh
new file mode 100644
index 000000000..83e05c867
--- /dev/null
+++ b/addons/dolt/scripts/setup-mysql-replication.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+set -eo pipefail
+
+# Configures Dolt as a MySQL binlog replica.
+# Expects env vars: MYSQL_SOURCE_HOST, MYSQL_SOURCE_PORT
+# Optional env vars: MYSQL_SOURCE_USER, MYSQL_SOURCE_PASSWORD, DOLT_REPLICA_SERVER_ID
+
+SOURCE_HOST="${MYSQL_SOURCE_HOST}"
+SOURCE_PORT="${MYSQL_SOURCE_PORT:-3306}"
+SOURCE_USER="${MYSQL_SOURCE_USER:-root}"
+SOURCE_PASSWORD="${MYSQL_SOURCE_PASSWORD:-}"
+SERVER_ID="${DOLT_REPLICA_SERVER_ID:-10}"
+
+dolt_sql() {
+ dolt --host 127.0.0.1 --port 3306 --no-tls sql -q "$1"
+}
+
+echo "Configuring Dolt as MySQL binlog replica of ${SOURCE_HOST}:${SOURCE_PORT}..."
+
+dolt_sql "SET @@PERSIST.server_id = ${SERVER_ID};"
+
+if [ -n "${SOURCE_PASSWORD}" ]; then
+ dolt_sql "CHANGE REPLICATION SOURCE TO SOURCE_HOST='${SOURCE_HOST}', SOURCE_USER='${SOURCE_USER}', SOURCE_PASSWORD='${SOURCE_PASSWORD}', SOURCE_PORT=${SOURCE_PORT};"
+else
+ dolt_sql "CHANGE REPLICATION SOURCE TO SOURCE_HOST='${SOURCE_HOST}', SOURCE_USER='${SOURCE_USER}', SOURCE_PORT=${SOURCE_PORT};"
+fi
+
+dolt_sql "START REPLICA;"
+
+echo "MySQL binlog replication started. Source: ${SOURCE_HOST}:${SOURCE_PORT}"
diff --git a/addons/dolt/scripts/start-standalone.sh b/addons/dolt/scripts/start-standalone.sh
new file mode 100644
index 000000000..0609e0f4c
--- /dev/null
+++ b/addons/dolt/scripts/start-standalone.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+set -eo pipefail
+
+DATA_DIR="${DATA_DIR:-/var/lib/dolt}"
+SERVER_CONFIG="${DATA_DIR}/.server-config.yaml"
+
+mkdir -p /etc/dolt/servercfg.d/config.yaml
+mkdir -p "${DATA_DIR}/.doltcfg"
+
+sed \
+ -e "s|\${DATA_DIR}|${DATA_DIR}|g" \
+ /config/config.yaml > "${SERVER_CONFIG}"
+
+# MySQL source is configured — start the entrypoint in background, then
+# wait for the server to be ready and configure binlog replication.
+/scripts/docker-entrypoint.sh --config="${SERVER_CONFIG}" &
+EP_PID=$!
+
+if [ -n "${MYSQL_SOURCE_HOST:-}" ]; then
+ until dolt --host 127.0.0.1 --port 3306 --no-tls sql -q "SELECT 1" >/dev/null 2>&1; do
+ if ! kill -0 "$EP_PID" 2>/dev/null; then
+ echo "Entrypoint exited before server became ready"
+ exit 1
+ fi
+ sleep 2
+ done
+
+ /scripts/setup-mysql-replication.sh
+fi
+
+wait "$EP_PID"
diff --git a/addons/dolt/scripts/start.sh b/addons/dolt/scripts/start.sh
new file mode 100644
index 000000000..62ef96f3c
--- /dev/null
+++ b/addons/dolt/scripts/start.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+set -eu
+
+DATA_DIR="${DATA_DIR:-/var/lib/dolt}"
+INIT_COMPLETED="${DATA_DIR}/.init_completed"
+SERVER_CONFIG="${DATA_DIR}/.server-config.yaml"
+REMOTES_API_PORT="${REMOTES_API_PORT:-50051}"
+
+if [ -f "${INIT_COMPLETED}" ]; then
+ # Data dir already initialized: reuse persisted server config (roles/remotes are in data).
+ :
+else
+ POD_NAME="${CURRENT_POD_NAME:-}"
+ if [ -z "${POD_NAME}" ]; then
+ echo "CURRENT_POD_NAME is required on first start" >&2
+ exit 1
+ fi
+
+ POD_BASENAME="${POD_NAME%-*}"
+ POD_ORDINAL="${POD_NAME##*-}"
+
+ if [ "${POD_ORDINAL}" = "0" ]; then
+ BOOTSTRAP_ROLE="primary"
+ STANDBY_HOST="${POD_BASENAME}-1"
+ else
+ BOOTSTRAP_ROLE="standby"
+ STANDBY_HOST="${POD_BASENAME}-0"
+ fi
+
+ HEADLESS_SERVICE_NAME="${POD_BASENAME}-headless"
+
+ sed \
+ -e "s|\${BOOTSTRAP_ROLE}|${BOOTSTRAP_ROLE}|g" \
+ -e "s|\${STANDBY_HOST}|${STANDBY_HOST}|g" \
+ -e "s|\${HEADLESS_SERVICE_NAME}|${HEADLESS_SERVICE_NAME}|g" \
+ -e "s|\${REMOTES_API_PORT}|${REMOTES_API_PORT}|g" \
+ -e "s|\${DATA_DIR}|${DATA_DIR}|g" \
+ /config/config.yaml > "${SERVER_CONFIG}"
+fi
+
+# Same as start-standalone.sh: official entrypoint handles server readiness, root host,
+# optional env users/db, initdb.d, .init_completed, signals, and wait.
+exec /scripts/docker-entrypoint.sh --config="${SERVER_CONFIG}"
diff --git a/addons/dolt/scripts/switchover.sh b/addons/dolt/scripts/switchover.sh
new file mode 100644
index 000000000..0bf58a661
--- /dev/null
+++ b/addons/dolt/scripts/switchover.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+set -eu
+
+# KubeBlocks invokes this on the current primary only. We demote this instance
+# then promote the candidate using the same configuration epoch (current + 1).
+
+if [ "${KB_SWITCHOVER_ROLE:-}" != "primary" ]; then
+ echo "switchover not triggered for primary, nothing to do, exit 0."
+ exit 0
+fi
+
+if [ -z "${KB_SWITCHOVER_CANDIDATE_FQDN:-}" ] && [ -z "${KB_SWITCHOVER_CANDIDATE_NAME:-}" ]; then
+ echo "KB_SWITCHOVER_CANDIDATE_FQDN or KB_SWITCHOVER_CANDIDATE_NAME is required for Dolt switchover" >&2
+ exit 1
+fi
+
+CANDIDATE_HOST="${KB_SWITCHOVER_CANDIDATE_FQDN:-${KB_SWITCHOVER_CANDIDATE_NAME}}"
+
+echo "KB_SWITCHOVER_CANDIDATE_FQDN: ${KB_SWITCHOVER_CANDIDATE_FQDN}"
+echo "KB_SWITCHOVER_CANDIDATE_NAME: ${KB_SWITCHOVER_CANDIDATE_NAME}"
+echo "CANDIDATE_HOST: ${CANDIDATE_HOST}"
+
+dolt_local_q() {
+ dolt --host 127.0.0.1 --port 3306 --no-tls sql -q "$1"
+}
+
+dolt_remote_q() {
+ dolt --host "$1" --port 3306 --no-tls sql -q "$2"
+}
+
+extract_scalar() {
+ _hdr=$1
+ _out=$2
+ printf '%s\n' "$_out" | awk -v hdr="$_hdr" '/^\|/{
+ gsub(/^[ \t]*\|[ \t]*/, "");
+ gsub(/[ \t]*\|[ \t]*$/, "");
+ if ($1 != hdr && $1 != "") { print $1; exit }
+ }'
+}
+
+epoch_out=$(dolt_local_q "select @@GLOBAL.dolt_cluster_role_epoch;" 2>/dev/null || true)
+current_epoch=$(extract_scalar '@@GLOBAL.dolt_cluster_role_epoch' "$epoch_out")
+
+case "$current_epoch" in
+ '' | *[!0-9]*)
+ echo "Could not read @@GLOBAL.dolt_cluster_role_epoch from local instance" >&2
+ exit 1
+ ;;
+esac
+
+new_epoch=$((current_epoch + 1))
+
+role_out=$(dolt_local_q "select @@GLOBAL.dolt_cluster_role;" 2>/dev/null || true)
+current_role=$(extract_scalar '@@GLOBAL.dolt_cluster_role' "$role_out")
+
+if [ "$current_role" != "primary" ]; then
+ echo "Local instance role is '${current_role}', expected primary; aborting" >&2
+ exit 0
+fi
+
+echo "Demoting local primary to standby with epoch ${new_epoch}"
+dolt_local_q "CALL dolt_assume_cluster_role('standby', ${new_epoch});"
+
+echo "Promoting candidate ${CANDIDATE_HOST} to primary with epoch ${new_epoch}"
+dolt_remote_q "$CANDIDATE_HOST" "CALL dolt_assume_cluster_role('primary', ${new_epoch});"
+
+echo "Dolt switchover completed."
diff --git a/addons/dolt/templates/NOTES.txt b/addons/dolt/templates/NOTES.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/addons/dolt/templates/_helpers.tpl b/addons/dolt/templates/_helpers.tpl
new file mode 100644
index 000000000..bf652f652
--- /dev/null
+++ b/addons/dolt/templates/_helpers.tpl
@@ -0,0 +1,106 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "dolt.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Selector labels.
+*/}}
+{{- define "dolt.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "dolt.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "dolt.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels.
+*/}}
+{{- define "dolt.labels" -}}
+helm.sh/chart: {{ include "dolt.chart" . }}
+{{ include "dolt.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Common annotations.
+*/}}
+{{- define "dolt.annotations" -}}
+{{ include "dolt.apiVersion" . }}
+{{- end }}
+
+{{/*
+API version annotation.
+*/}}
+{{- define "dolt.apiVersion" -}}
+kubeblocks.io/crd-api-version: apps.kubeblocks.io/v1
+{{- end }}
+
+{{/*
+ComponentDefinition name (replication / primary-standby) with chart version as suffix.
+*/}}
+{{- define "dolt.cmpdName" -}}
+dolt-replication-{{ .Chart.Version }}
+{{- end -}}
+
+{{/*
+ComponentDefinition regexp for replication cmpd.
+*/}}
+{{- define "dolt.cmpdRegexpPattern" -}}
+^dolt-replication$
+{{- end -}}
+
+{{/*
+Config template name.
+*/}}
+{{- define "dolt.configTemplate" -}}
+dolt-config-template-{{ .Chart.Version }}
+{{- end }}
+
+{{/*
+Script template name.
+*/}}
+{{- define "dolt.scriptTemplate" -}}
+dolt-script-template-{{ .Chart.Version }}
+{{- end }}
+
+{{/*
+Standalone ComponentDefinition name.
+*/}}
+{{- define "dolt.standaloneCmpdName" -}}
+dolt-standalone-{{ .Chart.Version }}
+{{- end -}}
+
+{{/*
+Standalone ComponentDefinition regexp.
+*/}}
+{{- define "dolt.standaloneCmpdRegexpPattern" -}}
+^dolt-standalone$
+{{- end -}}
+
+{{/*
+Standalone config template name.
+*/}}
+{{- define "dolt.standaloneConfigTemplate" -}}
+dolt-standalone-config-template-{{ .Chart.Version }}
+{{- end }}
+
+{{/*
+Generate scripts configmap.
+*/}}
+{{- define "dolt.extend.scripts" -}}
+{{- range $path, $_ := $.Files.Glob "scripts/**" }}
+{{ $path | base }}: |-
+{{- $.Files.Get $path | nindent 2 }}
+{{- end }}
+{{- end }}
diff --git a/addons/dolt/templates/clusterdefinition.yaml b/addons/dolt/templates/clusterdefinition.yaml
new file mode 100644
index 000000000..af4024e94
--- /dev/null
+++ b/addons/dolt/templates/clusterdefinition.yaml
@@ -0,0 +1,19 @@
+apiVersion: apps.kubeblocks.io/v1
+kind: ClusterDefinition
+metadata:
+ name: dolt
+ labels:
+ {{- include "dolt.labels" . | nindent 4 }}
+ annotations:
+ {{- include "dolt.annotations" . | nindent 4 }}
+spec:
+ topologies:
+ - name: standalone
+ default: true
+ components:
+ - name: dolt
+ compDef: {{ include "dolt.standaloneCmpdRegexpPattern" . }}
+ - name: replication
+ components:
+ - name: dolt
+ compDef: {{ include "dolt.cmpdRegexpPattern" . }}
diff --git a/addons/dolt/templates/cmpd-repl.yaml b/addons/dolt/templates/cmpd-repl.yaml
new file mode 100644
index 000000000..65ecb52b8
--- /dev/null
+++ b/addons/dolt/templates/cmpd-repl.yaml
@@ -0,0 +1,117 @@
+apiVersion: apps.kubeblocks.io/v1
+kind: ComponentDefinition
+metadata:
+ name: {{ include "dolt.cmpdName" . }}
+ labels:
+ {{- include "dolt.labels" . | nindent 4 }}
+ annotations:
+ {{- include "dolt.annotations" . | nindent 4 }}
+spec:
+ provider: community
+ description: {{ .Chart.Description }}
+ serviceKind: {{ .Chart.Name }}
+ serviceVersion: {{ .Chart.AppVersion }}
+ updateStrategy: Parallel
+ podManagementPolicy: Parallel
+ replicasLimit:
+ minReplicas: 2
+ maxReplicas: 2
+ runtime:
+ containers:
+ - name: dolt
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ command:
+ - /bin/sh
+ - -c
+ - /scripts/start.sh
+ ports:
+ - containerPort: 3306
+ name: mysql
+ - containerPort: {{ .Values.remotesApiPort }}
+ name: remotesapi
+ - containerPort: {{ .Values.metricsPort }}
+ name: metrics
+ env:
+ - name: CURRENT_POD_NAME
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: metadata.name
+ - name: REMOTES_API_PORT
+ value: {{ .Values.remotesApiPort | quote }}
+ - name: DOLT_ROOT_HOST
+ value: {{ .Values.auth.rootHost | quote}}
+ livenessProbe:
+ exec:
+ command: ["dolt", "--host", "127.0.0.1", "--port", "3306", "--no-tls", "sql", "-q", "select current_timestamp();"]
+ initialDelaySeconds: 60
+ periodSeconds: 10
+ readinessProbe:
+ exec:
+ command: ["dolt", "--host", "127.0.0.1", "--port", "3306", "--no-tls", "sql", "-q", "select current_timestamp();"]
+ initialDelaySeconds: 40
+ periodSeconds: 10
+ volumeMounts:
+ - name: data
+ mountPath: {{ .Values.dataDir }}
+ - name: scripts
+ mountPath: /scripts
+ - name: config
+ mountPath: /config
+ volumes:
+ - name: data
+ needSnapshot: true
+ services:
+ - name: mysql
+ serviceName: dolt
+ spec:
+ ports:
+ - name: mysql
+ port: 3306
+ targetPort: mysql
+ - name: remotesapi
+ port: {{ .Values.remotesApiPort }}
+ targetPort: remotesapi
+ - name: metrics
+ port: {{ .Values.metricsPort }}
+ targetPort: metrics
+ disableAutoProvision: false
+ configs:
+ - name: config
+ template: {{ include "dolt.configTemplate" . }}
+ namespace: {{ .Release.Namespace }}
+ volumeName: config
+ defaultMode: 0644
+ restartOnFileChange: true
+ scripts:
+ - name: scripts
+ template: {{ include "dolt.scriptTemplate" . }}
+ namespace: {{ .Release.Namespace }}
+ volumeName: scripts
+ defaultMode: 0555
+ roles:
+ - name: primary
+ updatePriority: 2
+ participatesInQuorum: false
+ isExclusive: true
+ - name: standby
+ updatePriority: 1
+ participatesInQuorum: false
+ lifecycleActions:
+ roleProbe:
+ exec:
+ container: dolt
+ command:
+ - /bin/sh
+ - -c
+ - |
+ role=$(dolt --host 127.0.0.1 --no-tls sql -q "select @@GLOBAL.dolt_cluster_role;" 2>/dev/null \
+ | awk '/^\|/{gsub(/^[ \t]*\|[ \t]*/,""); gsub(/[ \t]*\|[ \t]*$/,""); if($1 != "@@GLOBAL.dolt_cluster_role" && $1 != "") {print $1; exit}}')
+ echo -n "$role"
+ switchover:
+ exec:
+ container: dolt
+ command:
+ - /bin/sh
+ - -c
+ - /scripts/switchover.sh > /tmp/switchover.log 2>&1
diff --git a/addons/dolt/templates/cmpd-standalone.yaml b/addons/dolt/templates/cmpd-standalone.yaml
new file mode 100644
index 000000000..6c49b7c33
--- /dev/null
+++ b/addons/dolt/templates/cmpd-standalone.yaml
@@ -0,0 +1,108 @@
+apiVersion: apps.kubeblocks.io/v1
+kind: ComponentDefinition
+metadata:
+ name: {{ include "dolt.standaloneCmpdName" . }}
+ labels:
+ {{- include "dolt.labels" . | nindent 4 }}
+ annotations:
+ {{- include "dolt.annotations" . | nindent 4 }}
+spec:
+ provider: community
+ description: {{ .Chart.Description }}
+ serviceKind: {{ .Chart.Name }}
+ serviceVersion: {{ .Chart.AppVersion }}
+ replicasLimit:
+ minReplicas: 1
+ maxReplicas: 1
+ serviceRefDeclarations:
+ - name: mysql-source
+ serviceRefDeclarationSpecs:
+ - serviceKind: mysql
+ serviceVersion: "^*"
+ optional: true
+ vars:
+ - name: MYSQL_SOURCE_HOST
+ valueFrom:
+ serviceRefVarRef:
+ name: mysql-source
+ optional: true
+ host: Required
+ - name: MYSQL_SOURCE_PORT
+ valueFrom:
+ serviceRefVarRef:
+ name: mysql-source
+ optional: true
+ port: Required
+ - name: MYSQL_SOURCE_USER
+ valueFrom:
+ serviceRefVarRef:
+ name: mysql-source
+ optional: true
+ username: Optional
+ - name: MYSQL_SOURCE_PASSWORD
+ valueFrom:
+ serviceRefVarRef:
+ name: mysql-source
+ optional: true
+ password: Optional
+ runtime:
+ containers:
+ - name: dolt
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ command:
+ - /bin/sh
+ - -c
+ - /scripts/start-standalone.sh
+ ports:
+ - containerPort: 3306
+ name: mysql
+ - containerPort: {{ .Values.metricsPort }}
+ name: metrics
+ env:
+ - name: DOLT_ROOT_HOST
+ value: {{ .Values.auth.rootHost | quote}}
+ livenessProbe:
+ exec:
+ command: ["dolt", "--host", "127.0.0.1", "--port", "3306", "--no-tls", "sql", "-q", "select current_timestamp();"]
+ initialDelaySeconds: 60
+ periodSeconds: 10
+ readinessProbe:
+ exec:
+ command: ["dolt", "--host", "127.0.0.1", "--port", "3306", "--no-tls", "sql", "-q", "select current_timestamp();"]
+ initialDelaySeconds: 40
+ periodSeconds: 10
+ volumeMounts:
+ - name: data
+ mountPath: {{ .Values.dataDir }}
+ - name: scripts
+ mountPath: /scripts
+ - name: config
+ mountPath: /config
+ volumes:
+ - name: data
+ needSnapshot: true
+ services:
+ - name: mysql
+ serviceName: dolt
+ spec:
+ ports:
+ - name: mysql
+ port: 3306
+ targetPort: mysql
+ - name: metrics
+ port: {{ .Values.metricsPort }}
+ targetPort: metrics
+ disableAutoProvision: false
+ configs:
+ - name: config
+ template: {{ include "dolt.standaloneConfigTemplate" . }}
+ namespace: {{ .Release.Namespace }}
+ volumeName: config
+ defaultMode: 0644
+ restartOnFileChange: true
+ scripts:
+ - name: scripts
+ template: {{ include "dolt.scriptTemplate" . }}
+ namespace: {{ .Release.Namespace }}
+ volumeName: scripts
+ defaultMode: 0555
diff --git a/addons/dolt/templates/cmpv.yaml b/addons/dolt/templates/cmpv.yaml
new file mode 100644
index 000000000..daa005559
--- /dev/null
+++ b/addons/dolt/templates/cmpv.yaml
@@ -0,0 +1,30 @@
+apiVersion: apps.kubeblocks.io/v1
+kind: ComponentVersion
+metadata:
+ name: dolt
+ labels:
+ {{- include "dolt.labels" . | nindent 4 }}
+ annotations:
+ {{- include "dolt.apiVersion" . | nindent 4 }}
+spec:
+ compatibilityRules:
+ - compDefs:
+ - {{ include "dolt.cmpdRegexpPattern" . }}
+ - {{ include "dolt.standaloneCmpdRegexpPattern" . }}
+ releases:
+ - replica-1.84.0
+ - compDefs:
+ - {{ include "dolt.standaloneCmpdRegexpPattern" . }}
+ releases:
+ - standalone-1.84.0
+ releases:
+ - name: replica-1.84.0
+ serviceVersion: 1.84.0
+ images:
+ dolt: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}
+ roleProbe: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}
+ switchover: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}
+ - name: standalone-1.84.0
+ serviceVersion: 1.84.0
+ images:
+ dolt: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}
\ No newline at end of file
diff --git a/addons/dolt/templates/config-template-standalone.yaml b/addons/dolt/templates/config-template-standalone.yaml
new file mode 100644
index 000000000..eb0e321be
--- /dev/null
+++ b/addons/dolt/templates/config-template-standalone.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ include "dolt.standaloneConfigTemplate" . }}
+ labels:
+ {{- include "dolt.labels" . | nindent 4 }}
+ annotations:
+ {{- include "dolt.annotations" . | nindent 4 }}
+data:
+ config.yaml: |
+ {{- .Files.Get "config/dolt-standalone.yaml.tpl" | nindent 4 }}
diff --git a/addons/dolt/templates/config-template.yaml b/addons/dolt/templates/config-template.yaml
new file mode 100644
index 000000000..eec8562a1
--- /dev/null
+++ b/addons/dolt/templates/config-template.yaml
@@ -0,0 +1,11 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ include "dolt.configTemplate" . }}
+ labels:
+ {{- include "dolt.labels" . | nindent 4 }}
+ annotations:
+ {{- include "dolt.annotations" . | nindent 4 }}
+data:
+ config.yaml: |
+ {{- .Files.Get "config/dolt-server.yaml.tpl" | nindent 4 }}
diff --git a/addons/dolt/templates/script-template.yaml b/addons/dolt/templates/script-template.yaml
new file mode 100644
index 000000000..99f08cbcb
--- /dev/null
+++ b/addons/dolt/templates/script-template.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ include "dolt.scriptTemplate" . }}
+ labels:
+ {{- include "dolt.labels" . | nindent 4 }}
+ annotations:
+ {{- include "dolt.annotations" . | nindent 4 }}
+data:
+ {{- with include "dolt.extend.scripts" . }}
+ {{- . | nindent 2 }}
+ {{- end }}
diff --git a/addons/dolt/values.yaml b/addons/dolt/values.yaml
new file mode 100644
index 000000000..8d527cc9d
--- /dev/null
+++ b/addons/dolt/values.yaml
@@ -0,0 +1,16 @@
+nameOverride: ""
+
+image:
+ registry: docker.io
+ repository: dolthub/dolt-sql-server
+ tag: 1.84.0
+ pullPolicy: IfNotPresent
+
+dataDir: /var/lib/dolt
+
+remotesApiPort: 50051
+
+metricsPort: 11228
+
+auth:
+ rootHost: "%"
\ No newline at end of file
diff --git a/examples/dolt/README.md b/examples/dolt/README.md
new file mode 100644
index 000000000..403608743
--- /dev/null
+++ b/examples/dolt/README.md
@@ -0,0 +1,168 @@
+# Dolt
+
+[Dolt](https://github.com/dolthub/dolt) is a MySQL-compatible SQL database with Git-style versioning. This addon runs `dolt sql-server` in replication (primary / standby) or standalone mode.
+
+## Prerequisites
+
+- Kubernetes cluster >= v1.21
+- `kubectl` installed, refer to [K8s Install Tools](https://kubernetes.io/docs/tasks/tools/)
+- Helm, refer to [Installing Helm](https://helm.sh/docs/intro/install/)
+- KubeBlocks installed and running, refer to [Install Kubeblocks](../docs/prerequisites.md)
+- Dolt Addon Enabled, refer to [Install Addons](../docs/install-addon.md)
+- Create K8s Namespace `demo`, to keep resources created in this tutorial isolated:
+
+ ```bash
+ kubectl create ns demo
+ ```
+
+## Install addon
+
+```bash
+helm install dolt ./addons/dolt -n kb-system
+```
+
+Adjust `kb-system` to the namespace where your KubeBlocks addons are installed.
+
+## Examples
+
+### Create a primary / standby cluster
+
+[`cluster-replication.yaml`](cluster-replication.yaml) creates a two-replica Dolt replication cluster (Dolt primary + standby).
+
+```bash
+kubectl apply -f examples/dolt/cluster-replication.yaml
+```
+
+Check supported versions:
+
+```bash
+kubectl get cmpv dolt
+```
+
+Match `spec.componentSpecs[].componentDef` to the addon (`dolt-replication` or `dolt-standalone`), and `serviceVersion` to a release in that ComponentVersion (for example `1.84.0`).
+
+#### Switchover
+
+After the cluster is healthy, trigger a planned switchover so another replica becomes primary.
+
+**With explicit candidate** — [`switchover-specified-instance.yaml`](switchover-specified-instance.yaml):
+
+```bash
+kubectl apply -f examples/dolt/switchover-specified-instance.yaml
+```
+
+Edit `instanceName` / `candidateName` to match your pod names (`-dolt-`). Get pods:
+
+```bash
+kubectl get pods -n demo -l app.kubernetes.io/instance=dolt-repl -L kubeblocks.io/role
+```
+
+### Create a single-node (standalone) cluster
+
+[`cluster-standalone.yaml`](cluster-standalone.yaml) uses the standalone ComponentDefinition (exactly one replica).
+
+```bash
+kubectl apply -f examples/dolt/cluster-standalone.yaml
+```
+
+Switchover does not apply to standalone topology (single replica).
+
+### Create a Dolt replica of a MySQL cluster
+
+Dolt can act as a [versioned MySQL replica](https://docs.dolthub.com/introduction/getting-started/versioned-mysql-replica) — it consumes MySQL binlog events and automatically creates Dolt commits, giving you time travel, diff, and rollback on every write that happens on the MySQL primary.
+
+[`cluster-mysql-replica.yaml`](cluster-mysql-replica.yaml) deploys two clusters:
+
+1. **mysql-source** — a standalone MySQL 8.0 cluster (the replication primary)
+2. **dolt-mysql-replica** — a standalone Dolt cluster that references the MySQL cluster via `serviceRefs`
+
+```bash
+kubectl apply -f examples/dolt/cluster-mysql-replica.yaml
+```
+
+Wait for both clusters to become ready:
+
+```bash
+kubectl -n demo get cluster mysql-source dolt-mysql-replica
+```
+
+#### Verify replication
+
+Connect to the **MySQL primary** and create some data:
+
+```bash
+# get the mysql root password
+MYSQL_ROOT_PASSWORD=$(kubectl -n demo get secret mysql-source-mysql-account-root \
+ -o jsonpath='{.data.password}' | base64 -d)
+
+# connect to MySQL
+kubectl -n demo exec -it mysql-source-mysql-0 -- \
+ mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" -e "
+ CREATE DATABASE foo;
+ USE foo;
+ CREATE TABLE t (c1 INT PRIMARY KEY, c2 INT);
+ INSERT INTO t VALUES (1, 100), (2, 200), (3, 300);
+ "
+```
+
+Connect to the **Dolt replica** and confirm the data has replicated:
+
+```bash
+kubectl -n demo exec -it dolt-mysql-replica-dolt-0 -- \
+ dolt --host 127.0.0.1 --port 3306 --no-tls sql -q "USE foo; SELECT * FROM t;"
+```
+
+You should see the three rows from the MySQL primary.
+
+#### Inspect the Dolt commit log
+
+Every replicated transaction becomes a Dolt commit. Query the version history:
+
+```bash
+kubectl -n demo exec -it dolt-mysql-replica-dolt-0 -- \
+ dolt --host 127.0.0.1 --port 3306 --no-tls sql -q "USE foo; SELECT * FROM dolt_log;"
+```
+
+#### Inspect diffs
+
+See exactly what changed in the last commit using the `dolt_diff()` table function:
+
+```bash
+kubectl -n demo exec -it dolt-mysql-replica-dolt-0 -- \
+ dolt --host 127.0.0.1 --port 3306 --no-tls sql -q "
+ USE foo;
+ SELECT * FROM dolt_diff('HEAD^', 'HEAD', 't');
+ "
+```
+
+#### Find and revert a bad change
+
+Make a bad change on the MySQL primary:
+
+```bash
+kubectl -n demo exec -it mysql-source-mysql-0 -- \
+ mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" -e "
+ USE foo;
+ UPDATE t SET c2 = 0 WHERE c1 = 2;
+ "
+```
+
+On the Dolt replica, identify the bad commit and generate a revert patch:
+
+```bash
+kubectl -n demo exec -it dolt-mysql-replica-dolt-0 -- \
+ dolt --host 127.0.0.1 --port 3306 --no-tls sql -q "
+ USE foo;
+ SELECT * FROM dolt_diff('HEAD^', 'HEAD', 't');
+ SELECT statement FROM dolt_patch('HEAD', 'HEAD^');
+ "
+```
+
+The `dolt_patch()` output gives you the exact SQL statements to run on the MySQL primary to revert the change. Apply them to the primary to restore the original data.
+
+#### Check replication status
+
+```bash
+kubectl -n demo exec -it dolt-mysql-replica-dolt-0 -- \
+ dolt --host 127.0.0.1 --port 3306 --no-tls sql -q "SHOW REPLICA STATUS"
+```
diff --git a/examples/dolt/cluster-mysql-replica.yaml b/examples/dolt/cluster-mysql-replica.yaml
new file mode 100644
index 000000000..f54a79fa4
--- /dev/null
+++ b/examples/dolt/cluster-mysql-replica.yaml
@@ -0,0 +1,102 @@
+---
+# Step 1: Create a standalone MySQL cluster as the replication source.
+# Ensure GTID mode is enabled (default in MySQL 8.0+).
+apiVersion: apps.kubeblocks.io/v1
+kind: Cluster
+metadata:
+ name: mysql-source
+ namespace: demo
+spec:
+ terminationPolicy: Delete
+ clusterDef: mysql
+ topology: semisync
+ componentSpecs:
+ - name: mysql
+ serviceVersion: "8.0.35"
+ disableExporter: true
+ replicas: 1
+ resources:
+ limits:
+ cpu: "0.5"
+ memory: 0.5Gi
+ requests:
+ cpu: "0.5"
+ memory: 0.5Gi
+ volumeClaimTemplates:
+ - name: data
+ spec:
+ storageClassName: ""
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 20Gi
+---
+# Step 2: Set BINLOG_FORMAT to ROW when cluster is running
+apiVersion: operations.kubeblocks.io/v1alpha1
+kind: OpsRequest
+metadata:
+ name: mysql-reconfiguring
+ namespace: demo
+spec:
+ # Specifies the name of the Cluster resource that this operation is targeting.
+ clusterName: mysql-source
+ force: false
+ reconfigures:
+ - componentName: mysql
+ parameters:
+ # Represents the name of the parameter that is to be updated.
+ - key: binlog_format
+ value: 'ROW'
+ # wait up to 2 min till cluster is running
+ preConditionDeadlineSeconds: 120
+ type: Reconfiguring
+---
+# Step 3: When MySQL is running, create a standalone Dolt cluster configured as a MySQL binlog replica.
+# The serviceRef "mysql-source" binds to the MySQL cluster above, injecting
+# MYSQL_SOURCE_HOST, MYSQL_SOURCE_PORT, MYSQL_SOURCE_USER, MYSQL_SOURCE_PASSWORD
+# into the Dolt container. The start script detects these and runs:
+# SET @@PERSIST.server_id = 2;
+# CHANGE REPLICATION SOURCE TO SOURCE_HOST=..., SOURCE_USER=..., SOURCE_PORT=...;
+# START REPLICA;
+apiVersion: apps.kubeblocks.io/v1
+kind: Cluster
+metadata:
+ name: dolt-mysql-replica
+ namespace: demo
+spec:
+ terminationPolicy: Delete
+ clusterDef: dolt
+ topology: standalone
+ componentSpecs:
+ - name: dolt
+ serviceVersion: "1.84.0"
+ disableExporter: true
+ replicas: 1
+ resources:
+ limits:
+ cpu: "0.5"
+ memory: 0.5Gi
+ requests:
+ cpu: "0.5"
+ memory: 0.5Gi
+ volumeClaimTemplates:
+ - name: data
+ spec:
+ storageClassName: ""
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 20Gi
+ serviceRefs:
+ - name: mysql-source
+ namespace: demo
+ clusterServiceSelector:
+ cluster: mysql-source
+ credential:
+ name: root
+ component: mysql
+ service:
+ service: ""
+ component: mysql
diff --git a/examples/dolt/cluster-replication.yaml b/examples/dolt/cluster-replication.yaml
new file mode 100644
index 000000000..e3f6340f8
--- /dev/null
+++ b/examples/dolt/cluster-replication.yaml
@@ -0,0 +1,32 @@
+apiVersion: apps.kubeblocks.io/v1
+kind: Cluster
+metadata:
+ name: dolt-repl
+ namespace: demo
+spec:
+ # Valid options are: [DoNotTerminate, Delete, WipeOut]
+ terminationPolicy: Delete
+ clusterDef: dolt
+ topology: replication
+ componentSpecs:
+ - name: dolt
+ # Must match a release in ComponentVersion `dolt` (e.g. replica-1.84.0).
+ serviceVersion: "1.84.0"
+ disableExporter: false
+ replicas: 2
+ resources:
+ limits:
+ cpu: "0.5"
+ memory: 0.5Gi
+ requests:
+ cpu: "0.5"
+ memory: 0.5Gi
+ volumeClaimTemplates:
+ - name: data
+ spec:
+ storageClassName: ""
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 20Gi
diff --git a/examples/dolt/cluster-standalone.yaml b/examples/dolt/cluster-standalone.yaml
new file mode 100644
index 000000000..25eddae35
--- /dev/null
+++ b/examples/dolt/cluster-standalone.yaml
@@ -0,0 +1,30 @@
+apiVersion: apps.kubeblocks.io/v1
+kind: Cluster
+metadata:
+ name: dolt-standalone
+ namespace: demo
+spec:
+ terminationPolicy: Delete
+ clusterDef: dolt
+ topology: standalone
+ componentSpecs:
+ - name: dolt
+ serviceVersion: "1.84.0"
+ disableExporter: false
+ replicas: 1
+ resources:
+ limits:
+ cpu: "0.5"
+ memory: 0.5Gi
+ requests:
+ cpu: "0.5"
+ memory: 0.5Gi
+ volumeClaimTemplates:
+ - name: data
+ spec:
+ storageClassName: ""
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: 20Gi
diff --git a/examples/dolt/podMonitor.yaml b/examples/dolt/podMonitor.yaml
new file mode 100644
index 000000000..82159ffc3
--- /dev/null
+++ b/examples/dolt/podMonitor.yaml
@@ -0,0 +1,25 @@
+apiVersion: monitoring.coreos.com/v1
+kind: PodMonitor
+metadata:
+ labels:
+ release: prometheus
+ name: dolt-metrics
+ namespace: demo
+spec:
+ jobLabel: app.kubernetes.io/managed-by
+ namespaceSelector:
+ matchNames:
+ - demo
+ podMetricsEndpoints:
+ - path: /metrics
+ port: metrics
+ scheme: http
+ podTargetLabels:
+ - app.kubernetes.io/instance
+ - app.kubernetes.io/managed-by
+ - apps.kubeblocks.io/component-name
+ - apps.kubeblocks.io/pod-name
+ selector:
+ matchLabels:
+ app.kubernetes.io/managed-by: kubeblocks
+ apps.kubeblocks.io/component-name: dolt
\ No newline at end of file
diff --git a/examples/dolt/switchover-specified-instance.yaml b/examples/dolt/switchover-specified-instance.yaml
new file mode 100644
index 000000000..e2e606404
--- /dev/null
+++ b/examples/dolt/switchover-specified-instance.yaml
@@ -0,0 +1,15 @@
+apiVersion: operations.kubeblocks.io/v1alpha1
+kind: OpsRequest
+metadata:
+ name: dolt-switchover
+ namespace: demo
+spec:
+ clusterName: dolt-repl
+ type: Switchover
+ switchover:
+ - componentName: dolt
+ # Instance whose role is transferred (typically the current primary).
+ instanceName: dolt-repl-dolt-0
+ # Optional: explicitly choose which pod becomes the new primary.
+ # The name must match a pod in the `dolt` component.
+ candidateName: dolt-repl-dolt-1